diff --git a/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java b/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java index 38b89230725..2f9d35332ed 100644 --- a/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java +++ b/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java @@ -114,12 +114,12 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg int cardWidth = getCardWidth(); int cardHeight = getCardHeight(); int cardTopHeight = CardRenderer.getCardTopHeight(cardWidth); - int dx = x % (cardWidth + GRID_PADDING); - int col = x / (cardWidth + GRID_PADDING); + int dx = x % (cardWidth + getGridPadding()); + int col = x / (cardWidth + getGridPadding()); int gridWidth = cardGrid.isEmpty() ? 0 : cardGrid.get(0).size(); int countLabelHeight = getCountLabelHeight(); - if (dx < GRID_PADDING && col < gridWidth) { + if (dx < getGridPadding() && col < gridWidth) { // Which row to add to? int curY = countLabelHeight; int rowIndex = 0; @@ -142,7 +142,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg // Insert between two columns insertArrow.setIcon(INSERT_COL_ICON); insertArrow.setSize(64, 64); - insertArrow.setLocation((cardWidth + GRID_PADDING) * col + GRID_PADDING / 2 - 32, curY); + insertArrow.setLocation((cardWidth + getGridPadding()) * col + getGridPadding() / 2 - 32, curY); } else { // Clamp to a new col one after the current last one col = Math.min(col, gridWidth); @@ -184,7 +184,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg // Position arrow insertArrow.setIcon(INSERT_ROW_ICON); insertArrow.setSize(64, 32); - insertArrow.setLocation((cardWidth + GRID_PADDING) * col + GRID_PADDING + cardWidth / 2 - 32, curY + stackInsertIndex * cardTopHeight - 32); + insertArrow.setLocation((cardWidth + getGridPadding()) * col + getGridPadding() + cardWidth / 2 - 32, curY + stackInsertIndex * cardTopHeight - 32); } } @@ -225,12 +225,12 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg int cardWidth = getCardWidth(); int cardHeight = getCardHeight(); int cardTopHeight = CardRenderer.getCardTopHeight(cardWidth); - int dx = x % (cardWidth + GRID_PADDING); - int col = x / (cardWidth + GRID_PADDING); + int dx = x % (cardWidth + getGridPadding()); + int col = x / (cardWidth + getGridPadding()); int gridWidth = cardGrid.isEmpty() ? 0 : cardGrid.get(0).size(); int countLabelHeight = getCountLabelHeight(); - if (dx < GRID_PADDING && col < gridWidth) { + if (dx < getGridPadding() && col < gridWidth) { // Which row to add to? int curY = countLabelHeight; int rowIndex = 0; @@ -334,7 +334,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg // Add new cards to grid for (CardView card : cards) { card.setSelected(true); - addCardView(card, false); + addCardView(card, null); eventSource.fireEvent(card, ClientEventType.DECK_ADD_SPECIFIC_CARD); } layoutGrid(); @@ -575,7 +575,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg // Constants private static final int DEFAULT_COUNT_LABEL_HEIGHT = 40; // can contain 1 or 2 lines - public static final int GRID_PADDING = 10; + public static final int GRID_PADDING = 20; private static final ImageIcon INSERT_ROW_ICON = new ImageIcon(DragCardGrid.class.getClassLoader().getResource("editor_insert_row.png")); private static final ImageIcon INSERT_COL_ICON = new ImageIcon(DragCardGrid.class.getClassLoader().getResource("editor_insert_col.png")); @@ -1044,7 +1044,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg selectBySearchPanelC.fill = GridBagConstraints.VERTICAL; searchByTextField = new JTextField(); - searchByTextField.setToolTipText("Searches for card names, types, rarity, casting cost and rules text. NB: Mana symbols are written like {W},{U},{C} etc"); + searchByTextField.setToolTipText("Search cards by any data like name or mana symbols like {W}, {U}, {C}, etc (use quotes for exact search)"); searchByTextField.addKeyListener(new KeyAdapter() { @Override public void keyReleased(KeyEvent e) { @@ -1253,10 +1253,10 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg selectionPanel.setSize(x2 - x1, y2 - y1); // First and last cols - int col1 = x1 / (cardWidth + GRID_PADDING); - int col2 = x2 / (cardWidth + GRID_PADDING); - int offsetIntoCol2 = x2 % (cardWidth + GRID_PADDING); - if (offsetIntoCol2 < GRID_PADDING) { + int col1 = x1 / (cardWidth + getGridPadding()); + int col2 = x2 / (cardWidth + getGridPadding()); + int offsetIntoCol2 = x2 % (cardWidth + getGridPadding()); + if (offsetIntoCol2 < getGridPadding()) { --col2; } @@ -1322,7 +1322,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg // re-insert for (CardView card : allCards) { - sortIntoGrid(card); + sortIntoGrid(card, null); } trimGrid(); @@ -1717,7 +1717,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg if (acard.getName().equals(card.getName())) { CardView pimpedCard = new CardView(acard); - addCardView(pimpedCard, false); + addCardView(pimpedCard, null); eventSource.fireEvent(pimpedCard, ClientEventType.DECK_ADD_SPECIFIC_CARD); pimpedCards.put(pimpedCard, 1); didModify = true; @@ -1729,7 +1729,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg if (didModify) { for (CardView c : pimpedCards.keySet()) { - sortIntoGrid(c); + sortIntoGrid(c, null); } trimGrid(); @@ -1762,7 +1762,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg CardView oldestCardView = new CardView(oldestCardInfo.createMockCard()); this.removeCardView(card); eventSource.fireEvent(card, ClientEventType.DECK_REMOVE_SPECIFIC_CARD); - this.addCardView(oldestCardView, false); + this.addCardView(oldestCardView, null); eventSource.fireEvent(oldestCardView, ClientEventType.DECK_ADD_SPECIFIC_CARD); newStack.add(oldestCardView); } else { @@ -1814,10 +1814,10 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg for (CardView newCard : cardsView.values()) { if (!cardViews.containsKey(newCard.getId())) { // Is a new card - addCardView(newCard, false); + addCardView(newCard, null); // Put it into the appropirate place in the grid given the current sort - sortIntoGrid(newCard); + sortIntoGrid(newCard, null); // Mark didModify = true; @@ -1837,7 +1837,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg for (CardView newCard : cardsView.values()) { if (!cardViews.containsKey(newCard.getId())) { // Add the new card - addCardView(newCard, false); + addCardView(newCard, null); // Add the new card to tracking Map> forSetCode; @@ -1889,7 +1889,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg for (List orphans : tracked.values()) { for (CardView orphan : orphans) { logger.info("Orphan when setting with layout: "); - sortIntoGrid(orphan); + sortIntoGrid(orphan, null); } } } @@ -1984,7 +1984,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg menu.show(e.getComponent(), e.getX(), e.getY()); } - public void addCardView(final CardView card, boolean duplicated) { + public void addCardView(final CardView card, final CardView duplicatedFromCard) { allCards.add(card); // Update counts @@ -2019,8 +2019,8 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg cardContent.add(cardPanel); cardViews.put(card.getId(), cardPanel); - if (duplicated) { - sortIntoGrid(card); + if (duplicatedFromCard != null) { + sortIntoGrid(card, duplicatedFromCard); eventSource.fireEvent(card, ClientEventType.DECK_ADD_SPECIFIC_CARD); // clear grid from empty rows @@ -2094,7 +2094,21 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg * * @param newCard Card to add to the cardGrid array. */ - private void sortIntoGrid(CardView newCard) { + private void sortIntoGrid(CardView newCard, CardView duplicatedFromCard) { + // fast put duplicated card to the same place as original + if (duplicatedFromCard != null) { + for (List> gridRow : cardGrid) { + for (List gridStack : gridRow) { + for (int i = 0; i < gridStack.size(); i++) { + if (gridStack.get(i).equals(duplicatedFromCard)) { + gridStack.add(i, newCard); + return; + } + } + } + } + } + // row 1 must exists if (cardGrid.isEmpty()) { cardGrid.add(0, new ArrayList<>()); @@ -2336,7 +2350,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg } else { String description = cardSort.getComparator().getCategoryName(stack.get(0)); DragCardGrid.updateCountLabel(countLabel, stack.size(), description); - countLabel.setLocation(GRID_PADDING + (cardWidth + GRID_PADDING) * colIndex, currentY - countLabelHeight); + countLabel.setLocation(getGridPadding() + (cardWidth + getGridPadding()) * colIndex, currentY - countLabelHeight); countLabel.setSize(cardWidth, countLabelHeight); countLabel.setVisible(true); } @@ -2348,7 +2362,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg for (int i = 0; i < stack.size(); ++i) { CardView card = stack.get(i); MageCard view = cardViews.get(card.getId()); - int x = GRID_PADDING + (cardWidth + GRID_PADDING) * colIndex; + int x = getGridPadding() + (cardWidth + getGridPadding()) * colIndex; int y = currentY + i * cardTopHeight; view.setCardBounds(x, y, cardWidth, cardHeight); cardContent.setLayer(view, layerIndex++); @@ -2356,14 +2370,18 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg } // Update the max stack size for this row and the max width - maxWidth = Math.max(maxWidth, GRID_PADDING + (GRID_PADDING + cardWidth) * gridRow.size()); + maxWidth = Math.max(maxWidth, getGridPadding() + (getGridPadding() + cardWidth) * gridRow.size()); maxStackSize.set(rowIndex, rowMaxStackSize); currentY += (cardTopHeight * (rowMaxStackSize - 1) + cardHeight) + countLabelHeight; } // Resize card container - cardContent.setPreferredSize(new Dimension(maxWidth, currentY - countLabelHeight + GRID_PADDING)); - //cardContent.setSize(maxWidth, currentY - COUNT_LABEL_HEIGHT + GRID_PADDING); + cardContent.setPreferredSize(new Dimension(maxWidth, currentY - countLabelHeight + getGridPadding())); + //cardContent.setSize(maxWidth, currentY - COUNT_LABEL_HEIGHT + getGridPadding()); + } + + private int getGridPadding() { + return Math.max(GRID_PADDING, Math.round(cardSizeMod * GRID_PADDING * GUISizeHelper.dialogGuiScale)); } public static int getCountLabelHeight() { diff --git a/Mage.Client/src/main/java/mage/client/components/BracketLegalityLabel.java b/Mage.Client/src/main/java/mage/client/components/BracketLegalityLabel.java new file mode 100644 index 00000000000..c45fb575270 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/components/BracketLegalityLabel.java @@ -0,0 +1,460 @@ +package mage.client.components; + +import mage.MageObject; +import mage.cards.Card; +import mage.cards.decks.Deck; +import mage.client.util.GUISizeHelper; +import org.apache.log4j.Logger; + +import java.awt.*; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.List; +import java.util.*; +import java.util.stream.Stream; + +/** + * Inject bracket level inside validation panel + * See more details at wiki + *

+ * Support: + * - [x] game changers + * - [x] infinite combos + * - [x] mass land destruction + * - [x] extra turns + * - [x] tutors + * Features: + * - [x] find possible bracket level of the deck + * - [x] find affected cards by checking group + * - [x] can auto-generate infinite combos list, see verify test downloadAndPrepareCommanderBracketsData + * - [ ] TODO: tests + * - [ ] TODO: table - players brackets level disclose settings + * - [ ] TODO: generate - convert card name to xmage format and assert on bad names (ascii only) + * + * @author JayDi85 + */ +public class BracketLegalityLabel extends LegalityLabel { + + private static final Logger logger = Logger.getLogger(BracketLegalityLabel.class); + + private static final String GROUP_GAME_CHANGES = "Game Changers"; + private static final String GROUP_INFINITE_COMBOS = "Infinite Combos"; + private static final String GROUP_MASS_LAND_DESTRUCTION = "Mass Land Destruction"; + private static final String GROUP_EXTRA_TURN = "Extra Turns"; + private static final String GROUP_TUTORS = "Tutors"; + + private static final Map> MAX_GROUP_LIMITS = new LinkedHashMap<>(); + + static { + // 1 + // No cards from the Game Changer list. + // No intentional two-card infinite combos. + // No mass land destruction. + // No extra turn cards. + // Tutors should be sparse. + // 2 + // No cards from the Game Changer list. + // No intentional two-card infinite combos. + // No mass land destruction. + // Extra turn cards should only appear in low quantities and should not be chained in succession or looped. + // Tutors should be sparse. + // 3 + // Up to three (3) cards from the Game Changer list. + // No intentional early game two-card infinite combos. + // No mass land destruction. + // Extra turn cards should only appear in low quantities and should not be chained in succession or looped. + // 4 + // 5 + // allow any cards + + // cards limits per brackets level, it's ok to use 99 as max + // group - levels 0, 1, 2, 3, 4, 5 + MAX_GROUP_LIMITS.put(GROUP_GAME_CHANGES, + Arrays.asList(0, 0, 0, 3, 99, 99)); + MAX_GROUP_LIMITS.put(GROUP_INFINITE_COMBOS, + Arrays.asList(0, 0, 0, 0, 99, 99)); + MAX_GROUP_LIMITS.put(GROUP_MASS_LAND_DESTRUCTION, + Arrays.asList(0, 0, 0, 0, 99, 99)); + MAX_GROUP_LIMITS.put(GROUP_EXTRA_TURN, + Arrays.asList(0, 0, 0, 3, 99, 99)); + MAX_GROUP_LIMITS.put(GROUP_TUTORS, + Arrays.asList(0, 3, 3, 99, 99, 99)); + } + + private static final String RESOURCE_INFINITE_COMBOS = "brackets/infinite-combos.txt"; + + private final String fullName; + private final String shortName; + private final int maxLevel; + + private final List foundGameChangers = new ArrayList<>(); + private final List foundInfiniteCombos = new ArrayList<>(); + private final List foundMassLandDestruction = new ArrayList<>(); + private final List foundExtraTurn = new ArrayList<>(); + private final List foundTutors = new ArrayList<>(); + + private final List badCards = new ArrayList<>(); + private final List fullGameChanges = new ArrayList<>(); + private final Set fullInfiniteCombos = new HashSet<>(); // card1@card2, sorted by names, name must be xmage compatible + + public BracketLegalityLabel(String fullName, String shortName, int maxLevel) { + super(shortName, null); + this.fullName = fullName; + this.shortName = shortName; + this.maxLevel = maxLevel; + setPreferredSize(DIM_PREFERRED_1_OF_5); + } + + @Override + public List selectCards() { + return new ArrayList<>(this.badCards); + } + + private void validateBracketLevel() { + this.badCards.clear(); + + if (this.foundGameChangers.size() > getMaxCardsLimit(GROUP_GAME_CHANGES)) { + this.badCards.addAll(this.foundGameChangers); + } + if (this.foundInfiniteCombos.size() > getMaxCardsLimit(GROUP_INFINITE_COMBOS)) { + this.badCards.addAll(this.foundInfiniteCombos); + } + if (this.foundMassLandDestruction.size() > getMaxCardsLimit(GROUP_MASS_LAND_DESTRUCTION)) { + this.badCards.addAll(this.foundMassLandDestruction); + } + if (this.foundExtraTurn.size() > getMaxCardsLimit(GROUP_EXTRA_TURN)) { + this.badCards.addAll(this.foundExtraTurn); + } + if (this.foundTutors.size() > getMaxCardsLimit(GROUP_TUTORS)) { + this.badCards.addAll(this.foundTutors); + } + } + + private Integer getMaxCardsLimit(String groupName) { + return MAX_GROUP_LIMITS.get(groupName).get(this.maxLevel); + } + + @Override + public void validateDeck(Deck deck) { + collectAll(deck); + validateBracketLevel(); + + int infoFontHeaderSize = Math.round(GUISizeHelper.cardTooltipFont.getSize() * 1.0f); + int infoFontTextSize = Math.round(GUISizeHelper.cardTooltipFont.getSize() * 0.6f); + + // show all found cards in any use cases + Color showColor = this.badCards.isEmpty() ? COLOR_LEGAL : COLOR_NOT_LEGAL; + + List showInfo = new ArrayList<>(); + if (this.badCards.isEmpty()) { + showInfo.add(String.format("

Deck is GOOD for %s

", + infoFontHeaderSize, + this.fullName + )); + } else { + showInfo.add(String.format("

Deck is BAD for %s

", + infoFontHeaderSize, + this.fullName + )); + showInfo.add("

(click here to select all bad cards)

"); + } + + Map> groups = new LinkedHashMap<>(); + groups.put(GROUP_GAME_CHANGES + getStats(GROUP_GAME_CHANGES), this.foundGameChangers); + groups.put(GROUP_INFINITE_COMBOS + getStats(GROUP_INFINITE_COMBOS), this.foundInfiniteCombos); + groups.put(GROUP_MASS_LAND_DESTRUCTION + getStats(GROUP_MASS_LAND_DESTRUCTION), this.foundMassLandDestruction); + groups.put(GROUP_EXTRA_TURN + getStats(GROUP_EXTRA_TURN), this.foundExtraTurn); + groups.put(GROUP_TUTORS + getStats(GROUP_TUTORS), this.foundTutors); + groups.forEach((group, cards) -> { + showInfo.add("
"); + showInfo.add("" + group + ""); + if (cards.isEmpty()) { + showInfo.add("
    "); + showInfo.add("
  • no cards
  • "); + showInfo.add("
"); + } else { + showInfo.add("
    "); + cards.forEach(s -> showInfo.add(String.format("
  • %s
  • ", s))); + showInfo.add("
"); + } + }); + + String showText = "" + String.join("\n", showInfo) + ""; + showState(showColor, showText, false); + } + + private String getStats(String groupName) { + int currentAmount = 0; + switch (groupName) { + case GROUP_GAME_CHANGES: + currentAmount = this.foundGameChangers.size(); + break; + case GROUP_INFINITE_COMBOS: + currentAmount = this.foundInfiniteCombos.size(); + break; + case GROUP_MASS_LAND_DESTRUCTION: + currentAmount = this.foundMassLandDestruction.size(); + break; + case GROUP_EXTRA_TURN: + currentAmount = this.foundExtraTurn.size(); + break; + case GROUP_TUTORS: + currentAmount = this.foundTutors.size(); + break; + default: + throw new IllegalArgumentException("Unknown group " + groupName); + } + int maxAmount = MAX_GROUP_LIMITS.get(groupName).get(this.maxLevel); + + String info; + if (currentAmount > maxAmount) { + info = " (%s of %s)"; + } else { + info = " (%s of %s)"; + } + + return String.format(info, currentAmount, maxAmount == 99 ? "any" : maxAmount); + } + + private void collectAll(Deck deck) { + collectGameChangers(deck); + collectInfiniteCombos(deck); + collectMassLandDestruction(deck); + collectExtraTurn(deck); + collectTutors(deck); + } + + private void collectGameChangers(Deck deck) { + this.foundGameChangers.clear(); + + if (fullGameChanges.isEmpty()) { + // https://mtg.wiki/page/Game_Changers + // TODO: share list with AbstractCommander and edh power level + fullGameChanges.addAll(Arrays.asList( + "Ad Nauseam", + "Ancient Tomb", + "Aura Shards", + "Bolas's Citadel", + "Braids, Cabal Minion", + "Demonic Tutor", + "Drannith Magistrate", + "Chrome Mox", + "Coalition Victory", + "Consecrated Sphinx", + "Crop Rotation", + "Cyclonic Rift", + "Deflecting Swat", + "Enlightened Tutor", + "Expropriate", + "Field of the Dead", + "Fierce Guardianship", + "Food Chain", + "Force of Will", + "Gaea's Cradle", + "Gamble", + "Gifts Ungiven", + "Glacial Chasm", + "Grand Arbiter Augustin IV", + "Grim Monolith", + "Humility", + "Imperial Seal", + "Intuition", + "Jeska's Will", + "Jin-Gitaxias, Core Augur", + "Kinnan, Bonder Prodigy", + "Lion's Eye Diamond", + "Mana Vault", + "Mishra's Workshop", + "Mox Diamond", + "Mystical Tutor", + "Narset, Parter of Veils", + "Natural Order", + "Necropotence", + "Notion Thief", + "Rhystic Study", + "Opposition Agent", + "Orcish Bowmasters", + "Panoptic Mirror", + "Seedborn Muse", + "Serra's Sanctum", + "Smothering Tithe", + "Survival of the Fittest", + "Sway of the Stars", + "Teferi's Protection", + "Tergrid, God of Fright", + "Thassa's Oracle", + "The One Ring", + "The Tabernacle at Pendrell Vale", + "Underworld Breach", + "Urza, Lord High Artificer", + "Vampiric Tutor", + "Vorinclex, Voice of Hunger", + "Yuriko, the Tiger's Shadow", + "Winota, Joiner of Forces", + "Worldly Tutor" + )); + } + + Stream.concat(deck.getCards().stream(), deck.getSideboard().stream()) + .map(MageObject::getName) + .filter(fullGameChanges::contains) + .sorted() + .forEach(this.foundGameChangers::add); + } + + private void collectInfiniteCombos(Deck deck) { + this.foundInfiniteCombos.clear(); + + if (this.fullInfiniteCombos.isEmpty()) { + InputStream in = BracketLegalityLabel.class.getClassLoader().getResourceAsStream(RESOURCE_INFINITE_COMBOS); + if (in == null) { + throw new RuntimeException("Commander brackets: can't load infinite combos list"); + } + try (InputStreamReader input = new InputStreamReader(in); + BufferedReader reader = new BufferedReader(input)) { + String line = reader.readLine(); + while (line != null) { + try { + line = line.trim(); + if (line.startsWith("#")) { + continue; + } + List cards = Arrays.asList(line.split("@")); + if (cards.size() != 2) { + logger.warn("wrong line format in commander brackets file: " + line); + continue; + } + + Collections.sort(cards); + this.fullInfiniteCombos.add(String.join("@", cards)); + } finally { + line = reader.readLine(); + } + } + } catch (Exception e) { + throw new RuntimeException("Tokens brackets: can't load infinite combos list - " + e); + } + } + + // search and check all x2 combinations + List deckCards = new ArrayList<>(); + Set foundCards = new HashSet<>(); + deckCards.addAll(deck.getCards()); + deckCards.addAll(deck.getSideboard()); + for (Card card1 : deckCards) { + for (Card card2 : deckCards) { + if (card1 == card2) { + continue; + } + List names = Arrays.asList(card1.getName(), card2.getName()); + Collections.sort(names); + String deckCombo = String.join("@", names); + if (this.fullInfiniteCombos.contains(deckCombo)) { + foundCards.add(card1); + foundCards.add(card2); + break; + } + } + } + + foundCards.stream() + .map(MageObject::getName) + .sorted() + .forEach(this.foundInfiniteCombos::add); + } + + private void collectMassLandDestruction(Deck deck) { + // https://mtg.wiki/page/Land_destruction + // https://draftsim.com/mtg-mass-land-destruction/ + this.foundMassLandDestruction.clear(); + Stream.concat(deck.getCards().stream(), deck.getSideboard().stream()) + .filter(card -> card.getRules().stream() + .map(s -> s.toLowerCase(Locale.ENGLISH)) + .anyMatch(s -> (s.contains("destroy") || s.contains("sacrifice")) + && (s.contains("all") || s.contains("x target") || s.contains("{x} target")) + && isTextContainsLandName(s) + ) + ) + .map(Card::getName) + .sorted() + .forEach(this.foundMassLandDestruction::add); + } + + private void collectExtraTurn(Deck deck) { + this.foundExtraTurn.clear(); + Stream.concat(deck.getCards().stream(), deck.getSideboard().stream()) + .filter(card -> card.getRules().stream() + .map(s -> s.toLowerCase(Locale.ENGLISH)) + .anyMatch(s -> s.contains("extra turn")) + ) + .map(Card::getName) + .sorted() + .forEach(this.foundExtraTurn::add); + } + + private void collectTutors(Deck deck) { + // edh power level uses search for land and non-land card, but bracket need only non-land cards searching + this.foundTutors.clear(); + Stream.concat(deck.getCards().stream(), deck.getSideboard().stream()) + .filter(card -> card.getRules().stream() + .map(s -> s.toLowerCase(Locale.ENGLISH)) + .anyMatch(s -> s.contains("search your library") && !isTextContainsLandCard(s)) + ) + .map(Card::getName) + .sorted() + .forEach(this.foundTutors::add); + } + + private boolean isTextContainsLandCard(String lowerText) { + // TODO: share code with AbstractCommander and edh power level + // TODO: add tests + return lowerText.contains("basic ") + || lowerText.contains("plains card") + || lowerText.contains("island card") + || lowerText.contains("swamp card") + || lowerText.contains("mountain card") + || lowerText.contains("forest card"); + } + + private boolean isTextContainsLandName(String lowerText) { + // TODO: add tests to find all cards from https://mtg.wiki/page/Land_destruction + // TODO: add tests + /* +// mass land destruction +Ajani Vengeant +Armageddon +Avalanche +Bend or Break +Boil +Boiling Seas +Boom // Bust +Burning of Xinye +Catastrophe +Decree of Annihilation +Desolation Angel +Devastation +Fall of the Thran +From the Ashes +Impending Disaster +Jokulhaups +Myojin of Infinite Rage +Numot, the Devastator +Obliterate +Orcish Settlers +Ravages of War +Ruination +Rumbling Crescendo +Scorched Earth +Tsunami +Wake of Destruction +Wildfire + */ + return lowerText.contains("lands") + || lowerText.contains("plains") + || lowerText.contains("island") + || lowerText.contains("swamp") + || lowerText.contains("mountain") + || lowerText.contains("forest"); + } +} \ No newline at end of file diff --git a/Mage.Client/src/main/java/mage/client/components/EdhPowerLevelLegalityLabel.java b/Mage.Client/src/main/java/mage/client/components/EdhPowerLevelLegalityLabel.java new file mode 100644 index 00000000000..0dac9d88697 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/components/EdhPowerLevelLegalityLabel.java @@ -0,0 +1,75 @@ +package mage.client.components; + +import mage.cards.decks.Deck; +import mage.client.util.GUISizeHelper; +import mage.deck.Commander; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Inject power level info inside validation panel + * + * @author JayDi85 + */ +public class EdhPowerLevelLegalityLabel extends LegalityLabel { + + private final Commander commanderDeckType = new Commander(); + private final List foundPowerCards = new ArrayList<>(); + + public EdhPowerLevelLegalityLabel() { + super("EDH Power Level: ?", null); + setPreferredSize(DIM_PREFERRED_3_OF_3); + } + + @Override + public List selectCards() { + // choose cards with power level + return this.foundPowerCards; + } + + @Override + public void validateDeck(Deck deck) { + // find and save power level and card hints + + List foundInfo = new ArrayList<>(); + int level = this.commanderDeckType.getEdhPowerLevel(deck, foundPowerCards, foundInfo); + this.setText(String.format("EDH Power Level: %d", level)); + + // sort by score "+5 from xxx" + Pattern pattern = Pattern.compile("\\+(\\d+)"); + foundInfo.sort((o1, o2) -> { + Matcher matcher = pattern.matcher(o1); + int score1 = matcher.find() ? Integer.parseInt(matcher.group(1)) : 0; + matcher = pattern.matcher(o2); + int score2 = matcher.find() ? Integer.parseInt(matcher.group(1)) : 0; + if (score1 != score2) { + return Integer.compare(score2, score1); + } + return o1.compareTo(o2); + }); + + showStateInfo(formatCardsInfoTooltip(level, foundInfo)); + } + + private String formatCardsInfoTooltip(int level, List foundInfo) { + // use 60% font for better and compatible list + int infoFontSize = Math.round(GUISizeHelper.cardTooltipFont.getSize() * 0.6f); + int maxLimit = 25; + String extraInfo = this.foundPowerCards.size() <= maxLimit ? "" : String.format("
  • and %d more cards
  • ", this.foundPowerCards.size() - maxLimit); + return foundInfo.stream() + .limit(maxLimit) + .reduce("" + + "

    EDH Power Level: " + level + "

    " + + "
    " + + "Found " + this.foundPowerCards.size() + " cards with power levels (click to select it)" + + "
    " + + "
      ", + (str, info) -> str + String.format("
    • %s
    • ", info), String::concat) + + extraInfo + + "
    " + + ""; + } +} diff --git a/Mage.Client/src/main/java/mage/client/components/LegalityLabel.java b/Mage.Client/src/main/java/mage/client/components/LegalityLabel.java index aed4e61bf66..6f02be7bebe 100644 --- a/Mage.Client/src/main/java/mage/client/components/LegalityLabel.java +++ b/Mage.Client/src/main/java/mage/client/components/LegalityLabel.java @@ -3,17 +3,18 @@ package mage.client.components; import mage.cards.decks.Deck; import mage.cards.decks.DeckValidator; import mage.cards.decks.DeckValidatorError; -import mage.cards.decks.importer.DeckImporter; import org.unbescape.html.HtmlEscape; import org.unbescape.html.HtmlEscapeLevel; import org.unbescape.html.HtmlEscapeType; import javax.swing.*; import java.awt.*; -import java.io.File; +import java.util.Collections; +import java.util.Objects; +import java.util.stream.Collectors; /** - * @author Elandril + * @author Elandril, JayDi85 */ public class LegalityLabel extends JLabel { @@ -24,9 +25,12 @@ public class LegalityLabel extends JLabel { protected static final Color COLOR_TEXT = new Color(255, 255, 255); protected static final Dimension DIM_MINIMUM = new Dimension(75, 25); protected static final Dimension DIM_MAXIMUM = new Dimension(150, 75); - protected static final Dimension DIM_PREFERRED = new Dimension(75, 25); + protected static final Dimension DIM_PREFERRED_1_OF_3 = new Dimension(75, 25); + protected static final Dimension DIM_PREFERRED_2_OF_3 = new Dimension(DIM_PREFERRED_1_OF_3.width * 2 + 5, 25); + protected static final Dimension DIM_PREFERRED_3_OF_3 = new Dimension(DIM_PREFERRED_1_OF_3.width * 3 + 5 * 2, 25); + protected static final Dimension DIM_PREFERRED_1_OF_5 = new Dimension((DIM_PREFERRED_3_OF_3.width - 5 * 4) / 5, 25); - protected static final int TOOLTIP_TABLE_WIDTH = 300; // size of the label's tooltip + protected static final int TOOLTIP_TABLE_WIDTH = 400; // size of the label's tooltip protected static final int TOOLTIP_MAX_ERRORS = 20; // max errors to show in tooltip protected Deck currentDeck; @@ -51,7 +55,7 @@ public class LegalityLabel extends JLabel { setMaximumSize(DIM_MAXIMUM); setName(text); // NOI18N setOpaque(true); - setPreferredSize(DIM_PREFERRED); + setPreferredSize(DIM_PREFERRED_1_OF_3); } /** @@ -77,7 +81,7 @@ public class LegalityLabel extends JLabel { setMinimumSize(DIM_MINIMUM); setMaximumSize(DIM_MAXIMUM); setOpaque(true); - setPreferredSize(DIM_PREFERRED); + setPreferredSize(DIM_PREFERRED_1_OF_3); } /** @@ -88,23 +92,10 @@ public class LegalityLabel extends JLabel { button.setHorizontalAlignment(SwingConstants.CENTER); button.setMinimumSize(DIM_MINIMUM); button.setMaximumSize(DIM_MAXIMUM); - button.setPreferredSize(DIM_PREFERRED); + button.setPreferredSize(DIM_PREFERRED_1_OF_3); return button; } - public String getErrorMessage() { - return errorMessage; - } - - public DeckValidator getValidator() { - return validator; - } - - public void setValidator(DeckValidator validator) { - this.validator = validator; - revalidateDeck(); - } - protected String escapeHtml(String string) { return HtmlEscape.escapeHtml(string, HtmlEscapeType.HTML4_NAMED_REFERENCES_DEFAULT_TO_HEXA, HtmlEscapeLevel.LEVEL_0_ONLY_MARKUP_SIGNIFICANT_EXCEPT_APOS); } @@ -146,25 +137,33 @@ public class LegalityLabel extends JLabel { setBackground(color); } - public void showState(Color color, String tooltip) { + public void showState(Color color, String tooltip, boolean useErrors) { setBackground(color); - setToolTipText(appendErrorMessage(tooltip)); + if (useErrors) { + setToolTipText(appendErrorMessage(tooltip)); + } else { + setToolTipText(tooltip); + } + } + + public void showStateInfo(String tooltip) { + showState(COLOR_LEGAL, tooltip, false); } public void showStateUnknown(String tooltip) { - showState(COLOR_UNKNOWN, tooltip); + showState(COLOR_UNKNOWN, tooltip, true); } public void showStateLegal(String tooltip) { - showState(COLOR_LEGAL, tooltip); + showState(COLOR_LEGAL, tooltip, true); } public void showStatePartlyLegal(String tooltip) { - showState(COLOR_PARTLY_LEGAL, tooltip); + showState(COLOR_PARTLY_LEGAL, tooltip, true); } public void showStateNotLegal(String tooltip) { - showState(COLOR_NOT_LEGAL, tooltip); + showState(COLOR_NOT_LEGAL, tooltip, true); } public void validateDeck(Deck deck) { @@ -191,28 +190,13 @@ public class LegalityLabel extends JLabel { } } - public void validateDeck(File deckFile) { - deckFile = deckFile.getAbsoluteFile(); - if (!deckFile.exists()) { - errorMessage = String.format("Deck file '%s' does not exist.", deckFile.getAbsolutePath()); - showStateUnknown("No Deck loaded!"); - return; + public java.util.List selectCards() { + if (this.validator == null) { + return Collections.emptyList(); } - try { - StringBuilder errorMessages = new StringBuilder(); - Deck deck = Deck.load(DeckImporter.importDeckFromFile(deckFile.getAbsolutePath(), errorMessages, false), true, true); - errorMessage = errorMessages.toString(); - validateDeck(deck); - } catch (Exception ex) { - errorMessage = String.format("Error importing deck from file '%s'!", deckFile.getAbsolutePath()); - } - } - - public void revalidateDeck() { - validateDeck(currentDeck); - } - - public void validateDeck(String deckFile) { - validateDeck(new File(deckFile)); + return this.validator.getErrorsList().stream() + .map(DeckValidatorError::getCardName) + .filter(Objects::nonNull) + .collect(Collectors.toList()); } } diff --git a/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java b/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java index f1644cb1b86..b5b5e59857b 100644 --- a/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java +++ b/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java @@ -5,20 +5,21 @@ import mage.cards.decks.Deck; import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; import mage.cards.repository.ExpansionRepository; -import mage.client.dialog.PreferencesDialog; import mage.client.util.sets.ConstructedFormats; import mage.constants.CardType; import mage.constants.ColoredManaSymbol; import mage.constants.SuperType; import mage.util.RandomUtil; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; /** * Generates random card pool and builds a deck. * - * @author nantuko - * @author Simown + * @author nantuko, Simown, JayDi85 */ public final class DeckGenerator { @@ -124,7 +125,7 @@ public final class DeckGenerator { genPool = new DeckGeneratorPool(deckSize, genDialog.getCreaturePercentage(), genDialog.getNonCreaturePercentage(), genDialog.getLandPercentage(), allowedColors, genDialog.isSingleton(), genDialog.isColorless(), - genDialog.isAdvanced(), genDialog.getDeckGeneratorCMC()); + genDialog.isCommander(), genDialog.isAdvanced(), genDialog.getDeckGeneratorCMC()); final String[] sets = setsToUse.toArray(new String[setsToUse.size()]); @@ -155,8 +156,8 @@ public final class DeckGenerator { // Generate basic land cards Map> basicLands = DeckGeneratorPool.generateBasicLands(setsToUse); - DeckGeneratorPool.generateSpells(creatureCriteria, genPool.getCreatureCount()); - DeckGeneratorPool.generateSpells(nonCreatureCriteria, genPool.getNonCreatureCount()); + DeckGeneratorPool.generateSpells(creatureCriteria, genPool.getCreatureCount(), genPool.getCommandersCount()); + DeckGeneratorPool.generateSpells(nonCreatureCriteria, genPool.getNonCreatureCount(), 0); DeckGeneratorPool.generateLands(genDialog.useNonBasicLand(), nonBasicLandCriteria, basicLands); // Reconstructs the final deck and adjusts for Math rounding and/or missing cards diff --git a/Mage.Client/src/main/java/mage/client/deck/generator/DeckGeneratorCMC.java b/Mage.Client/src/main/java/mage/client/deck/generator/DeckGeneratorCMC.java index 7ed336c88e5..217a0d81bed 100644 --- a/Mage.Client/src/main/java/mage/client/deck/generator/DeckGeneratorCMC.java +++ b/Mage.Client/src/main/java/mage/client/deck/generator/DeckGeneratorCMC.java @@ -1,46 +1,76 @@ - package mage.client.deck.generator; import com.google.common.collect.ImmutableList; import java.util.List; +/** + * Mana value distribution between cards in diff deck sizes + */ public enum DeckGeneratorCMC { - Low(ImmutableList.builder() - .add(new CMC(0, 2, 0.60f)) - .add(new CMC(3, 4, 0.30f)) - .add(new CMC(5, 6, 0.10f)).build(), + Low( + // 100 + ImmutableList.builder() + .add(new CMC(0, 2, 0.55f)) + .add(new CMC(3, 4, 0.30f)) + .add(new CMC(5, 6, 0.15f)).build(), + // 60 + ImmutableList.builder() + .add(new CMC(0, 2, 0.60f)) + .add(new CMC(3, 4, 0.30f)) + .add(new CMC(5, 6, 0.10f)).build(), + // 40 ImmutableList.builder() .add(new CMC(0, 2, 0.65f)) .add(new CMC(3, 4, 0.30f)) .add(new CMC(5, 5, 0.05f)).build()), - Default(ImmutableList.builder() - .add(new CMC(0, 2, 0.20f)) - .add(new CMC(3, 5, 0.50f)) - .add(new CMC(6, 7, 0.25f)) - .add(new CMC(8, 100, 0.05f)).build(), + Default( + // 100 + ImmutableList.builder() + .add(new CMC(0, 2, 0.15f)) + .add(new CMC(3, 5, 0.50f)) + .add(new CMC(6, 7, 0.30f)) + .add(new CMC(8, 100, 0.05f)).build(), + // 60 + ImmutableList.builder() + .add(new CMC(0, 2, 0.20f)) + .add(new CMC(3, 5, 0.50f)) + .add(new CMC(6, 7, 0.25f)) + .add(new CMC(8, 100, 0.05f)).build(), + // 40 ImmutableList.builder() .add(new CMC(0, 2, 0.30f)) .add(new CMC(3, 4, 0.45f)) .add(new CMC(5, 6, 0.20f)) .add(new CMC(7, 100, 0.05f)).build()), - High(ImmutableList.builder(). - add(new CMC(0, 2, 0.05f)) - .add(new CMC(3, 5, 0.35f)) - .add(new CMC(6, 7, 0.40f)) - .add(new CMC(8, 100, 0.15f)).build(), + High( + // 100 + ImmutableList.builder(). + add(new CMC(0, 2, 0.05f)) + .add(new CMC(3, 5, 0.40f)) + .add(new CMC(6, 7, 0.40f)) + .add(new CMC(8, 100, 0.15f)).build(), + // 60 + ImmutableList.builder(). + add(new CMC(0, 2, 0.05f)) + .add(new CMC(3, 5, 0.35f)) + .add(new CMC(6, 7, 0.40f)) + .add(new CMC(8, 100, 0.15f)).build(), + // 40 ImmutableList.builder(). add(new CMC(0, 2, 0.10f)) .add(new CMC(3, 4, 0.30f)) .add(new CMC(5, 6, 0.45f)) .add(new CMC(7, 100, 0.15f)).build()); + private final List poolCMCs100; private final List poolCMCs60; private final List poolCMCs40; - DeckGeneratorCMC(List CMCs60, List CMCs40) { + DeckGeneratorCMC(List CMCs100, List CMCs60, List CMCs40) { + this.poolCMCs100 = CMCs100; this.poolCMCs60 = CMCs60; this.poolCMCs40 = CMCs40; } @@ -53,6 +83,10 @@ public enum DeckGeneratorCMC { return this.poolCMCs60; } + public List get100CardPoolCMC() { + return this.poolCMCs100; + } + static class CMC { public final int min; public final int max; diff --git a/Mage.Client/src/main/java/mage/client/deck/generator/DeckGeneratorDialog.java b/Mage.Client/src/main/java/mage/client/deck/generator/DeckGeneratorDialog.java index d445663c0c5..761fc57568e 100644 --- a/Mage.Client/src/main/java/mage/client/deck/generator/DeckGeneratorDialog.java +++ b/Mage.Client/src/main/java/mage/client/deck/generator/DeckGeneratorDialog.java @@ -32,7 +32,7 @@ public class DeckGeneratorDialog { private static String selectedColors; private static JComboBox cbSets, cbDeckSize, cbCMC; private static JButton btnGenerate, btnCancel, btnReset; - private static JCheckBox cArtifacts, cSingleton, cNonBasicLands, cColorless, cAdvanced; + private static JCheckBox cArtifacts, cSingleton, cNonBasicLands, cColorless, cAdvanced, cCommander; private static JLabel averageCMCLabel; private static SimpleDateFormat dateFormat; private static RatioAdjustingSliderPanel adjustingSliderPanel; @@ -63,7 +63,7 @@ public class DeckGeneratorDialog { c.insets = new Insets(5, 10, 0, 10); c.gridx = 1; c.gridy = 0; - String chosen = MageFrame.getPreferences().get("genDeckColor", "u"); + String chosen = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_COLORS, "u"); final ColorsChooser colorsChooser = new ColorsChooser(chosen); mainPanel.add(colorsChooser, c); @@ -135,7 +135,7 @@ public class DeckGeneratorDialog { c.ipadx = 30; c.insets = new Insets(5, 10, 0, 10); c.weightx = 0.90; - cbDeckSize = new JComboBox<>(new String[]{"40", "60"}); + cbDeckSize = new JComboBox<>(new String[]{"40", "60", "100"}); cbDeckSize.setSelectedIndex(0); cbDeckSize.setAlignmentX(Component.LEFT_ALIGNMENT); mainPanel.add(cbDeckSize, c); @@ -148,31 +148,33 @@ public class DeckGeneratorDialog { JPanel jCheckBoxes = new JPanel(new FlowLayout(FlowLayout.LEFT)); // Singletons + boolean commanderEnabled = Boolean.parseBoolean(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_COMMANDER, "false")); cSingleton = new JCheckBox("Singleton", false); cSingleton.setToolTipText("Allow only a single copy of each non-land card in your deck."); String singletonEnabled = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_SINGLETON, "false"); - cSingleton.setSelected(Boolean.valueOf(singletonEnabled)); + cSingleton.setSelected(Boolean.parseBoolean(singletonEnabled)); jCheckBoxes.add(cSingleton); + cSingleton.setEnabled(!commanderEnabled); // Artifacts cArtifacts = new JCheckBox("Artifacts", false); cArtifacts.setToolTipText("Use artifacts and artifact creatures in your deck."); String artifactEnabled = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_ARTIFACTS, "false"); - cArtifacts.setSelected(Boolean.valueOf(artifactEnabled)); + cArtifacts.setSelected(Boolean.parseBoolean(artifactEnabled)); jCheckBoxes.add(cArtifacts); // Non-basic lands cNonBasicLands = new JCheckBox("Non-basic Lands", false); cNonBasicLands.setToolTipText("Use non-basic lands in your deck (if applicable)."); String nonBasicEnabled = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_NON_BASIC_LANDS, "false"); - cNonBasicLands.setSelected(Boolean.valueOf(nonBasicEnabled)); + cNonBasicLands.setSelected(Boolean.parseBoolean(nonBasicEnabled)); jCheckBoxes.add(cNonBasicLands); // Colorless mana cColorless = new JCheckBox("Colorless mana", false); cColorless.setToolTipText("Allow cards with colorless mana cost."); String colorlessEnabled = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_COLORLESS, "false"); - cColorless.setSelected(Boolean.valueOf(colorlessEnabled)); + cColorless.setSelected(Boolean.parseBoolean(colorlessEnabled)); jCheckBoxes.add(cColorless); c.ipadx = 0; c.gridx = 0; @@ -181,12 +183,28 @@ public class DeckGeneratorDialog { c.gridwidth = 3; mainPanel.add(jCheckBoxes, c); + // Commander + cCommander = new JCheckBox("Commander", false); + cCommander.setToolTipText("Add legendary creature as commander"); + cCommander.setSelected(commanderEnabled); + jCheckBoxes.add(cCommander); + c.ipadx = 0; + c.gridx = 0; + c.gridy = 3; + c.weightx = 1; + c.gridwidth = 3; + mainPanel.add(jCheckBoxes, c); + cCommander.addItemListener(itemEvent -> { + // commander require singletone mode + cSingleton.setEnabled(!cCommander.isSelected()); + }); + // Create the advanced configuration panel JPanel advancedPanel = createAdvancedPanel(); // Advanced checkbox (enable/disable advanced configuration) - cAdvanced = new JCheckBox("Advanced"); - cAdvanced.setToolTipText("Enable advanced configuration options"); + cAdvanced = new JCheckBox("Customize distribution"); + cAdvanced.setToolTipText("Customize cards distribution due mana values and types"); cAdvanced.addItemListener(itemEvent -> { boolean enable = cAdvanced.isSelected(); enableAdvancedPanel(enable); @@ -194,7 +212,7 @@ public class DeckGeneratorDialog { // Advanced Checkbox String advancedSavedValue = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_ADVANCED, "false"); - boolean advancedEnabled = Boolean.valueOf(advancedSavedValue); + boolean advancedEnabled = Boolean.parseBoolean(advancedSavedValue); enableAdvancedPanel(advancedEnabled); cAdvanced.setSelected(advancedEnabled); c.gridy = 4; @@ -212,7 +230,7 @@ public class DeckGeneratorDialog { colorsChooser.setEnabled(false); selectedColors = (String) colorsChooser.getSelectedItem(); dlg.setVisible(false); - MageFrame.getPreferences().put("genDeckColor", selectedColors); + PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_COLORS, selectedColors); }); btnCancel = new JButton("Cancel"); btnCancel.addActionListener(e -> { @@ -297,7 +315,7 @@ public class DeckGeneratorDialog { c.gridwidth = 1; c.gridy = 2; btnReset = new JButton("Reset"); - btnReset.setToolTipText("Reset advanced dialog to default values"); + btnReset.setToolTipText("Reset custom cards distribution to default values"); btnReset.addActionListener(actionEvent -> { cbCMC.setSelectedItem(DeckGeneratorCMC.Default); adjustingSliderPanel.resetValues(); @@ -374,6 +392,12 @@ public class DeckGeneratorDialog { return selected; } + public boolean isCommander() { + boolean selected = cCommander.isSelected(); + PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_COMMANDER, Boolean.toString(selected)); + return selected; + } + public boolean isAdvanced() { boolean selected = cAdvanced.isSelected(); PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_ADVANCED, Boolean.toString(selected)); diff --git a/Mage.Client/src/main/java/mage/client/deck/generator/DeckGeneratorPool.java b/Mage.Client/src/main/java/mage/client/deck/generator/DeckGeneratorPool.java index 5c60b989043..3f611168e97 100644 --- a/Mage.Client/src/main/java/mage/client/deck/generator/DeckGeneratorPool.java +++ b/Mage.Client/src/main/java/mage/client/deck/generator/DeckGeneratorPool.java @@ -1,6 +1,6 @@ - package mage.client.deck.generator; +import mage.ObjectColor; import mage.abilities.Ability; import mage.cards.Card; import mage.cards.decks.Deck; @@ -11,16 +11,17 @@ import mage.constants.ColoredManaSymbol; import mage.constants.Rarity; import mage.util.RandomUtil; import mage.util.TournamentUtil; +import org.apache.log4j.Logger; import java.util.*; +import java.util.stream.Collectors; /** - * - * @author Simown + * @author Simown, JayDi85 */ -public class DeckGeneratorPool -{ +public class DeckGeneratorPool { + private static final Logger logger = Logger.getLogger(DeckGeneratorPool.class); public static final int DEFAULT_CREATURE_PERCENTAGE = 38; public static final int DEFAULT_NON_CREATURE_PERCENTAGE = 21; @@ -31,6 +32,7 @@ public class DeckGeneratorPool private final List poolCMCs; private final int creatureCount; private final int nonCreatureCount; + private final int commandersCount; private final int landCount; private final boolean isSingleton; private final int deckSize; @@ -48,65 +50,83 @@ public class DeckGeneratorPool /** * Creates a card pool with specified criterea used when generating a deck. * - * @param deckSize the size of the complete deck - * @param creaturePercentage what percentage of creatures to use when generating the deck. + * @param deckSize the size of the complete deck + * @param creaturePercentage what percentage of creatures to use when generating the deck. * @param nonCreaturePercentage percentage of non-creatures to use when generating the deck. - * @param landPercentage percentage of lands to use when generating the deck. - * @param allowedColors which card colors are allowed in the generated deck. - * @param isSingleton if the deck only has 1 copy of each non-land card. - * @param colorlessAllowed if colourless mana symbols are allowed in costs in the deck. - * @param isAdvanced if the user has provided advanced options to generate the deck. - * @param deckGeneratorCMC the CMC curve to use for this deck + * @param landPercentage percentage of lands to use when generating the deck. + * @param allowedColors which card colors are allowed in the generated deck. + * @param isSingleton if the deck only has 1 copy of each non-land card. + * @param colorlessAllowed if colourless mana symbols are allowed in costs in the deck. + * @param isAdvanced if the user has provided advanced options to generate the deck. + * @param isCommander reserve commander card + * @param deckGeneratorCMC the CMC curve to use for this deck */ public DeckGeneratorPool(final int deckSize, final int creaturePercentage, final int nonCreaturePercentage, final int landPercentage, - final List allowedColors, boolean isSingleton, boolean colorlessAllowed, boolean isAdvanced, DeckGeneratorCMC deckGeneratorCMC) - { + final List allowedColors, boolean isSingleton, boolean colorlessAllowed, boolean isCommander, + boolean isAdvanced, DeckGeneratorCMC deckGeneratorCMC) { this.deckSize = deckSize; this.allowedColors = allowedColors; - this.isSingleton = isSingleton; this.colorlessAllowed = colorlessAllowed; + this.commandersCount = isCommander ? 1 : 0; + this.isSingleton = isSingleton || isCommander; // commander must use singleton mode only this.deck = new Deck(); // Advanced (CMC Slider panel and curve drop-down in the dialog) - if(isAdvanced) { - this.creatureCount = (int)Math.ceil((deckSize / 100.0) * creaturePercentage); - this.nonCreatureCount = (int)Math.ceil((deckSize / 100.0)* nonCreaturePercentage); - this.landCount = (int)Math.ceil((deckSize / 100.0)* landPercentage); - if(this.deckSize == 60) { - this.poolCMCs = deckGeneratorCMC.get60CardPoolCMC(); - } else { - this.poolCMCs = deckGeneratorCMC.get40CardPoolCMC(); + if (isAdvanced) { + this.creatureCount = (int) Math.ceil((deckSize / 100.0) * creaturePercentage); + this.nonCreatureCount = (int) Math.ceil((deckSize / 100.0) * nonCreaturePercentage); + this.landCount = (int) Math.ceil((deckSize / 100.0) * landPercentage); + switch (this.deckSize) { + case 100: + this.poolCMCs = deckGeneratorCMC.get100CardPoolCMC(); + break; + case 60: + this.poolCMCs = deckGeneratorCMC.get60CardPoolCMC(); + break; + case 40: + this.poolCMCs = deckGeneratorCMC.get40CardPoolCMC(); + break; + default: + throw new IllegalArgumentException("Unsupported deck size: " + this.deckSize); } } else { // Ignore the advanced group, just use defaults - this.creatureCount = (int)Math.ceil((deckSize / 100.0) * DEFAULT_CREATURE_PERCENTAGE); - this.nonCreatureCount = (int)Math.ceil((deckSize / 100.0) * DEFAULT_NON_CREATURE_PERCENTAGE); - this.landCount = (int)Math.ceil((deckSize / 100.0) * DEFAULT_LAND_PERCENTAGE); - if(this.deckSize == 60) { - this.poolCMCs = DeckGeneratorCMC.Default.get60CardPoolCMC(); - } else { - this.poolCMCs = DeckGeneratorCMC.Default.get40CardPoolCMC(); + this.creatureCount = (int) Math.ceil((deckSize / 100.0) * DEFAULT_CREATURE_PERCENTAGE); + this.nonCreatureCount = (int) Math.ceil((deckSize / 100.0) * DEFAULT_NON_CREATURE_PERCENTAGE); + this.landCount = (int) Math.ceil((deckSize / 100.0) * DEFAULT_LAND_PERCENTAGE); + switch (this.deckSize) { + case 100: + this.poolCMCs = DeckGeneratorCMC.Default.get100CardPoolCMC(); + break; + case 60: + this.poolCMCs = DeckGeneratorCMC.Default.get60CardPoolCMC(); + break; + case 40: + this.poolCMCs = DeckGeneratorCMC.Default.get40CardPoolCMC(); + break; + default: + throw new IllegalArgumentException("Unsupported deck size: " + this.deckSize); } } - if(allowedColors.size() == 1) { + if (allowedColors.size() == 1) { monoColored = true; } - } /** * Adjusts the number of spell cards that should be in a converted mana cost (CMC) range, given the amount of cards total. + * * @param cardsCount the number of total cards. * @return a list of CMC ranges, with the amount of cards for each CMC range */ public List getCMCsForSpellCount(int cardsCount) { List adjustedCMCs = new ArrayList<>(this.poolCMCs); // For each CMC calculate how many spell cards are needed, given the total amount of cards - for(DeckGeneratorCMC.CMC deckCMC : adjustedCMCs) { - deckCMC.setAmount((int)Math.ceil(deckCMC.percentage * cardsCount)); + for (DeckGeneratorCMC.CMC deckCMC : adjustedCMCs) { + deckCMC.setAmount((int) Math.ceil(deckCMC.percentage * cardsCount)); } return adjustedCMCs; } @@ -115,15 +135,15 @@ public class DeckGeneratorPool * Verifies if the spell card supplied is valid for this pool of cards. * Checks that there isn't too many copies of this card in the deck. * Checks that the card fits the chosen colors for this pool. + * * @param card the spell card * @return if the spell card is valid for this pool. */ - public boolean isValidSpellCard(Card card) - { + public boolean isValidSpellCard(Card card) { int cardCount = getCardCount((card.getName())); // Check it hasn't already got the maximum number of copies in a deck - if(cardCount < (isSingleton ? 1 : 4)) { - if(cardFitsChosenColors(card)) { + if (cardCount < (isSingleton ? 1 : 4)) { + if (cardFitsChosenColors(card)) { return true; } } @@ -132,11 +152,11 @@ public class DeckGeneratorPool /** * Verifies if the non-basic land card supplied is valid for this pool of cards. + * * @param card the non-basic land card * @return if the land card generates the allowed colors for this pool. */ - public boolean isValidLandCard(Card card) - { + public boolean isValidLandCard(Card card) { int cardCount = getCardCount((card.getName())); // No need to check if the land is valid for the colors chosen // They are all filtered before searching for lands to include in the deck. @@ -146,60 +166,56 @@ public class DeckGeneratorPool /** * Adds a card to the pool and updates the count of this card. + * * @param card the card to add. */ - public void addCard(Card card) - { + public void addCard(Card card) { Object cnt = cardCounts.get((card.getName())); - if(cnt == null) + if (cnt == null) cardCounts.put(card.getName(), 0); int existingCount = cardCounts.get((card.getName())); - cardCounts.put(card.getName(), existingCount+1); + cardCounts.put(card.getName(), existingCount + 1); deckCards.add(card); } + public void clearCards(boolean isClearReserve) { + cardCounts.clear(); + deckCards.clear(); + if (isClearReserve) { + reserveSpells.clear(); + } + } + /** * Adds a card to the reserve pool. * Reserve pool is used when the deck generation fails to build a complete deck, or * a partially complete deck (e.g. if there are no cards found that match a CMC) - * @param card the card to add + * + * @param card the card to add * @param cardCMC the converted mana cost of the card */ public boolean tryAddReserve(Card card, int cardCMC) { // Only cards with CMC < 7 and don't already exist in the deck // can be added to our reserve pool as not to overwhelm the curve // with high CMC cards and duplicates. - if(cardCMC < 7 && getCardCount(card.getName()) == 0) { + if (cardCMC < 7 && getCardCount(card.getName()) == 0) { this.reserveSpells.add(card); return true; } return false; } - /** - * Checks if the mana symbols in the card all match the allowed colors for this pool. - * @param card the spell card to check. - * @return if all the mana symbols fit the chosen colors. - */ private boolean cardFitsChosenColors(Card card) { - for (String symbol : card.getManaCostSymbols()) { - boolean found = false; - symbol = symbol.replace("{", "").replace("}", ""); - if (isColoredManaSymbol(symbol)) { - for (ColoredManaSymbol allowed : allowedColors) { - if (symbol.contains(allowed.toString())) { - found = true; - break; - } - } - if (!found) { - return false; - } - } - if (symbol.equals("C") && !colorlessAllowed) { + Set needColors = allowedColors.stream().map(ColoredManaSymbol::toString).collect(Collectors.toSet()); + List cardColors = card.getColorIdentity().getColors(); + for (ObjectColor cardColor : cardColors) { + if (!needColors.contains(cardColor.toString())) { return false; } } + if (cardColors.isEmpty() && !colorlessAllowed) { + return false; + } return true; } @@ -208,6 +224,7 @@ public class DeckGeneratorPool * Calculates the percentage of colored mana symbols over all spell cards in the deck. * Used to balance the generation of basic lands so the amount of lands matches the * cards mana costs. + * * @return a list of colored mana symbols and the percentage of symbols seen in cards mana costs. */ public Map calculateSpellColorPercentages() { @@ -221,14 +238,14 @@ public class DeckGeneratorPool int totalCount = 0; List fixedSpells = getFixedSpells(); - for(Card spell: fixedSpells) { + for (Card spell : fixedSpells) { for (String symbol : spell.getManaCostSymbols()) { symbol = symbol.replace("{", "").replace("}", ""); if (isColoredManaSymbol(symbol)) { for (ColoredManaSymbol allowed : allowedColors) { if (symbol.contains(allowed.toString())) { int cnt = colorCount.get(allowed.toString()); - colorCount.put(allowed.toString(), cnt+1); + colorCount.put(allowed.toString(), cnt + 1); totalCount++; } } @@ -236,7 +253,7 @@ public class DeckGeneratorPool } } final Map percentages = new HashMap<>(); - for(Map.Entry singleCount: colorCount.entrySet()) { + for (Map.Entry singleCount : colorCount.entrySet()) { String color = singleCount.getKey(); int count = singleCount.getValue(); // Calculate the percentage this color has out of the total color counts @@ -248,20 +265,20 @@ public class DeckGeneratorPool /** * Calculates how many of each mana the non-basic lands produce. + * * @param deckLands the non-basic lands which will be used in the deck. * @return a mapping of colored mana symbol to the amount that can be produced. */ - public Map countManaProduced(List deckLands) - { + public Map countManaProduced(List deckLands) { Map manaCounts = new HashMap<>(); for (final ColoredManaSymbol color : ColoredManaSymbol.values()) { manaCounts.put(color.toString(), 0); } - for(Card land: deckLands) { - for(Ability landAbility: land.getAbilities()) { + for (Card land : deckLands) { + for (Ability landAbility : land.getAbilities()) { for (ColoredManaSymbol symbol : allowedColors) { String abilityString = landAbility.getRule(); - if(landTapsForAllowedColor(abilityString, symbol.toString())) { + if (landTapsForAllowedColor(abilityString, symbol.toString())) { Integer count = manaCounts.get(symbol.toString()); manaCounts.put(symbol.toString(), count + 1); } @@ -271,15 +288,17 @@ public class DeckGeneratorPool return manaCounts; } - /** Filter all the non-basic lands retrieved from the database. + /** + * Filter all the non-basic lands retrieved from the database. + * * @param landCardsInfo information about all the cards. * @return a list of cards that produce the allowed colors for this pool. */ public List filterLands(List landCardsInfo) { List matchingLandList = new ArrayList<>(); - for(CardInfo landCardInfo: landCardsInfo) { + for (CardInfo landCardInfo : landCardsInfo) { Card landCard = landCardInfo.createMockCard(); - if(landProducesChosenColors(landCard)) { + if (landProducesChosenColors(landCard)) { matchingLandList.add(landCard); } } @@ -288,11 +307,12 @@ public class DeckGeneratorPool /** * Returns the card name that represents the basic land for this color. + * * @param symbolString the colored mana symbol. * @return the name of a basic land card. */ public static String getBasicLandName(String symbolString) { - switch(symbolString) { + switch (symbolString) { case "B": return "Swamp"; case "G": @@ -311,25 +331,50 @@ public class DeckGeneratorPool /** * Returns a complete deck. + * * @return the deck. */ public Deck getDeck() { - Set actualDeck = deck.getCards(); - actualDeck.addAll(deckCards); + deck.getCards().clear(); + deck.getSideboard().clear(); + + List useCards = new ArrayList<>(deckCards); + List useCommanders = new ArrayList<>(); + + // take random commanders + if (commandersCount > 0) { + List possibleCommanders = deckCards.stream() + .filter(this::isValidCommander) + .collect(Collectors.toList()); + Card nextCommander = RandomUtil.randomFromCollection(possibleCommanders); + while (nextCommander != null && useCommanders.size() < commandersCount) { + useCards.remove(nextCommander); + useCommanders.add(nextCommander); + } + } + + deck.getCards().addAll(useCards); + deck.getSideboard().addAll(useCommanders); return deck; } /** * Returns the number of creatures needed in this pool. + * * @return the number of creatures. */ public int getCreatureCount() { return creatureCount; } + public int getCommandersCount() { + return commandersCount; + } + /** * Returns the number of non-creatures needed in this pool. + * * @return the number of non-creatures. */ public int getNonCreatureCount() { @@ -338,6 +383,7 @@ public class DeckGeneratorPool /** * Returns the number of lands (basic + non-basic) needed in this pool. + * * @return the number of lands. */ public int getLandCount() { @@ -346,6 +392,7 @@ public class DeckGeneratorPool /** * Returns if this pool only uses one color. + * * @return if this pool is monocolored. */ public boolean isMonoColoredDeck() { @@ -354,6 +401,7 @@ public class DeckGeneratorPool /** * Returns the size of the deck to generate from this pool. + * * @return the deck size. */ public int getDeckSize() { @@ -364,20 +412,20 @@ public class DeckGeneratorPool * Fixes undersized or oversized decks that have been generated. * Removes random cards from an oversized deck until it is the correct size. * Uses the reserve pool to fill up and undersized deck with cards. + * * @return a fixed list of cards for this deck. */ - private List getFixedSpells() - { + private List getFixedSpells() { int spellSize = deckCards.size(); int nonLandSize = (deckSize - landCount); // Less spells than needed - if(spellSize < nonLandSize) { + if (spellSize < nonLandSize) { - int spellsNeeded = nonLandSize-spellSize; + int spellsNeeded = nonLandSize - spellSize; // If we haven't got enough spells in reserve to fulfil the amount we need, skip adding any. - if(reserveSpells.size() >= spellsNeeded) { + if (reserveSpells.size() >= spellsNeeded) { List spellsToAdd = new ArrayList<>(spellsNeeded); @@ -398,16 +446,16 @@ public class DeckGeneratorPool } // More spells than needed - else if(spellSize > (deckSize - landCount)) { - int spellsRemoved = (spellSize)-(deckSize-landCount); - for(int i = 0; i < spellsRemoved; ++i) { + else if (spellSize > (deckSize - landCount)) { + int spellsRemoved = (spellSize) - (deckSize - landCount); + for (int i = 0; i < spellsRemoved; ++i) { deckCards.remove(RandomUtil.nextInt(deckCards.size())); } } // Check we have exactly the right amount of cards for a deck. - if(deckCards.size() != nonLandSize) { - throw new IllegalStateException("Not enough cards found to generate deck."); + if (deckCards.size() != nonLandSize) { + logger.info("Can't generate full deck for selected settings - try again or choose more sets and less colors"); } // Return the fixed amount return deckCards; @@ -416,16 +464,18 @@ public class DeckGeneratorPool /** * Returns if this land taps for the given color. * Basic string matching to check the ability adds one of the chosen mana when tapped. + * * @param ability MockAbility of the land card - * @param symbol colored mana symbol. + * @param symbol colored mana symbol. * @return if the ability is tapping to produce the mana the symbol represents. */ - private boolean landTapsForAllowedColor(String ability, String symbol) { + private boolean landTapsForAllowedColor(String ability, String symbol) { return ability.matches(".*Add \\{" + symbol + "\\}."); } /** * Returns if this land will produce the chosen colors for this pool. + * * @param card a non-basic land card. * @return if this land card taps to produces the colors chosen. */ @@ -434,32 +484,49 @@ public class DeckGeneratorPool // and other Abilities so we have to do some basic string matching on land cards for now. List landAbilities = card.getAbilities(); int count = 0; - for(Ability ability : landAbilities) { + for (Ability ability : landAbilities) { String abilityString = ability.getRule(); // Lands that tap to produce mana of the chosen colors - for(ColoredManaSymbol symbol : allowedColors) { - if(landTapsForAllowedColor(abilityString, symbol.toString())) { + for (ColoredManaSymbol symbol : allowedColors) { + if (landTapsForAllowedColor(abilityString, symbol.toString())) { count++; } } - if(count > 1) { + if (count > 1) { return true; } } return false; } + private boolean isValidCommander(Card card) { + // commander must be legendary creature + if (!card.isCreature() || !card.isLegendary()) { + return false; + } + + // commander must have all chosen colors + for (ColoredManaSymbol symbol : allowedColors) { + if (!card.getColor().contains(new ObjectColor(symbol.toString()))) { + return false; + } + } + + return true; + } + /** * Returns if the symbol is a colored mana symbol. + * * @param symbol the symbol to check. * @return If it is a basic mana symbol or a hybrid mana symbol. */ private static boolean isColoredManaSymbol(String symbol) { // Hybrid mana - if(symbol.contains("/")) { + if (symbol.contains("/")) { return true; } - for(ColoredManaSymbol c: ColoredManaSymbol.values()) { + for (ColoredManaSymbol c : ColoredManaSymbol.values()) { if (symbol.charAt(0) == (c.toString().charAt(0))) { return true; } @@ -470,14 +537,15 @@ public class DeckGeneratorPool /** * Returns how many of this card is in the pool. * If there are none in the pool it will initalise the card count. + * * @param cardName the name of the card to check. * @return the number of cards in the pool of this name. */ private int getCardCount(String cardName) { Object cC = cardCounts.get((cardName)); - if(cC == null) + if (cC == null) cardCounts.put(cardName, 0); - return cardCounts.get((cardName)); + return cardCounts.get((cardName)); } @@ -490,8 +558,8 @@ public class DeckGeneratorPool * color of cards. * * @param useNonBasicLand - * @param criteria the criteria of the lands to search for in the database. - * @param basicLands information about the basic lands from the sets used. + * @param criteria the criteria of the lands to search for in the database. + * @param basicLands information about the basic lands from the sets used. */ protected static void generateLands(boolean useNonBasicLand, CardCriteria criteria, Map> basicLands) { DeckGeneratorPool genPool = DeckGenerator.genPool; @@ -541,45 +609,69 @@ public class DeckGeneratorPool * non-creatures are retrieved separately to ensure the deck contains a * reasonable mix of both. * - * @param criteria the criteria to search for in the database. - * @param spellCount the number of spells that match the criteria needed in - * the deck. + * @param criteria the criteria to search for in the database. + * @param needCardsCount the number of spells that match the criteria needed in + * the deck. + * @param needCommandersCount make sure it contains commander creature (must be uses on first generateSpells only) */ - protected static void generateSpells(CardCriteria criteria, int spellCount) { + protected static void generateSpells(CardCriteria criteria, int needCardsCount, int needCommandersCount) { DeckGeneratorPool genPool = DeckGenerator.genPool; + if (needCommandersCount > 0 && !genPool.cardCounts.isEmpty()) { + throw new IllegalArgumentException("Wrong code usage: generateSpells with creatures and commanders must be called as first"); + } List cardPool = CardRepository.instance.findCards(criteria); - int retrievedCount = cardPool.size(); - List deckCMCs = genPool.getCMCsForSpellCount(spellCount); + List deckCMCs = genPool.getCMCsForSpellCount(needCardsCount); int count = 0; + int validCommanders = 0; int reservesAdded = 0; - boolean added; - if (retrievedCount > 0 && retrievedCount >= spellCount) { + if (cardPool.size() > 0 && cardPool.size() >= needCardsCount) { int tries = 0; - while (count < spellCount) { - Card card = cardPool.get(RandomUtil.nextInt(retrievedCount)).createMockCard(); - if (genPool.isValidSpellCard(card)) { - int cardCMC = card.getManaValue(); - for (DeckGeneratorCMC.CMC deckCMC : deckCMCs) { - if (cardCMC >= deckCMC.min && cardCMC <= deckCMC.max) { - int currentAmount = deckCMC.getAmount(); - if (currentAmount > 0) { - deckCMC.setAmount(currentAmount - 1); - genPool.addCard(card.copy()); - count++; - } - } else if (reservesAdded < (genPool.getDeckSize() / 2)) { - added = genPool.tryAddReserve(card, cardCMC); - if (added) { - reservesAdded++; + while (true) { + tries++; + + // can't finish deck, stop and use reserved cards later + if (tries > DeckGenerator.MAX_TRIES) { + logger.info("Can't generate full deck for selected settings - try again or choose more sets and less colors"); + break; + } + + // can finish deck - but make sure it has commander + if (count >= needCardsCount) { + if (validCommanders < needCommandersCount) { + // reset deck search from scratch (except reserved cards) + count = 0; + validCommanders = 0; + deckCMCs = genPool.getCMCsForSpellCount(needCardsCount); + genPool.clearCards(false); + continue; + } + break; + } + + Card card = cardPool.get(RandomUtil.nextInt(cardPool.size())).createMockCard(); + if (!genPool.isValidSpellCard(card)) { + continue; + } + + int cardCMC = card.getManaValue(); + for (DeckGeneratorCMC.CMC deckCMC : deckCMCs) { + if (cardCMC >= deckCMC.min && cardCMC <= deckCMC.max) { + int currentAmount = deckCMC.getAmount(); + if (currentAmount > 0) { + deckCMC.setAmount(currentAmount - 1); + genPool.addCard(card.copy()); + count++; + // make sure it has compatible commanders + if (genPool.isValidCommander(card)) { + validCommanders++; } } + } else if (reservesAdded < (genPool.getDeckSize() / 2)) { + if (genPool.tryAddReserve(card, cardCMC)) { + reservesAdded++; + } } } - tries++; - if (tries > DeckGenerator.MAX_TRIES) { - // Break here, we'll fill in random missing ones later - break; - } } } else { throw new IllegalStateException("Not enough cards to generate deck."); diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.form b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.form index 88895a22e1b..94ff570fdef 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.form +++ b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.form @@ -780,7 +780,7 @@ - + diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java index cc2c4cc9657..88e006f6f84 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/CardSelector.java @@ -1085,7 +1085,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene } }); - jTextFieldSearch.setToolTipText("Searches for card names and in the rule text of the card."); + jTextFieldSearch.setToolTipText("Search cards by any data like name or mana symbols like {W}, {U}, {C}, etc (use quotes for exact search)"); chkNames.setSelected(true); chkNames.setText("Names"); diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckArea.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckArea.java index a1f34640c58..7ce8d8c987d 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckArea.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckArea.java @@ -110,7 +110,7 @@ public class DeckArea extends javax.swing.JPanel { sideboardList.deselectAll(); for (CardView card : cards) { CardView newCard = new CardView(card); - deckList.addCardView(newCard, true); + deckList.addCardView(newCard, card); } } @@ -150,7 +150,7 @@ public class DeckArea extends javax.swing.JPanel { deckList.deselectAll(); for (CardView card : cards) { CardView newCard = new CardView(card); - sideboardList.addCardView(newCard, true); + sideboardList.addCardView(newCard, card); } } diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.form b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.form index df74cb92578..0df29a4f479 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.form +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.form @@ -533,10 +533,10 @@ - + - + diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java index 7671604596a..326b38bbea9 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java @@ -121,12 +121,8 @@ public class DeckEditorPanel extends javax.swing.JPanel { if (!SwingUtilities.isLeftMouseButton(e)) { return; } - List cardNames = new ArrayList<>(); LegalityLabel label = (LegalityLabel) e.getComponent(); - label.getValidator().getErrorsList().stream() - .map(DeckValidatorError::getCardName) - .filter(Objects::nonNull) - .forEach(cardNames::add); + List cardNames = new ArrayList<>(label.selectCards()); deckArea.getDeckList().deselectAll(); deckArea.getDeckList().selectByName(cardNames); deckArea.getSideboardList().deselectAll(); @@ -1290,8 +1286,8 @@ public class DeckEditorPanel extends javax.swing.JPanel { panelInfo.setOpaque(false); - deckLegalityDisplay.setMaximumSize(new java.awt.Dimension(245, 155)); - deckLegalityDisplay.setMinimumSize(new java.awt.Dimension(85, 155)); + deckLegalityDisplay.setMaximumSize(new java.awt.Dimension(245, 255)); + deckLegalityDisplay.setMinimumSize(new java.awt.Dimension(85, 255)); deckLegalityDisplay.setOpaque(false); deckLegalityDisplay.setVisible(false); @@ -1313,7 +1309,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { panelInfoLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(panelInfoLayout.createSequentialGroup() .addContainerGap() - .addComponent(deckLegalityDisplay, javax.swing.GroupLayout.PREFERRED_SIZE, 155, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(deckLegalityDisplay, javax.swing.GroupLayout.PREFERRED_SIZE, 255, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(bigCard, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addContainerGap()) diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckLegalityPanel.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckLegalityPanel.java index 0e5e2ff3d91..f9ecdb01acd 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckLegalityPanel.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckLegalityPanel.java @@ -5,6 +5,8 @@ import mage.cards.decks.Deck; import mage.cards.decks.DeckValidator; import mage.cards.mock.MockCard; import mage.cards.mock.MockSplitCard; +import mage.client.components.BracketLegalityLabel; +import mage.client.components.EdhPowerLevelLegalityLabel; import mage.client.components.LegalityLabel; import mage.deck.*; import org.apache.log4j.Logger; @@ -15,7 +17,7 @@ import java.util.stream.Stream; /** - * @author Elandril + * @author Elandril, JayDi85 */ public class DeckLegalityPanel extends javax.swing.JPanel { @@ -101,6 +103,16 @@ public class DeckLegalityPanel extends javax.swing.JPanel { new Frontier(), new HistoricalType2(), new PennyDreadfulCommander(), new EuropeanHighlander(), new CanadianHighlander() // not used: new Eternal(), new Momir(), new TinyLeaders() ).forEach(this::addLegalityLabel); + + // extra buttons like score + this.add(new EdhPowerLevelLegalityLabel()); + // only 3 buttons allowed for one line + this.add(new BracketLegalityLabel("Bracket 1", "B1", 1)); + this.add(new BracketLegalityLabel("Bracket 2", "B2", 2)); + this.add(new BracketLegalityLabel("Bracket 3", "B3", 3)); + this.add(new BracketLegalityLabel("Bracket 4", "B4", 4)); + this.add(new BracketLegalityLabel("Bracket 5", "B5", 5)); + addHidePanelButton(); revalidate(); @@ -147,5 +159,4 @@ public class DeckLegalityPanel extends javax.swing.JPanel { .map(LegalityLabel.class::cast) .forEach(label -> label.validateDeck(deckToValidate)); } - } diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java index 7d302cad11d..532ea79bc9d 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java @@ -594,7 +594,7 @@ public class NewTableDialog extends MageDialog { private MatchOptions getMatchOptions() { // current settings GameTypeView gameType = (GameTypeView) cbGameType.getSelectedItem(); - MatchOptions options = new MatchOptions(this.txtName.getText(), gameType.getName(), false, 2); + MatchOptions options = new MatchOptions(this.txtName.getText(), gameType.getName(), false); options.getPlayerTypes().add(PlayerType.HUMAN); for (TablePlayerPanel player : players) { options.getPlayerTypes().add(player.getPlayerType()); diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.form b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.form index 82a2df067f9..83ba9d4551e 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.form @@ -99,19 +99,21 @@ + + - - - - + + + + + - - + @@ -119,10 +121,7 @@ - - - - + @@ -200,11 +199,7 @@ - - - - - + @@ -222,8 +217,6 @@ - - @@ -287,7 +280,7 @@ - + @@ -295,15 +288,17 @@ - - + + + + + + + + + - - - - - - + @@ -504,19 +499,18 @@ - - - - - - + + + + + - + @@ -615,7 +609,7 @@ - + @@ -672,8 +666,6 @@ - - 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 188f63d6056..b83408d981a 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java @@ -40,6 +40,11 @@ public class NewTournamentDialog extends MageDialog { private static final Logger logger = Logger.getLogger(NewTournamentDialog.class); + // it's ok to have 4 players at the screen, 6 is fine for big screens too + private static final int MAX_WORKABLE_PLAYERS_PER_GAME = 6; + + private static final String CUBE_FROM_DECK_NAME = "Cube From Deck"; + // temp settings on loading players list private final List prefPlayerTypes = new ArrayList<>(); private final List prefPlayerSkills = new ArrayList<>(); @@ -77,12 +82,15 @@ public class NewTournamentDialog extends MageDialog { private int getCurrentNumPlayers() { int res = (Integer) spnNumPlayers.getValue(); - return res > 0 ? res : 2; + return Math.max(2, res); } private int getCurrentNumSeats() { - int res = (Integer) spnNumSeats.getValue(); - return res > 0 ? res : 2; + if (chkSingleMultiplayerGame.isSelected()) { + return getCurrentNumPlayers(); + } else { + return 2; + } } public void showDialog(UUID roomId) { @@ -159,9 +167,8 @@ public class NewTournamentDialog extends MageDialog { lblPacks = new javax.swing.JLabel(); pnlPacks = new javax.swing.JPanel(); lblNbrPlayers = new javax.swing.JLabel(); - lblNbrSeats = new javax.swing.JLabel(); spnNumPlayers = new javax.swing.JSpinner(); - spnNumSeats = new javax.swing.JSpinner(); + chkSingleMultiplayerGame = new javax.swing.JCheckBox(); pnlDraftOptions = new javax.swing.JPanel(); jLabel6 = new javax.swing.JLabel(); cbDraftTiming = new javax.swing.JComboBox(); @@ -321,17 +328,17 @@ public class NewTournamentDialog extends MageDialog { lblNbrPlayers.setText("Players:"); - lblNbrSeats.setText("Seats:"); - spnNumPlayers.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent evt) { spnNumPlayersStateChanged(evt); } }); - spnNumSeats.addChangeListener(new javax.swing.event.ChangeListener() { - public void stateChanged(javax.swing.event.ChangeEvent evt) { - spnNumSeatsStateChanged(evt); + chkSingleMultiplayerGame.setText("play as single game"); + chkSingleMultiplayerGame.setToolTipText("Allow to play single game with all tourney's players -- e.g. play one game after draft with 4 players"); + chkSingleMultiplayerGame.addItemListener(new java.awt.event.ItemListener() { + public void itemStateChanged(java.awt.event.ItemEvent evt) { + chkSingleMultiplayerGameItemStateChanged(evt); } }); @@ -390,7 +397,7 @@ public class NewTournamentDialog extends MageDialog { ); pnlPlayersLayout.setVerticalGroup( pnlPlayersLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(pnlOtherPlayers, javax.swing.GroupLayout.DEFAULT_SIZE, 8, Short.MAX_VALUE) + .addComponent(pnlOtherPlayers, javax.swing.GroupLayout.DEFAULT_SIZE, 15, Short.MAX_VALUE) ); btnOk.setText("Create"); @@ -460,25 +467,26 @@ public class NewTournamentDialog extends MageDialog { .addComponent(pnlPacks, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(lblPacks) + .addComponent(lblPlayer1) .addGroup(layout.createSequentialGroup() .addComponent(lblNbrPlayers) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(spnNumPlayers, javax.swing.GroupLayout.PREFERRED_SIZE, 46, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblNbrSeats) + .addComponent(spnNumPlayers, javax.swing.GroupLayout.PREFERRED_SIZE, 46, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(spnNumSeats, javax.swing.GroupLayout.PREFERRED_SIZE, 46, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addComponent(lblPacks) - .addComponent(lblPlayer1)) + .addComponent(lblNumWins) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(chkSingleMultiplayerGame))) + .addGap(21, 21, 21) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGap(28, 28, 28) .addComponent(pnlDraftOptions, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(lblNumRounds)) - .addGroup(layout.createSequentialGroup() - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(lblConstructionTime))) + .addComponent(lblConstructionTime)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(spnConstructTime, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) @@ -545,11 +553,7 @@ public class NewTournamentDialog extends MageDialog { .addComponent(lbBufferTime) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(cbBufferTime, javax.swing.GroupLayout.PREFERRED_SIZE, 101, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(lblNumWins) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGap(87, 87, 87) .addComponent(chkRollbackTurnsAllowed) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(cbAllowSpectators))))) @@ -561,8 +565,6 @@ public class NewTournamentDialog extends MageDialog { .addGap(2, 2, 2) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(lblNumWins) - .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(chkRollbackTurnsAllowed) .addComponent(cbAllowSpectators, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) @@ -612,20 +614,21 @@ public class NewTournamentDialog extends MageDialog { .addGroup(layout.createSequentialGroup() .addComponent(pnlPacks, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(pnlRandomPacks, javax.swing.GroupLayout.DEFAULT_SIZE, 9, Short.MAX_VALUE) + .addComponent(pnlRandomPacks, javax.swing.GroupLayout.DEFAULT_SIZE, 20, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(spnNumRounds, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(lblNumRounds)) .addComponent(lblNbrPlayers, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(spnNumPlayers, javax.swing.GroupLayout.DEFAULT_SIZE, 23, Short.MAX_VALUE) - .addComponent(pnlDraftOptions, javax.swing.GroupLayout.PREFERRED_SIZE, 23, Short.MAX_VALUE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addComponent(lblNbrSeats, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(spnNumSeats)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(spnNumPlayers) + .addComponent(chkSingleMultiplayerGame) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lblNumWins) + .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addComponent(pnlDraftOptions, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)) + .addGap(27, 27, 27) .addComponent(lblPlayer1, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(spnConstructTime, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) @@ -652,7 +655,7 @@ public class NewTournamentDialog extends MageDialog { }// //GEN-END:initComponents private void cbTournamentTypeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbTournamentTypeActionPerformed - prepareTourneyView(false, prepareVersionStr(-1, false), getCurrentNumPlayers(), getCurrentNumSeats()); + loadTourneyView(false, prepareVersionStr(-1, false), getCurrentNumPlayers(), chkSingleMultiplayerGame.isSelected()); jumpstartPacksFilename = ""; if (cbTournamentType.getSelectedItem().toString().matches(".*Jumpstart.*Custom.*")) { @@ -694,6 +697,38 @@ public class NewTournamentDialog extends MageDialog { } } + // players count limited by GUI size + // draft bots are loses and hide at the start, so count only human and AI + if (tOptions.getMatchOptions().isSingleGameTourney()) { + int workablePlayers = tOptions.getPlayerTypes().stream() + .mapToInt(p -> p.isWorkablePlayer() ? 1 : 0) + .sum(); + if (workablePlayers > MAX_WORKABLE_PLAYERS_PER_GAME) { + JOptionPane.showMessageDialog( + MageFrame.getDesktop(), + String.format("Warning, in single game mode you can choose %d human/ai players but selected %d", MAX_WORKABLE_PLAYERS_PER_GAME, workablePlayers), + "Warning", + JOptionPane.WARNING_MESSAGE + ); + return; + } + } + + // cube from deck uses weird choose logic from combobox select, so players can forget or cancel it + if (tournamentType.isDraft() + && tOptions.getLimitedOptions().getDraftCubeName() != null + && tOptions.getLimitedOptions().getDraftCubeName().contains(CUBE_FROM_DECK_NAME)) { + if (tOptions.getLimitedOptions().getCubeFromDeck() == null || tOptions.getLimitedOptions().getCubeFromDeck().getCards().isEmpty()) { + JOptionPane.showMessageDialog( + MageFrame.getDesktop(), + "Found empty cube. You must choose Cube From Deck again and select existing deck file.", + "Warning", + JOptionPane.WARNING_MESSAGE + ); + return; + } + } + // save last settings onSaveSettings(0, tOptions); @@ -741,44 +776,29 @@ public class NewTournamentDialog extends MageDialog { doClose(); }//GEN-LAST:event_btnCancelActionPerformed - private void updateNumSeats() { - int numSeats = (Integer) this.spnNumSeats.getValue(); + private void applyNewPlayersCount() { + // make sure players count is compatible + int numPlayers = getCurrentNumPlayers(); + int compatiblePlayers = getCompatiblePlayersCount(numPlayers); + if (numPlayers != compatiblePlayers) { + numPlayers = compatiblePlayers; + spnNumPlayers.setValue(numPlayers); + } + createPlayers(numPlayers - 1); - if (numSeats > 2) { - TournamentTypeView tournamentType = (TournamentTypeView) cbTournamentType.getSelectedItem(); - if (numSeats >= tournamentType.getMinPlayers()) { - createPlayers(numSeats - 1); - spnNumPlayers.setValue(numSeats); - } else { - numSeats = tournamentType.getMinPlayers(); - createPlayers(numSeats - 1); - spnNumPlayers.setValue(numSeats); - spnNumSeats.setValue(numSeats); - } + // make sure wins is compatible + // is's can be a too long match for 2+ wins in 4+ game + if (chkSingleMultiplayerGame.isSelected()) { spnNumWins.setValue(1); } } private void spnNumPlayersStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_spnNumPlayersStateChanged - int numPlayers = getCurrentNumPlayers(); - createPlayers(numPlayers - 1); - int numSeats = (Integer) this.spnNumSeats.getValue(); - if (numSeats > 2 && numPlayers != numSeats) { - updateNumSeats(); - } + applyNewPlayersCount(); }//GEN-LAST:event_spnNumPlayersStateChanged - private void spnNumSeatsStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_spnNumSeatsStateChanged - int numSeats = (Integer) this.spnNumSeats.getValue(); - this.spnNumPlayers.setEnabled(numSeats <= 2); - updateNumSeats(); - }//GEN-LAST:event_spnNumSeatsStateChanged - private void spnNumWinsnumPlayersChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_spnNumWinsnumPlayersChanged - int numSeats = getCurrentNumSeats(); - if (numSeats > 2) { - spnNumWins.setValue(1); - } + applyNewPlayersCount(); }//GEN-LAST:event_spnNumWinsnumPlayersChanged private JFileChooser fcSelectDeck = null; @@ -823,7 +843,7 @@ public class NewTournamentDialog extends MageDialog { private void cbDraftCubeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbDraftCubeActionPerformed cubeFromDeckFilename = ""; - if (cbDraftCube.getSelectedItem().toString().equals("Cube From Deck")) { + if (cbDraftCube.getSelectedItem().toString().startsWith(CUBE_FROM_DECK_NAME)) { cubeFromDeckFilename = playerLoadDeck(); } }//GEN-LAST:event_cbDraftCubeActionPerformed @@ -880,27 +900,52 @@ public class NewTournamentDialog extends MageDialog { customOptions.showDialog(); }//GEN-LAST:event_btnCustomOptionsActionPerformed + private void chkSingleMultiplayerGameItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_chkSingleMultiplayerGameItemStateChanged + // for checkboxes - it's important to use ItemStateChanged instead stateChanged + // (the last one will raise on moving mouse over, not on checkbox state change only) + applyNewPlayersCount(); + }//GEN-LAST:event_chkSingleMultiplayerGameItemStateChanged + private void setGameOptions() { createPlayers(getCurrentNumPlayers() - 1); } - private void prepareTourneyView(boolean loadPlayerSettings, String versionStr, int numPlayers, int numSeats) { + private int getCompatiblePlayersCount(int count) { + TournamentTypeView tournamentType = (TournamentTypeView) cbTournamentType.getSelectedItem(); + if (tournamentType == null) { + return count; + } + int compatibleMin = tournamentType.getMinPlayers(); + int compatibleMax = tournamentType.getMaxPlayers(); + + if (chkSingleMultiplayerGame.isSelected()) { + // user can select any amount of draft bots, real amount checks on submit + //compatibleMax = Math.min(MAX_PLAYERS_PER_GAME, compatibleMax); + } + + int compatibleCount = count; + compatibleCount = Math.max(compatibleCount, compatibleMin); + compatibleCount = Math.min(compatibleCount, compatibleMax); + return compatibleCount; + } + + private void loadTourneyView(boolean loadPlayerSettings, String versionStr, int numPlayers, boolean isSingleMultiplayerGame) { TournamentTypeView tournamentType = (TournamentTypeView) cbTournamentType.getSelectedItem(); activatePanelElements(tournamentType); - if (numPlayers < tournamentType.getMinPlayers() || numPlayers > tournamentType.getMaxPlayers()) { - numPlayers = tournamentType.getMinPlayers(); - } + numPlayers = getCompatiblePlayersCount(numPlayers); this.spnNumPlayers.setModel(new SpinnerNumberModel(numPlayers, tournamentType.getMinPlayers(), tournamentType.getMaxPlayers(), 1)); this.spnNumPlayers.setEnabled(tournamentType.getMinPlayers() != tournamentType.getMaxPlayers()); - this.spnNumSeats.setModel(new SpinnerNumberModel(2, 2, tournamentType.getMaxPlayers(), 1)); - // manual call change events to apply players/seats restrictions and create miss panels + // manual call change events to apply players restrictions and create miss panels before load player related settings // TODO: refactor to use isLoading and restrictions from a code instead restrictions from a component + this.chkSingleMultiplayerGame.setSelected(isSingleMultiplayerGame); this.spnNumPlayers.setValue(numPlayers); spnNumPlayersStateChanged(null); - this.spnNumSeats.setValue(numSeats); - spnNumSeatsStateChanged(null); + + // wins must be loaded after chkSingleMultiplayerGame change, cause it can be limited by 1 + int numWins = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_NUMBER_OF_WINS + versionStr, "2")); + this.spnNumWins.setValue(numWins); if (loadPlayerSettings) { // load player data @@ -1259,8 +1304,7 @@ public class NewTournamentDialog extends MageDialog { private TournamentOptions getTournamentOptions() { TournamentTypeView tournamentType = (TournamentTypeView) cbTournamentType.getSelectedItem(); - int numSeats = (Integer) this.spnNumSeats.getValue(); - TournamentOptions tOptions = new TournamentOptions(this.txtName.getText(), "", numSeats); + TournamentOptions tOptions = new TournamentOptions(this.txtName.getText(), "", chkSingleMultiplayerGame.isSelected()); tOptions.setTournamentType(tournamentType.getName()); tOptions.setPassword(txtPassword.getText()); tOptions.getPlayerTypes().add(PlayerType.HUMAN); @@ -1423,7 +1467,6 @@ public class NewTournamentDialog extends MageDialog { break; } } - this.spnNumWins.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_NUMBER_OF_WINS + versionStr, "2"))); this.spnQuitRatio.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_QUIT_RATIO + versionStr, "100"))); this.spnMinimumRating.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_MINIMUM_RATING + versionStr, "0"))); @@ -1431,7 +1474,6 @@ public class NewTournamentDialog extends MageDialog { activatePanelElements(tournamentType); int defaultNumberPlayers = 2; - int defaultNumberSeats = 2; if (tournamentType.isLimited()) { if (tournamentType.isDraft()) { defaultNumberPlayers = 4; @@ -1462,8 +1504,8 @@ public class NewTournamentDialog extends MageDialog { } int numPlayers = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_NUMBER_PLAYERS + versionStr, String.valueOf(defaultNumberPlayers))); - int numSeats = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_NUMBER_SEATS + versionStr, String.valueOf(defaultNumberSeats))); - prepareTourneyView(true, versionStr, numPlayers, numSeats); + boolean isSingleMultiplayerGame = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_SINGLE_MULTIPLAYER_GAME + versionStr, "No").equals("Yes"); + loadTourneyView(true, versionStr, numPlayers, isSingleMultiplayerGame); this.customOptions.onLoadSettings(version); } @@ -1511,7 +1553,7 @@ public class NewTournamentDialog extends MageDialog { } PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_NUMBER_PLAYERS + versionStr, Integer.toString(tOptions.getPlayerTypes().size())); - PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_NUMBER_SEATS + versionStr, Integer.toString((Integer) this.spnNumSeats.getValue())); + PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_SINGLE_MULTIPLAYER_GAME + versionStr, (tOptions.getMatchOptions().isSingleGameTourney() ? "Yes" : "No")); // save player data // player type @@ -1552,6 +1594,7 @@ public class NewTournamentDialog extends MageDialog { private javax.swing.JComboBox cbTournamentType; private javax.swing.JCheckBox chkRated; private javax.swing.JCheckBox chkRollbackTurnsAllowed; + private javax.swing.JCheckBox chkSingleMultiplayerGame; private javax.swing.JLabel jLabel6; private javax.swing.JLabel lbBufferTime; private javax.swing.JLabel lbDeckType; @@ -1563,7 +1606,6 @@ public class NewTournamentDialog extends MageDialog { private javax.swing.JLabel lblMinimumRating; private javax.swing.JLabel lblName; private javax.swing.JLabel lblNbrPlayers; - private javax.swing.JLabel lblNbrSeats; private javax.swing.JLabel lblNumRounds; private javax.swing.JLabel lblNumWins; private javax.swing.JLabel lblPacks; @@ -1592,7 +1634,6 @@ public class NewTournamentDialog extends MageDialog { private javax.swing.JSpinner spnMinimumRating; private javax.swing.JSpinner spnNumPlayers; private javax.swing.JSpinner spnNumRounds; - private javax.swing.JSpinner spnNumSeats; private javax.swing.JSpinner spnNumWins; private javax.swing.JSpinner spnQuitRatio; private javax.swing.JTextField txtName; diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form index 0138479d0b1..e6bf6d18f40 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form @@ -2183,7 +2183,7 @@ - + @@ -2224,7 +2224,7 @@ - + @@ -2283,7 +2283,6 @@ - @@ -2318,15 +2317,48 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - @@ -2354,6 +2386,28 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java index 4e8581be71f..1afc6ea294b 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java @@ -4,10 +4,7 @@ import mage.client.MageFrame; import mage.client.SessionHandler; import mage.client.components.KeyBindButton; import mage.client.themes.ThemeType; -import mage.client.util.CardLanguage; -import mage.client.util.ClientDefaultSettings; -import mage.client.util.GUISizeHelper; -import mage.client.util.ImageHelper; +import mage.client.util.*; import mage.client.util.audio.MusicPlayer; import mage.client.util.gui.BufferedImageBuilder; import mage.client.util.gui.GuiDisplayUtil; @@ -37,7 +34,6 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.prefs.BackingStoreException; import java.util.prefs.Preferences; -import java.util.stream.Collectors; import static mage.client.constants.Constants.AUTO_TARGET_NON_FEEL_BAD; import static mage.constants.Constants.*; @@ -89,8 +85,7 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_CARD_IMAGES_SAVE_TO_ZIP = "cardImagesSaveToZip"; public static final String KEY_CARD_IMAGES_PREF_LANGUAGE = "cardImagesPreferredImageLaguage"; - public static final String KEY_CARD_RENDERING_IMAGE_MODE = "cardRenderingFallback"; - public static final String KEY_CARD_RENDERING_ENABLE_RETRO_FRAMES = "cardRenderingRetroFrames"; + public static final String KEY_CARD_RENDERING_IMAGE_MODE = "cardRenderingMode"; public static final String KEY_CARD_RENDERING_ICONS_FOR_ABILITIES = "cardRenderingIconsForAbilities"; public static final String KEY_CARD_RENDERING_ICONS_FOR_PLAYABLE = "cardRenderingIconsForPlayable"; public static final String KEY_CARD_RENDERING_REMINDER_TEXT = "cardRenderingReminderText"; @@ -249,7 +244,7 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_NEW_TOURNAMENT_PACKS_DRAFT = "newTournamentPacksDraft"; public static final String KEY_NEW_TOURNAMENT_PACKS_RANDOM_DRAFT = "newTournamentPacksRandomDraft"; public static final String KEY_NEW_TOURNAMENT_NUMBER_PLAYERS = "newTournamentNumberPlayers"; - public static final String KEY_NEW_TOURNAMENT_NUMBER_SEATS = "newTournamentNumberSeats"; + public static final String KEY_NEW_TOURNAMENT_SINGLE_MULTIPLAYER_GAME = "newTournamentSingleMultiplayerGame"; public static final String KEY_NEW_TOURNAMENT_PLAYER_TYPES = "newTournamentPlayerTypes"; public static final String KEY_NEW_TOURNAMENT_PLAYER_SKILLS = "newTournamentPlayerSkills"; public static final String KEY_NEW_TOURNAMENT_DRAFT_TIMING = "newTournamentDraftTiming"; @@ -268,12 +263,14 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_AUTO_TARGET_LEVEL = "autoTargetLevel"; // pref setting for deck generator + public static final String KEY_NEW_DECK_GENERATOR_COLORS = "newDeckGeneratorDeckColors"; public static final String KEY_NEW_DECK_GENERATOR_DECK_SIZE = "newDeckGeneratorDeckSize"; public static final String KEY_NEW_DECK_GENERATOR_SET = "newDeckGeneratorSet"; public static final String KEY_NEW_DECK_GENERATOR_SINGLETON = "newDeckGeneratorSingleton"; public static final String KEY_NEW_DECK_GENERATOR_ARTIFACTS = "newDeckGeneratorArtifacts"; public static final String KEY_NEW_DECK_GENERATOR_NON_BASIC_LANDS = "newDeckGeneratorNonBasicLands"; public static final String KEY_NEW_DECK_GENERATOR_COLORLESS = "newDeckGeneratorColorless"; + public static final String KEY_NEW_DECK_GENERATOR_COMMANDER = "newDeckGeneratorCommander"; public static final String KEY_NEW_DECK_GENERATOR_ADVANCED = "newDeckGeneratorAdvanced"; public static final String KEY_NEW_DECK_GENERATOR_CREATURE_PERCENTAGE = "newDeckGeneratorCreaturePercentage"; public static final String KEY_NEW_DECK_GENERATOR_NON_CREATURE_PERCENTAGE = "newDeckGeneratorNonCreaturePercentage"; @@ -761,6 +758,7 @@ public class PreferencesDialog extends javax.swing.JDialog { } cbPreferredImageLanguage.setModel(new DefaultComboBoxModel<>(CardLanguage.toList())); + cbCardRenderImageFallback.setModel(new DefaultComboBoxModel<>(CardRenderMode.toList())); } private void createSizeSetting(Integer position, String key, Integer defaultValue, boolean useExample, String name, String hint) { @@ -962,14 +960,14 @@ public class PreferencesDialog extends javax.swing.JDialog { cbPreferredImageLanguage = new javax.swing.JComboBox<>(); labelPreferredImageLanguage = new javax.swing.JLabel(); panelCardStyles = new javax.swing.JPanel(); - cbCardRenderImageFallback = new javax.swing.JCheckBox(); - cbCardRenderRetroFrames = new javax.swing.JCheckBox(); cbCardRenderIconsForAbilities = new javax.swing.JCheckBox(); cbCardRenderIconsForPlayable = new javax.swing.JCheckBox(); jSeparator1 = new javax.swing.JSeparator(); cbCardRenderShowReminderText = new javax.swing.JCheckBox(); cbCardRenderHideSetSymbol = new javax.swing.JCheckBox(); cbCardRenderShowAbilityTextOverlay = new javax.swing.JCheckBox(); + labelRenderMode = new javax.swing.JLabel(); + cbCardRenderImageFallback = new javax.swing.JComboBox<>(); tabPhases = new javax.swing.JPanel(); jLabelHeadLine = new javax.swing.JLabel(); jLabelYourTurn = new javax.swing.JLabel(); @@ -2279,7 +2277,6 @@ public class PreferencesDialog extends javax.swing.JDialog { } }); - cbPreferredImageLanguage.setMaximumRowCount(20); cbPreferredImageLanguage.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" })); labelPreferredImageLanguage.setText("Default images language:"); @@ -2305,7 +2302,7 @@ public class PreferencesDialog extends javax.swing.JDialog { .add(labelPreferredImageLanguage) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(cbPreferredImageLanguage, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 153, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))) - .add(0, 480, Short.MAX_VALUE))) + .add(0, 0, Short.MAX_VALUE))) .addContainerGap()) ); panelCardImagesLayout.setVerticalGroup( @@ -2325,29 +2322,59 @@ public class PreferencesDialog extends javax.swing.JDialog { ); panelCardStyles.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Card styles (restart xmage to apply new settings)")); - panelCardStyles.setLayout(new javax.swing.BoxLayout(panelCardStyles, javax.swing.BoxLayout.Y_AXIS)); - - cbCardRenderImageFallback.setText("Render mode: MTGO style (off) or IMAGE style (on)"); - panelCardStyles.add(cbCardRenderImageFallback); - - cbCardRenderRetroFrames.setText("Force retro frames (MTGO render mode will use old border for all cards)"); - panelCardStyles.add(cbCardRenderRetroFrames); cbCardRenderIconsForAbilities.setText("Enable card icons for abilities (example: flying, deathtouch)"); - panelCardStyles.add(cbCardRenderIconsForAbilities); cbCardRenderIconsForPlayable.setText("Enable card icons for playable abilities (example: if you can activate card's ability then show a special icon in the corner)"); - panelCardStyles.add(cbCardRenderIconsForPlayable); - panelCardStyles.add(jSeparator1); cbCardRenderShowReminderText.setText("Show reminder text in rendered card textboxes"); - panelCardStyles.add(cbCardRenderShowReminderText); cbCardRenderHideSetSymbol.setText("Hide set symbols on cards (more space on the type line for card types)"); - panelCardStyles.add(cbCardRenderHideSetSymbol); cbCardRenderShowAbilityTextOverlay.setText("Show ability text as overlay in big card view"); - panelCardStyles.add(cbCardRenderShowAbilityTextOverlay); + + labelRenderMode.setText("Render Mode:"); + labelRenderMode.setToolTipText("Image - Renders card image with text overlay
    MTGO - Renders card frame around card art
    Forced M15 - Renders all cards in the modern frame
    Forced Retro - Renders all cards in the retro frame"); + + cbCardRenderImageFallback.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" })); + cbCardRenderImageFallback.setToolTipText("Image - Renders card image with text overlay
    MTGO - Renders card frame around card art
    Forced M15 - Renders all cards in the MTGO style with the modern frame
    Forced Retro - Renders all cards in the MTGO style with the retro frame"); + + org.jdesktop.layout.GroupLayout panelCardStylesLayout = new org.jdesktop.layout.GroupLayout(panelCardStyles); + panelCardStyles.setLayout(panelCardStylesLayout); + panelCardStylesLayout.setHorizontalGroup( + panelCardStylesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(cbCardRenderIconsForAbilities) + .add(cbCardRenderIconsForPlayable) + .add(jSeparator1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 775, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + .add(cbCardRenderShowReminderText) + .add(cbCardRenderHideSetSymbol) + .add(cbCardRenderShowAbilityTextOverlay) + .add(panelCardStylesLayout.createSequentialGroup() + .add(6, 6, 6) + .add(labelRenderMode) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(cbCardRenderImageFallback, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 122, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + ); + panelCardStylesLayout.setVerticalGroup( + panelCardStylesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(panelCardStylesLayout.createSequentialGroup() + .add(0, 0, 0) + .add(panelCardStylesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(labelRenderMode) + .add(cbCardRenderImageFallback, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + .add(0, 0, 0) + .add(cbCardRenderIconsForAbilities) + .add(0, 0, 0) + .add(cbCardRenderIconsForPlayable) + .add(0, 0, 0) + .add(jSeparator1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) + .add(0, 0, 0) + .add(cbCardRenderShowReminderText) + .add(0, 0, 0) + .add(cbCardRenderHideSetSymbol) + .add(0, 0, 0) + .add(cbCardRenderShowAbilityTextOverlay)) + ); org.jdesktop.layout.GroupLayout tabGuiImagesLayout = new org.jdesktop.layout.GroupLayout(tabGuiImages); tabGuiImages.setLayout(tabGuiImagesLayout); @@ -2367,7 +2394,7 @@ public class PreferencesDialog extends javax.swing.JDialog { .add(panelCardStyles, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(panelCardImages, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) - .addContainerGap(306, Short.MAX_VALUE)) + .addContainerGap(309, Short.MAX_VALUE)) ); tabsPanel.addTab("GUI Images", tabGuiImages); @@ -3038,8 +3065,7 @@ public class PreferencesDialog extends javax.swing.JDialog { save(prefs, dialog.cbUseRandomBattleImage, KEY_BATTLEFIELD_IMAGE_RANDOM, "true", "false"); // rendering - save(prefs, dialog.cbCardRenderImageFallback, KEY_CARD_RENDERING_IMAGE_MODE, "true", "false"); - save(prefs, dialog.cbCardRenderRetroFrames, KEY_CARD_RENDERING_ENABLE_RETRO_FRAMES, "true", "false"); + save(prefs, dialog.cbCardRenderImageFallback, KEY_CARD_RENDERING_IMAGE_MODE); save(prefs, dialog.cbCardRenderIconsForAbilities, KEY_CARD_RENDERING_ICONS_FOR_ABILITIES, "true", "false"); save(prefs, dialog.cbCardRenderIconsForPlayable, KEY_CARD_RENDERING_ICONS_FOR_PLAYABLE, "true", "false"); save(prefs, dialog.cbCardRenderHideSetSymbol, KEY_CARD_RENDERING_SET_SYMBOL, "true", "false"); @@ -3490,8 +3516,7 @@ public class PreferencesDialog extends javax.swing.JDialog { dialog.cbPreferredImageLanguage.setSelectedItem(MageFrame.getPreferences().get(KEY_CARD_IMAGES_PREF_LANGUAGE, CardLanguage.ENGLISH.getCode())); // rendering settings - load(prefs, dialog.cbCardRenderImageFallback, KEY_CARD_RENDERING_IMAGE_MODE, "true", "false"); - load(prefs, dialog.cbCardRenderRetroFrames, KEY_CARD_RENDERING_ENABLE_RETRO_FRAMES, "true", "false"); + dialog.cbCardRenderImageFallback.setSelectedItem(MageFrame.getPreferences().get(KEY_CARD_RENDERING_IMAGE_MODE, CardRenderMode.MTGO.toString())); load(prefs, dialog.cbCardRenderIconsForAbilities, KEY_CARD_RENDERING_ICONS_FOR_ABILITIES, "true", "true"); load(prefs, dialog.cbCardRenderIconsForPlayable, KEY_CARD_RENDERING_ICONS_FOR_PLAYABLE, "true", "true"); load(prefs, dialog.cbCardRenderHideSetSymbol, KEY_CARD_RENDERING_SET_SYMBOL, "true"); @@ -3815,15 +3840,7 @@ public class PreferencesDialog extends javax.swing.JDialog { } public static int getRenderMode() { - if (getCachedValue(PreferencesDialog.KEY_CARD_RENDERING_IMAGE_MODE, "false").equals("false")) { - return 0; // mtgo - } else { - return 1; // image - } - } - - public static boolean getRenderRetroFrames() { - return (getCachedValue(PreferencesDialog.KEY_CARD_RENDERING_ENABLE_RETRO_FRAMES, "true").endsWith("true")); + return CardRenderMode.fromString(getCachedValue(PreferencesDialog.KEY_CARD_RENDERING_IMAGE_MODE, CardRenderMode.MTGO.toString())).getId(); } public static boolean getRenderIconsForAbilities() { @@ -4053,8 +4070,7 @@ public class PreferencesDialog extends javax.swing.JDialog { private javax.swing.JCheckBox cbCardRenderHideSetSymbol; private javax.swing.JCheckBox cbCardRenderIconsForAbilities; private javax.swing.JCheckBox cbCardRenderIconsForPlayable; - private javax.swing.JCheckBox cbCardRenderImageFallback; - private javax.swing.JCheckBox cbCardRenderRetroFrames; + private javax.swing.JComboBox cbCardRenderImageFallback; private javax.swing.JCheckBox cbCardRenderShowAbilityTextOverlay; private javax.swing.JCheckBox cbCardRenderShowReminderText; private javax.swing.JCheckBox cbConfirmEmptyManaPool; @@ -4186,6 +4202,7 @@ public class PreferencesDialog extends javax.swing.JDialog { private javax.swing.JLabel labelNextTurn; private javax.swing.JLabel labelPreferredImageLanguage; private javax.swing.JLabel labelPriorEnd; + private javax.swing.JLabel labelRenderMode; private javax.swing.JLabel labelSizeGroup1; private javax.swing.JLabel labelSizeGroup2; private javax.swing.JLabel labelSkipStep; diff --git a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.form b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.form index 1c5f6220a6d..febdc1c3d85 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.form @@ -116,11 +116,9 @@ - - - - + + diff --git a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java index 51eaabe44bf..cdc5d658831 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java @@ -16,7 +16,7 @@ import mage.client.cards.BigCard; import mage.client.game.PlayAreaPanel; import mage.client.game.PlayerPanelExt; import mage.client.themes.ThemeType; -import mage.client.util.ClientEventType; +import mage.client.util.*; import mage.client.util.Event; import mage.client.util.GUISizeHelper; import mage.client.util.Listener; @@ -82,6 +82,7 @@ public class TestCardRenderDialog extends MageDialog { getRootPane().setDefaultButton(buttonCancel); // init render mode + this.comboRenderMode.setModel(new DefaultComboBoxModel<>(CardRenderMode.toList())); this.comboRenderMode.setSelectedIndex(PreferencesDialog.getRenderMode()); // init themes list @@ -431,6 +432,7 @@ public class TestCardRenderDialog extends MageDialog { cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "185", 0, 0, 0, true, false, null)); // Judith, the Scourge Diva cardViews.add(createHandCard(game, playerYou.getId(), "DIS", "153")); // Odds // Ends (split card) cardViews.add(createHandCard(game, playerYou.getId(), "ELD", "38")); // Animating Faerie (adventure card) + cardViews.add(createHandCard(game, playerYou.getId(), "LEA", "278")); // Bayou (retro frame) cardViews.add(createFaceDownCard(game, playerOpponent.getId(), "ELD", "38", false, false, false)); // face down cardViews.add(createFaceDownCard(game, playerOpponent.getId(), "ELD", "38", true, false, true)); // morphed cardViews.add(createFaceDownCard(game, playerOpponent.getId(), "ELD", "38", false, true, false)); // manifested @@ -635,7 +637,7 @@ public class TestCardRenderDialog extends MageDialog { labelRenderMode.setText("Render mode:"); - comboRenderMode.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "MTGO", "Image" })); + comboRenderMode.setToolTipText("Image - Renders card image with text overlay
    MTGO - Renders card frame around card art
    Forced M15 - Renders all cards in the MTGO style with the modern frame
    Forced Retro - Renders all cards in the MTGO style with the retro frame"); comboRenderMode.addItemListener(new java.awt.event.ItemListener() { public void itemStateChanged(java.awt.event.ItemEvent evt) { comboRenderModeItemStateChanged(evt); diff --git a/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java b/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java index 1118801bf5f..41b4904fadb 100644 --- a/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java +++ b/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java @@ -191,15 +191,15 @@ } public void updateDraft(DraftView draftView) { - if (draftView.getSets().size() != 3) { + if (draftView.getSetNames().size() != 3) { // Random draft - TODO: can we access the type of draft here? this.editPack1.setText("Random Boosters"); this.editPack2.setText("Random Boosters"); this.editPack3.setText("Random Boosters"); } else { - this.editPack1.setText(String.format("%s - %s", draftView.getSetCodes().get(0), draftView.getSets().get(0))); - this.editPack2.setText(String.format("%s - %s", draftView.getSetCodes().get(1), draftView.getSets().get(1))); - this.editPack3.setText(String.format("%s - %s", draftView.getSetCodes().get(2), draftView.getSets().get(2))); + this.editPack1.setText(draftView.getBoosterInfo(0)); + this.editPack2.setText(draftView.getBoosterInfo(1)); + this.editPack3.setText(draftView.getBoosterInfo(2)); } // scroll too long text to the start diff --git a/Mage.Client/src/main/java/mage/client/remote/XmageURLConnection.java b/Mage.Client/src/main/java/mage/client/remote/XmageURLConnection.java index 4ff6869ee0e..ef4e38fba03 100644 --- a/Mage.Client/src/main/java/mage/client/remote/XmageURLConnection.java +++ b/Mage.Client/src/main/java/mage/client/remote/XmageURLConnection.java @@ -291,16 +291,25 @@ public class XmageURLConnection { } } + public static String downloadText(String resourceUrl) { + return downloadText(resourceUrl, null); + } + /** * Fast download of text data * + * @param additionalHeaders set extra headers like application/json + * * @return downloaded text on OK 200 response or empty on any other errors */ - public static String downloadText(String resourceUrl) { + public static String downloadText(String resourceUrl, Map additionalHeaders) { XmageURLConnection con = new XmageURLConnection(resourceUrl); con.startConnection(); if (con.isConnected()) { try { + if (additionalHeaders != null) { + con.setRequestHeaders(additionalHeaders); + } con.connect(); if (con.getResponseCode() == 200) { return con.getGoodResponseAsString(); diff --git a/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java b/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java index 34840279052..61c7614e6ad 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java @@ -41,6 +41,10 @@ public class TablePlayerPanel extends javax.swing.JPanel { this.newPlayerPanel.setSkillLevel(playerSkill); } + public static String extractAiPlayerNumberFromLabel(String label) { + return ClientDefaultSettings.computerName + " " + label.substring(Math.max(0, label.length() - 2)).trim(); + } + public boolean joinTable(UUID roomId, UUID tableId) throws IOException, ClassNotFoundException { if (this.cbPlayerType.getSelectedItem() != PlayerType.HUMAN) { return SessionHandler.joinTable(roomId, tableId, this.newPlayerPanel.getPlayerName(), (PlayerType) this.cbPlayerType.getSelectedItem(), this.newPlayerPanel.getSkillLevel(), DeckImporter.importDeckFromFile(this.newPlayerPanel.getDeckFile(), true), ""); @@ -124,7 +128,7 @@ public class TablePlayerPanel extends javax.swing.JPanel { private void cbPlayerTypeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbPlayerTypeActionPerformed if (getPlayerType() != PlayerType.HUMAN) { this.newPlayerPanel.setVisible(true); - this.newPlayerPanel.setPlayerName(ClientDefaultSettings.computerName + " " + this.lblPlayerNum.getText().charAt(this.lblPlayerNum.getText().length() - 1)); + this.newPlayerPanel.setPlayerName(extractAiPlayerNumberFromLabel(this.lblPlayerNum.getText())); } else { this.newPlayerPanel.setVisible(false); } diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.form b/Mage.Client/src/main/java/mage/client/table/TablesPanel.form index b970a25e99f..49390111ad9 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.form +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.form @@ -201,7 +201,7 @@ - + @@ -216,7 +216,7 @@ - + diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java index d94baacf55f..302fe95ffbb 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -1163,7 +1163,7 @@ public class TablesPanel extends javax.swing.JPanel { filterBar1.add(btnTypeMatch); btnTypeTourneyConstructed.setSelected(true); - btnTypeTourneyConstructed.setText("Constructed tourn."); + btnTypeTourneyConstructed.setText("Constructed tourney"); btnTypeTourneyConstructed.setToolTipText("Shows all constructed tournament tables."); btnTypeTourneyConstructed.setActionCommand("typeTourneyConstructed"); btnTypeTourneyConstructed.setFocusPainted(false); @@ -1178,7 +1178,7 @@ public class TablesPanel extends javax.swing.JPanel { filterBar1.add(btnTypeTourneyConstructed); btnTypeTourneyLimited.setSelected(true); - btnTypeTourneyLimited.setText("Limited tourn."); + btnTypeTourneyLimited.setText("Limited tourney"); btnTypeTourneyLimited.setToolTipText("Shows all limited tournament tables."); btnTypeTourneyLimited.setActionCommand("typeTourneyLimited"); btnTypeTourneyLimited.setFocusPainted(false); @@ -1694,13 +1694,13 @@ public class TablesPanel extends javax.swing.JPanel { DeckCardLists testDeck = DeckImporter.importDeckFromFile(testDeckFile, false); PlayerType aiType = useMonteCarloAI ? PlayerType.COMPUTER_MONTE_CARLO : PlayerType.COMPUTER_MAD; - int numSeats = gameName.contains("2") || gameName.contains("Monte Carlo") ? 2 : 4; - boolean multiPlayer = numSeats > 2; + int numPlayers = gameName.contains("2") || gameName.contains("Monte Carlo") ? 2 : 4; + boolean multiPlayer = numPlayers > 2; - MatchOptions options = new MatchOptions(gameName, gameType, multiPlayer, numSeats); + MatchOptions options = new MatchOptions(gameName, gameType, multiPlayer); options.getPlayerTypes().add(PlayerType.HUMAN); options.getPlayerTypes().add(aiType); - for (int i=2 ; i < numSeats ; i++) { + for (int i=2 ; i < numPlayers ; i++) { options.getPlayerTypes().add(aiType); } options.setDeckType("Variant Magic - Freeform Commander"); @@ -1720,7 +1720,7 @@ public class TablesPanel extends javax.swing.JPanel { SessionHandler.joinTable(roomId, table.getTableId(), "Human", PlayerType.HUMAN, 1, testDeck, ""); SessionHandler.joinTable(roomId, table.getTableId(), "Computer", aiType, 1, testDeck, ""); - for (int i=2 ; i < numSeats ; i++) { + for (int i=2 ; i < numPlayers ; i++) { SessionHandler.joinTable(roomId, table.getTableId(), "Computer" + i, aiType, 1, testDeck, ""); } SessionHandler.startMatch(roomId, table.getTableId()); diff --git a/Mage.Client/src/main/java/mage/client/table/TournamentPlayerPanel.java b/Mage.Client/src/main/java/mage/client/table/TournamentPlayerPanel.java index a6eca85360b..f4e34e82dec 100644 --- a/Mage.Client/src/main/java/mage/client/table/TournamentPlayerPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TournamentPlayerPanel.java @@ -145,7 +145,7 @@ public class TournamentPlayerPanel extends javax.swing.JPanel { private void cbPlayerTypeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbPlayerTypeActionPerformed if (this.cbPlayerType.getSelectedItem() != PlayerType.HUMAN) { this.pnlPlayerName.setVisible(true); - this.txtPlayerName.setText(ClientDefaultSettings.computerName + " " + this.lblPlayerNum.getText().charAt(this.lblPlayerNum.getText().length() - 1)); + this.txtPlayerName.setText(TablePlayerPanel.extractAiPlayerNumberFromLabel(this.lblPlayerNum.getText())); this.txtPlayerName.setEditable(false); this.txtPlayerName.setEnabled(false); } else { diff --git a/Mage.Client/src/main/java/mage/client/tournament/TournamentPanel.java b/Mage.Client/src/main/java/mage/client/tournament/TournamentPanel.java index aba4906af42..d614593caa2 100644 --- a/Mage.Client/src/main/java/mage/client/tournament/TournamentPanel.java +++ b/Mage.Client/src/main/java/mage/client/tournament/TournamentPanel.java @@ -234,12 +234,15 @@ public class TournamentPanel extends javax.swing.JPanel { if (tournament.getStepStartTime() != null) { usedTime = Format.getDuration((tournament.getServerTime().getTime() - tournament.getStepStartTime().getTime()) / 1000); } - txtTournamentState.setText(tournament.getTournamentState() + " (" + usedTime + ") " + tournament.getRunningInfo()); + txtTournamentState.setText(tournament.getTournamentState() + " (" + usedTime + ")"); break; default: txtTournamentState.setText(tournament.getTournamentState()); break; } + if (!tournament.getRunningInfo().isEmpty()) { + txtTournamentState.setText(txtTournamentState.getText() + ", " + tournament.getRunningInfo()); + } if (txtEndTime == null) { return; diff --git a/Mage.Client/src/main/java/mage/client/util/CardRenderMode.java b/Mage.Client/src/main/java/mage/client/util/CardRenderMode.java new file mode 100644 index 00000000000..fb63c780d1a --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/CardRenderMode.java @@ -0,0 +1,59 @@ +package mage.client.util; + +import java.util.ArrayList; +import java.util.List; + +public enum CardRenderMode { + + MTGO("MTGO", 0), + IMAGE("Image", 1), + FORCED_M15("Forced M15", 2), + FORCED_RETRO("Forced Retro", 3); + + private final String text; + private final int id; + + CardRenderMode(String text, int id) { + this.text = text; + this.id = id; + } + + @Override + public String toString() { + return text; + } + + public String getText() { + return text; + } + + public int getId() { + return id; + } + + public static String[] toList() { + List list = new ArrayList<>(); + for (CardRenderMode mode : CardRenderMode.values()) { + list.add(mode.toString()); + } + return list.toArray(new String[0]); + } + + public static CardRenderMode fromId(int id) { + for (CardRenderMode mode : CardRenderMode.values()) { + if (mode.getId() == id) { + return mode; + } + } + return MTGO; + } + + public static CardRenderMode fromString(String text) { + for (CardRenderMode mode : CardRenderMode.values()) { + if (mode.text.equals(text)) { + return mode; + } + } + return MTGO; + } +} diff --git a/Mage.Client/src/main/java/mage/client/util/comparators/CardViewEDHPowerLevelComparator.java b/Mage.Client/src/main/java/mage/client/util/comparators/CardViewEDHPowerLevelComparator.java index 4e456b2155c..9018369f884 100644 --- a/Mage.Client/src/main/java/mage/client/util/comparators/CardViewEDHPowerLevelComparator.java +++ b/Mage.Client/src/main/java/mage/client/util/comparators/CardViewEDHPowerLevelComparator.java @@ -19,6 +19,7 @@ public class CardViewEDHPowerLevelComparator implements CardViewComparator { return "EDH: " + getPowerLevel(sample); } + // TODO: it's outdated code, must migrate to shared code from AbstractCommander private int getPowerLevel(CardView card) { int thisMaxPower = 0; diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeMTGO.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeMTGO.java index db9c89f93a2..c0851b79b5e 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeMTGO.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeMTGO.java @@ -50,6 +50,7 @@ public class CardPanelRenderModeMTGO extends CardPanel { private CardRenderer cardRenderer; private int updateArtImageStamp; + private final int cardRenderMode; private static class ImageKey { final BufferedImage artImage; @@ -84,7 +85,7 @@ public class CardPanelRenderModeMTGO extends CardPanel { sb.append((char) (this.view.isCanAttack() ? 1 : 0)); sb.append((char) (this.view.isCanBlock() ? 1 : 0)); sb.append((char) (this.view.isFaceDown() ? 1 : 0)); - sb.append((char) this.view.getFrameStyle().ordinal()); + sb.append((char) (this.view.getFrameStyle() != null ? this.view.getFrameStyle().ordinal() : -1)); if (this.view instanceof PermanentView) { sb.append((char) (((PermanentView) this.view).hasSummoningSickness() ? 1 : 0)); sb.append((char) (((PermanentView) this.view).getDamage())); @@ -143,12 +144,13 @@ public class CardPanelRenderModeMTGO extends CardPanel { } public CardPanelRenderModeMTGO(CardView newGameCard, UUID gameId, final boolean loadImage, ActionCallback callback, - final boolean foil, Dimension dimension, boolean needFullPermanentRender) { + final boolean foil, Dimension dimension, boolean needFullPermanentRender, int renderMode) { // Call to super super(newGameCard, gameId, loadImage, callback, foil, dimension, needFullPermanentRender); // Renderer - cardRenderer = cardRendererFactory.create(getGameCard()); + cardRenderMode = renderMode; + cardRenderer = cardRendererFactory.create(getGameCard(), cardRenderMode); // Draw the parts initialDraw(); @@ -265,7 +267,7 @@ public class CardPanelRenderModeMTGO extends CardPanel { // Update renderer cardImage = null; - cardRenderer = cardRendererFactory.create(getGameCard()); + cardRenderer = cardRendererFactory.create(getGameCard(), cardRenderMode); cardRenderer.setArtImage(artImage); // Repaint diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererFactory.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererFactory.java index 4be35b5e155..10f99aa662c 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererFactory.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererFactory.java @@ -2,6 +2,7 @@ package org.mage.card.arcane; import mage.cards.FrameStyle; import mage.client.dialog.PreferencesDialog; +import mage.client.util.CardRenderMode; import mage.view.CardView; /** @@ -13,12 +14,27 @@ public class CardRendererFactory { } public CardRenderer create(CardView card) { + return create(card, -1); + } + + public CardRenderer create(CardView card, int renderModeOverride) { if (card.isSplitCard()) { return new ModernSplitCardRenderer(card); - } else if (card.getFrameStyle().equals(FrameStyle.RETRO) || card.getFrameStyle().equals(FrameStyle.LEA_ORIGINAL_DUAL_LAND_ART_BASIC) || PreferencesDialog.getRenderRetroFrames()) { + } else if (shouldRenderRetro(card, renderModeOverride)) { + // TODO: implement split card renderer for retro cards return new RetroCardRenderer(card); } else { return new ModernCardRenderer(card); } } + + private static boolean shouldRenderRetro(CardView card, int renderModeOverride) { + int renderMode = PreferencesDialog.getRenderMode(); + if (renderModeOverride != -1) { + renderMode = renderModeOverride; + } + boolean renderMTGO = (card.getFrameStyle().equals(FrameStyle.RETRO) || card.getFrameStyle().equals(FrameStyle.LEA_ORIGINAL_DUAL_LAND_ART_BASIC)) && renderMode == CardRenderMode.MTGO.ordinal(); + boolean forcedRetro = renderMode == CardRenderMode.FORCED_RETRO.ordinal(); + return renderMTGO || forcedRetro; + } } diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java b/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java index c5fc47f5b51..e3288ff43c7 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java @@ -3,7 +3,7 @@ package org.mage.plugins.card; import mage.cards.MageCard; import mage.cards.MagePermanent; import mage.cards.action.ActionCallback; -import mage.client.util.GUISizeHelper; +import mage.client.util.*; import mage.interfaces.plugin.CardPlugin; import mage.view.CardView; import mage.view.CounterView; @@ -35,6 +35,8 @@ import java.util.List; import java.util.*; import java.util.concurrent.TimeUnit; +import static mage.client.util.CardRenderMode.*; + /** * {@link CardPlugin} implementation. * @@ -102,16 +104,19 @@ public class CardPluginImpl implements CardPlugin { * yet, so use old component based rendering for the split cards. */ private CardPanel makeCardPanel(CardView view, UUID gameId, boolean loadImage, ActionCallback callback, - boolean isFoil, Dimension dimension, int renderMode, boolean needFullPermanentRender) { - switch (renderMode) { - case 0: + boolean isFoil, Dimension dimension, int renderModeId, boolean needFullPermanentRender) { + CardRenderMode cardRenderMode = CardRenderMode.fromId(renderModeId); + switch (cardRenderMode) { + case MTGO: + case FORCED_M15: + case FORCED_RETRO: return new CardPanelRenderModeMTGO(view, gameId, loadImage, callback, isFoil, dimension, - needFullPermanentRender); - case 1: + needFullPermanentRender, renderModeId); + case IMAGE: return new CardPanelRenderModeImage(view, gameId, loadImage, callback, isFoil, dimension, needFullPermanentRender); default: - throw new IllegalStateException("Unknown render mode " + renderMode); + throw new IllegalStateException("Unknown render mode " + cardRenderMode); } } diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java index 9c1ae4f2f3f..7bbdd07853b 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java @@ -587,6 +587,7 @@ public class ScryfallImageSupportCards { add("TDC"); // Tarkir: Dragonstorm Commander add("FIN"); // Final Fantasy add("FIC"); // Final Fantasy Commander + add("FCA"); // Final Fantasy: Through the Ages add("SPE"); // Marvel's Spider-Man Eternal // Custom sets using Scryfall images - must provide a direct link for each card in directDownloadLinks diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java index a6479a6c40a..19aecedcf0e 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java @@ -2201,6 +2201,7 @@ public class ScryfallImageSupportTokens { put("WHO/Human/2", "https://api.scryfall.com/cards/twho/5/en?format=image"); put("WHO/Human Noble", "https://api.scryfall.com/cards/twho/7/en?format=image"); put("WHO/Mark of the Rani", "https://api.scryfall.com/cards/twho/15?format=image"); + put("WHO/Mutant", "https://api.scryfall.com/cards/twho/18?format=image"); put("WHO/Soldier", "https://api.scryfall.com/cards/twho/8?format=image"); put("WHO/Treasure/1", "https://api.scryfall.com/cards/twho/28?format=image"); put("WHO/Treasure/2", "https://api.scryfall.com/cards/twho/29?format=image"); diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPicturesService.java b/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPicturesService.java index 650eedf5e98..a8fb8ddc6c1 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPicturesService.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPicturesService.java @@ -853,7 +853,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements connection.startConnection(); if (connection.isConnected()) { - // custom headers (ues + // custom headers connection.setRequestHeaders(selectedSource.getHttpRequestHeaders(currentUrl)); try { diff --git a/Mage.Common/src/main/java/mage/utils/testers/ChooseAmountTestableDialog.java b/Mage.Common/src/main/java/mage/utils/testers/ChooseAmountTestableDialog.java index b95937a88b9..6a7847ec4fc 100644 --- a/Mage.Common/src/main/java/mage/utils/testers/ChooseAmountTestableDialog.java +++ b/Mage.Common/src/main/java/mage/utils/testers/ChooseAmountTestableDialog.java @@ -15,6 +15,8 @@ import java.util.List; /** * Part of testable game dialogs *

    + * It's a complex dialog with 2 steps: choose targets list + distribute amount between targets + *

    * Supported methods: * - player.chooseTarget(amount) * diff --git a/Mage.Common/src/main/java/mage/utils/testers/GetAmountTestableDialog.java b/Mage.Common/src/main/java/mage/utils/testers/GetAmountTestableDialog.java index 22a4659dd78..0a66bac8f9d 100644 --- a/Mage.Common/src/main/java/mage/utils/testers/GetAmountTestableDialog.java +++ b/Mage.Common/src/main/java/mage/utils/testers/GetAmountTestableDialog.java @@ -11,6 +11,8 @@ import java.util.List; /** * Part of testable game dialogs *

    + * Its simple dialog to get some amount (example: part of chooseTargetAmount) + *

    * Supported methods: * - player.getAmount() * diff --git a/Mage.Common/src/main/java/mage/utils/testers/GetMultiAmountTestableDialog.java b/Mage.Common/src/main/java/mage/utils/testers/GetMultiAmountTestableDialog.java new file mode 100644 index 00000000000..2c1f9046f3a --- /dev/null +++ b/Mage.Common/src/main/java/mage/utils/testers/GetMultiAmountTestableDialog.java @@ -0,0 +1,118 @@ +package mage.utils.testers; + +import mage.abilities.Ability; +import mage.constants.MultiAmountType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; +import mage.util.MultiAmountMessage; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Part of testable game dialogs + *

    + * Its simple dialog to get distributed value between multiple options (example: part of combat damage distributing) + *

    + * Supported methods: + * - player.getMultiAmountWithIndividualConstraints() + * - player.getMultiAmount() - simple version of constraints + * + * @author JayDi85 + */ +class GetMultiAmountTestableDialog extends BaseTestableDialog { + + boolean isYou; // who choose - you or opponent + int totalMin; + int totalMax; + List amountOptions = new ArrayList<>(); + + /** + * @param options min, max, default + */ + public GetMultiAmountTestableDialog(boolean isYou, String info, int totalMin, int totalMax, List> options) { + super(String.format("player.getMultiAmount(%s)", isYou ? "you" : "AI"), + String.format("%s, %d options from [%d-%d]", info, options.size(), totalMin, totalMax), + ""); + this.isYou = isYou; + this.totalMin = totalMin; + this.totalMax = totalMax; + int optionNumber = 0; + for (List single : options) { + optionNumber++; + String mes = "option " + optionNumber + " with html"; + this.amountOptions.add(new MultiAmountMessage(mes, single.get(0), single.get(1), single.get(2))); + } + } + + @Override + public List showDialog(Player player, Ability source, Game game, Player opponent) { + Player choosingPlayer = this.isYou ? player : opponent; + //String message = "message with html"; + List chooseRes; + List options = this.amountOptions.stream().map(MultiAmountMessage::copy).collect(Collectors.toList()); + chooseRes = choosingPlayer.getMultiAmountWithIndividualConstraints( + Outcome.Benefit, + options, + this.totalMin, + this.totalMax, + MultiAmountType.DAMAGE, + game + ); + + List result = new ArrayList<>(); + result.add(getGroup() + " - " + this.getName()); + int selectedIndex = -1; + int selectedTotal = 0; + for (Integer selectedValue : chooseRes) { + selectedIndex++; + selectedTotal += selectedValue; + MultiAmountMessage option = this.amountOptions.get(selectedIndex); + result.add(String.format("%d from [%d-%d, def %d]", + selectedValue, + option.min, + option.max, + option.defaultValue + )); + } + result.add("total selected: " + selectedTotal); + + return result; + } + + static public void register(TestableDialogsRunner runner) { + List isYous = Arrays.asList(false, true); + for (boolean isYou : isYous) { + // make sure default values are valid due min/max settings + + // single target + runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "one, 0 def", 0, 1, genSameOptions(1, 0, 1, 0))); + runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "one, 0 def", 0, 3, genSameOptions(1, 0, 3, 0))); + runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "one, 1 def", 1, 1, genSameOptions(1, 1, 1, 1))); + runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "one, 1 def", 1, 3, genSameOptions(1, 1, 3, 1))); + runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "one, 5 def", 0, 10, genSameOptions(1, 0, 10, 5))); + runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "one, 10 def", 10, 10, genSameOptions(1, 0, 10, 10))); + // multiple targets + runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "many, 0 def", 0, 5, genSameOptions(3, 0, 3, 0))); + runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "many, 0 def", 0, 5, genSameOptions(3, 0, 3, 0))); + runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "many, 1 def", 1, 5, genSameOptions(3, 1, 3, 1))); + runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "many, 1 def", 1, 5, genSameOptions(3, 1, 3, 1))); + runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "many, 20 def", 0, 60, genSameOptions(3, 0, 60, 20))); + runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "many, 20 def", 60, 60, genSameOptions(3, 0, 60, 20))); + // big lists + runner.registerDialog(new GetMultiAmountTestableDialog(isYou, "big list", 0, 100, genSameOptions(20, 0, 100, 0))); + } + } + + private static List> genSameOptions(int amount, int min, int max, int def) { + List> res = new ArrayList<>(); + for (int i = 0; i < amount; i++) { + // min, max, default + res.add(Arrays.asList(min, max, def)); + } + return res; + } +} diff --git a/Mage.Common/src/main/java/mage/utils/testers/TestableDialogsRunner.java b/Mage.Common/src/main/java/mage/utils/testers/TestableDialogsRunner.java index 6217877dbce..3c2cfb3840d 100644 --- a/Mage.Common/src/main/java/mage/utils/testers/TestableDialogsRunner.java +++ b/Mage.Common/src/main/java/mage/utils/testers/TestableDialogsRunner.java @@ -31,7 +31,7 @@ import java.util.stream.Collectors; * [x] choosePile * [x] announceX * [x] getAmount - * [ ] getMultiAmountWithIndividualConstraints // TODO: implement + * [x] getMultiAmountWithIndividualConstraints *

    * Support of priority dialogs (can be called by game engine, some can be implemented in theory): * --- priority @@ -75,6 +75,7 @@ public class TestableDialogsRunner { ChooseAmountTestableDialog.register(this); AnnounceXTestableDialog.register(this); GetAmountTestableDialog.register(this); + GetMultiAmountTestableDialog.register(this); } void registerDialog(TestableDialog dialog) { diff --git a/Mage.Common/src/main/java/mage/view/DraftView.java b/Mage.Common/src/main/java/mage/view/DraftView.java index 9b0100c74d5..8e9c84868c6 100644 --- a/Mage.Common/src/main/java/mage/view/DraftView.java +++ b/Mage.Common/src/main/java/mage/view/DraftView.java @@ -1,50 +1,63 @@ - - package mage.view; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; import mage.cards.ExpansionSet; import mage.game.draft.Draft; import mage.game.draft.DraftCube; import mage.game.draft.DraftPlayer; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + /** - * - * @author BetaSteward_at_googlemail.com + * @author BetaSteward_at_googlemail.com, JayDi85 */ public class DraftView implements Serializable { private static final long serialVersionUID = 1L; - private final List sets = new ArrayList<>(); + private final List setNames = new ArrayList<>(); private final List setCodes = new ArrayList<>(); private final int boosterNum; // starts with 1 private final int cardNum; // starts with 1 + private final boolean isCube; + private final List players = new ArrayList<>(); public DraftView(Draft draft) { - if (draft.getDraftCube() != null) { + this.isCube = draft.getDraftCube() != null; + if (this.isCube) { for (int i = 0; i < draft.getNumberBoosters(); i++) { DraftCube cube = draft.getDraftCube(); - sets.add(cube.getName()); + setNames.add(cube.getName()); setCodes.add(cube.getCode()); } } else { - for (ExpansionSet set: draft.getSets()) { - sets.add(set.getName()); + for (ExpansionSet set : draft.getSets()) { + setNames.add(set.getName()); setCodes.add(set.getCode()); } } this.boosterNum = draft.getBoosterNum(); this.cardNum = draft.getCardNum(); - for(DraftPlayer draftPlayer :draft.getPlayers()) { + for (DraftPlayer draftPlayer : draft.getPlayers()) { players.add(draftPlayer.getPlayer().getName()); } } - public List getSets() { - return sets; + public String getBoosterInfo(int index) { + if (index >= this.setCodes.size() || this.setCodes.size() != this.setNames.size()) { + return "error"; + } + + if (this.isCube) { + return this.setNames.get(index); + } else { + return String.join(" - ", this.setCodes.get(index), this.setNames.get(index)); + } + } + + public List getSetNames() { + return setNames; } public List getSetCodes() { diff --git a/Mage.Common/src/main/java/mage/view/MatchView.java b/Mage.Common/src/main/java/mage/view/MatchView.java index 2aa07d3010a..52a92f98d06 100644 --- a/Mage.Common/src/main/java/mage/view/MatchView.java +++ b/Mage.Common/src/main/java/mage/view/MatchView.java @@ -121,8 +121,10 @@ public class MatchView implements Serializable { for (TournamentPlayer tPlayer : table.getTournament().getPlayers()) { sb2.append(tPlayer.getPlayer().getName()).append(": ").append(tPlayer.getResults()).append(' '); } + } else if (table.getTournament().getOptions().getMatchOptions().isSingleGameTourney()) { + sb2.append("Started single game"); } else { - sb2.append("Canceled"); + sb2.append("Canceled"); } this.result = sb2.toString(); this.startTime = table.getTournament().getStartTime(); diff --git a/Mage.Common/src/main/java/mage/view/TableView.java b/Mage.Common/src/main/java/mage/view/TableView.java index 63abb2e3f5d..8f7e33e435a 100644 --- a/Mage.Common/src/main/java/mage/view/TableView.java +++ b/Mage.Common/src/main/java/mage/view/TableView.java @@ -134,6 +134,8 @@ public class TableView implements Serializable { // TOURNAMENT if (table.getTournament().getOptions().getNumberRounds() > 0) { this.gameType = this.gameType + ' ' + table.getTournament().getOptions().getNumberRounds() + " Rounds"; + } else if (table.getTournament().getOptions().getMatchOptions().isSingleGameTourney()) { + this.gameType = this.gameType + " Single Game"; } StringBuilder sb1 = new StringBuilder(); for (TournamentPlayer tp : table.getTournament().getPlayers()) { @@ -167,6 +169,10 @@ public class TableView implements Serializable { infoTextShort.append(", Pick time: ").append(draftOptions.getTiming().getShortName()); infoTextLong.append("
    Pick time: ").append(draftOptions.getTiming().getName()); } + if (table.getTournament().getOptions().getMatchOptions().isSingleGameTourney()) { + infoTextShort.append(", 1 GAME"); + infoTextLong.append("
    Single Game with all players (1 GAME)"); + } if (table.getTournament().getOptions().isWatchingAllowed()) { infoTextShort.append(", SP"); infoTextLong.append("
    Spectators allowed (SP)"); diff --git a/Mage.Common/src/main/java/mage/view/TournamentView.java b/Mage.Common/src/main/java/mage/view/TournamentView.java index 9da79bd8cad..48a7f9add3c 100644 --- a/Mage.Common/src/main/java/mage/view/TournamentView.java +++ b/Mage.Common/src/main/java/mage/view/TournamentView.java @@ -54,6 +54,8 @@ public class TournamentView implements Serializable { if (tournament.getTournamentState().equals("Drafting") && tournament.getDraft() != null) { runningInfo = "booster/card: " + tournament.getDraft().getBoosterNum() + '/' + (tournament.getDraft().getCardNum()); + } else if (tournament.getOptions().getMatchOptions().isSingleGameTourney()) { + runningInfo = "running single game match"; } else { runningInfo = ""; } diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/AbstractCommander.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/AbstractCommander.java index fa47ac8d512..99168e5ea3b 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/AbstractCommander.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/AbstractCommander.java @@ -19,7 +19,7 @@ import java.util.*; import java.util.stream.Stream; /** - * @author TheElk801 + * @author TheElk801, JayDi85 */ public abstract class AbstractCommander extends Constructed { @@ -88,13 +88,13 @@ public abstract class AbstractCommander extends Constructed { boolean valid = true; for (Card card : deck.getCards()) { if (!ManaUtil.isColorIdentityCompatible(colorIdentity, card.getColorIdentity())) { - addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + colorIdentity.toString() + ')', true); + addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (need " + colorIdentity + ", but get " + card.getColorIdentity() + ")", true); valid = false; } } for (Card card : deck.getSideboard()) { if (!ManaUtil.isColorIdentityCompatible(colorIdentity, card.getColorIdentity())) { - addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + colorIdentity.toString() + ')', true); + addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (need " + colorIdentity + ", but get " + card.getColorIdentity() + ")", true); valid = false; } } @@ -258,14 +258,18 @@ public abstract class AbstractCommander extends Constructed { } @Override - public int getEdhPowerLevel(Deck deck) { - if (deck == null) { - return 0; - } - + public int getEdhPowerLevel(Deck deck, List foundPowerCards, List foundInfo) { + // calculate power level and find all related cards with it to show in hints (see deck validation in deck editor) + // example: https://edhpowerlevel.com int edhPowerLevel = 0; int commanderColors = 0; int numberInfinitePieces = 0; + foundPowerCards.clear(); + foundInfo.clear(); + + if (deck == null) { + return 0; + } for (Card card : deck.getCards()) { @@ -330,6 +334,7 @@ public abstract class AbstractCommander extends Constructed { boolean yourOpponentsControl = false; boolean whenYouCast = false; + List cardStates = new ArrayList<>(); for (String str : card.getRules()) { String s = str.toLowerCase(Locale.ENGLISH); annihilator |= s.contains("annihilator"); @@ -406,178 +411,236 @@ public abstract class AbstractCommander extends Constructed { if (extraTurns) { thisMaxPower = Math.max(thisMaxPower, 7); + cardStates.add(String.format("extraTurns %d", 7)); } if (buyback) { thisMaxPower = Math.max(thisMaxPower, 6); + cardStates.add(String.format("buyback %d", 6)); } if (tutor) { thisMaxPower = Math.max(thisMaxPower, 6); + cardStates.add(String.format("tutor %d", 6)); } if (annihilator) { thisMaxPower = Math.max(thisMaxPower, 5); + cardStates.add(String.format("annihilator %d", 5)); } if (cantUntap) { thisMaxPower = Math.max(thisMaxPower, 5); + cardStates.add(String.format("cantUntap %d", 5)); } if (costLessEach) { thisMaxPower = Math.max(thisMaxPower, 5); + cardStates.add(String.format("costLessEach %d", 5)); } if (infect) { thisMaxPower = Math.max(thisMaxPower, 5); + cardStates.add(String.format("infect %d", 5)); } if (overload) { thisMaxPower = Math.max(thisMaxPower, 5); + cardStates.add(String.format("overload %d", 5)); } if (twiceAs) { thisMaxPower = Math.max(thisMaxPower, 5); + cardStates.add(String.format("twiceAs %d", 5)); } if (cascade) { thisMaxPower = Math.max(thisMaxPower, 4); + cardStates.add(String.format("cascade %d", 4)); } if (doesntUntap) { thisMaxPower = Math.max(thisMaxPower, 4); + cardStates.add(String.format("doesntUntap %d", 4)); } if (each) { thisMaxPower = Math.max(thisMaxPower, 4); + cardStates.add(String.format("each %d", 4)); } if (exileAll) { thisMaxPower = Math.max(thisMaxPower, 4); + cardStates.add(String.format("exileAll %d", 4)); } if (flash) { thisMaxPower = Math.max(thisMaxPower, 4); + cardStates.add(String.format("flash %d", 4)); } if (flashback) { thisMaxPower = Math.max(thisMaxPower, 4); + cardStates.add(String.format("flashback %d", 4)); } if (flicker) { thisMaxPower = Math.max(thisMaxPower, 4); + cardStates.add(String.format("flicker %d", 4)); } if (gainControl) { thisMaxPower = Math.max(thisMaxPower, 4); + cardStates.add(String.format("gainControl %d", 4)); } if (lifeTotalBecomes) { thisMaxPower = Math.max(thisMaxPower, 4); + cardStates.add(String.format("lifeTotalBecomes %d", 4)); } if (mayCastForFree) { thisMaxPower = Math.max(thisMaxPower, 4); + cardStates.add(String.format("mayCastForFree %d", 4)); } if (preventDamage) { thisMaxPower = Math.max(thisMaxPower, 4); + cardStates.add(String.format("preventDamage %d", 4)); } if (proliferate) { thisMaxPower = Math.max(thisMaxPower, 4); + cardStates.add(String.format("proliferate %d", 4)); } if (protection) { thisMaxPower = Math.max(thisMaxPower, 4); + cardStates.add(String.format("protection %d", 4)); } if (putUnderYourControl) { thisMaxPower = Math.max(thisMaxPower, 4); + cardStates.add(String.format("putUnderYourControl %d", 4)); } if (returnFromYourGY) { thisMaxPower = Math.max(thisMaxPower, 4); + cardStates.add(String.format("returnFromYourGY %d", 4)); } if (sacrifice) { thisMaxPower = Math.max(thisMaxPower, 2); + cardStates.add(String.format("sacrifice %d", 2)); } if (skip) { thisMaxPower = Math.max(thisMaxPower, 4); + cardStates.add(String.format("skip %d", 4)); } if (storm) { thisMaxPower = Math.max(thisMaxPower, 4); + cardStates.add(String.format("storm %d", 4)); } if (unblockable) { thisMaxPower = Math.max(thisMaxPower, 4); + cardStates.add(String.format("unblockable %d", 4)); } if (whenCounterThatSpell) { thisMaxPower = Math.max(thisMaxPower, 4); + cardStates.add(String.format("whenCounterThatSpell %d", 4)); } if (wheneverEnters) { thisMaxPower = Math.max(thisMaxPower, 4); + cardStates.add(String.format("wheneverEnters %d", 4)); } if (xCost) { thisMaxPower = Math.max(thisMaxPower, 4); + cardStates.add(String.format("xCost %d", 4)); } if (youControlTarget) { thisMaxPower = Math.max(thisMaxPower, 4); + cardStates.add(String.format("youControlTarget %d", 4)); } if (yourOpponentsControl) { thisMaxPower = Math.max(thisMaxPower, 4); + cardStates.add(String.format("yourOpponentsControl %d", 4)); } if (whenYouCast) { thisMaxPower = Math.max(thisMaxPower, 4); + cardStates.add(String.format("whenYouCast %d", 4)); } if (anyNumberOfTarget) { thisMaxPower = Math.max(thisMaxPower, 3); + cardStates.add(String.format("anyNumberOfTarget %d", 4)); } if (createToken) { thisMaxPower = Math.max(thisMaxPower, 3); + cardStates.add(String.format("createToken %d", 3)); } if (destroyAll) { thisMaxPower = Math.max(thisMaxPower, 3); + cardStates.add(String.format("destroyAll %d", 3)); } if (dredge) { thisMaxPower = Math.max(thisMaxPower, 3); + cardStates.add(String.format("dredge %d", 3)); } if (hexproof) { thisMaxPower = Math.max(thisMaxPower, 3); + cardStates.add(String.format("hexproof %d", 3)); } if (shroud) { thisMaxPower = Math.max(thisMaxPower, 3); + cardStates.add(String.format("shroud %d", 3)); } if (undying) { thisMaxPower = Math.max(thisMaxPower, 3); + cardStates.add(String.format("undying %d", 3)); } if (persist) { thisMaxPower = Math.max(thisMaxPower, 3); + cardStates.add(String.format("persist %d", 3)); } if (cantBe) { thisMaxPower = Math.max(thisMaxPower, 2); + cardStates.add(String.format("cantBe %d", 2)); } if (evoke) { thisMaxPower = Math.max(thisMaxPower, 2); + cardStates.add(String.format("evoke %d", 2)); } if (exile) { thisMaxPower = Math.max(thisMaxPower, 2); + cardStates.add(String.format("exile %d", 2)); } if (menace) { thisMaxPower = Math.max(thisMaxPower, 2); + cardStates.add(String.format("menace %d", 2)); } if (miracle) { thisMaxPower = Math.max(thisMaxPower, 2); + cardStates.add(String.format("miracle %d", 2)); } if (sliver) { thisMaxPower = Math.max(thisMaxPower, 2); + cardStates.add(String.format("sliver %d", 2)); } if (untapTarget) { thisMaxPower = Math.max(thisMaxPower, 2); + cardStates.add(String.format("untapTarget %d", 2)); } if (copy) { thisMaxPower = Math.max(thisMaxPower, 1); + cardStates.add(String.format("copy %d", 1)); } if (counter) { thisMaxPower = Math.max(thisMaxPower, 1); + cardStates.add(String.format("counter %d", 1)); } if (destroy) { thisMaxPower = Math.max(thisMaxPower, 1); + cardStates.add(String.format("destroy %d", 1)); } if (drawCards) { thisMaxPower = Math.max(thisMaxPower, 1); + cardStates.add(String.format("drawCards %d", 1)); } if (exalted) { thisMaxPower = Math.max(thisMaxPower, 1); + cardStates.add(String.format("exalted %d", 1)); } if (retrace) { thisMaxPower = Math.max(thisMaxPower, 1); + cardStates.add(String.format("retrace %d", 1)); } if (trample) { thisMaxPower = Math.max(thisMaxPower, 1); + cardStates.add(String.format("trample %d", 1)); } if (tutorBasic) { thisMaxPower = Math.max(thisMaxPower, 1); + cardStates.add(String.format("tutorBasic %d", 1)); } if (card.isPlaneswalker()) { thisMaxPower = Math.max(thisMaxPower, 6); + cardStates.add(String.format("isPlaneswalker %d", 6)); } String cn = card.getName().toLowerCase(Locale.ENGLISH); @@ -845,6 +908,7 @@ public abstract class AbstractCommander extends Constructed { || cn.equals("zealous conscripts") || cn.equals("zur the enchanter")) { thisMaxPower = Math.max(thisMaxPower, 12); + cardStates.add(String.format("saltiest card %d", 12)); } // Parts of infinite combos @@ -906,6 +970,7 @@ public abstract class AbstractCommander extends Constructed { || cn.equals("worthy cause") || cn.equals("yawgmoth's will") || cn.equals("zealous conscripts")) { thisMaxPower = Math.max(thisMaxPower, 15); + cardStates.add(String.format("infinite combo %d", 15)); numberInfinitePieces++; } @@ -951,11 +1016,26 @@ public abstract class AbstractCommander extends Constructed { || cn.equals("winota, joiner of forces") || cn.equals("yuriko, the tiger's shadow")) { thisMaxPower = Math.max(thisMaxPower, 20); + cardStates.add(String.format("game changer %d", 20)); } - } + + // keep card's level + if (!cardStates.isEmpty()) { + foundInfo.add(String.format("+%d from %s (%s)", + thisMaxPower, + card.getName(), + String.join(", ", cardStates) + )); + foundPowerCards.add(card.getName()); + } + + edhPowerLevel += thisMaxPower; + + } // cards list ObjectColor color = null; for (Card commander : deck.getSideboard()) { + List commanderStates = new ArrayList<>(); int thisMaxPower = 0; String cn = commander.getName().toLowerCase(Locale.ENGLISH); if (color == null) { @@ -1024,6 +1104,7 @@ public abstract class AbstractCommander extends Constructed { || cn.equals("vorinclex, voice of hunger") || cn.equals("zur the enchanter")) { thisMaxPower = Math.max(thisMaxPower, 25); + commanderStates.add(String.format("not fun commander (+%d)", 25)); } // Saltiest commanders @@ -1060,21 +1141,40 @@ public abstract class AbstractCommander extends Constructed { || cn.equals("xanathar, guild kingpin") || cn.equals("zur the enchanter")) { thisMaxPower = Math.max(thisMaxPower, 20); + commanderStates.add(String.format("saltiest commander (+%d)", 20)); } + + // keep commander's level + if (!commanderStates.isEmpty()) { + foundInfo.add(String.format("+%d from %s (%s)", + thisMaxPower, + commander.getName(), + String.join(", ", commanderStates) + )); + foundPowerCards.add(commander.getName()); + } + edhPowerLevel += thisMaxPower; } - edhPowerLevel += numberInfinitePieces * 18; - edhPowerLevel = Math.round(edhPowerLevel / 10); - if (edhPowerLevel >= 100) { - edhPowerLevel = 99; + if (numberInfinitePieces > 0) { + edhPowerLevel += numberInfinitePieces * 18; + foundInfo.add(String.format("+%d from %d infinite pieces", numberInfinitePieces * 18, numberInfinitePieces)); } - if (color != null) { - edhPowerLevel += (color.isWhite() ? 10000000 : 0); - edhPowerLevel += (color.isBlue() ? 1000000 : 0); - edhPowerLevel += (color.isBlack() ? 100000 : 0); - edhPowerLevel += (color.isRed() ? 10000 : 0); - edhPowerLevel += (color.isGreen() ? 1000 : 0); + + // block colored decks by table's edh power level were disabled + // it's better to show real edh power level and allow for direct values control + if (false) { + if (edhPowerLevel >= 100) { + edhPowerLevel = 99; + } + if (color != null) { + edhPowerLevel += (color.isWhite() ? 10000000 : 0); + edhPowerLevel += (color.isBlue() ? 1000000 : 0); + edhPowerLevel += (color.isBlack() ? 100000 : 0); + edhPowerLevel += (color.isRed() ? 10000 : 0); + edhPowerLevel += (color.isGreen() ? 1000 : 0); + } } return edhPowerLevel; } diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Brawl.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Brawl.java index 5badfb5cf60..ec5633d0d7e 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Brawl.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Brawl.java @@ -122,7 +122,7 @@ public class Brawl extends Constructed { && !(colorIdentity.isColorless() && basicsInDeck.size() == 1 && basicsInDeck.contains(card.getName()))) { - addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + colorIdentity.toString() + ')', true); + addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (need " + colorIdentity + ", but get " + card.getColorIdentity() + ")", true); valid = false; } } @@ -131,7 +131,7 @@ public class Brawl extends Constructed { && !(colorIdentity.isColorless() && basicsInDeck.size() == 1 && basicsInDeck.contains(card.getName()))) { - addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + colorIdentity.toString() + ')', true); + addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (need " + colorIdentity + ", but get " + card.getColorIdentity() + ")", true); valid = false; } } diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Oathbreaker.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Oathbreaker.java index e441f1e0448..0a64c51dc24 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Oathbreaker.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Oathbreaker.java @@ -189,7 +189,7 @@ public class Oathbreaker extends Constructed { for (Card card : deck.getCards()) { if (!ManaUtil.isColorIdentityCompatible(allCommandersColor, card.getColorIdentity())) { - addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + card.getColorIdentity() + ')', true); + addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (need " + allCommandersColor + ", but get " + card.getColorIdentity() + ")", true); valid = false; } } diff --git a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/CubeFromDeck.java b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/CubeFromDeck.java index 796ad4557de..4d307ace43f 100644 --- a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/CubeFromDeck.java +++ b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/CubeFromDeck.java @@ -6,12 +6,15 @@ import mage.cards.decks.DeckCardLists; import mage.game.draft.DraftCube; /** - * @author spjspj + * @author spjspj, JayDi85 */ public class CubeFromDeck extends DraftCube { + /** + * Calls on table create, can use any names + */ public CubeFromDeck(Deck cubeFromDeck) { - super("Cube From Deck"); + this(); if (cubeFromDeck == null) { return; @@ -21,5 +24,16 @@ public class CubeFromDeck extends DraftCube { for (DeckCardInfo card : cards.getCards()) { cubeCards.add(new CardIdentity(card.getCardName(), card.getSetCode(), card.getCardNumber())); } + + // add useful info about cubes, but don't print user defined data like deck name due security reasons + this.setUpdateInfo(String.format("%d cards", cubeCards.size())); + } + + /** + * Calls on server's startng, must use default name - that's name will see all users after connection + */ + public CubeFromDeck() { + // don't change default name - new tourney dialog use it to choose a cube's deck + super("Cube From Deck", "your custom cube", 0, 0, 0); } } diff --git a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/LegacyCube.java b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/LegacyCube.java index 5e6cd1ee0ee..b87a3ab71d2 100644 --- a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/LegacyCube.java +++ b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/LegacyCube.java @@ -3,515 +3,524 @@ package mage.tournament.cubes; import mage.game.draft.DraftCube; /** + * Legacy cube stop updates after 2021 + *

    + * Data sources: + * - official + * * @author LevelX2 */ public class LegacyCube extends DraftCube { public LegacyCube() { - super("MTGO Legacy Cube"); + super("MTGO Legacy Cube", "", 2021, 8, 19); - cubeCards.add(new CardIdentity("Accorder Paladin", "")); + cubeCards.add(new CardIdentity("Abbot of Keral Keep", "")); + cubeCards.add(new CardIdentity("Abrade", "")); cubeCards.add(new CardIdentity("Abrupt Decay", "")); cubeCards.add(new CardIdentity("Acidic Slime", "")); - cubeCards.add(new CardIdentity("Act of Aggression", "")); - cubeCards.add(new CardIdentity("Adarkar Wastes", "")); - cubeCards.add(new CardIdentity("Aether Vial", "")); - cubeCards.add(new CardIdentity("Ajani Goldmane", "")); - cubeCards.add(new CardIdentity("Ajani Steadfast", "")); + cubeCards.add(new CardIdentity("Agadeem's Awakening", "")); cubeCards.add(new CardIdentity("Ajani Vengeant", "")); - cubeCards.add(new CardIdentity("Ajani, Caller of the Pride", "")); - cubeCards.add(new CardIdentity("Ajani, Mentor of Heroes", "")); + cubeCards.add(new CardIdentity("All Is Dust", "")); + cubeCards.add(new CardIdentity("Allosaurus Shepherd", "")); + cubeCards.add(new CardIdentity("Alrund's Epiphany", "")); + cubeCards.add(new CardIdentity("Anax, Hardened in the Forge", "")); cubeCards.add(new CardIdentity("Ancestral Vision", "")); cubeCards.add(new CardIdentity("Ancient Grudge", "")); cubeCards.add(new CardIdentity("Ancient Tomb", "")); - cubeCards.add(new CardIdentity("Angel of Serenity", "")); - cubeCards.add(new CardIdentity("Angelic Destiny", "")); cubeCards.add(new CardIdentity("Anger of the Gods", "")); cubeCards.add(new CardIdentity("Animate Dead", "")); cubeCards.add(new CardIdentity("Arbor Elf", "")); - cubeCards.add(new CardIdentity("Arc Trail", "")); - cubeCards.add(new CardIdentity("Archangel of Thune", "")); + cubeCards.add(new CardIdentity("Arcane Artisan", "")); + cubeCards.add(new CardIdentity("Archangel Avacyn", "")); + cubeCards.add(new CardIdentity("Archon of Cruelty", "")); cubeCards.add(new CardIdentity("Arid Mesa", "")); cubeCards.add(new CardIdentity("Armageddon", "")); - cubeCards.add(new CardIdentity("Ashcloud Phoenix", "")); + cubeCards.add(new CardIdentity("Ash Barrens", "")); cubeCards.add(new CardIdentity("Ashiok, Nightmare Weaver", "")); cubeCards.add(new CardIdentity("Augur of Bolas", "")); cubeCards.add(new CardIdentity("Avacyn's Pilgrim", "")); cubeCards.add(new CardIdentity("Avalanche Riders", "")); cubeCards.add(new CardIdentity("Avenger of Zendikar", "")); cubeCards.add(new CardIdentity("Awakening Zone", "")); - cubeCards.add(new CardIdentity("Bad Moon", "")); cubeCards.add(new CardIdentity("Badlands", "")); cubeCards.add(new CardIdentity("Baleful Strix", "")); + cubeCards.add(new CardIdentity("Banefire", "")); cubeCards.add(new CardIdentity("Baneslayer Angel", "")); - cubeCards.add(new CardIdentity("Banisher Priest", "")); cubeCards.add(new CardIdentity("Banishing Light", "")); + cubeCards.add(new CardIdentity("Baral, Chief of Compliance", "")); cubeCards.add(new CardIdentity("Basalt Monolith", "")); - cubeCards.add(new CardIdentity("Basilisk Collar", "")); cubeCards.add(new CardIdentity("Batterskull", "")); - cubeCards.add(new CardIdentity("Battlefield Forge", "")); cubeCards.add(new CardIdentity("Bayou", "")); cubeCards.add(new CardIdentity("Beast Within", "")); - cubeCards.add(new CardIdentity("Beetleback Chief", "")); - cubeCards.add(new CardIdentity("Bident of Thassa", "")); + cubeCards.add(new CardIdentity("Bedevil", "")); + cubeCards.add(new CardIdentity("Bedlam Reveler", "")); + cubeCards.add(new CardIdentity("Biogenic Ooze", "")); cubeCards.add(new CardIdentity("Birds of Paradise", "")); + cubeCards.add(new CardIdentity("Birgi, God of Storytelling", "")); cubeCards.add(new CardIdentity("Birthing Pod", "")); cubeCards.add(new CardIdentity("Bitterblossom", "")); - cubeCards.add(new CardIdentity("Black Sun's Zenith", "")); + cubeCards.add(new CardIdentity("Blackcleave Cliffs", "")); cubeCards.add(new CardIdentity("Blade Splicer", "")); + cubeCards.add(new CardIdentity("Blessed Alliance", "")); cubeCards.add(new CardIdentity("Blood Crypt", "")); cubeCards.add(new CardIdentity("Bloodbraid Elf", "")); + cubeCards.add(new CardIdentity("Bloodchief's Thirst", "")); cubeCards.add(new CardIdentity("Bloodghast", "")); cubeCards.add(new CardIdentity("Bloodline Keeper", "")); - cubeCards.add(new CardIdentity("Bloodsoaked Champion", "")); cubeCards.add(new CardIdentity("Bloodstained Mire", "")); + cubeCards.add(new CardIdentity("Blooming Marsh", "")); + cubeCards.add(new CardIdentity("Bomat Courier", "")); cubeCards.add(new CardIdentity("Bone Shredder", "")); - cubeCards.add(new CardIdentity("Bonesplitter", "")); + cubeCards.add(new CardIdentity("Bonecrusher Giant", "")); cubeCards.add(new CardIdentity("Bonfire of the Damned", "")); - cubeCards.add(new CardIdentity("Boon Satyr", "")); - cubeCards.add(new CardIdentity("Borderland Marauder", "")); - cubeCards.add(new CardIdentity("Boros Charm", "")); - cubeCards.add(new CardIdentity("Boros Elite", "")); - cubeCards.add(new CardIdentity("Boros Reckoner", "")); - cubeCards.add(new CardIdentity("Brago, King Eternal", "")); - cubeCards.add(new CardIdentity("Brain Maggot", "")); + cubeCards.add(new CardIdentity("Botanical Sanctum", "")); cubeCards.add(new CardIdentity("Brainstorm", "")); + cubeCards.add(new CardIdentity("Brazen Borrower", "")); cubeCards.add(new CardIdentity("Breeding Pool", "")); - cubeCards.add(new CardIdentity("Brimaz, King of Oreskos", "")); - cubeCards.add(new CardIdentity("Brimstone Volley", "")); - cubeCards.add(new CardIdentity("Brushland", "")); - cubeCards.add(new CardIdentity("Burning-Tree Emissary", "")); + cubeCards.add(new CardIdentity("Brightling", "")); + cubeCards.add(new CardIdentity("Buried Alive", "")); cubeCards.add(new CardIdentity("Burst Lightning", "")); - cubeCards.add(new CardIdentity("Call of the Herd", "")); - cubeCards.add(new CardIdentity("Carnophage", "")); - cubeCards.add(new CardIdentity("Caves of Koilos", "")); + cubeCards.add(new CardIdentity("Careful Consideration", "")); + cubeCards.add(new CardIdentity("Cast Out", "")); + cubeCards.add(new CardIdentity("Casualties of War", "")); + cubeCards.add(new CardIdentity("Cavalier of Night", "")); + cubeCards.add(new CardIdentity("Cave of the Frost Dragon", "")); + cubeCards.add(new CardIdentity("Celestial Colonnade", "")); cubeCards.add(new CardIdentity("Chain Lightning", "")); - cubeCards.add(new CardIdentity("Chainer's Edict", "")); - cubeCards.add(new CardIdentity("Chameleon Colossus", "")); - cubeCards.add(new CardIdentity("Champion of the Parish", "")); - cubeCards.add(new CardIdentity("Chandra, Pyromaster", "")); - cubeCards.add(new CardIdentity("Chandra's Phoenix", "")); + cubeCards.add(new CardIdentity("Champion of Wits", "")); + cubeCards.add(new CardIdentity("Chandra, Acolyte of Flame", "")); + cubeCards.add(new CardIdentity("Chandra, Awakened Inferno", "")); + cubeCards.add(new CardIdentity("Chandra, Torch of Defiance", "")); cubeCards.add(new CardIdentity("Char", "")); - cubeCards.add(new CardIdentity("Chasm Skulker", "")); + cubeCards.add(new CardIdentity("Charming Prince", "")); + cubeCards.add(new CardIdentity("Chart a Course", "")); cubeCards.add(new CardIdentity("Chord of Calling", "")); - cubeCards.add(new CardIdentity("Chrome Mox", "")); + cubeCards.add(new CardIdentity("Chromatic Lantern", "")); + cubeCards.add(new CardIdentity("Circle of Dreams Druid", "")); cubeCards.add(new CardIdentity("City of Brass", "")); cubeCards.add(new CardIdentity("Clifftop Retreat", "")); - cubeCards.add(new CardIdentity("Cloudfin Raptor", "")); cubeCards.add(new CardIdentity("Cloudgoat Ranger", "")); cubeCards.add(new CardIdentity("Coalition Relic", "")); - cubeCards.add(new CardIdentity("Commune with the Gods", "")); + cubeCards.add(new CardIdentity("Coercive Portal", "")); + cubeCards.add(new CardIdentity("Coldsteel Heart", "")); + cubeCards.add(new CardIdentity("Collective Brutality", "")); + cubeCards.add(new CardIdentity("Collective Defiance", "")); cubeCards.add(new CardIdentity("Compulsive Research", "")); + cubeCards.add(new CardIdentity("Concealed Courtyard", "")); + cubeCards.add(new CardIdentity("Conclave Tribunal", "")); cubeCards.add(new CardIdentity("Condemn", "")); - cubeCards.add(new CardIdentity("Condescend", "")); cubeCards.add(new CardIdentity("Consecrated Sphinx", "")); - cubeCards.add(new CardIdentity("Consuming Vapors", "")); + cubeCards.add(new CardIdentity("Containment Priest", "")); cubeCards.add(new CardIdentity("Control Magic", "")); - cubeCards.add(new CardIdentity("Coralhelm Commander", "")); - cubeCards.add(new CardIdentity("Corrupt", "")); + cubeCards.add(new CardIdentity("Copperline Gorge", "")); cubeCards.add(new CardIdentity("Council's Judgment", "")); cubeCards.add(new CardIdentity("Counterspell", "")); cubeCards.add(new CardIdentity("Courser of Kruphix", "")); - cubeCards.add(new CardIdentity("Crater's Claws", "")); + cubeCards.add(new CardIdentity("Court of Bounty", "")); cubeCards.add(new CardIdentity("Craterhoof Behemoth", "")); - cubeCards.add(new CardIdentity("Crusade", "")); + cubeCards.add(new CardIdentity("Creeping Tar Pit", "")); + cubeCards.add(new CardIdentity("Crucible of Worlds", "")); + cubeCards.add(new CardIdentity("Cryptbreaker", "")); cubeCards.add(new CardIdentity("Cryptic Command", "")); cubeCards.add(new CardIdentity("Cultivate", "")); - cubeCards.add(new CardIdentity("Cunning Sparkmage", "")); - cubeCards.add(new CardIdentity("Curse of Predation", "")); - cubeCards.add(new CardIdentity("Cursed Scroll", "")); cubeCards.add(new CardIdentity("Cyclonic Rift", "")); + cubeCards.add(new CardIdentity("Dack Fayden", "")); + cubeCards.add(new CardIdentity("Dakkon, Shadow Slayer", "")); + cubeCards.add(new CardIdentity("Damn", "")); cubeCards.add(new CardIdentity("Damnation", "")); + cubeCards.add(new CardIdentity("Daretti, Ingenious Iconoclast", "")); cubeCards.add(new CardIdentity("Dark Confidant", "")); cubeCards.add(new CardIdentity("Dark Ritual", "")); - cubeCards.add(new CardIdentity("Darkblast", "")); + cubeCards.add(new CardIdentity("Darkslick Shores", "")); + cubeCards.add(new CardIdentity("Dauthi Voidwalker", "")); cubeCards.add(new CardIdentity("Day of Judgment", "")); cubeCards.add(new CardIdentity("Daze", "")); cubeCards.add(new CardIdentity("Deceiver Exarch", "")); - cubeCards.add(new CardIdentity("Deep Analysis", "")); + cubeCards.add(new CardIdentity("Deep Forest Hermit", "")); cubeCards.add(new CardIdentity("Delver of Secrets", "")); + cubeCards.add(new CardIdentity("Den Protector", "")); + cubeCards.add(new CardIdentity("Den of the Bugbear", "")); cubeCards.add(new CardIdentity("Deranged Hermit", "")); - cubeCards.add(new CardIdentity("Desecration Demon", "")); - cubeCards.add(new CardIdentity("Detention Sphere", "")); + cubeCards.add(new CardIdentity("Dermotaxi", "")); cubeCards.add(new CardIdentity("Devil's Play", "")); - cubeCards.add(new CardIdentity("Diabolic Servitude", "")); - cubeCards.add(new CardIdentity("Dictate of Heliod", "")); - cubeCards.add(new CardIdentity("Dig Through Time", "")); - cubeCards.add(new CardIdentity("Diregraf Ghoul", "")); - cubeCards.add(new CardIdentity("Disciple of Bolas", "")); - cubeCards.add(new CardIdentity("Disenchant", "")); - cubeCards.add(new CardIdentity("Disfigure", "")); + cubeCards.add(new CardIdentity("Devoted Druid", "")); cubeCards.add(new CardIdentity("Dismember", "")); - cubeCards.add(new CardIdentity("Dismiss", "")); - cubeCards.add(new CardIdentity("Dissolve", "")); - cubeCards.add(new CardIdentity("Domri Rade", "")); cubeCards.add(new CardIdentity("Doom Blade", "")); - cubeCards.add(new CardIdentity("Doomed Traveler", "")); - cubeCards.add(new CardIdentity("Dragon Fodder", "")); + cubeCards.add(new CardIdentity("Dragon's Rage Channeler", "")); + cubeCards.add(new CardIdentity("Dragonkin Berserker", "")); + cubeCards.add(new CardIdentity("Dragonlord Atarka", "")); + cubeCards.add(new CardIdentity("Dragonlord Silumgar", "")); cubeCards.add(new CardIdentity("Dragonskull Summit", "")); cubeCards.add(new CardIdentity("Dread Return", "")); - cubeCards.add(new CardIdentity("Dreadbore", "")); + cubeCards.add(new CardIdentity("Dread Wanderer", "")); cubeCards.add(new CardIdentity("Drowned Catacomb", "")); - cubeCards.add(new CardIdentity("Dualcaster Mage", "")); - cubeCards.add(new CardIdentity("Dungeon Geists", "")); cubeCards.add(new CardIdentity("Duplicant", "")); cubeCards.add(new CardIdentity("Duress", "")); + cubeCards.add(new CardIdentity("Earthshaker Giant", "")); cubeCards.add(new CardIdentity("Edric, Spymaster of Trest", "")); - cubeCards.add(new CardIdentity("Eight-and-a-Half-Tails", "")); + cubeCards.add(new CardIdentity("Elder Gargaroth", "")); cubeCards.add(new CardIdentity("Electrolyze", "")); cubeCards.add(new CardIdentity("Elesh Norn, Grand Cenobite", "")); - cubeCards.add(new CardIdentity("Elite Vanguard", "")); - cubeCards.add(new CardIdentity("Elspeth Tirel", "")); + cubeCards.add(new CardIdentity("Elite Spellbinder", "")); + cubeCards.add(new CardIdentity("Elspeth Conquers Death", "")); cubeCards.add(new CardIdentity("Elspeth, Knight-Errant", "")); cubeCards.add(new CardIdentity("Elspeth, Sun's Champion", "")); - cubeCards.add(new CardIdentity("Elves of Deep Shadow", "")); cubeCards.add(new CardIdentity("Elvish Mystic", "")); + cubeCards.add(new CardIdentity("Embercleave", "")); + cubeCards.add(new CardIdentity("Embereth Shieldbreaker", "")); cubeCards.add(new CardIdentity("Emeria Angel", "")); + cubeCards.add(new CardIdentity("Emeria's Call", "")); cubeCards.add(new CardIdentity("Emrakul, the Aeons Torn", "")); + cubeCards.add(new CardIdentity("Emrakul, the Promised End", "")); + cubeCards.add(new CardIdentity("Engineered Explosives", "")); cubeCards.add(new CardIdentity("Entomb", "")); - cubeCards.add(new CardIdentity("Entreat the Angels", "")); - cubeCards.add(new CardIdentity("Essence Scatter", "")); - cubeCards.add(new CardIdentity("Eternal Dragon", "")); + cubeCards.add(new CardIdentity("Escape to the Wilds", "")); + cubeCards.add(new CardIdentity("Esper Sentinel", "")); cubeCards.add(new CardIdentity("Eternal Witness", "")); - cubeCards.add(new CardIdentity("Eureka", "")); cubeCards.add(new CardIdentity("Everflowing Chalice", "")); - cubeCards.add(new CardIdentity("Exalted Angel", "")); cubeCards.add(new CardIdentity("Exhume", "")); cubeCards.add(new CardIdentity("Explore", "")); + cubeCards.add(new CardIdentity("Expressive Iteration", "")); + cubeCards.add(new CardIdentity("Exquisite Firecraft", "")); + cubeCards.add(new CardIdentity("Extinction Event", "")); + cubeCards.add(new CardIdentity("Fabled Passage", "")); cubeCards.add(new CardIdentity("Fact or Fiction", "")); cubeCards.add(new CardIdentity("Faith's Fetters", "")); + cubeCards.add(new CardIdentity("Faithless Looting", "")); cubeCards.add(new CardIdentity("Falkenrath Aristocrat", "")); - cubeCards.add(new CardIdentity("Far // Away", "")); + cubeCards.add(new CardIdentity("Fall from Favor", "")); + cubeCards.add(new CardIdentity("Fallen Shinobi", "")); cubeCards.add(new CardIdentity("Farseek", "")); + cubeCards.add(new CardIdentity("Fatal Push", "")); cubeCards.add(new CardIdentity("Fauna Shaman", "")); + cubeCards.add(new CardIdentity("Fblthp, the Lost", "")); + cubeCards.add(new CardIdentity("Feed the Swarm", "")); + cubeCards.add(new CardIdentity("Field of Ruin", "")); + cubeCards.add(new CardIdentity("Fiend Artisan", "")); + cubeCards.add(new CardIdentity("Fiery Confluence", "")); cubeCards.add(new CardIdentity("Figure of Destiny", "")); - cubeCards.add(new CardIdentity("Fire // Ice", "")); - cubeCards.add(new CardIdentity("Fireblast", "")); + cubeCards.add(new CardIdentity("Fire Prophecy", "")); cubeCards.add(new CardIdentity("Firebolt", "")); cubeCards.add(new CardIdentity("Firedrinker Satyr", "")); - cubeCards.add(new CardIdentity("Firefist Striker", "")); cubeCards.add(new CardIdentity("Flame Slash", "")); - cubeCards.add(new CardIdentity("Flametongue Kavu", "")); - cubeCards.add(new CardIdentity("Fleecemane Lion", "")); + cubeCards.add(new CardIdentity("Flametongue Yearling", "")); cubeCards.add(new CardIdentity("Flickerwisp", "")); cubeCards.add(new CardIdentity("Flooded Strand", "")); cubeCards.add(new CardIdentity("Forbid", "")); - cubeCards.add(new CardIdentity("Forbidden Alchemy", "")); + cubeCards.add(new CardIdentity("Force of Negation", "")); cubeCards.add(new CardIdentity("Force of Will", "")); - cubeCards.add(new CardIdentity("Force Spike", "")); - cubeCards.add(new CardIdentity("Frenzied Goblin", "")); - cubeCards.add(new CardIdentity("Freyalise, Llanowar's Fury", "")); - cubeCards.add(new CardIdentity("Frontline Medic", "")); + cubeCards.add(new CardIdentity("Fractured Identity", "")); cubeCards.add(new CardIdentity("Frost Titan", "")); - cubeCards.add(new CardIdentity("Future Sight", "")); cubeCards.add(new CardIdentity("Fyndhorn Elves", "")); cubeCards.add(new CardIdentity("Gaea's Cradle", "")); cubeCards.add(new CardIdentity("Garruk Relentless", "")); cubeCards.add(new CardIdentity("Garruk Wildspeaker", "")); - cubeCards.add(new CardIdentity("Garruk, Apex Predator", "")); - cubeCards.add(new CardIdentity("Garruk, Caller of Beasts", "")); cubeCards.add(new CardIdentity("Garruk, Primal Hunter", "")); cubeCards.add(new CardIdentity("Gatekeeper of Malakir", "")); - cubeCards.add(new CardIdentity("Gather the Townsfolk", "")); - cubeCards.add(new CardIdentity("Geist of Saint Traft", "")); cubeCards.add(new CardIdentity("Genesis Wave", "")); cubeCards.add(new CardIdentity("Geralf's Messenger", "")); - cubeCards.add(new CardIdentity("Gerrard's Verdict", "")); - cubeCards.add(new CardIdentity("Ghor-Clan Rampager", "")); + cubeCards.add(new CardIdentity("Gideon Blackblade", "")); cubeCards.add(new CardIdentity("Gideon Jura", "")); + cubeCards.add(new CardIdentity("Gideon, Ally of Zendikar", "")); cubeCards.add(new CardIdentity("Gifts Ungiven", "")); + cubeCards.add(new CardIdentity("Gilded Goose", "")); cubeCards.add(new CardIdentity("Gilded Lotus", "")); + cubeCards.add(new CardIdentity("Giver of Runes", "")); cubeCards.add(new CardIdentity("Glacial Fortress", "")); + cubeCards.add(new CardIdentity("Glare of Subdual", "")); + cubeCards.add(new CardIdentity("Glasspool Mimic", "")); cubeCards.add(new CardIdentity("Glen Elendra Archmage", "")); - cubeCards.add(new CardIdentity("Glorious Anthem", "")); - cubeCards.add(new CardIdentity("Gnarled Scarhide", "")); + cubeCards.add(new CardIdentity("Glorybringer", "")); cubeCards.add(new CardIdentity("Go for the Throat", "")); - cubeCards.add(new CardIdentity("Goblin Bombardment", "")); - cubeCards.add(new CardIdentity("Goblin Bushwhacker", "")); - cubeCards.add(new CardIdentity("Goblin Electromancer", "")); + cubeCards.add(new CardIdentity("Goblin Cratermaker", "")); cubeCards.add(new CardIdentity("Goblin Guide", "")); cubeCards.add(new CardIdentity("Goblin Rabblemaster", "")); - cubeCards.add(new CardIdentity("Goblin Wardriver", "")); cubeCards.add(new CardIdentity("Godless Shrine", "")); - cubeCards.add(new CardIdentity("Gore-House Chainwalker", "")); + cubeCards.add(new CardIdentity("Goldspan Dragon", "")); + cubeCards.add(new CardIdentity("Golos, Tireless Pilgrim", "")); + cubeCards.add(new CardIdentity("Gonti, Lord of Luxury", "")); cubeCards.add(new CardIdentity("Grafted Wargear", "")); cubeCards.add(new CardIdentity("Grave Titan", "")); - cubeCards.add(new CardIdentity("Graveborn Muse", "")); cubeCards.add(new CardIdentity("Gravecrawler", "")); cubeCards.add(new CardIdentity("Gray Merchant of Asphodel", "")); - cubeCards.add(new CardIdentity("Greater Gargadon", "")); cubeCards.add(new CardIdentity("Green Sun's Zenith", "")); cubeCards.add(new CardIdentity("Grim Lavamancer", "")); cubeCards.add(new CardIdentity("Grim Monolith", "")); cubeCards.add(new CardIdentity("Griselbrand", "")); - cubeCards.add(new CardIdentity("Grisly Salvage", "")); - cubeCards.add(new CardIdentity("Guttersnipe", "")); - cubeCards.add(new CardIdentity("Hall of Triumph", "")); + cubeCards.add(new CardIdentity("Grist, the Hunger Tide", "")); + cubeCards.add(new CardIdentity("Gutterbones", "")); cubeCards.add(new CardIdentity("Hallowed Fountain", "")); cubeCards.add(new CardIdentity("Hallowed Spiritkeeper", "")); - cubeCards.add(new CardIdentity("Hammer of Purphoros", "")); - cubeCards.add(new CardIdentity("Harmonize", "")); + cubeCards.add(new CardIdentity("Hangarback Walker", "")); + cubeCards.add(new CardIdentity("Hard Evidence", "")); + cubeCards.add(new CardIdentity("Hazoret the Fervent", "")); + cubeCards.add(new CardIdentity("Heartless Act", "")); + cubeCards.add(new CardIdentity("Hedron Archive", "")); cubeCards.add(new CardIdentity("Hellrider", "")); - cubeCards.add(new CardIdentity("Herald of Torment", "")); cubeCards.add(new CardIdentity("Hero of Bladehold", "")); - cubeCards.add(new CardIdentity("Hero of Oxid Ridge", "")); cubeCards.add(new CardIdentity("Hero's Downfall", "")); + cubeCards.add(new CardIdentity("Hexdrinker", "")); cubeCards.add(new CardIdentity("Hinterland Harbor", "")); + cubeCards.add(new CardIdentity("Hissing Quagmire", "")); + cubeCards.add(new CardIdentity("Hogaak, Arisen Necropolis", "")); cubeCards.add(new CardIdentity("Honor of the Pure", "")); - cubeCards.add(new CardIdentity("Hordeling Outburst", "")); + cubeCards.add(new CardIdentity("Horizon Canopy", "")); cubeCards.add(new CardIdentity("Hornet Queen", "")); cubeCards.add(new CardIdentity("Huntmaster of the Fells", "")); + cubeCards.add(new CardIdentity("Hydroid Krasis", "")); cubeCards.add(new CardIdentity("Hymn to Tourach", "")); - cubeCards.add(new CardIdentity("Hypnotic Specter", "")); + cubeCards.add(new CardIdentity("Ignoble Hierarch", "")); + cubeCards.add(new CardIdentity("Ilharg, the Raze-Boar", "")); cubeCards.add(new CardIdentity("Imperial Recruiter", "")); - cubeCards.add(new CardIdentity("Imposing Sovereign", "")); - cubeCards.add(new CardIdentity("Impulse", "")); cubeCards.add(new CardIdentity("Incinerate", "")); - cubeCards.add(new CardIdentity("Indrik Stomphowler", "")); cubeCards.add(new CardIdentity("Inferno Titan", "")); cubeCards.add(new CardIdentity("Inquisition of Kozilek", "")); + cubeCards.add(new CardIdentity("Inspiring Vantage", "")); cubeCards.add(new CardIdentity("Into the Roil", "")); - cubeCards.add(new CardIdentity("Intuition", "")); - cubeCards.add(new CardIdentity("Isamaru, Hound of Konda", "")); - cubeCards.add(new CardIdentity("Isochron Scepter", "")); + cubeCards.add(new CardIdentity("Iona, Shield of Emeria", "")); cubeCards.add(new CardIdentity("Isolated Chapel", "")); - cubeCards.add(new CardIdentity("Jace Beleren", "")); - cubeCards.add(new CardIdentity("Jace, Architect of Thought", "")); + cubeCards.add(new CardIdentity("Jace, Vryn's Prodigy", "")); cubeCards.add(new CardIdentity("Jace, the Mind Sculptor", "")); - cubeCards.add(new CardIdentity("Jackal Pup", "")); + cubeCards.add(new CardIdentity("Jadelight Ranger", "")); cubeCards.add(new CardIdentity("Joraga Treespeaker", "")); cubeCards.add(new CardIdentity("Journey to Nowhere", "")); - cubeCards.add(new CardIdentity("Kami of Ancient Law", "")); - cubeCards.add(new CardIdentity("Karmic Guide", "")); + cubeCards.add(new CardIdentity("Kaldra Compleat", "")); + cubeCards.add(new CardIdentity("Kalitas, Traitor of Ghet", "")); + cubeCards.add(new CardIdentity("Karakas", "")); cubeCards.add(new CardIdentity("Karn Liberated", "")); - cubeCards.add(new CardIdentity("Karplusan Forest", "")); - cubeCards.add(new CardIdentity("Keiga, the Tide Star", "")); - cubeCards.add(new CardIdentity("Keranos, God of Storms", "")); + cubeCards.add(new CardIdentity("Karn, Scion of Urza", "")); cubeCards.add(new CardIdentity("Kiki-Jiki, Mirror Breaker", "")); - cubeCards.add(new CardIdentity("Kiln Fiend", "")); - cubeCards.add(new CardIdentity("Kiora, the Crashing Wave", "")); - cubeCards.add(new CardIdentity("Kira, Great Glass-Spinner", "")); cubeCards.add(new CardIdentity("Kitchen Finks", "")); - cubeCards.add(new CardIdentity("Knight of Infamy", "")); - cubeCards.add(new CardIdentity("Kodama's Reach", "")); - cubeCards.add(new CardIdentity("Kokusho, the Evening Star", "")); - cubeCards.add(new CardIdentity("Kor Skyfisher", "")); + cubeCards.add(new CardIdentity("Kitesail Freebooter", "")); + cubeCards.add(new CardIdentity("Klothys, God of Destiny", "")); + cubeCards.add(new CardIdentity("Knight of Autumn", "")); + cubeCards.add(new CardIdentity("Kolaghan's Command", "")); + cubeCards.add(new CardIdentity("Koma, Cosmos Serpent", "")); cubeCards.add(new CardIdentity("Koth of the Hammer", "")); cubeCards.add(new CardIdentity("Kozilek, Butcher of Truth", "")); - cubeCards.add(new CardIdentity("Krenko's Command", "")); - cubeCards.add(new CardIdentity("Krosan Grip", "")); + cubeCards.add(new CardIdentity("Kroxa, Titan of Death's Hunger", "")); + cubeCards.add(new CardIdentity("Kytheon, Hero of Akros", "")); cubeCards.add(new CardIdentity("Land Tax", "")); - cubeCards.add(new CardIdentity("Legacy's Allure", "")); - cubeCards.add(new CardIdentity("Lifebane Zombie", "")); + cubeCards.add(new CardIdentity("Languish", "")); + cubeCards.add(new CardIdentity("Lava Coil", "")); + cubeCards.add(new CardIdentity("Lavaclaw Reaches", "")); + cubeCards.add(new CardIdentity("Legion Warboss", "")); + cubeCards.add(new CardIdentity("Legion's Landing", "")); cubeCards.add(new CardIdentity("Lightning Bolt", "")); cubeCards.add(new CardIdentity("Lightning Greaves", "")); cubeCards.add(new CardIdentity("Lightning Helix", "")); - cubeCards.add(new CardIdentity("Lightning Mauler", "")); + cubeCards.add(new CardIdentity("Lightning Phoenix", "")); cubeCards.add(new CardIdentity("Lightning Strike", "")); cubeCards.add(new CardIdentity("Liliana of the Veil", "")); - cubeCards.add(new CardIdentity("Liliana Vess", "")); + cubeCards.add(new CardIdentity("Liliana, the Last Hope", "")); cubeCards.add(new CardIdentity("Lingering Souls", "")); - cubeCards.add(new CardIdentity("Linvala, Keeper of Silence", "")); cubeCards.add(new CardIdentity("Living Death", "")); cubeCards.add(new CardIdentity("Llanowar Elves", "")); - cubeCards.add(new CardIdentity("Llanowar Wastes", "")); + cubeCards.add(new CardIdentity("Lolth, Spider Queen", "")); + cubeCards.add(new CardIdentity("Lonis, Cryptozoologist", "")); cubeCards.add(new CardIdentity("Looter il-Kor", "")); - cubeCards.add(new CardIdentity("Lotleth Troll", "")); cubeCards.add(new CardIdentity("Lotus Cobra", "")); - cubeCards.add(new CardIdentity("Loxodon Warhammer", "")); - cubeCards.add(new CardIdentity("Madcap Skills", "")); - cubeCards.add(new CardIdentity("Maelstrom Pulse", "")); - cubeCards.add(new CardIdentity("Magma Jet", "")); + cubeCards.add(new CardIdentity("Lumbering Falls", "")); + cubeCards.add(new CardIdentity("Luminarch Aspirant", "")); + cubeCards.add(new CardIdentity("Lyra Dawnbringer", "")); + cubeCards.add(new CardIdentity("Magda, Brazen Outlaw", "")); + cubeCards.add(new CardIdentity("Magister of Worth", "")); + cubeCards.add(new CardIdentity("Magmatic Channeler", "")); + cubeCards.add(new CardIdentity("Magus of the Order", "")); + cubeCards.add(new CardIdentity("Makeshift Mannequin", "")); + cubeCards.add(new CardIdentity("Malicious Affliction", "")); cubeCards.add(new CardIdentity("Man-o'-War", "")); cubeCards.add(new CardIdentity("Mana Confluence", "")); cubeCards.add(new CardIdentity("Mana Leak", "")); cubeCards.add(new CardIdentity("Mana Tithe", "")); cubeCards.add(new CardIdentity("Marsh Flats", "")); - cubeCards.add(new CardIdentity("Martial Coup", "")); cubeCards.add(new CardIdentity("Massacre Wurm", "")); - cubeCards.add(new CardIdentity("Master of the Feast", "")); cubeCards.add(new CardIdentity("Master of the Wild Hunt", "")); - cubeCards.add(new CardIdentity("Master of Waves", "")); - cubeCards.add(new CardIdentity("Meloku the Clouded Mirror", "")); - cubeCards.add(new CardIdentity("Mentor of the Meek", "")); + cubeCards.add(new CardIdentity("Maul of the Skyclaves", "")); cubeCards.add(new CardIdentity("Merfolk Looter", "")); - cubeCards.add(new CardIdentity("Mesmeric Fiend", "")); + cubeCards.add(new CardIdentity("Midnight Reaper", "")); + cubeCards.add(new CardIdentity("Mimic Vat", "")); + cubeCards.add(new CardIdentity("Mind Shatter", "")); cubeCards.add(new CardIdentity("Mind Stone", "")); + cubeCards.add(new CardIdentity("Mindslaver", "")); cubeCards.add(new CardIdentity("Mirari's Wake", "")); - cubeCards.add(new CardIdentity("Mirran Crusader", "")); cubeCards.add(new CardIdentity("Miscalculation", "")); cubeCards.add(new CardIdentity("Mishra's Factory", "")); cubeCards.add(new CardIdentity("Misty Rainforest", "")); - cubeCards.add(new CardIdentity("Mizzium Mortars", "")); - cubeCards.add(new CardIdentity("Mogg Fanatic", "")); - cubeCards.add(new CardIdentity("Mogg War Marshal", "")); - cubeCards.add(new CardIdentity("Molten Rain", "")); - cubeCards.add(new CardIdentity("Molten-Tail Masticore", "")); - cubeCards.add(new CardIdentity("Momentary Blink", "")); + cubeCards.add(new CardIdentity("Monastery Mentor", "")); + cubeCards.add(new CardIdentity("Monastery Swiftspear", "")); cubeCards.add(new CardIdentity("Mother of Runes", "")); - cubeCards.add(new CardIdentity("Mox Diamond", "")); - cubeCards.add(new CardIdentity("Mulch", "")); cubeCards.add(new CardIdentity("Mulldrifter", "")); - cubeCards.add(new CardIdentity("Murderous Cut", "")); - cubeCards.add(new CardIdentity("Murderous Redcap", "")); - cubeCards.add(new CardIdentity("Mutagenic Growth", "")); + cubeCards.add(new CardIdentity("Murderous Rider", "")); + cubeCards.add(new CardIdentity("Murktide Regent", "")); + cubeCards.add(new CardIdentity("Murmuring Mystic", "")); cubeCards.add(new CardIdentity("Mutavault", "")); cubeCards.add(new CardIdentity("Myr Battlesphere", "")); - cubeCards.add(new CardIdentity("Mystic Snake", "")); - cubeCards.add(new CardIdentity("Mystical Teachings", "")); - cubeCards.add(new CardIdentity("Nantuko Shade", "")); + cubeCards.add(new CardIdentity("Mystic Confluence", "")); + cubeCards.add(new CardIdentity("Nahiri, the Harbinger", "")); + cubeCards.add(new CardIdentity("Narset, Parter of Veils", "")); cubeCards.add(new CardIdentity("Natural Order", "")); - cubeCards.add(new CardIdentity("Naturalize", "")); + cubeCards.add(new CardIdentity("Necromancy", "")); + cubeCards.add(new CardIdentity("Needle Spires", "")); cubeCards.add(new CardIdentity("Negate", "")); cubeCards.add(new CardIdentity("Nekrataal", "")); cubeCards.add(new CardIdentity("Nicol Bolas, Planeswalker", "")); cubeCards.add(new CardIdentity("Night's Whisper", "")); - cubeCards.add(new CardIdentity("Nighthowler", "")); - cubeCards.add(new CardIdentity("Nightveil Specter", "")); - cubeCards.add(new CardIdentity("Nissa, Worldwaker", "")); + cubeCards.add(new CardIdentity("Nighthawk Scavenger", "")); + cubeCards.add(new CardIdentity("Nissa, Vastwood Seer", "")); + cubeCards.add(new CardIdentity("Nissa, Who Shakes the World", "")); + cubeCards.add(new CardIdentity("Niv-Mizzet Reborn", "")); + cubeCards.add(new CardIdentity("Niv-Mizzet, Parun", "")); cubeCards.add(new CardIdentity("Noble Hierarch", "")); cubeCards.add(new CardIdentity("Nykthos, Shrine to Nyx", "")); + cubeCards.add(new CardIdentity("Ob Nixilis Reignited", "")); cubeCards.add(new CardIdentity("Oblivion Ring", "")); - cubeCards.add(new CardIdentity("Obstinate Baloth", "")); - cubeCards.add(new CardIdentity("Old Man of the Sea", "")); - cubeCards.add(new CardIdentity("Olivia Voldaren", "")); + cubeCards.add(new CardIdentity("Oblivion Stone", "")); + cubeCards.add(new CardIdentity("Obosh, the Preypiercer", "")); + cubeCards.add(new CardIdentity("Omnath, Locus of Creation", "")); + cubeCards.add(new CardIdentity("Oona's Prowler", "")); cubeCards.add(new CardIdentity("Ophiomancer", "")); cubeCards.add(new CardIdentity("Opposition", "")); + cubeCards.add(new CardIdentity("Opt", "")); cubeCards.add(new CardIdentity("Oracle of Mul Daya", "")); cubeCards.add(new CardIdentity("Oust", "")); + cubeCards.add(new CardIdentity("Outpost Siege", "")); cubeCards.add(new CardIdentity("Overgrown Battlement", "")); cubeCards.add(new CardIdentity("Overgrown Tomb", "")); + cubeCards.add(new CardIdentity("Pack Rat", "")); cubeCards.add(new CardIdentity("Pact of Negation", "")); - cubeCards.add(new CardIdentity("Pain Seer", "")); + cubeCards.add(new CardIdentity("Palace Jailer", "")); + cubeCards.add(new CardIdentity("Paladin Class", "")); cubeCards.add(new CardIdentity("Parallax Wave", "")); cubeCards.add(new CardIdentity("Path to Exile", "")); cubeCards.add(new CardIdentity("Pestermite", "")); cubeCards.add(new CardIdentity("Phantasmal Image", "")); - cubeCards.add(new CardIdentity("Phantom Centaur", "")); cubeCards.add(new CardIdentity("Phyrexian Arena", "")); cubeCards.add(new CardIdentity("Phyrexian Metamorph", "")); cubeCards.add(new CardIdentity("Phyrexian Obliterator", "")); cubeCards.add(new CardIdentity("Phyrexian Revoker", "")); - cubeCards.add(new CardIdentity("Pillar of Flame", "")); + cubeCards.add(new CardIdentity("Pia Nalaar", "")); + cubeCards.add(new CardIdentity("Pia and Kiran Nalaar", "")); cubeCards.add(new CardIdentity("Plateau", "")); cubeCards.add(new CardIdentity("Plow Under", "")); cubeCards.add(new CardIdentity("Polluted Delta", "")); cubeCards.add(new CardIdentity("Polukranos, World Eater", "")); cubeCards.add(new CardIdentity("Ponder", "")); cubeCards.add(new CardIdentity("Porcelain Legionnaire", "")); - cubeCards.add(new CardIdentity("Precinct Captain", "")); - cubeCards.add(new CardIdentity("Precursor Golem", "")); + cubeCards.add(new CardIdentity("Portent", "")); + cubeCards.add(new CardIdentity("Power Word Kill", "")); cubeCards.add(new CardIdentity("Preordain", "")); + cubeCards.add(new CardIdentity("Priest of Fell Rites", "")); cubeCards.add(new CardIdentity("Primal Command", "")); - cubeCards.add(new CardIdentity("Prime Speaker Zegana", "")); + cubeCards.add(new CardIdentity("Primal Might", "")); cubeCards.add(new CardIdentity("Primeval Titan", "")); - cubeCards.add(new CardIdentity("Profane Command", "")); - cubeCards.add(new CardIdentity("Progenitus", "")); - cubeCards.add(new CardIdentity("Prophetic Bolt", "")); - cubeCards.add(new CardIdentity("Prophetic Flamespeaker", "")); - cubeCards.add(new CardIdentity("Psychatog", "")); - cubeCards.add(new CardIdentity("Purphoros, God of the Forge", "")); - cubeCards.add(new CardIdentity("Qasali Pridemage", "")); - cubeCards.add(new CardIdentity("Raise the Alarm", "")); - cubeCards.add(new CardIdentity("Rakdos Cackler", "")); - cubeCards.add(new CardIdentity("Ral Zarek", "")); + cubeCards.add(new CardIdentity("Prismari Command", "")); + cubeCards.add(new CardIdentity("Prismatic Ending", "")); + cubeCards.add(new CardIdentity("Prismatic Lens", "")); + cubeCards.add(new CardIdentity("Pteramander", "")); + cubeCards.add(new CardIdentity("Pyroclasm", "")); + cubeCards.add(new CardIdentity("Questing Beast", "")); + cubeCards.add(new CardIdentity("Ragavan, Nimble Pilferer", "")); + cubeCards.add(new CardIdentity("Raging Ravine", "")); + cubeCards.add(new CardIdentity("Rampaging Ferocidon", "")); cubeCards.add(new CardIdentity("Rampant Growth", "")); - cubeCards.add(new CardIdentity("Rancor", "")); - cubeCards.add(new CardIdentity("Ranger of Eos", "")); + cubeCards.add(new CardIdentity("Ranger Class", "")); + cubeCards.add(new CardIdentity("Ranger-Captain of Eos", "")); + cubeCards.add(new CardIdentity("Rankle, Master of Pranks", "")); + cubeCards.add(new CardIdentity("Rattleclaw Mystic", "")); cubeCards.add(new CardIdentity("Ravages of War", "")); + cubeCards.add(new CardIdentity("Ravenous Chupacabra", "")); + cubeCards.add(new CardIdentity("Read the Bones", "")); cubeCards.add(new CardIdentity("Reanimate", "")); cubeCards.add(new CardIdentity("Reclamation Sage", "")); + cubeCards.add(new CardIdentity("Recruiter of the Guard", "")); cubeCards.add(new CardIdentity("Recurring Nightmare", "")); - cubeCards.add(new CardIdentity("Reflecting Pool", "")); - cubeCards.add(new CardIdentity("Regrowth", "")); + cubeCards.add(new CardIdentity("Rekindling Phoenix", "")); + cubeCards.add(new CardIdentity("Relic of Progenitus", "")); cubeCards.add(new CardIdentity("Remand", "")); + cubeCards.add(new CardIdentity("Remorseful Cleric", "")); cubeCards.add(new CardIdentity("Remove Soul", "")); cubeCards.add(new CardIdentity("Repeal", "")); cubeCards.add(new CardIdentity("Restoration Angel", "")); + cubeCards.add(new CardIdentity("Retrofitter Foundry", "")); cubeCards.add(new CardIdentity("Reveillark", "")); cubeCards.add(new CardIdentity("Rift Bolt", "")); + cubeCards.add(new CardIdentity("Rift Sower", "")); cubeCards.add(new CardIdentity("Riftwing Cloudskate", "")); - cubeCards.add(new CardIdentity("Riptide Laboratory", "")); cubeCards.add(new CardIdentity("Rishadan Port", "")); cubeCards.add(new CardIdentity("Rofellos, Llanowar Emissary", "")); + cubeCards.add(new CardIdentity("Roil Eruption", "")); cubeCards.add(new CardIdentity("Rootbound Crag", "")); + cubeCards.add(new CardIdentity("Rotting Regisaur", "")); + cubeCards.add(new CardIdentity("Runaway Steam-Kin", "")); cubeCards.add(new CardIdentity("Sacred Foundry", "")); - cubeCards.add(new CardIdentity("Sakashima's Student", "")); + cubeCards.add(new CardIdentity("Saheeli, Sublime Artificer", "")); cubeCards.add(new CardIdentity("Sakura-Tribe Elder", "")); - cubeCards.add(new CardIdentity("Sarcomancy", "")); - cubeCards.add(new CardIdentity("Sarkhan, the Dragonspeaker", "")); cubeCards.add(new CardIdentity("Satyr Wayfinder", "")); cubeCards.add(new CardIdentity("Savannah", "")); - cubeCards.add(new CardIdentity("Savannah Lions", "")); + //cubeCards.add(new CardIdentity("Sawtusk Demolisher", "")); cubeCards.add(new CardIdentity("Scalding Tarn", "")); cubeCards.add(new CardIdentity("Scavenging Ooze", "")); - cubeCards.add(new CardIdentity("Scorched Rusalka", "")); cubeCards.add(new CardIdentity("Scrubland", "")); - cubeCards.add(new CardIdentity("Sea Gate Oracle", "")); - cubeCards.add(new CardIdentity("Seal of Cleansing", "")); - cubeCards.add(new CardIdentity("Seal of Fire", "")); + cubeCards.add(new CardIdentity("Seachrome Coast", "")); + cubeCards.add(new CardIdentity("Search for Azcanta", "")); cubeCards.add(new CardIdentity("Search for Tomorrow", "")); - cubeCards.add(new CardIdentity("Searing Blaze", "")); - cubeCards.add(new CardIdentity("Searing Blood", "")); cubeCards.add(new CardIdentity("Searing Spear", "")); - cubeCards.add(new CardIdentity("Seeker of the Way", "")); - cubeCards.add(new CardIdentity("Sensei's Divining Top", "")); - cubeCards.add(new CardIdentity("Serendib Efreet", "")); + cubeCards.add(new CardIdentity("Seasoned Pyromancer", "")); + cubeCards.add(new CardIdentity("Selfless Spirit", "")); + cubeCards.add(new CardIdentity("Serra's Emissary", "")); cubeCards.add(new CardIdentity("Serum Visions", "")); - cubeCards.add(new CardIdentity("Setessan Tactics", "")); - cubeCards.add(new CardIdentity("Shadowmage Infiltrator", "")); + cubeCards.add(new CardIdentity("Shambling Vent", "")); cubeCards.add(new CardIdentity("Shardless Agent", "")); + cubeCards.add(new CardIdentity("Shark Typhoon", "")); + cubeCards.add(new CardIdentity("Shelldock Isle", "")); cubeCards.add(new CardIdentity("Sheoldred, Whispering One", "")); - cubeCards.add(new CardIdentity("Shivan Reef", "")); + cubeCards.add(new CardIdentity("Shivan Fire", "")); cubeCards.add(new CardIdentity("Show and Tell", "")); + cubeCards.add(new CardIdentity("Showdown of the Skalds", "")); cubeCards.add(new CardIdentity("Shriekmaw", "")); - cubeCards.add(new CardIdentity("Shrine of Burning Rage", "")); cubeCards.add(new CardIdentity("Siege-Gang Commander", "")); - cubeCards.add(new CardIdentity("Silverblade Paladin", "")); - cubeCards.add(new CardIdentity("Sin Collector", "")); - cubeCards.add(new CardIdentity("Sinkhole", "")); - cubeCards.add(new CardIdentity("Skinrender", "")); - cubeCards.add(new CardIdentity("Skullcrack", "")); - cubeCards.add(new CardIdentity("Slagstorm", "")); - cubeCards.add(new CardIdentity("Slaughter Pact", "")); - cubeCards.add(new CardIdentity("Smash to Smithereens", "")); + cubeCards.add(new CardIdentity("Skyclave Apparition", "")); + cubeCards.add(new CardIdentity("Skyclave Shade", "")); + cubeCards.add(new CardIdentity("Slash the Ranks", "")); + cubeCards.add(new CardIdentity("Smuggler's Copter", "")); cubeCards.add(new CardIdentity("Snapcaster Mage", "")); cubeCards.add(new CardIdentity("Sneak Attack", "")); - cubeCards.add(new CardIdentity("Soldier of the Pantheon", "")); cubeCards.add(new CardIdentity("Solemn Simulacrum", "")); - cubeCards.add(new CardIdentity("Song of the Dryads", "")); - cubeCards.add(new CardIdentity("Sorin Markov", "")); - cubeCards.add(new CardIdentity("Sorin, Lord of Innistrad", "")); - cubeCards.add(new CardIdentity("Sorin, Solemn Visitor", "")); - cubeCards.add(new CardIdentity("Soul of Innistrad", "")); + cubeCards.add(new CardIdentity("Solitude", "")); cubeCards.add(new CardIdentity("Sower of Temptation", "")); cubeCards.add(new CardIdentity("Spear of Heliod", "")); cubeCards.add(new CardIdentity("Spectral Procession", "")); + cubeCards.add(new CardIdentity("Spellseeker", "")); cubeCards.add(new CardIdentity("Spellskite", "")); - cubeCards.add(new CardIdentity("Sphere of the Suns", "")); cubeCards.add(new CardIdentity("Sphinx's Revelation", "")); - cubeCards.add(new CardIdentity("Spikeshot Elder", "")); - cubeCards.add(new CardIdentity("Spiteful Returned", "")); + cubeCards.add(new CardIdentity("Spirebluff Canal", "")); cubeCards.add(new CardIdentity("Splinter Twin", "")); - cubeCards.add(new CardIdentity("Staggershock", "")); + cubeCards.add(new CardIdentity("Starnheim Unleashed", "")); cubeCards.add(new CardIdentity("Steam Vents", "")); - cubeCards.add(new CardIdentity("Stinkweed Imp", "")); + cubeCards.add(new CardIdentity("Stirring Wildwood", "")); cubeCards.add(new CardIdentity("Stoke the Flames", "")); cubeCards.add(new CardIdentity("Stomping Ground", "")); - cubeCards.add(new CardIdentity("Stormbreath Dragon", "")); - cubeCards.add(new CardIdentity("Stroke of Genius", "")); - cubeCards.add(new CardIdentity("Stromkirk Noble", "")); + cubeCards.add(new CardIdentity("Stonecoil Serpent", "")); + cubeCards.add(new CardIdentity("Stoneforge Mystic", "")); + cubeCards.add(new CardIdentity("Stratus Dancer", "")); cubeCards.add(new CardIdentity("Student of Warfare", "")); - cubeCards.add(new CardIdentity("Sublime Archangel", "")); + cubeCards.add(new CardIdentity("Sudden Edict", "")); cubeCards.add(new CardIdentity("Sulfur Falls", "")); cubeCards.add(new CardIdentity("Sulfuric Vortex", "")); - cubeCards.add(new CardIdentity("Sulfurous Springs", "")); - cubeCards.add(new CardIdentity("Summoning Trap", "")); cubeCards.add(new CardIdentity("Sun Titan", "")); cubeCards.add(new CardIdentity("Sundering Titan", "")); cubeCards.add(new CardIdentity("Sunpetal Grove", "")); cubeCards.add(new CardIdentity("Supreme Verdict", "")); + cubeCards.add(new CardIdentity("Supreme Will", "")); + cubeCards.add(new CardIdentity("Sweltering Suns", "")); + cubeCards.add(new CardIdentity("Sword of Body and Mind", "")); + cubeCards.add(new CardIdentity("Sword of Feast and Famine", "")); + cubeCards.add(new CardIdentity("Sword of Fire and Ice", "")); + cubeCards.add(new CardIdentity("Sword of Hearth and Home", "")); + cubeCards.add(new CardIdentity("Sword of Light and Shadow", "")); cubeCards.add(new CardIdentity("Swords to Plowshares", "")); cubeCards.add(new CardIdentity("Sylvan Caryatid", "")); cubeCards.add(new CardIdentity("Sylvan Library", "")); @@ -519,97 +528,93 @@ public class LegacyCube extends DraftCube { cubeCards.add(new CardIdentity("Talrand, Sky Summoner", "")); cubeCards.add(new CardIdentity("Tamiyo, the Moon Sage", "")); cubeCards.add(new CardIdentity("Tangle Wire", "")); - cubeCards.add(new CardIdentity("Tarmogoyf", "")); - cubeCards.add(new CardIdentity("Tectonic Edge", "")); - cubeCards.add(new CardIdentity("Teetering Peaks", "")); - cubeCards.add(new CardIdentity("Teferi, Mage of Zhalfir", "")); + cubeCards.add(new CardIdentity("Tangled Florahedron", "")); + cubeCards.add(new CardIdentity("Tasigur, the Golden Fang", "")); + cubeCards.add(new CardIdentity("Teferi, Hero of Dominaria", "")); + cubeCards.add(new CardIdentity("Teferi, Time Raveler", "")); cubeCards.add(new CardIdentity("Temple Garden", "")); - cubeCards.add(new CardIdentity("Temple of Abandon", "")); - cubeCards.add(new CardIdentity("Temple of Deceit", "")); - cubeCards.add(new CardIdentity("Temple of Enlightenment", "")); - cubeCards.add(new CardIdentity("Temple of Epiphany", "")); - cubeCards.add(new CardIdentity("Temple of Malady", "")); - cubeCards.add(new CardIdentity("Temple of Malice", "")); - cubeCards.add(new CardIdentity("Temple of Mystery", "")); - cubeCards.add(new CardIdentity("Temple of Plenty", "")); - cubeCards.add(new CardIdentity("Temple of Silence", "")); - cubeCards.add(new CardIdentity("Temple of Triumph", "")); - cubeCards.add(new CardIdentity("Tempt with Vengeance", "")); - cubeCards.add(new CardIdentity("Tendrils of Corruption", "")); + cubeCards.add(new CardIdentity("Temporal Mastery", "")); cubeCards.add(new CardIdentity("Terastodon", "")); - cubeCards.add(new CardIdentity("Terminate", "")); cubeCards.add(new CardIdentity("Terminus", "")); cubeCards.add(new CardIdentity("Thalia, Guardian of Thraben", "")); - cubeCards.add(new CardIdentity("Thassa, God of the Sea", "")); - cubeCards.add(new CardIdentity("Think Twice", "")); + cubeCards.add(new CardIdentity("The Great Henge", "")); + cubeCards.add(new CardIdentity("The Immortal Sun", "")); + cubeCards.add(new CardIdentity("The Scarab God", "")); + cubeCards.add(new CardIdentity("Thing in the Ice", "")); + cubeCards.add(new CardIdentity("Thirst for Knowledge", "")); cubeCards.add(new CardIdentity("Thoughtseize", "")); + cubeCards.add(new CardIdentity("Thraben Inspector", "")); cubeCards.add(new CardIdentity("Thragtusk", "")); cubeCards.add(new CardIdentity("Thran Dynamo", "")); + cubeCards.add(new CardIdentity("Thrashing Brontodon", "")); + cubeCards.add(new CardIdentity("Thrill of Possibility", "")); cubeCards.add(new CardIdentity("Through the Breach", "")); cubeCards.add(new CardIdentity("Thrun, the Last Troll", "")); cubeCards.add(new CardIdentity("Thundermaw Hellkite", "")); cubeCards.add(new CardIdentity("Tidehollow Sculler", "")); cubeCards.add(new CardIdentity("Time Warp", "")); + cubeCards.add(new CardIdentity("Timeless Dragon", "")); + cubeCards.add(new CardIdentity("Tireless Tracker", "")); cubeCards.add(new CardIdentity("Tooth and Nail", "")); + cubeCards.add(new CardIdentity("Torrential Gearhulk", "")); + cubeCards.add(new CardIdentity("Toski, Bearer of Secrets", "")); cubeCards.add(new CardIdentity("Toxic Deluge", "")); - cubeCards.add(new CardIdentity("Tracker's Instincts", "")); - cubeCards.add(new CardIdentity("Tradewind Rider", "")); cubeCards.add(new CardIdentity("Treachery", "")); - cubeCards.add(new CardIdentity("Troll Ascetic", "")); + cubeCards.add(new CardIdentity("Treasure Map", "")); cubeCards.add(new CardIdentity("Tropical Island", "")); - cubeCards.add(new CardIdentity("Trygon Predator", "")); + cubeCards.add(new CardIdentity("Trostani Discordant", "")); cubeCards.add(new CardIdentity("Tundra", "")); + cubeCards.add(new CardIdentity("Turntimber Symbiosis", "")); + cubeCards.add(new CardIdentity("Ugin, the Spirit Dragon", "")); + cubeCards.add(new CardIdentity("Ulamog, the Ceaseless Hunger", "")); cubeCards.add(new CardIdentity("Ulamog, the Infinite Gyre", "")); - cubeCards.add(new CardIdentity("Ultimate Price", "")); cubeCards.add(new CardIdentity("Umezawa's Jitte", "")); cubeCards.add(new CardIdentity("Unburial Rites", "")); - cubeCards.add(new CardIdentity("Underground River", "")); cubeCards.add(new CardIdentity("Underground Sea", "")); - cubeCards.add(new CardIdentity("Underworld Connections", "")); cubeCards.add(new CardIdentity("Unexpectedly Absent", "")); + cubeCards.add(new CardIdentity("Unholy Heat", "")); cubeCards.add(new CardIdentity("Upheaval", "")); - cubeCards.add(new CardIdentity("Vampire Hexmage", "")); - cubeCards.add(new CardIdentity("Vampire Lacerator", "")); + cubeCards.add(new CardIdentity("Uro, Titan of Nature's Wrath", "")); + cubeCards.add(new CardIdentity("Urza's Saga", "")); + cubeCards.add(new CardIdentity("Urza, Lord High Artificer", "")); + cubeCards.add(new CardIdentity("Usher of the Fallen", "")); + cubeCards.add(new CardIdentity("Utopia Sprawl", "")); + cubeCards.add(new CardIdentity("Valki, God of Lies", "")); cubeCards.add(new CardIdentity("Vampire Nighthawk", "")); - cubeCards.add(new CardIdentity("Vedalken Shackles", "")); + cubeCards.add(new CardIdentity("Vanishing Verse", "")); + cubeCards.add(new CardIdentity("Velomachus Lorehold", "")); cubeCards.add(new CardIdentity("Vendilion Clique", "")); - cubeCards.add(new CardIdentity("Vengevine", "")); + cubeCards.add(new CardIdentity("Venerated Loxodon", "")); cubeCards.add(new CardIdentity("Venser, Shaper Savant", "")); - cubeCards.add(new CardIdentity("Venser, the Sojourner", "")); cubeCards.add(new CardIdentity("Verdant Catacombs", "")); cubeCards.add(new CardIdentity("Vindicate", "")); - cubeCards.add(new CardIdentity("Visara the Dreadful", "")); + cubeCards.add(new CardIdentity("Vivien, Monsters' Advocate", "")); cubeCards.add(new CardIdentity("Voice of Resurgence", "")); cubeCards.add(new CardIdentity("Volcanic Island", "")); - cubeCards.add(new CardIdentity("Volrath's Stronghold", "")); - cubeCards.add(new CardIdentity("Vraska the Unseen", "")); - cubeCards.add(new CardIdentity("Wake Thrasher", "")); + cubeCards.add(new CardIdentity("Vraska, Relic Seeker", "")); + cubeCards.add(new CardIdentity("Walking Ballista", "")); cubeCards.add(new CardIdentity("Wall of Blossoms", "")); cubeCards.add(new CardIdentity("Wall of Omens", "")); - cubeCards.add(new CardIdentity("Wall of Roots", "")); - cubeCards.add(new CardIdentity("Warleader's Helix", "")); + cubeCards.add(new CardIdentity("Wandering Fumarole", "")); cubeCards.add(new CardIdentity("Wasteland", "")); - cubeCards.add(new CardIdentity("Waterfront Bouncer", "")); cubeCards.add(new CardIdentity("Watery Grave", "")); cubeCards.add(new CardIdentity("Whip of Erebos", "")); - cubeCards.add(new CardIdentity("Wild Mongrel", "")); + cubeCards.add(new CardIdentity("Whisperwood Elemental", "")); cubeCards.add(new CardIdentity("Windbrisk Heights", "")); cubeCards.add(new CardIdentity("Windswept Heath", "")); - cubeCards.add(new CardIdentity("Winter Orb", "")); - cubeCards.add(new CardIdentity("Wolfir Silverheart", "")); - cubeCards.add(new CardIdentity("Wood Elves", "")); cubeCards.add(new CardIdentity("Wooded Foothills", "")); cubeCards.add(new CardIdentity("Woodfall Primus", "")); cubeCards.add(new CardIdentity("Woodland Cemetery", "")); cubeCards.add(new CardIdentity("Worn Powerstone", "")); cubeCards.add(new CardIdentity("Wrath of God", "")); + cubeCards.add(new CardIdentity("Wretched Confluence", "")); cubeCards.add(new CardIdentity("Wurmcoil Engine", "")); - cubeCards.add(new CardIdentity("Xathrid Necromancer", "")); cubeCards.add(new CardIdentity("Xenagos, the Reveler", "")); - cubeCards.add(new CardIdentity("Yavimaya Coast", "")); - cubeCards.add(new CardIdentity("Yavimaya Elder", "")); - cubeCards.add(new CardIdentity("Yosei, the Morning Star", "")); + cubeCards.add(new CardIdentity("Yawgmoth, Thran Physician", "")); + cubeCards.add(new CardIdentity("Yorion, Sky Nomad", "")); cubeCards.add(new CardIdentity("Young Pyromancer", "")); + cubeCards.add(new CardIdentity("Zareth San, the Trickster", "")); cubeCards.add(new CardIdentity("Zealous Conscripts", "")); + cubeCards.add(new CardIdentity("Zurgo Bellstriker", "")); } } diff --git a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/AdamStyborskisPauperCube.java b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/PauperCube.java similarity index 98% rename from Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/AdamStyborskisPauperCube.java rename to Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/PauperCube.java index 61b5a84afdc..28f4bbb6f47 100644 --- a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/AdamStyborskisPauperCube.java +++ b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/PauperCube.java @@ -3,14 +3,18 @@ package mage.tournament.cubes; import mage.game.draft.DraftCube; /** + * Formerly maintained by Adam Styborski and now curated by a committee and the community surrounding it. + *

    + * Data sources: + * - actual + * * @author fireshoes */ -public class AdamStyborskisPauperCube extends DraftCube { +public class PauperCube extends DraftCube { - public AdamStyborskisPauperCube() { - super("Adam Styborkski's Pauper Cube"); // https://docs.google.com/spreadsheets/d/12iQhC4bHqFW7hEWxPBjyC8yBDehFZ0_4DkqzyA8EL3o/edit#gid=0 + public PauperCube() { + super("The Pauper Cube", "", 2025, 5, 7); - // last updated with Dominaria 5/1/18 cubeCards.add(new CardIdentity("Academy Journeymage", "")); cubeCards.add(new CardIdentity("Act of Treason", "")); cubeCards.add(new CardIdentity("Adventuring Gear", "")); diff --git a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/VintageCube.java b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/VintageCube.java new file mode 100644 index 00000000000..f32948f8ba3 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/VintageCube.java @@ -0,0 +1,560 @@ +package mage.tournament.cubes; + +import mage.game.draft.DraftCube; + +/** + * MTGO Vintage Cube, latest version + *

    + * Data sources: + * - official + * + * @author JayDi85 + */ +public class VintageCube extends DraftCube { + + public VintageCube() { + super("MTGO Vintage Cube", "", 2025, 4, 23); + + cubeCards.add(new CardIdentity("Abhorrent Oculus", "")); + cubeCards.add(new CardIdentity("Abrade", "")); + cubeCards.add(new CardIdentity("Adeline, Resplendent Cathar", "")); + cubeCards.add(new CardIdentity("Aether Spellbomb", "")); + cubeCards.add(new CardIdentity("Agatha's Soul Cauldron", "")); + cubeCards.add(new CardIdentity("Ajani, Nacatl Pariah", "")); + cubeCards.add(new CardIdentity("Amped Raptor", "")); + cubeCards.add(new CardIdentity("Ancestral Recall", "")); + cubeCards.add(new CardIdentity("Ancient Tomb", "")); + cubeCards.add(new CardIdentity("Animate Dead", "")); + cubeCards.add(new CardIdentity("Archon of Cruelty", "")); + cubeCards.add(new CardIdentity("Arena of Glory", "")); + cubeCards.add(new CardIdentity("Arid Mesa", "")); + cubeCards.add(new CardIdentity("Arwen, Mortal Queen", "")); + cubeCards.add(new CardIdentity("Atraxa, Grand Unifier", "")); + cubeCards.add(new CardIdentity("Avacyn's Pilgrim", "")); + cubeCards.add(new CardIdentity("Badlands", "")); + cubeCards.add(new CardIdentity("Balance", "")); + cubeCards.add(new CardIdentity("Baleful Mastery", "")); + cubeCards.add(new CardIdentity("Baleful Strix", "")); + cubeCards.add(new CardIdentity("Barrowgoyf", "")); + cubeCards.add(new CardIdentity("Basalt Monolith", "")); + cubeCards.add(new CardIdentity("Bayou", "")); + cubeCards.add(new CardIdentity("Birds of Paradise", "")); + cubeCards.add(new CardIdentity("Bitter Triumph", "")); + cubeCards.add(new CardIdentity("Black Lotus", "")); + cubeCards.add(new CardIdentity("Blackcleave Cliffs", "")); + cubeCards.add(new CardIdentity("Blazemire Verge", "")); + cubeCards.add(new CardIdentity("Bleachbone Verge", "")); + cubeCards.add(new CardIdentity("Blightsteel Colossus", "")); + cubeCards.add(new CardIdentity("Blood Crypt", "")); + cubeCards.add(new CardIdentity("Bloodbraid Challenger", "")); + cubeCards.add(new CardIdentity("Bloodstained Mire", "")); + cubeCards.add(new CardIdentity("Bloodtithe Harvester", "")); + cubeCards.add(new CardIdentity("Blooming Marsh", "")); + cubeCards.add(new CardIdentity("Bolas's Citadel", "")); + cubeCards.add(new CardIdentity("Bone Shards", "")); + cubeCards.add(new CardIdentity("Bonecrusher Giant", "")); + cubeCards.add(new CardIdentity("Bonehoard Dracosaur", "")); + cubeCards.add(new CardIdentity("Boseiju, Who Endures", "")); + cubeCards.add(new CardIdentity("Botanical Sanctum", "")); + cubeCards.add(new CardIdentity("Brain Freeze", "")); + cubeCards.add(new CardIdentity("Brainstorm", "")); + cubeCards.add(new CardIdentity("Brainsurge", "")); + cubeCards.add(new CardIdentity("Brazen Borrower", "")); + cubeCards.add(new CardIdentity("Breeding Pool", "")); + cubeCards.add(new CardIdentity("Brightglass Gearhulk", "")); + cubeCards.add(new CardIdentity("Bristly Bill, Spine Sower", "")); + cubeCards.add(new CardIdentity("Broadside Bombardiers", "")); + cubeCards.add(new CardIdentity("Burst Lightning", "")); + cubeCards.add(new CardIdentity("Cabal Ritual", "")); + cubeCards.add(new CardIdentity("Candelabra of Tawnos", "")); + cubeCards.add(new CardIdentity("Cankerbloom", "")); + cubeCards.add(new CardIdentity("Carnage Interpreter", "")); + cubeCards.add(new CardIdentity("Cathar Commando", "")); + cubeCards.add(new CardIdentity("Caustic Bronco", "")); + cubeCards.add(new CardIdentity("Celestial Colonnade", "")); + cubeCards.add(new CardIdentity("Chain Lightning", "")); + cubeCards.add(new CardIdentity("Chain of Smog", "")); + cubeCards.add(new CardIdentity("Chainsaw", "")); + cubeCards.add(new CardIdentity("Chandra, Torch of Defiance", "")); + cubeCards.add(new CardIdentity("Channel", "")); + cubeCards.add(new CardIdentity("Chromatic Star", "")); + cubeCards.add(new CardIdentity("Chrome Mox", "")); + cubeCards.add(new CardIdentity("City of Traitors", "")); + cubeCards.add(new CardIdentity("Clarion Conqueror", "")); + cubeCards.add(new CardIdentity("Coalition Relic", "")); + cubeCards.add(new CardIdentity("Collective Brutality", "")); + cubeCards.add(new CardIdentity("Commercial District", "")); + cubeCards.add(new CardIdentity("Concealed Courtyard", "")); + cubeCards.add(new CardIdentity("Concealing Curtains", "")); + cubeCards.add(new CardIdentity("Consider", "")); + cubeCards.add(new CardIdentity("Containment Priest", "")); + cubeCards.add(new CardIdentity("Copperline Gorge", "")); + cubeCards.add(new CardIdentity("Cori-Steel Cutter", "")); + cubeCards.add(new CardIdentity("Corpse Dance", "")); + cubeCards.add(new CardIdentity("Council's Judgment", "")); + cubeCards.add(new CardIdentity("Counterspell", "")); + cubeCards.add(new CardIdentity("Courser of Kruphix", "")); + cubeCards.add(new CardIdentity("Coveted Jewel", "")); + cubeCards.add(new CardIdentity("Crabomination", "")); + cubeCards.add(new CardIdentity("Craterhoof Behemoth", "")); + cubeCards.add(new CardIdentity("Creeping Tar Pit", "")); + cubeCards.add(new CardIdentity("Crop Rotation", "")); + cubeCards.add(new CardIdentity("Cryptic Command", "")); + cubeCards.add(new CardIdentity("Currency Converter", "")); + cubeCards.add(new CardIdentity("Cut Down", "")); + cubeCards.add(new CardIdentity("Dack Fayden", "")); + cubeCards.add(new CardIdentity("Damn", "")); + cubeCards.add(new CardIdentity("Dark Confidant", "")); + cubeCards.add(new CardIdentity("Dark Depths", "")); + cubeCards.add(new CardIdentity("Dark Ritual", "")); + cubeCards.add(new CardIdentity("Darkslick Shores", "")); + cubeCards.add(new CardIdentity("Dauthi Voidwalker", "")); + cubeCards.add(new CardIdentity("Daze", "")); + cubeCards.add(new CardIdentity("Death-Greeter's Champion", "")); + cubeCards.add(new CardIdentity("Deathrite Shaman", "")); + cubeCards.add(new CardIdentity("Deep-Cavern Bat", "")); + cubeCards.add(new CardIdentity("Delayed Blast Fireball", "")); + cubeCards.add(new CardIdentity("Delighted Halfling", "")); + cubeCards.add(new CardIdentity("Demonic Tutor", "")); + cubeCards.add(new CardIdentity("Descendant of Storms", "")); + cubeCards.add(new CardIdentity("Detective's Phoenix", "")); + cubeCards.add(new CardIdentity("Dismember", "")); + cubeCards.add(new CardIdentity("Displacer Kitten", "")); + cubeCards.add(new CardIdentity("Doomsday", "")); + cubeCards.add(new CardIdentity("Dragon's Rage Channeler", "")); + cubeCards.add(new CardIdentity("Duress", "")); + cubeCards.add(new CardIdentity("Eagles of the North", "")); + cubeCards.add(new CardIdentity("Echo of Eons", "")); + cubeCards.add(new CardIdentity("Elite Spellbinder", "")); + cubeCards.add(new CardIdentity("Elspeth, Knight-Errant", "")); + cubeCards.add(new CardIdentity("Elspeth, Storm Slayer", "")); + cubeCards.add(new CardIdentity("Elvish Mystic", "")); + cubeCards.add(new CardIdentity("Elvish Reclaimer", "")); + cubeCards.add(new CardIdentity("Embereth Shieldbreaker", "")); + cubeCards.add(new CardIdentity("Emperor of Bones", "")); + cubeCards.add(new CardIdentity("Emrakul, the Aeons Torn", "")); + cubeCards.add(new CardIdentity("Endurance", "")); + cubeCards.add(new CardIdentity("Enduring Curiosity", "")); + cubeCards.add(new CardIdentity("Enduring Innocence", "")); + cubeCards.add(new CardIdentity("Entomb", "")); + cubeCards.add(new CardIdentity("Ephemerate", "")); + cubeCards.add(new CardIdentity("Ertai Resurrected", "")); + cubeCards.add(new CardIdentity("Esika's Chariot", "")); + cubeCards.add(new CardIdentity("Etali, Primal Conqueror", "")); + cubeCards.add(new CardIdentity("Eternal Witness", "")); + cubeCards.add(new CardIdentity("Everflowing Chalice", "")); + cubeCards.add(new CardIdentity("Exhume", "")); + cubeCards.add(new CardIdentity("Expedition Map", "")); + cubeCards.add(new CardIdentity("Exploration", "")); + cubeCards.add(new CardIdentity("Expressive Iteration", "")); + cubeCards.add(new CardIdentity("Fable of the Mirror-Breaker", "")); + cubeCards.add(new CardIdentity("Fabled Passage", "")); + cubeCards.add(new CardIdentity("Faerie Mastermind", "")); + cubeCards.add(new CardIdentity("Faithless Looting", "")); + cubeCards.add(new CardIdentity("Fallen Shinobi", "")); + cubeCards.add(new CardIdentity("Fanatic of Rhonas", "")); + cubeCards.add(new CardIdentity("Fastbond", "")); + cubeCards.add(new CardIdentity("Fatal Push", "")); + cubeCards.add(new CardIdentity("Fear of Missing Out", "")); + cubeCards.add(new CardIdentity("Field of the Dead", "")); + cubeCards.add(new CardIdentity("Fiery Confluence", "")); + cubeCards.add(new CardIdentity("Figure of Destiny", "")); + cubeCards.add(new CardIdentity("Fire // Ice", "")); + cubeCards.add(new CardIdentity("Fire Covenant", "")); + cubeCards.add(new CardIdentity("Fireblast", "")); + cubeCards.add(new CardIdentity("Firebolt", "")); + cubeCards.add(new CardIdentity("Flame Slash", "")); + cubeCards.add(new CardIdentity("Flash", "")); + cubeCards.add(new CardIdentity("Flickerwisp", "")); + cubeCards.add(new CardIdentity("Flooded Strand", "")); + cubeCards.add(new CardIdentity("Floodpits Drowner", "")); + cubeCards.add(new CardIdentity("Force of Negation", "")); + cubeCards.add(new CardIdentity("Force of Will", "")); + cubeCards.add(new CardIdentity("Forensic Gadgeteer", "")); + cubeCards.add(new CardIdentity("Forth Eorlingas!", "")); + cubeCards.add(new CardIdentity("Fractured Identity", "")); + cubeCards.add(new CardIdentity("Frantic Search", "")); + cubeCards.add(new CardIdentity("Fury", "")); + cubeCards.add(new CardIdentity("Fyndhorn Elves", "")); + cubeCards.add(new CardIdentity("Gaea's Cradle", "")); + cubeCards.add(new CardIdentity("Galvanic Blast", "")); + cubeCards.add(new CardIdentity("Galvanic Discharge", "")); + cubeCards.add(new CardIdentity("General Ferrous Rokiric", "")); + cubeCards.add(new CardIdentity("Generous Ent", "")); + cubeCards.add(new CardIdentity("Generous Plunderer", "")); + cubeCards.add(new CardIdentity("Get Lost", "")); + cubeCards.add(new CardIdentity("Ghost Vacuum", "")); + cubeCards.add(new CardIdentity("Gitaxian Probe", "")); + cubeCards.add(new CardIdentity("Giver of Runes", "")); + cubeCards.add(new CardIdentity("Glimmer Lens", "")); + cubeCards.add(new CardIdentity("Glorybringer", "")); + cubeCards.add(new CardIdentity("Goblin Bombardment", "")); + cubeCards.add(new CardIdentity("Goblin Rabblemaster", "")); + cubeCards.add(new CardIdentity("Godless Shrine", "")); + cubeCards.add(new CardIdentity("Goldspan Dragon", "")); + cubeCards.add(new CardIdentity("Golos, Tireless Pilgrim", "")); + cubeCards.add(new CardIdentity("Grave Titan", "")); + cubeCards.add(new CardIdentity("Greasewrench Goblin", "")); + cubeCards.add(new CardIdentity("Green Sun's Zenith", "")); + cubeCards.add(new CardIdentity("Grief", "")); + cubeCards.add(new CardIdentity("Grim Monolith", "")); + cubeCards.add(new CardIdentity("Griselbrand", "")); + cubeCards.add(new CardIdentity("Grist, the Hunger Tide", "")); + cubeCards.add(new CardIdentity("Guide of Souls", "")); + cubeCards.add(new CardIdentity("Gush", "")); + cubeCards.add(new CardIdentity("Gut, True Soul Zealot", "")); + cubeCards.add(new CardIdentity("Hallowed Fountain", "")); + cubeCards.add(new CardIdentity("Harvester of Misery", "")); + cubeCards.add(new CardIdentity("Haywire Mite", "")); + cubeCards.add(new CardIdentity("Headliner Scarlett", "")); + cubeCards.add(new CardIdentity("Hedge Maze", "")); + cubeCards.add(new CardIdentity("Heritage Reclamation", "")); + cubeCards.add(new CardIdentity("Hero of Bladehold", "")); + cubeCards.add(new CardIdentity("Hexdrinker", "")); + cubeCards.add(new CardIdentity("High Tide", "")); + cubeCards.add(new CardIdentity("Horizon Canopy", "")); + cubeCards.add(new CardIdentity("Huatli, Poet of Unity", "")); + cubeCards.add(new CardIdentity("Hullbreacher", "")); + cubeCards.add(new CardIdentity("Hymn to Tourach", "")); + cubeCards.add(new CardIdentity("Ignoble Hierarch", "")); + cubeCards.add(new CardIdentity("Imperial Seal", "")); + cubeCards.add(new CardIdentity("Indatha Triome", "")); + cubeCards.add(new CardIdentity("Infernal Grasp", "")); + cubeCards.add(new CardIdentity("Inquisition of Kozilek", "")); + cubeCards.add(new CardIdentity("Inspiring Vantage", "")); + cubeCards.add(new CardIdentity("Inti, Seneschal of the Sun", "")); + cubeCards.add(new CardIdentity("Intrepid Adversary", "")); + cubeCards.add(new CardIdentity("Ivora, Insatiable Heir", "")); + cubeCards.add(new CardIdentity("Jace, Vryn's Prodigy", "")); + cubeCards.add(new CardIdentity("Jace, Wielder of Mysteries", "")); + cubeCards.add(new CardIdentity("Jace, the Mind Sculptor", "")); + cubeCards.add(new CardIdentity("Jacked Rabbit", "")); + cubeCards.add(new CardIdentity("Jetmir's Garden", "")); + cubeCards.add(new CardIdentity("Kaito, Bane of Nightmares", "")); + cubeCards.add(new CardIdentity("Kaldra Compleat", "")); + cubeCards.add(new CardIdentity("Kappa Cannoneer", "")); + cubeCards.add(new CardIdentity("Karakas", "")); + cubeCards.add(new CardIdentity("Kari Zev, Skyship Raider", "")); + cubeCards.add(new CardIdentity("Karn, Scion of Urza", "")); + cubeCards.add(new CardIdentity("Keen-Eyed Curator", "")); + cubeCards.add(new CardIdentity("Kellan, Planar Trailblazer", "")); + cubeCards.add(new CardIdentity("Ketria Triome", "")); + cubeCards.add(new CardIdentity("Kitesail Freebooter", "")); + cubeCards.add(new CardIdentity("Knight of the Reliquary", "")); + cubeCards.add(new CardIdentity("Kolaghan's Command", "")); + cubeCards.add(new CardIdentity("Laelia, the Blade Reforged", "")); + cubeCards.add(new CardIdentity("Lavaspur Boots", "")); + cubeCards.add(new CardIdentity("Ledger Shredder", "")); + cubeCards.add(new CardIdentity("Legion Extruder", "")); + cubeCards.add(new CardIdentity("Leovold, Emissary of Trest", "")); + cubeCards.add(new CardIdentity("Lethal Scheme", "")); + cubeCards.add(new CardIdentity("Leyline Binding", "")); + cubeCards.add(new CardIdentity("Library of Alexandria", "")); + cubeCards.add(new CardIdentity("Life // Death", "")); + cubeCards.add(new CardIdentity("Lightning Bolt", "")); + cubeCards.add(new CardIdentity("Lightning Greaves", "")); + cubeCards.add(new CardIdentity("Liliana of the Veil", "")); + cubeCards.add(new CardIdentity("Lingering Souls", "")); + cubeCards.add(new CardIdentity("Lion Sash", "")); + cubeCards.add(new CardIdentity("Lion's Eye Diamond", "")); + cubeCards.add(new CardIdentity("Llanowar Elves", "")); + cubeCards.add(new CardIdentity("Loot, the Pathfinder", "")); + cubeCards.add(new CardIdentity("Loran of the Third Path", "")); + cubeCards.add(new CardIdentity("Lose Focus", "")); + cubeCards.add(new CardIdentity("Lotus Cobra", "")); + cubeCards.add(new CardIdentity("Lotus Petal", "")); + cubeCards.add(new CardIdentity("Luminarch Aspirant", "")); + cubeCards.add(new CardIdentity("Lurrus of the Dream-Den", "")); + cubeCards.add(new CardIdentity("Lush Portico", "")); + cubeCards.add(new CardIdentity("Lutri, the Spellchaser", "")); + cubeCards.add(new CardIdentity("Lorien Revealed", "")); + cubeCards.add(new CardIdentity("Magda, Brazen Outlaw", "")); + cubeCards.add(new CardIdentity("Malcolm, Alluring Scoundrel", "")); + cubeCards.add(new CardIdentity("Malevolent Rumble", "")); + cubeCards.add(new CardIdentity("Mana Crypt", "")); + cubeCards.add(new CardIdentity("Mana Drain", "")); + cubeCards.add(new CardIdentity("Mana Leak", "")); + cubeCards.add(new CardIdentity("Mana Tithe", "")); + cubeCards.add(new CardIdentity("Mana Vault", "")); + cubeCards.add(new CardIdentity("Manamorphose", "")); + cubeCards.add(new CardIdentity("Manifold Key", "")); + cubeCards.add(new CardIdentity("March of Otherworldly Light", "")); + cubeCards.add(new CardIdentity("Marsh Flats", "")); + cubeCards.add(new CardIdentity("Memory Jar", "")); + cubeCards.add(new CardIdentity("Memory Lapse", "")); + cubeCards.add(new CardIdentity("Metamorphosis Fanatic", "")); + cubeCards.add(new CardIdentity("Meticulous Archive", "")); + cubeCards.add(new CardIdentity("Mind Stone", "")); + cubeCards.add(new CardIdentity("Mind Twist", "")); + cubeCards.add(new CardIdentity("Mine Collapse", "")); + cubeCards.add(new CardIdentity("Minsc & Boo, Timeless Heroes", "")); + cubeCards.add(new CardIdentity("Miscalculation", "")); + cubeCards.add(new CardIdentity("Mishra's Bauble", "")); + cubeCards.add(new CardIdentity("Mishra's Workshop", "")); + cubeCards.add(new CardIdentity("Misty Rainforest", "")); + cubeCards.add(new CardIdentity("Monastery Mentor", "")); + cubeCards.add(new CardIdentity("Mother of Runes", "")); + cubeCards.add(new CardIdentity("Mox Diamond", "")); + cubeCards.add(new CardIdentity("Mox Emerald", "")); + cubeCards.add(new CardIdentity("Mox Jet", "")); + cubeCards.add(new CardIdentity("Mox Opal", "")); + cubeCards.add(new CardIdentity("Mox Pearl", "")); + cubeCards.add(new CardIdentity("Mox Ruby", "")); + cubeCards.add(new CardIdentity("Mox Sapphire", "")); + cubeCards.add(new CardIdentity("Myr Battlesphere", "")); + cubeCards.add(new CardIdentity("Mystic Confluence", "")); + cubeCards.add(new CardIdentity("Mystical Tutor", "")); + cubeCards.add(new CardIdentity("Nadu, Winged Wisdom", "")); + cubeCards.add(new CardIdentity("Narset, Parter of Veils", "")); + cubeCards.add(new CardIdentity("Natural Order", "")); + cubeCards.add(new CardIdentity("Nature's Lore", "")); + cubeCards.add(new CardIdentity("Necromancy", "")); + cubeCards.add(new CardIdentity("Nethergoyf", "")); + cubeCards.add(new CardIdentity("Nettlecyst", "")); + cubeCards.add(new CardIdentity("Nexus of Becoming", "")); + cubeCards.add(new CardIdentity("Night's Whisper", "")); + cubeCards.add(new CardIdentity("Nissa, Ascended Animist", "")); + cubeCards.add(new CardIdentity("Nissa, Who Shakes the World", "")); + cubeCards.add(new CardIdentity("No More Lies", "")); + cubeCards.add(new CardIdentity("Noble Hierarch", "")); + cubeCards.add(new CardIdentity("Oath of Druids", "")); + cubeCards.add(new CardIdentity("Occult Epiphany", "")); + cubeCards.add(new CardIdentity("Ocelot Pride", "")); + cubeCards.add(new CardIdentity("Oko, Thief of Crowns", "")); + cubeCards.add(new CardIdentity("Oliphaunt", "")); + cubeCards.add(new CardIdentity("Omnath, Locus of Creation", "")); + cubeCards.add(new CardIdentity("Once Upon a Time", "")); + cubeCards.add(new CardIdentity("Orcish Bowmasters", "")); + cubeCards.add(new CardIdentity("Orcish Lumberjack", "")); + cubeCards.add(new CardIdentity("Otawara, Soaring City", "")); + cubeCards.add(new CardIdentity("Otharri, Suns' Glory", "")); + cubeCards.add(new CardIdentity("Oust", "")); + cubeCards.add(new CardIdentity("Overgrown Tomb", "")); + cubeCards.add(new CardIdentity("Overlord of the Balemurk", "")); + cubeCards.add(new CardIdentity("Overlord of the Mistmoors", "")); + cubeCards.add(new CardIdentity("Palace Jailer", "")); + cubeCards.add(new CardIdentity("Palantir of Orthanc", "")); + cubeCards.add(new CardIdentity("Paradoxical Outcome", "")); + cubeCards.add(new CardIdentity("Parallax Wave", "")); + cubeCards.add(new CardIdentity("Path to Exile", "")); + cubeCards.add(new CardIdentity("Pentad Prism", "")); + cubeCards.add(new CardIdentity("Pest Infestation", "")); + cubeCards.add(new CardIdentity("Phantasmal Image", "")); + cubeCards.add(new CardIdentity("Phelia, Exuberant Shepherd", "")); + cubeCards.add(new CardIdentity("Phlage, Titan of Fire's Fury", "")); + cubeCards.add(new CardIdentity("Phyrexian Metamorph", "")); + cubeCards.add(new CardIdentity("Phyrexian Revoker", "")); + cubeCards.add(new CardIdentity("Pillage the Bog", "")); + cubeCards.add(new CardIdentity("Plateau", "")); + cubeCards.add(new CardIdentity("Polluted Delta", "")); + cubeCards.add(new CardIdentity("Ponder", "")); + cubeCards.add(new CardIdentity("Portable Hole", "")); + cubeCards.add(new CardIdentity("Portal to Phyrexia", "")); + cubeCards.add(new CardIdentity("Preacher of the Schism", "")); + cubeCards.add(new CardIdentity("Preordain", "")); + cubeCards.add(new CardIdentity("Primeval Titan", "")); + cubeCards.add(new CardIdentity("Prismatic Ending", "")); + cubeCards.add(new CardIdentity("Prismatic Vista", "")); + cubeCards.add(new CardIdentity("Psychic Frog", "")); + cubeCards.add(new CardIdentity("Pyrogoyf", "")); + cubeCards.add(new CardIdentity("Pyrokinesis", "")); + cubeCards.add(new CardIdentity("Qarsi Revenant", "")); + cubeCards.add(new CardIdentity("Questing Beast", "")); + cubeCards.add(new CardIdentity("Raffine's Tower", "")); + cubeCards.add(new CardIdentity("Raffine, Scheming Seer", "")); + cubeCards.add(new CardIdentity("Ragavan, Nimble Pilferer", "")); + cubeCards.add(new CardIdentity("Rain of Filth", "")); + cubeCards.add(new CardIdentity("Rampaging Raptor", "")); + cubeCards.add(new CardIdentity("Ramunap Excavator", "")); + cubeCards.add(new CardIdentity("Raucous Theater", "")); + cubeCards.add(new CardIdentity("Raugrin Triome", "")); + cubeCards.add(new CardIdentity("Razorverge Thicket", "")); + cubeCards.add(new CardIdentity("Reanimate", "")); + cubeCards.add(new CardIdentity("Recurring Nightmare", "")); + cubeCards.add(new CardIdentity("Relic of Sauron", "")); + cubeCards.add(new CardIdentity("Remand", "")); + cubeCards.add(new CardIdentity("Reprieve", "")); + cubeCards.add(new CardIdentity("Restless Vinestalk", "")); + cubeCards.add(new CardIdentity("Retrofitter Foundry", "")); + cubeCards.add(new CardIdentity("Riverpyre Verge", "")); + cubeCards.add(new CardIdentity("Robber of the Rich", "")); + cubeCards.add(new CardIdentity("Rofellos, Llanowar Emissary", "")); + cubeCards.add(new CardIdentity("Sacred Foundry", "")); + cubeCards.add(new CardIdentity("Saheeli, Sublime Artificer", "")); + cubeCards.add(new CardIdentity("Sandstorm Salvager", "")); + cubeCards.add(new CardIdentity("Satya, Aetherflux Genius", "")); + cubeCards.add(new CardIdentity("Savai Triome", "")); + cubeCards.add(new CardIdentity("Savannah", "")); + cubeCards.add(new CardIdentity("Scalding Tarn", "")); + cubeCards.add(new CardIdentity("Scrapwork Mutt", "")); + cubeCards.add(new CardIdentity("Screaming Nemesis", "")); + cubeCards.add(new CardIdentity("Scrubland", "")); + cubeCards.add(new CardIdentity("Scythecat Cub", "")); + cubeCards.add(new CardIdentity("Seachrome Coast", "")); + cubeCards.add(new CardIdentity("Seasoned Pyromancer", "")); + cubeCards.add(new CardIdentity("Securitron Squadron", "")); + cubeCards.add(new CardIdentity("Sedgemoor Witch", "")); + cubeCards.add(new CardIdentity("Seething Song", "")); + cubeCards.add(new CardIdentity("Sensei's Divining Top", "")); + cubeCards.add(new CardIdentity("Sentinel of the Nameless City", "")); + cubeCards.add(new CardIdentity("Serra Paragon", "")); + cubeCards.add(new CardIdentity("Shadowspear", "")); + cubeCards.add(new CardIdentity("Shadowy Backstreet", "")); + cubeCards.add(new CardIdentity("Shallow Grave", "")); + cubeCards.add(new CardIdentity("Shelldock Isle", "")); + cubeCards.add(new CardIdentity("Sheoldred's Edict", "")); + cubeCards.add(new CardIdentity("Sheoldred, the Apocalypse", "")); + cubeCards.add(new CardIdentity("Shifting Woodland", "")); + cubeCards.add(new CardIdentity("Shorikai, Genesis Engine", "")); + cubeCards.add(new CardIdentity("Show and Tell", "")); + cubeCards.add(new CardIdentity("Sink into Stupor", "")); + cubeCards.add(new CardIdentity("Six", "")); + cubeCards.add(new CardIdentity("Skullclamp", "")); + cubeCards.add(new CardIdentity("Skyclave Apparition", "")); + cubeCards.add(new CardIdentity("Slimefoot and Squee", "")); + cubeCards.add(new CardIdentity("Smuggler's Copter", "")); + cubeCards.add(new CardIdentity("Snapcaster Mage", "")); + cubeCards.add(new CardIdentity("Sneak Attack", "")); + cubeCards.add(new CardIdentity("Snuff Out", "")); + cubeCards.add(new CardIdentity("Sol Ring", "")); + cubeCards.add(new CardIdentity("Solitude", "")); + cubeCards.add(new CardIdentity("Sorin of House Markov", "")); + cubeCards.add(new CardIdentity("Soul-Guide Lantern", "")); + cubeCards.add(new CardIdentity("Sowing Mycospawn", "")); + cubeCards.add(new CardIdentity("Spara's Headquarters", "")); + cubeCards.add(new CardIdentity("Spell Pierce", "")); + cubeCards.add(new CardIdentity("Spellseeker", "")); + cubeCards.add(new CardIdentity("Spirebluff Canal", "")); + cubeCards.add(new CardIdentity("Springheart Nantuko", "")); + cubeCards.add(new CardIdentity("Spyglass Siren", "")); + cubeCards.add(new CardIdentity("Staff of the Storyteller", "")); + cubeCards.add(new CardIdentity("Static Prison", "")); + cubeCards.add(new CardIdentity("Steam Vents", "")); + cubeCards.add(new CardIdentity("Stern Scolding", "")); + cubeCards.add(new CardIdentity("Stock Up", "")); + cubeCards.add(new CardIdentity("Stomping Ground", "")); + cubeCards.add(new CardIdentity("Stoneforge Mystic", "")); + cubeCards.add(new CardIdentity("Stormchaser's Talent", "")); + cubeCards.add(new CardIdentity("Strip Mine", "")); + cubeCards.add(new CardIdentity("Subtlety", "")); + cubeCards.add(new CardIdentity("Sunbaked Canyon", "")); + cubeCards.add(new CardIdentity("Sunbillow Verge", "")); + cubeCards.add(new CardIdentity("Sunfall", "")); + cubeCards.add(new CardIdentity("Sunken Ruins", "")); + cubeCards.add(new CardIdentity("Sword of the Meek", "")); + cubeCards.add(new CardIdentity("Swords to Plowshares", "")); + cubeCards.add(new CardIdentity("Sylvan Caryatid", "")); + cubeCards.add(new CardIdentity("Sylvan Safekeeper", "")); + cubeCards.add(new CardIdentity("Taiga", "")); + cubeCards.add(new CardIdentity("Talisman of Conviction", "")); + cubeCards.add(new CardIdentity("Talisman of Creativity", "")); + cubeCards.add(new CardIdentity("Talisman of Curiosity", "")); + cubeCards.add(new CardIdentity("Talisman of Dominance", "")); + cubeCards.add(new CardIdentity("Talisman of Progress", "")); + cubeCards.add(new CardIdentity("Talon Gates of Madara", "")); + cubeCards.add(new CardIdentity("Tamiyo, Collector of Tales", "")); + cubeCards.add(new CardIdentity("Tamiyo, Inquisitive Student", "")); + cubeCards.add(new CardIdentity("Tarfire", "")); + cubeCards.add(new CardIdentity("Tarmogoyf", "")); + cubeCards.add(new CardIdentity("Tear Asunder", "")); + cubeCards.add(new CardIdentity("Teferi, Hero of Dominaria", "")); + cubeCards.add(new CardIdentity("Teferi, Time Raveler", "")); + cubeCards.add(new CardIdentity("Temple Garden", "")); + cubeCards.add(new CardIdentity("Tendrils of Agony", "")); + cubeCards.add(new CardIdentity("Territorial Kavu", "")); + cubeCards.add(new CardIdentity("Tersa Lightshatter", "")); + cubeCards.add(new CardIdentity("Thalia, Guardian of Thraben", "")); + cubeCards.add(new CardIdentity("Thassa's Oracle", "")); + cubeCards.add(new CardIdentity("The Aetherspark", "")); + cubeCards.add(new CardIdentity("The Mightstone and Weakstone", "")); + cubeCards.add(new CardIdentity("The One Ring", "")); + cubeCards.add(new CardIdentity("The Wandering Emperor", "")); + cubeCards.add(new CardIdentity("Thespian's Stage", "")); + cubeCards.add(new CardIdentity("Thieving Skydiver", "")); + cubeCards.add(new CardIdentity("Third Path Iconoclast", "")); + cubeCards.add(new CardIdentity("Thopter Foundry", "")); + cubeCards.add(new CardIdentity("Thornspire Verge", "")); + cubeCards.add(new CardIdentity("Thought Scour", "")); + cubeCards.add(new CardIdentity("Thoughtseize", "")); + cubeCards.add(new CardIdentity("Thraben Inspector", "")); + cubeCards.add(new CardIdentity("Through the Breach", "")); + cubeCards.add(new CardIdentity("Thundering Falls", "")); + cubeCards.add(new CardIdentity("Tidehollow Sculler", "")); + cubeCards.add(new CardIdentity("Time Spiral", "")); + cubeCards.add(new CardIdentity("Time Walk", "")); + cubeCards.add(new CardIdentity("Time Warp", "")); + cubeCards.add(new CardIdentity("Timetwister", "")); + cubeCards.add(new CardIdentity("Tinker", "")); + cubeCards.add(new CardIdentity("Tireless Tracker", "")); + cubeCards.add(new CardIdentity("Tishana's Tidebinder", "")); + cubeCards.add(new CardIdentity("Titania, Protector of Argoth", "")); + cubeCards.add(new CardIdentity("Tolarian Academy", "")); + cubeCards.add(new CardIdentity("Torsten, Founder of Benalia", "")); + cubeCards.add(new CardIdentity("Touch the Spirit Realm", "")); + cubeCards.add(new CardIdentity("Toxic Deluge", "")); + cubeCards.add(new CardIdentity("Treachery", "")); + cubeCards.add(new CardIdentity("Treasure Cruise", "")); + cubeCards.add(new CardIdentity("Trinket Mage", "")); + cubeCards.add(new CardIdentity("Triplicate Titan", "")); + //cubeCards.add(new CardIdentity("Troll of Khazad-dom", "")); + cubeCards.add(new CardIdentity("Tropical Island", "")); + cubeCards.add(new CardIdentity("True-Name Nemesis", "")); + cubeCards.add(new CardIdentity("Trumpeting Carnosaur", "")); + cubeCards.add(new CardIdentity("Tundra", "")); + cubeCards.add(new CardIdentity("Turnabout", "")); + cubeCards.add(new CardIdentity("Ugin, Eye of the Storms", "")); + cubeCards.add(new CardIdentity("Ulamog, the Infinite Gyre", "")); + cubeCards.add(new CardIdentity("Ulvenwald Oddity", "")); + cubeCards.add(new CardIdentity("Umezawa's Jitte", "")); + cubeCards.add(new CardIdentity("Undercity Sewers", "")); + cubeCards.add(new CardIdentity("Underground Mortuary", "")); + cubeCards.add(new CardIdentity("Underground Sea", "")); + cubeCards.add(new CardIdentity("Underworld Breach", "")); + cubeCards.add(new CardIdentity("Unearth", "")); + cubeCards.add(new CardIdentity("Unexpectedly Absent", "")); + cubeCards.add(new CardIdentity("Unholy Heat", "")); + cubeCards.add(new CardIdentity("Upheaval", "")); + cubeCards.add(new CardIdentity("Urborg, Tomb of Yawgmoth", "")); + cubeCards.add(new CardIdentity("Uro, Titan of Nature's Wrath", "")); + cubeCards.add(new CardIdentity("Ursine Monstrosity", "")); + cubeCards.add(new CardIdentity("Urza's Bauble", "")); + cubeCards.add(new CardIdentity("Urza's Saga", "")); + cubeCards.add(new CardIdentity("Urza, Lord High Artificer", "")); + cubeCards.add(new CardIdentity("Vampire Hexmage", "")); + cubeCards.add(new CardIdentity("Vampiric Tutor", "")); + cubeCards.add(new CardIdentity("Vaultborn Tyrant", "")); + cubeCards.add(new CardIdentity("Verdant Catacombs", "")); + cubeCards.add(new CardIdentity("Vindicate", "")); + cubeCards.add(new CardIdentity("Virtue of Loyalty", "")); + cubeCards.add(new CardIdentity("Voice of Victory", "")); + cubeCards.add(new CardIdentity("Volcanic Island", "")); + cubeCards.add(new CardIdentity("Voldaren Epicure", "")); + //cubeCards.add(new CardIdentity("Walk-In Closet // Forgotten Cellar", "")); + cubeCards.add(new CardIdentity("Walking Ballista", "")); + cubeCards.add(new CardIdentity("Wasteland", "")); + cubeCards.add(new CardIdentity("Wastewood Verge", "")); + cubeCards.add(new CardIdentity("Waterlogged Grove", "")); + cubeCards.add(new CardIdentity("Watery Grave", "")); + cubeCards.add(new CardIdentity("Wheel of Fortune", "")); + cubeCards.add(new CardIdentity("Wight of the Reliquary", "")); + cubeCards.add(new CardIdentity("Winds of Abandon", "")); + cubeCards.add(new CardIdentity("Windswept Heath", "")); + cubeCards.add(new CardIdentity("Wishclaw Talisman", "")); + cubeCards.add(new CardIdentity("Witch Enchanter", "")); + cubeCards.add(new CardIdentity("Witherbloom Apprentice", "")); + cubeCards.add(new CardIdentity("Wooded Foothills", "")); + cubeCards.add(new CardIdentity("Woodfall Primus", "")); + cubeCards.add(new CardIdentity("Worldly Tutor", "")); + cubeCards.add(new CardIdentity("Worldspine Wurm", "")); + cubeCards.add(new CardIdentity("Wrath of God", "")); + cubeCards.add(new CardIdentity("Wrenn and Six", "")); + cubeCards.add(new CardIdentity("Xander's Lounge", "")); + cubeCards.add(new CardIdentity("Yavimaya, Cradle of Growth", "")); + cubeCards.add(new CardIdentity("Yawgmoth's Will", "")); + cubeCards.add(new CardIdentity("Zagoth Triome", "")); + cubeCards.add(new CardIdentity("Ziatora's Proving Ground", "")); + cubeCards.add(new CardIdentity("Zirda, the Dawnwaker", "")); + cubeCards.add(new CardIdentity("Zuran Orb", "")); + } +} + diff --git a/Mage.Server/config/config.xml b/Mage.Server/config/config.xml index 3f1eba60fb0..928cb18c62f 100644 --- a/Mage.Server/config/config.xml +++ b/Mage.Server/config/config.xml @@ -21,7 +21,7 @@ minUserNameLength - minmal allowed length of a user name to connect to the server maxUserNameLength - maximal allowed length of a user name to connect to the server userNamePattern - pattern for user name validity check - maxAiOpponents - number of allowed AI opponents on the server + maxAiOpponents - number of allowed workable AI opponents on the server (draft bots are unlimited) saveGameActivated - allow game save and replay options (not working correctly yet) authenticationActivated - "true" = user have to register to signon "false" = user need not to register @@ -113,7 +113,21 @@ - + + + + + + + + + + + + + + + @@ -121,13 +135,11 @@ - - - - - + + + + + diff --git a/Mage.Server/release/config/config.xml b/Mage.Server/release/config/config.xml index 1f77ab3d9b7..1e7a126def9 100644 --- a/Mage.Server/release/config/config.xml +++ b/Mage.Server/release/config/config.xml @@ -18,7 +18,7 @@ minUserNameLength - minmal allowed length of a user name to connect to the server maxUserNameLength - maximal allowed length of a user name to connect to the server userNamePattern - pattern for user name validity check - maxAiOpponents - number of allowed AI opponents on the server + maxAiOpponents - number of allowed workable AI opponents on the server (draft bots are unlimited) saveGameActivated - allow game save and replay options (not working correctly yet) authenticationActivated - "true" = user have to register to signon "false" = user need not to register * mail configs only needed if authentication is activated: @@ -96,8 +96,8 @@ - - + + @@ -107,21 +107,33 @@ - + + + + + + + + + + + + + + + - - + + - - + - - - - + + + + + diff --git a/Mage.Server/src/main/java/mage/server/MageServerImpl.java b/Mage.Server/src/main/java/mage/server/MageServerImpl.java index bb69e76ff44..dcc20c38256 100644 --- a/Mage.Server/src/main/java/mage/server/MageServerImpl.java +++ b/Mage.Server/src/main/java/mage/server/MageServerImpl.java @@ -222,15 +222,12 @@ public class MageServerImpl implements MageServer { throw new MageException("No message"); } - // check AI players max + // limit number of workable AI opponents (draft bots are unlimited) String maxAiOpponents = managerFactory.configSettings().getMaxAiOpponents(); if (maxAiOpponents != null) { - int aiPlayers = 0; - for (PlayerType playerType : options.getPlayerTypes()) { - if (playerType != PlayerType.HUMAN) { - aiPlayers++; - } - } + int aiPlayers = options.getPlayerTypes().stream() + .mapToInt(t -> t.isAI() && t.isWorkablePlayer() ? 1 : 0) + .sum(); int max = Integer.parseInt(maxAiOpponents); if (aiPlayers > max) { user.showUserMessage("Create tournament", "It's only allowed to use a maximum of " + max + " AI players."); @@ -324,7 +321,7 @@ public class MageServerImpl implements MageServer { UUID userId = session.get().getUserId(); if (logger.isTraceEnabled()) { Optional user = managerFactory.userManager().getUser(userId); - user.ifPresent(user1 -> logger.trace("join tourn. tableId: " + tableId + ' ' + name)); + user.ifPresent(user1 -> logger.trace("join tourney tableId: " + tableId + ' ' + name)); } if (userId == null) { logger.fatal("Got no userId from sessionId" + sessionId + " tableId" + tableId); @@ -1001,7 +998,7 @@ public class MageServerImpl implements MageServer { public void handleException(Exception ex) throws MageException { if (ex.getMessage() != null && !ex.getMessage().equals("No message")) { - throw new MageException("Server error: " + ex.getMessage()); + throw new MageException(ex.getMessage()); } if (ex instanceof ConcurrentModificationException) { diff --git a/Mage.Server/src/main/java/mage/server/Main.java b/Mage.Server/src/main/java/mage/server/Main.java index b603e88069b..51f43b87591 100644 --- a/Mage.Server/src/main/java/mage/server/Main.java +++ b/Mage.Server/src/main/java/mage/server/Main.java @@ -4,7 +4,9 @@ import mage.cards.ExpansionSet; import mage.cards.RateCard; import mage.cards.Sets; import mage.cards.decks.DeckValidatorFactory; -import mage.cards.repository.*; +import mage.cards.repository.CardScanner; +import mage.cards.repository.PluginClassloaderRegistery; +import mage.cards.repository.RepositoryUtil; import mage.game.match.MatchType; import mage.game.tournament.TournamentType; import mage.interfaces.MageServer; @@ -507,8 +509,19 @@ public final class Main { private static Class loadPlugin(Plugin plugin) { try { - classLoader.addURL(new File(pluginFolder, plugin.getJar()).toURI().toURL()); logger.debug("Loading plugin: " + plugin.getClassName()); + if (plugin.getName() == null || plugin.getName().isEmpty() + || plugin.getJar() == null || plugin.getJar().isEmpty() + || plugin.getClassName() == null || plugin.getClassName().isEmpty() + ) { + logger.error(String.format("Can't load plugin, found miss fields in config.xml: %s, %s, %s", + plugin.getName(), + plugin.getJar(), + plugin.getClassName() + )); + return null; + } + classLoader.addURL(new File(pluginFolder, plugin.getJar()).toURI().toURL()); return Class.forName(plugin.getClassName(), true, classLoader); } catch (ClassNotFoundException ex) { logger.warn(new StringBuilder("Plugin not Found: ").append(plugin.getClassName()).append(" - ").append(plugin.getJar()).append(" - check plugin folder"), ex); diff --git a/Mage.Server/src/main/java/mage/server/Session.java b/Mage.Server/src/main/java/mage/server/Session.java index 783c246d074..9aba9c4ea82 100644 --- a/Mage.Server/src/main/java/mage/server/Session.java +++ b/Mage.Server/src/main/java/mage/server/Session.java @@ -153,11 +153,11 @@ public class Session { if (userName.length() > config.getMaxUserNameLength()) { return "User name may not be longer than " + config.getMaxUserNameLength() + " characters"; } - if (userName.length() <= 3) { - return "User name is too short (3 characters or fewer)"; + if (userName.length() <= 2) { + return "User name is too short (2 characters or fewer)"; } - if (userName.length() >= 500) { - return "User name is too long (500 characters or more)"; + if (userName.length() >= 250) { + return "User name is too long (250 characters or more)"; } return null; } diff --git a/Mage.Server/src/main/java/mage/server/TableController.java b/Mage.Server/src/main/java/mage/server/TableController.java index f9625d148cd..b23895416cd 100644 --- a/Mage.Server/src/main/java/mage/server/TableController.java +++ b/Mage.Server/src/main/java/mage/server/TableController.java @@ -94,7 +94,7 @@ public class TableController { } table = new Table(roomId, options.getTournamentType(), options.getName(), controllerName, DeckValidatorFactory.instance.createDeckValidator(options.getMatchOptions().getDeckType()), options.getPlayerTypes(), new TableRecorderImpl(managerFactory.userManager()), tournament, options.getMatchOptions().getBannedUsers(), options.isPlaneChase()); - chatId = managerFactory.chatManager().createChatSession("Tourn. table " + table.getId()); + chatId = managerFactory.chatManager().createChatSession("Tourney table " + table.getId()); } private void init() { @@ -325,7 +325,7 @@ public class TableController { // user - restrict by deck power level and cards colors (see edh power level for details) int edhPowerLevel = table.getMatch().getOptions().getEdhPowerLevel(); if (edhPowerLevel > 0 && table.getValidator().getName().toLowerCase(Locale.ENGLISH).equals("commander")) { - int deckEdhPowerLevel = table.getValidator().getEdhPowerLevel(deck); + int deckEdhPowerLevel = table.getValidator().getEdhPowerLevel(deck, new ArrayList<>(), new ArrayList<>()); if (deckEdhPowerLevel % 100 > edhPowerLevel) { String message = new StringBuilder("Your deck appears to be too powerful for this table.\n\nReduce the number of extra turn cards, infect, counters, fogs, reconsider your commander. ") .append("\nThe table requirement has a maximum power level of ").append(edhPowerLevel).append(" whilst your deck has a calculated power level of ") @@ -575,7 +575,7 @@ public class TableController { logger.error("No tournament object - userId: " + userId + " table: " + table.getId()); return; } - if (this.userId != null && this.userId.equals(userId) // tourn. sub tables have no creator user + if (this.userId != null && this.userId.equals(userId) // tourney sub tables have no creator user && (table.getState() == TableState.WAITING || table.getState() == TableState.READY_TO_START)) { // table not started yet and user is the owner, removeUserFromAllTablesAndChat the table diff --git a/Mage.Server/src/main/java/mage/server/draft/CubeFactory.java b/Mage.Server/src/main/java/mage/server/draft/CubeFactory.java index 3fd22546cb3..be3ab3f8053 100644 --- a/Mage.Server/src/main/java/mage/server/draft/CubeFactory.java +++ b/Mage.Server/src/main/java/mage/server/draft/CubeFactory.java @@ -1,4 +1,3 @@ - package mage.server.draft; import mage.cards.decks.Deck; @@ -11,8 +10,7 @@ import java.util.Map; import java.util.Set; /** - * - * @author LevelX2 + * @author LevelX2, JayDi85 */ public enum CubeFactory { @@ -22,13 +20,12 @@ public enum CubeFactory { private final Map draftCubes = new LinkedHashMap<>(); - public DraftCube createDraftCube(String draftCubeName) { DraftCube draftCube; try { Constructor con = draftCubes.get(draftCubeName).getConstructor(); - draftCube = (DraftCube)con.newInstance(); + draftCube = (DraftCube) con.newInstance(); } catch (Exception ex) { logger.fatal("CubeFactory error", ex); return null; @@ -43,7 +40,7 @@ public enum CubeFactory { DraftCube draftCube; try { Constructor con = draftCubes.get(draftCubeName).getConstructor(Deck.class); - draftCube = (DraftCube)con.newInstance(cubeFromDeck); + draftCube = (DraftCube) con.newInstance(cubeFromDeck); } catch (Exception ex) { logger.fatal("CubeFactory error", ex); return null; @@ -57,10 +54,24 @@ public enum CubeFactory { return draftCubes.keySet(); } - public void addDraftCube(String name, Class draftCube) { - if (draftCube != null) { - this.draftCubes.put(name, draftCube); + public void addDraftCube(String configName, Class configCubeClass) { + // store cubes by auto-generated names, not from config + if (configCubeClass == null) { + return; } - } + DraftCube draftCube = null; + try { + Constructor con = configCubeClass.getConstructor(); + draftCube = (DraftCube) con.newInstance(); + if (this.draftCubes.containsKey(draftCube.getName())) { + throw new IllegalArgumentException("already exists " + draftCube.getName()); + } + } catch (Exception e) { + logger.error("Can't create draft cube named by " + configName, e); + return; + } + + this.draftCubes.put(draftCube.getName(), configCubeClass); + } } diff --git a/Mage.Server/src/main/java/mage/server/game/GameFactory.java b/Mage.Server/src/main/java/mage/server/game/GameFactory.java index a0a3621e0c0..1adcec33aba 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameFactory.java +++ b/Mage.Server/src/main/java/mage/server/game/GameFactory.java @@ -51,7 +51,7 @@ public enum GameFactory { } public void addGameType(String name, MatchType matchType, Class game) { - if (game != null) { + if (matchType != null && game != null) { this.games.put(name, game); this.gameTypes.put(name, matchType); this.gameTypeViews.add(new GameTypeView(matchType)); diff --git a/Mage.Server/src/main/java/mage/server/game/PlayerFactory.java b/Mage.Server/src/main/java/mage/server/game/PlayerFactory.java index a8150268f06..2f9e46d91c3 100644 --- a/Mage.Server/src/main/java/mage/server/game/PlayerFactory.java +++ b/Mage.Server/src/main/java/mage/server/game/PlayerFactory.java @@ -45,12 +45,10 @@ public enum PlayerFactory { } public void addPlayerType(String name, Class playerType) { + // will raise error and stop on unknown player and that's ok - it's require HumanPlayer anyway PlayerType type = PlayerType.getByDescription(name); - if (type != null) { - if (playerType != null) { - this.playerTypes.put(type, playerType); - } + if (playerType != null) { + this.playerTypes.put(type, playerType); } } - } diff --git a/Mage.Server/src/main/java/mage/server/tournament/TournamentController.java b/Mage.Server/src/main/java/mage/server/tournament/TournamentController.java index f486de1f0d6..939ee2db5b9 100644 --- a/Mage.Server/src/main/java/mage/server/tournament/TournamentController.java +++ b/Mage.Server/src/main/java/mage/server/tournament/TournamentController.java @@ -208,9 +208,18 @@ public class TournamentController { } private void endTournament() { - for (TournamentPlayer player : tournament.getPlayers()) { - player.setStateAtTournamentEnd(); + boolean setFinishPlayersStatus = true; + if (tournament.getRounds().isEmpty() && tournament.getOptions().getMatchOptions().isSingleGameTourney()) { + // single multiplayer game immediately finish the tourney, so keep dueling info all the time in tourney window + setFinishPlayersStatus = false; } + + if (setFinishPlayersStatus) { + for (TournamentPlayer player : tournament.getPlayers()) { + player.setStateAtTournamentEnd(); + } + } + for (final TournamentSession tournamentSession : tournamentSessions.values()) { tournamentSession.tournamentOver(); } @@ -270,9 +279,14 @@ public class TournamentController { table.setTournamentSubTable(this.tableId); table.setTournament(tournament); table.setState(TableState.WAITING); - if (round.getAllPlayers().stream().allMatch(tournamentPlayer -> getPlayerUserId(tournamentPlayer.getPlayer().getId()).isPresent())) { + if (round.getAllPlayers().stream() + .filter(t -> t.getPlayerType().equals(PlayerType.HUMAN)) + .allMatch(t -> getPlayerUserId(t.getPlayer().getId()).isPresent()) + ) { for (TournamentPlayer player : round.getAllPlayers()) { - tableManager.addPlayer(getPlayerUserId(player.getPlayer().getId()).get(), table.getId(), player); + // userId = null - it's AI opponent + UUID userId = getPlayerUserId(player.getPlayer().getId()).orElse(null); + tableManager.addPlayer(userId, table.getId(), player); } table.setState(TableState.STARTING); tableManager.startTournamentSubMatch(null, table.getId()); @@ -284,9 +298,11 @@ public class TournamentController { player.setState(TournamentPlayerState.DUELING); } }); + } else { + logger.error("tourney - startMultiplayerMatch can't start due disconnected players"); } } catch (GameException ex) { - logger.fatal("TournamentController startMatch error", ex); + logger.fatal("tourney - startMultiplayerMatch error", ex); } } diff --git a/Mage.Server/src/main/java/mage/server/tournament/TournamentFactory.java b/Mage.Server/src/main/java/mage/server/tournament/TournamentFactory.java index e7749b83539..71e7c0a219d 100644 --- a/Mage.Server/src/main/java/mage/server/tournament/TournamentFactory.java +++ b/Mage.Server/src/main/java/mage/server/tournament/TournamentFactory.java @@ -1,5 +1,3 @@ - - package mage.server.tournament; import mage.cards.Sets; @@ -15,7 +13,6 @@ import java.lang.reflect.Constructor; import java.util.*; /** - * * @author BetaSteward_at_googlemail.com */ public enum TournamentFactory { @@ -27,7 +24,6 @@ public enum TournamentFactory { private final List tournamentTypeViews = new ArrayList<>(); - public Tournament createTournament(String tournamentType, TournamentOptions options) { Tournament tournament; @@ -37,8 +33,8 @@ public enum TournamentFactory { // transfer set information, create short info string for included sets tournament.setTournamentType(tournamentTypes.get(tournamentType)); if (tournament.getTournamentType().isLimited()) { - Map setInfo = new LinkedHashMap<>(); - for (String setCode: options.getLimitedOptions().getSetCodes()) { + Map setInfo = new LinkedHashMap<>(); + for (String setCode : options.getLimitedOptions().getSetCodes()) { tournament.getSets().add(Sets.findSet(setCode)); int count = setInfo.getOrDefault(setCode, 0); setInfo.put(setCode, count + 1); @@ -52,25 +48,31 @@ public enum TournamentFactory { } else { draftCube = CubeFactory.instance.createDraftCube(tournament.getOptions().getLimitedOptions().getDraftCubeName()); } - tournament.getOptions().getLimitedOptions().setDraftCube(draftCube); - tournament.setBoosterInfo(tournament.getOptions().getLimitedOptions().getDraftCubeName()); + String boosterInfo = ""; + if (draftCube != null) { + // make sure all loaded (will raise error on empty cards list) + draftCube.validateData(); + tournament.getOptions().getLimitedOptions().setDraftCube(draftCube); + boosterInfo = draftCube.getName(); + } + tournament.setBoosterInfo(boosterInfo); } else if (tournament.getTournamentType().isRandom()) { - StringBuilder rv = new StringBuilder( "Chaos Draft using sets: "); - for (Map.Entry entry: setInfo.entrySet()){ + StringBuilder rv = new StringBuilder("Chaos Draft using sets: "); + for (Map.Entry entry : setInfo.entrySet()) { rv.append(entry.getKey()); rv.append(';'); } tournament.setBoosterInfo(rv.toString()); } else if (tournament.getTournamentType().isReshuffled()) { - StringBuilder rv = new StringBuilder( "Chaos Reshuffled Draft using sets: "); - for (Map.Entry entry: setInfo.entrySet()){ + StringBuilder rv = new StringBuilder("Chaos Reshuffled Draft using sets: "); + for (Map.Entry entry : setInfo.entrySet()) { rv.append(entry.getKey()); rv.append(';'); } tournament.setBoosterInfo(rv.toString()); } else { StringBuilder sb = new StringBuilder(); - for (Map.Entry entry:setInfo.entrySet()) { + for (Map.Entry entry : setInfo.entrySet()) { sb.append(entry.getValue().toString()).append('x').append(entry.getKey()).append(' '); } tournament.setBoosterInfo(sb.toString()); @@ -78,12 +80,9 @@ public enum TournamentFactory { } else { tournament.setBoosterInfo(""); } - - } catch (Exception ex) { - logger.fatal("TournamentFactory error ", ex); - return null; + } catch (Exception e) { + throw new IllegalStateException("Can't create new tourney: " + e, e); } - logger.debug("Tournament created: " + tournamentType + ' ' + tournament.getId()); return tournament; } @@ -94,7 +93,7 @@ public enum TournamentFactory { public void addTournamentType(String name, TournamentType tournamentType, Class tournament) { - if (tournament != null) { + if (tournamentType != null && tournament != null) { this.tournaments.put(name, tournament); this.tournamentTypes.put(name, tournamentType); this.tournamentTypeViews.add(new TournamentTypeView(tournamentType)); diff --git a/Mage.Server/src/test/data/config_error.xml b/Mage.Server/src/test/data/config_error.xml index fd5bffb2f95..dd184d2f9c9 100644 --- a/Mage.Server/src/test/data/config_error.xml +++ b/Mage.Server/src/test/data/config_error.xml @@ -32,148 +32,5 @@ /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Mage.Sets/src/mage/cards/a/AKillerAmongUs.java b/Mage.Sets/src/mage/cards/a/AKillerAmongUs.java index 9922e9ebc15..b11b7952303 100644 --- a/Mage.Sets/src/mage/cards/a/AKillerAmongUs.java +++ b/Mage.Sets/src/mage/cards/a/AKillerAmongUs.java @@ -191,7 +191,7 @@ class AKillerAmongUsEffect extends OneShotEffect { return false; } SubType creatureType = ChooseHumanMerfolkOrGoblinEffect.getSecretCreatureType(source, game); - if (creatureType != null && creature.getSubtype().contains(creatureType)) { + if (creatureType != null && creature.hasSubtype(creatureType, game)) { creature.addCounters(CounterType.P1P1.createInstance(3), source, game); game.addEffect(new GainAbilityTargetEffect( DeathtouchAbility.getInstance(), Duration.EndOfTurn diff --git a/Mage.Sets/src/mage/cards/a/Aberrant.java b/Mage.Sets/src/mage/cards/a/Aberrant.java index ea06d8710c0..dc2f62010a8 100644 --- a/Mage.Sets/src/mage/cards/a/Aberrant.java +++ b/Mage.Sets/src/mage/cards/a/Aberrant.java @@ -12,7 +12,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -42,7 +42,7 @@ public final class Aberrant extends CardImpl { Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new DestroyTargetEffect(), false, true); ability.withFlavorWord("Heavy Power Hammer"); ability.addTarget(new TargetPermanent(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/AbruptDecay.java b/Mage.Sets/src/mage/cards/a/AbruptDecay.java index 5a46ad39be8..f3d379037cb 100644 --- a/Mage.Sets/src/mage/cards/a/AbruptDecay.java +++ b/Mage.Sets/src/mage/cards/a/AbruptDecay.java @@ -1,7 +1,5 @@ - package mage.cards.a; -import java.util.UUID; import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; @@ -10,10 +8,11 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.filter.common.FilterNonlandPermanent; import mage.filter.predicate.mageobject.ManaValuePredicate; -import mage.target.common.TargetNonlandPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class AbruptDecay extends CardImpl { @@ -25,14 +24,14 @@ public final class AbruptDecay extends CardImpl { } public AbruptDecay(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{B}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}{G}"); // This spell can't be countered. this.addAbility(new CantBeCounteredSourceAbility().setRuleAtTheTop(true)); // Destroy target nonland permanent with converted mana cost 3 or less. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetNonlandPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); } private AbruptDecay(final AbruptDecay card) { diff --git a/Mage.Sets/src/mage/cards/a/AbstergoEntertainment.java b/Mage.Sets/src/mage/cards/a/AbstergoEntertainment.java index 6027d609d90..f405a13545b 100644 --- a/Mage.Sets/src/mage/cards/a/AbstergoEntertainment.java +++ b/Mage.Sets/src/mage/cards/a/AbstergoEntertainment.java @@ -14,7 +14,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SuperType; import mage.filter.FilterCard; -import mage.filter.common.FilterHistoricCard; +import mage.filter.predicate.mageobject.HistoricPredicate; import mage.target.common.TargetCardInYourGraveyard; import java.util.UUID; @@ -24,7 +24,10 @@ import java.util.UUID; */ public final class AbstergoEntertainment extends CardImpl { - private static final FilterCard filter = new FilterHistoricCard("historic card from your graveyard"); + private static final FilterCard filter = new FilterCard("historic card from your graveyard"); + static { + filter.add(HistoricPredicate.instance); + } public AbstergoEntertainment(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); diff --git a/Mage.Sets/src/mage/cards/a/AbzanBeastmaster.java b/Mage.Sets/src/mage/cards/a/AbzanBeastmaster.java index 07baf140a0e..a413743d361 100644 --- a/Mage.Sets/src/mage/cards/a/AbzanBeastmaster.java +++ b/Mage.Sets/src/mage/cards/a/AbzanBeastmaster.java @@ -1,36 +1,35 @@ package mage.cards.a; -import java.util.UUID; import mage.MageInt; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.condition.common.ControlsCreatureGreatestToughnessCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class AbzanBeastmaster extends CardImpl { public AbzanBeastmaster(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); this.subtype.add(SubType.DOG); this.subtype.add(SubType.SHAMAN); this.power = new MageInt(2); this.toughness = new MageInt(1); // At the beginning of your upkeep, draw a card if you control the creature with the greatest toughness or tied for the greatest toughness. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility(new DrawCardSourceControllerEffect(1)), - ControlsCreatureGreatestToughnessCondition.instance, - "At the beginning of your upkeep, draw a card if you control the creature with the greatest toughness or tied for the greatest toughness." - )); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new ConditionalOneShotEffect( + new DrawCardSourceControllerEffect(1), ControlsCreatureGreatestToughnessCondition.instance, + "draw a card if you control the creature with the greatest toughness or tied for the greatest toughness" + ))); } private AbzanBeastmaster(final AbzanBeastmaster card) { diff --git a/Mage.Sets/src/mage/cards/a/AbzanMonument.java b/Mage.Sets/src/mage/cards/a/AbzanMonument.java index b9c120e1fba..07bbccebb90 100644 --- a/Mage.Sets/src/mage/cards/a/AbzanMonument.java +++ b/Mage.Sets/src/mage/cards/a/AbzanMonument.java @@ -6,8 +6,9 @@ import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.dynamicvalue.common.GreatestToughnessAmongControlledCreaturesValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -53,7 +54,7 @@ public final class AbzanMonument extends CardImpl { ); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); - this.addAbility(ability.addHint(GreatestToughnessAmongControlledCreaturesValue.ALL.getHint())); + this.addAbility(ability.addHint(GreatestAmongPermanentsValue.TOUGHNESS_CONTROLLED_CREATURES.getHint())); } private AbzanMonument(final AbzanMonument card) { @@ -85,8 +86,7 @@ class AbzanMonumentEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - return new SpiritXXToken( - GreatestToughnessAmongControlledCreaturesValue.ALL.calculate(game, source, this) - ).putOntoBattlefield(1, game, source); + int value = GreatestAmongPermanentsValue.TOUGHNESS_CONTROLLED_CREATURES.calculate(game, source, this); + return new CreateTokenEffect(new SpiritXXToken(value)).apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/a/AcceleratedMutation.java b/Mage.Sets/src/mage/cards/a/AcceleratedMutation.java index f7773673194..34f43902b95 100644 --- a/Mage.Sets/src/mage/cards/a/AcceleratedMutation.java +++ b/Mage.Sets/src/mage/cards/a/AcceleratedMutation.java @@ -1,8 +1,6 @@ package mage.cards.a; -import java.util.UUID; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.common.HighestManaValueCount; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -10,19 +8,23 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author nigelzor */ public final class AcceleratedMutation extends CardImpl { - private static final DynamicValue xValue = new HighestManaValueCount(); - public AcceleratedMutation(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{G}{G}"); - // Target creature gets +X/+X until end of turn, where X is the highest converted mana cost among permanents you control. - this.getSpellAbility().addEffect(new BoostTargetEffect(xValue, xValue, Duration.EndOfTurn)); + // Target creature gets +X/+X until end of turn, where X is the greatest converted mana cost among permanents you control. + this.getSpellAbility().addEffect(new BoostTargetEffect( + GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_PERMANENTS, + GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_PERMANENTS, + Duration.EndOfTurn + )); + this.getSpellAbility().addHint(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_PERMANENTS.getHint()); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/cards/a/AcclaimedContender.java b/Mage.Sets/src/mage/cards/a/AcclaimedContender.java index 0450d449e5f..2fbf272c272 100644 --- a/Mage.Sets/src/mage/cards/a/AcclaimedContender.java +++ b/Mage.Sets/src/mage/cards/a/AcclaimedContender.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -25,7 +24,7 @@ import java.util.UUID; */ public final class AcclaimedContender extends CardImpl { - private static final FilterPermanent filter = new FilterControlledPermanent(SubType.KNIGHT); + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.KNIGHT, "you control another Knight"); private static final FilterCard filter2 = new FilterCard("a Knight, Aura, Equipment, or legendary artifact card"); @@ -53,14 +52,9 @@ public final class AcclaimedContender extends CardImpl { this.toughness = new MageInt(3); // When Acclaimed Contender enters the battlefield, if you control another Knight, look at the top five cards of your library. You may reveal a Knight, Aura, Equipment, or legendary artifact card from among them and put it into your hand. Put the rest on the bottom of your library in a random order. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new LookLibraryAndPickControllerEffect( - 5, 1, filter2, PutCards.HAND, PutCards.BOTTOM_RANDOM - )), condition, "When {this} enters, " + - "if you control another Knight, look at the top five cards of your library. " + - "You may reveal a Knight, Aura, Equipment, or legendary artifact card from among them " + - "and put it into your hand. Put the rest on the bottom of your library in a random order." - )); + this.addAbility(new EntersBattlefieldTriggeredAbility(new LookLibraryAndPickControllerEffect( + 5, 1, filter2, PutCards.HAND, PutCards.BOTTOM_RANDOM + )).withInterveningIf(condition)); } private AcclaimedContender(final AcclaimedContender card) { diff --git a/Mage.Sets/src/mage/cards/a/AcererakTheArchlich.java b/Mage.Sets/src/mage/cards/a/AcererakTheArchlich.java index 11eda4d0318..d429f82da8c 100644 --- a/Mage.Sets/src/mage/cards/a/AcererakTheArchlich.java +++ b/Mage.Sets/src/mage/cards/a/AcererakTheArchlich.java @@ -6,7 +6,6 @@ import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.CompletedDungeonCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.abilities.effects.keyword.VentureIntoTheDungeonEffect; @@ -22,8 +21,6 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.ZombieToken; import mage.players.Player; -import mage.target.TargetPermanent; -import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetSacrifice; import mage.watchers.common.CompletedDungeonWatcher; @@ -44,13 +41,9 @@ public final class AcererakTheArchlich extends CardImpl { this.toughness = new MageInt(5); // When Acererak the Archlich enters the battlefield, if you have not completed Tomb of Annihilation, return Acererak the Archlich to its owner's hand and venture into the dungeon. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new ReturnToHandSourceEffect(true)), - AcererakTheArchlichCondition.instance, "When {this} enters, " + - "if you haven't completed Tomb of Annihilation, return {this} " + - "to its owner's hand and venture into the dungeon." - ); - ability.addEffect(new VentureIntoTheDungeonEffect()); + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandSourceEffect(true)) + .withInterveningIf(AcererakTheArchlichCondition.instance); + ability.addEffect(new VentureIntoTheDungeonEffect().concatBy("and")); ability.addHint(CurrentDungeonHint.instance); ability.addHint(CompletedDungeonCondition.getHint()); this.addAbility(ability, new CompletedDungeonWatcher()); @@ -78,6 +71,11 @@ enum AcererakTheArchlichCondition implements Condition { source.getControllerId(), game ).contains("Tomb of Annihilation"); } + + @Override + public String toString() { + return "you haven't completed Tomb of Annihilation"; + } } class AcererakTheArchlichEffect extends OneShotEffect { diff --git a/Mage.Sets/src/mage/cards/a/AcesBaseballBat.java b/Mage.Sets/src/mage/cards/a/AcesBaseballBat.java index 75383b17811..0a54026f17a 100644 --- a/Mage.Sets/src/mage/cards/a/AcesBaseballBat.java +++ b/Mage.Sets/src/mage/cards/a/AcesBaseballBat.java @@ -2,9 +2,10 @@ package mage.cards.a; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.Condition; +import mage.abilities.condition.common.AttachedAttackingCondition; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.decorator.ConditionalRequirementEffect; import mage.abilities.effects.common.combat.MustBeBlockedByAtLeastOneAttachedEffect; import mage.abilities.effects.common.continuous.BoostEquippedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; @@ -14,8 +15,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; import java.util.UUID; @@ -27,12 +26,14 @@ public final class AcesBaseballBat extends CardImpl { private static final FilterControlledCreaturePermanent filterLegendary = new FilterControlledCreaturePermanent("legendary creature"); + static { filterLegendary.add(SuperType.LEGENDARY.getPredicate()); } private static final FilterControlledCreaturePermanent filterDalek = new FilterControlledCreaturePermanent("a Dalek"); + static { filterDalek.add(SubType.DALEK.getPredicate()); } @@ -48,12 +49,19 @@ public final class AcesBaseballBat extends CardImpl { // As long as equipped creature is attacking, it has first strike and must be blocked by a Dalek if able. Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( new GainAbilityAttachedEffect(FirstStrikeAbility.getInstance(), AttachmentType.EQUIPMENT), - AttachedToAttackingCondition.instance, "As long as equipped creature is attacking, it has first strike")); - ability.addEffect(new MustBeBlockedByAtLeastOneAttachedEffect(filterDalek).concatBy("and")); + AttachedAttackingCondition.instance, "as long as equipped creature is attacking, it has first strike" + )); + ability.addEffect(new ConditionalRequirementEffect( + new MustBeBlockedByAtLeastOneAttachedEffect(filterDalek), + AttachedAttackingCondition.instance, "and must be blocked by a Dalek if able" + )); this.addAbility(ability); // Equip legendary creature (1) - this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(1), new TargetControlledCreaturePermanent(filterLegendary), false)); + this.addAbility(new EquipAbility( + Outcome.AddAbility, new GenericManaCost(1), + new TargetControlledCreaturePermanent(filterLegendary), false + )); // Equip {3} this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(3), false)); @@ -68,19 +76,3 @@ public final class AcesBaseballBat extends CardImpl { return new AcesBaseballBat(this); } } -enum AttachedToAttackingCondition implements Condition { - instance; - - @Override - public boolean apply(Game game, Ability source) { - Permanent attachment = game.getPermanent(source.getSourceId()); - if (attachment == null || attachment.getAttachedTo() == null) { - return false; - } - Permanent attachedTo = game.getPermanent(attachment.getAttachedTo()); - if (attachedTo == null) { - return false; - } - return attachedTo.isAttacking(); - } -} diff --git a/Mage.Sets/src/mage/cards/a/AchHansRun.java b/Mage.Sets/src/mage/cards/a/AchHansRun.java index 69985502ea7..198dac5c4b1 100644 --- a/Mage.Sets/src/mage/cards/a/AchHansRun.java +++ b/Mage.Sets/src/mage/cards/a/AchHansRun.java @@ -21,6 +21,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInLibrary; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -82,7 +83,7 @@ class AchHansRunEffect extends OneShotEffect { if (card == null || !controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { return false; } - Permanent creature = game.getPermanent(card.getId()); + Permanent creature = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (creature == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/a/AcidWebSpider.java b/Mage.Sets/src/mage/cards/a/AcidWebSpider.java index 54440c41a8b..8ef5ae16d0a 100644 --- a/Mage.Sets/src/mage/cards/a/AcidWebSpider.java +++ b/Mage.Sets/src/mage/cards/a/AcidWebSpider.java @@ -1,8 +1,5 @@ - - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -12,29 +9,25 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author Loki */ public final class AcidWebSpider extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("Equipment"); - static { - filter.add(SubType.EQUIPMENT.getPredicate()); - } - - public AcidWebSpider (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}{G}"); + public AcidWebSpider(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{G}"); this.subtype.add(SubType.SPIDER); this.power = new MageInt(3); this.toughness = new MageInt(5); this.addAbility(ReachAbility.getInstance()); Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), true); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_EQUIPMENT)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/AdaptiveTrainingPost.java b/Mage.Sets/src/mage/cards/a/AdaptiveTrainingPost.java index 332776d8e14..36f48cb6f75 100644 --- a/Mage.Sets/src/mage/cards/a/AdaptiveTrainingPost.java +++ b/Mage.Sets/src/mage/cards/a/AdaptiveTrainingPost.java @@ -11,6 +11,7 @@ import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.counters.CounterType; import mage.filter.StaticFilters; @@ -21,7 +22,7 @@ import java.util.UUID; */ public final class AdaptiveTrainingPost extends CardImpl { - private static final Condition condition = new SourceHasCounterCondition(CounterType.CHARGE, 0, 2); + private static final Condition condition = new SourceHasCounterCondition(CounterType.CHARGE, ComparisonType.FEWER_THAN, 3); public AdaptiveTrainingPost(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{U}"); diff --git a/Mage.Sets/src/mage/cards/a/AdelbertSteiner.java b/Mage.Sets/src/mage/cards/a/AdelbertSteiner.java new file mode 100644 index 00000000000..65b3676a654 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AdelbertSteiner.java @@ -0,0 +1,53 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.common.FilterControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AdelbertSteiner extends CardImpl { + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(new FilterControlledPermanent(SubType.EQUIPMENT)); + private static final Hint hint = new ValueHint("Equipment you control", xValue); + + public AdelbertSteiner(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.KNIGHT); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Adelbert Steiner gets +1/+1 for each Equipment you control. + this.addAbility(new SimpleStaticAbility(new BoostSourceEffect(xValue, xValue, Duration.WhileOnBattlefield)).addHint(hint)); + } + + private AdelbertSteiner(final AdelbertSteiner card) { + super(card); + } + + @Override + public AdelbertSteiner copy() { + return new AdelbertSteiner(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AdherentOfHope.java b/Mage.Sets/src/mage/cards/a/AdherentOfHope.java index df3e76f6db4..62f145afd02 100644 --- a/Mage.Sets/src/mage/cards/a/AdherentOfHope.java +++ b/Mage.Sets/src/mage/cards/a/AdherentOfHope.java @@ -1,45 +1,41 @@ package mage.cards.a; import mage.MageInt; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; -import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterControlledPlaneswalkerPermanent; import java.util.UUID; /** - * * @author htrajan */ public final class AdherentOfHope extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent(); - - static { - filter.add(CardType.PLANESWALKER.getPredicate()); - filter.add(SubType.BASRI.getPredicate()); - } + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + new FilterControlledPlaneswalkerPermanent(SubType.BASRI, "you control a Basri planeswalker") + ); + private static final Hint hint = new ConditionHint(condition); public AdherentOfHope(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); - + this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SOLDIER); this.power = new MageInt(2); this.toughness = new MageInt(1); // At the beginning of combat on your turn, if you control a Basri planeswalker, put a +1/+1 counter on Adherent of Hope. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfCombatTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance())), - new PermanentsOnTheBattlefieldCondition(filter), - "At the beginning of combat on your turn, if you control a Basri planeswalker, put a +1/+1 counter on {this}.")); + this.addAbility(new BeginningOfCombatTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance())).withInterveningIf(condition).addHint(hint)); } private AdherentOfHope(final AdherentOfHope card) { diff --git a/Mage.Sets/src/mage/cards/a/AdmiralBeckettBrass.java b/Mage.Sets/src/mage/cards/a/AdmiralBeckettBrass.java index fdee119783e..ef68e75d2f3 100644 --- a/Mage.Sets/src/mage/cards/a/AdmiralBeckettBrass.java +++ b/Mage.Sets/src/mage/cards/a/AdmiralBeckettBrass.java @@ -1,13 +1,12 @@ package mage.cards.a; -import java.util.*; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -18,11 +17,12 @@ import mage.game.Game; import mage.game.events.DamagedPlayerEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; -import mage.target.common.TargetNonlandPermanent; +import mage.target.TargetPermanent; import mage.watchers.Watcher; +import java.util.*; + /** - * * @author TheElk801 */ public final class AdmiralBeckettBrass extends CardImpl { @@ -33,7 +33,7 @@ public final class AdmiralBeckettBrass extends CardImpl { static { filter.add(SubType.PIRATE.getPredicate()); filter.add(TargetController.YOU.getControllerPredicate()); - filter2.add(new ControllerDealtDamageByPiratesPredicate()); + filter2.add(ControllerDealtDamageByPiratesPredicate.instance); } public AdmiralBeckettBrass(UUID ownerId, CardSetInfo setInfo) { @@ -50,7 +50,7 @@ public final class AdmiralBeckettBrass extends CardImpl { // At the beginning of your end step, gain control of target nonland permanent controlled by a player who was dealt combat damage by three or more Pirates this turn. Ability ability = new BeginningOfEndStepTriggeredAbility(new GainControlTargetEffect(Duration.Custom, true)); - ability.addTarget(new TargetNonlandPermanent(filter2)); + ability.addTarget(new TargetPermanent(filter2)); this.addAbility(ability, new DamagedByPiratesWatcher()); } @@ -101,7 +101,8 @@ class DamagedByPiratesWatcher extends Watcher { } } -class ControllerDealtDamageByPiratesPredicate implements Predicate { +enum ControllerDealtDamageByPiratesPredicate implements Predicate { + instance; @Override public boolean apply(Permanent input, Game game) { diff --git a/Mage.Sets/src/mage/cards/a/Adrestia.java b/Mage.Sets/src/mage/cards/a/Adrestia.java index c0df6888563..7536ee8ad98 100644 --- a/Mage.Sets/src/mage/cards/a/Adrestia.java +++ b/Mage.Sets/src/mage/cards/a/Adrestia.java @@ -1,40 +1,38 @@ package mage.cards.a; -import java.util.*; - import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.AddCardSubTypeSourceEffect; import mage.abilities.hint.ConditionHint; import mage.abilities.hint.Hint; -import mage.constants.*; -import mage.abilities.keyword.IslandwalkAbility; import mage.abilities.keyword.CrewAbility; +import mage.abilities.keyword.IslandwalkAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.watchers.Watcher; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + /** - * * @author Grath */ public final class Adrestia extends CardImpl { - private static final Condition condition = AdrestiaCondition.instance; public Adrestia(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); - + this.supertype.add(SuperType.LEGENDARY); this.subtype.add(SubType.VEHICLE); this.power = new MageInt(4); @@ -44,16 +42,14 @@ public final class Adrestia extends CardImpl { this.addAbility(new IslandwalkAbility()); // Whenever Adrestia attacks, if an Assassin crewed it this turn, draw a card. Adrestia becomes an Assassin in addition to its other types until end of turn. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new AttacksTriggeredAbility(new DrawCardSourceControllerEffect(1), false), - condition, "Whenever {this} attacks, if an Assassin crewed it this turn, draw a card. {this} becomes an Assassin in addition to its other types until end of turn."); + Ability ability = new AttacksTriggeredAbility(new DrawCardSourceControllerEffect(1), false) + .withInterveningIf(AdrestiaCondition.instance); ability.addEffect(new AddCardSubTypeSourceEffect(Duration.EndOfTurn, true, SubType.ASSASSIN)); ability.addHint(AdrestiaCondition.getHint()); this.addAbility(ability, new AdrestiaWatcher()); // Crew 1 this.addAbility(new CrewAbility(1)); - } private Adrestia(final Adrestia card) { @@ -68,13 +64,18 @@ public final class Adrestia extends CardImpl { enum AdrestiaCondition implements Condition { instance; - private static final Hint hint = new ConditionHint(instance, "an Assassin crewed it this turn"); + private static final Hint hint = new ConditionHint(instance); @Override public boolean apply(Game game, Ability source) { return AdrestiaWatcher.checkIfAssassinCrewed(source.getSourcePermanentOrLKI(game), game); } + @Override + public String toString() { + return "an Assassin crewed it this turn"; + } + public static Hint getHint() { return hint; } @@ -97,8 +98,7 @@ class AdrestiaWatcher extends Watcher { if (crewer != null) { if (!crewMap.containsKey(vehicle)) { crewMap.put(vehicle, filter.match(crewer, game)); - } - else { + } else { crewMap.put(vehicle, crewMap.get(vehicle) || filter.match(crewer, game)); } } diff --git a/Mage.Sets/src/mage/cards/a/AdventurersAirship.java b/Mage.Sets/src/mage/cards/a/AdventurersAirship.java new file mode 100644 index 00000000000..b4c01ace72e --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AdventurersAirship.java @@ -0,0 +1,45 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.DrawDiscardControllerEffect; +import mage.abilities.keyword.CrewAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AdventurersAirship extends CardImpl { + + public AdventurersAirship(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When this Vehicle attacks, draw a card then discard a card. + this.addAbility(new AttacksTriggeredAbility(new DrawDiscardControllerEffect(1, 1))); + + // Crew 2 + this.addAbility(new CrewAbility(2)); + } + + private AdventurersAirship(final AdventurersAirship card) { + super(card); + } + + @Override + public AdventurersAirship copy() { + return new AdventurersAirship(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AerialSurveyor.java b/Mage.Sets/src/mage/cards/a/AerialSurveyor.java index fae375b3700..0aaed9d279b 100644 --- a/Mage.Sets/src/mage/cards/a/AerialSurveyor.java +++ b/Mage.Sets/src/mage/cards/a/AerialSurveyor.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; import mage.abilities.hint.Hint; import mage.abilities.hint.common.LandsYouControlHint; @@ -46,13 +45,10 @@ public final class AerialSurveyor extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever Aerial Surveyor attacks, if defending player controls more lands than you, search your library for a basic Plains card, put it onto the battlefield tapped, then shuffle. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new AttacksTriggeredAbility( - new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter), true) - ), AerialSurveyorCondition.instance, "Whenever {this} attacks, if defending player " + - "controls more lands than you, search your library for a basic Plains card, " + - "put it onto the battlefield tapped, then shuffle." - ).addHint(LandsYouControlHint.instance).addHint(AerialSurveyorHint.instance)); + this.addAbility(new AttacksTriggeredAbility(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter), true)) + .withInterveningIf(AerialSurveyorCondition.instance) + .addHint(LandsYouControlHint.instance) + .addHint(AerialSurveyorHint.instance)); // Crew 2 this.addAbility(new CrewAbility(2)); @@ -85,6 +81,11 @@ enum AerialSurveyorCondition implements Condition { source.getControllerId(), source, game ); } + + @Override + public String toString() { + return ""; + } } enum AerialSurveyorHint implements Hint { diff --git a/Mage.Sets/src/mage/cards/a/AerithRescueMission.java b/Mage.Sets/src/mage/cards/a/AerithRescueMission.java new file mode 100644 index 00000000000..f63fd779d57 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AerithRescueMission.java @@ -0,0 +1,45 @@ +package mage.cards.a; + +import mage.abilities.Mode; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.game.permanent.token.HeroToken; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AerithRescueMission extends CardImpl { + + public AerithRescueMission(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{W}"); + + // Choose one-- + // * Take the Elevator -- Create three 1/1 colorless Hero creature tokens. + this.getSpellAbility().addEffect(new CreateTokenEffect(new HeroToken(), 3)); + this.getSpellAbility().withFirstModeFlavorWord("Take the Elevator"); + + // * Take 59 Flights of Stairs -- Tap up to three target creatures. Put a stun counter on one of them. + this.getSpellAbility().addMode(new Mode(new TapTargetEffect()) + .addEffect(new AddCountersTargetEffect(CounterType.STUN.createInstance()) + .setText("Put a stun counter on one of them")) + .addTarget(new TargetCreaturePermanent(0, 3)) + .withFlavorWord("Take 59 Flights of Stairs")); + } + + private AerithRescueMission(final AerithRescueMission card) { + super(card); + } + + @Override + public AerithRescueMission copy() { + return new AerithRescueMission(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AetherChanneler.java b/Mage.Sets/src/mage/cards/a/AetherChanneler.java index 24a3d6f09e8..e4faec4ab4f 100644 --- a/Mage.Sets/src/mage/cards/a/AetherChanneler.java +++ b/Mage.Sets/src/mage/cards/a/AetherChanneler.java @@ -14,12 +14,11 @@ import mage.constants.SubType; import mage.filter.common.FilterNonlandPermanent; import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.BirdToken; -import mage.target.common.TargetNonlandPermanent; +import mage.target.TargetPermanent; import java.util.UUID; /** - * * @author weirddan455 */ public final class AetherChanneler extends CardImpl { @@ -44,7 +43,7 @@ public final class AetherChanneler extends CardImpl { // * Draw a card. Ability ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new BirdToken())); Mode mode = new Mode(new ReturnToHandTargetEffect()); - mode.addTarget(new TargetNonlandPermanent(filter)); + mode.addTarget(new TargetPermanent(filter)); ability.addMode(mode); ability.addMode(new Mode(new DrawCardSourceControllerEffect(1))); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/a/AetherGale.java b/Mage.Sets/src/mage/cards/a/AetherGale.java index 830164797b2..f5fa8e37cec 100644 --- a/Mage.Sets/src/mage/cards/a/AetherGale.java +++ b/Mage.Sets/src/mage/cards/a/AetherGale.java @@ -1,26 +1,25 @@ package mage.cards.a; -import java.util.UUID; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.StaticFilters; -import mage.target.common.TargetNonlandPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class AetherGale extends CardImpl { public AetherGale(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{U}{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}{U}"); // Return six target nonland permanents to their owners' hands. this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); - this.getSpellAbility().addTarget(new TargetNonlandPermanent(6,6, StaticFilters.FILTER_PERMANENTS_NON_LAND, false)); + this.getSpellAbility().addTarget(new TargetPermanent(6, StaticFilters.FILTER_PERMANENTS_NON_LAND)); } private AetherGale(final AetherGale card) { diff --git a/Mage.Sets/src/mage/cards/a/AetherfluxConduit.java b/Mage.Sets/src/mage/cards/a/AetherfluxConduit.java index d21de17365e..f4f9316a48e 100644 --- a/Mage.Sets/src/mage/cards/a/AetherfluxConduit.java +++ b/Mage.Sets/src/mage/cards/a/AetherfluxConduit.java @@ -1,8 +1,5 @@ package mage.cards.a; -import java.util.Optional; -import java.util.UUID; - import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; @@ -10,14 +7,20 @@ import mage.abilities.costs.common.PayEnergyCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.abilities.effects.common.continuous.CastFromHandWithoutPayingManaCostEffect; import mage.abilities.effects.common.counter.GetEnergyCountersControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.stack.Spell; +import mage.players.Player; +import mage.util.CardUtil; + +import java.util.Optional; +import java.util.UUID; /** * @author sobiech @@ -28,14 +31,12 @@ public final class AetherfluxConduit extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{6}"); // Whenever you cast a spell, you get an amount of {E} equal to the amount of mana spent to cast that spell. - this.addAbility(new SpellCastControllerTriggeredAbility( - new AetherfluxConduitEffect(),false - )); + this.addAbility(new SpellCastControllerTriggeredAbility(new AetherfluxConduitManaEffect(), false)); // {T}, Pay fifty {E}: Draw seven cards. You may cast any number of spells from your hand without paying their mana costs. final Ability ability = new SimpleActivatedAbility(new DrawCardSourceControllerEffect(7), new TapSourceCost()); ability.addCost(new PayEnergyCost(50).setText("Pay fifty {E}")); - ability.addEffect(new CastFromHandWithoutPayingManaCostEffect()); + ability.addEffect(new AetherfluxConduitCastEffect()); this.addAbility(ability); } @@ -48,19 +49,21 @@ public final class AetherfluxConduit extends CardImpl { return new AetherfluxConduit(this); } } -class AetherfluxConduitEffect extends OneShotEffect { - AetherfluxConduitEffect() { +class AetherfluxConduitManaEffect extends OneShotEffect { + + AetherfluxConduitManaEffect() { super(Outcome.Benefit); this.staticText = "you get an amount of {E} (energy counters) equal to the amount of mana spent to cast that spell"; } - private AetherfluxConduitEffect(AetherfluxConduitEffect effect) { + + private AetherfluxConduitManaEffect(AetherfluxConduitManaEffect effect) { super(effect); } @Override - public AetherfluxConduitEffect copy() { - return new AetherfluxConduitEffect(this); + public AetherfluxConduitManaEffect copy() { + return new AetherfluxConduitManaEffect(this); } @Override @@ -72,3 +75,31 @@ class AetherfluxConduitEffect extends OneShotEffect { } } +class AetherfluxConduitCastEffect extends OneShotEffect { + + AetherfluxConduitCastEffect() { + super(Outcome.Benefit); + staticText = "You may cast any number of spells from your hand without paying their mana costs"; + } + + private AetherfluxConduitCastEffect(final AetherfluxConduitCastEffect effect) { + super(effect); + } + + @Override + public AetherfluxConduitCastEffect copy() { + return new AetherfluxConduitCastEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + CardUtil.castMultipleWithAttributeForFree( + player, source, game, new CardsImpl(player.getHand()), StaticFilters.FILTER_CARD + ); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/a/AethermagesTouch.java b/Mage.Sets/src/mage/cards/a/AethermagesTouch.java index 76784a34140..461311e8726 100644 --- a/Mage.Sets/src/mage/cards/a/AethermagesTouch.java +++ b/Mage.Sets/src/mage/cards/a/AethermagesTouch.java @@ -17,6 +17,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetCard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @@ -73,7 +74,7 @@ class AethermagesTouchEffect extends OneShotEffect { cards.remove(card); if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { // It gains \"At the beginning of your end step, return this creature to its owner's hand.\" - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { Ability ability = new BeginningOfEndStepTriggeredAbility(TargetController.YOU, new ReturnToHandSourceEffect(true), false, null); ContinuousEffect effect = new GainAbilityTargetEffect(ability, Duration.Custom); diff --git a/Mage.Sets/src/mage/cards/a/AettirAndPriwen.java b/Mage.Sets/src/mage/cards/a/AettirAndPriwen.java new file mode 100644 index 00000000000..2cb9a46c063 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AettirAndPriwen.java @@ -0,0 +1,78 @@ +package mage.cards.a; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.keyword.EquipAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Controllable; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AettirAndPriwen extends CardImpl { + + public AettirAndPriwen(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{6}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.EQUIPMENT); + + // Equipped creature has base power and toughness X/X, where X is your life total. + this.addAbility(new SimpleStaticAbility(new AettirAndPriwenEffect())); + + // Equip {5} + this.addAbility(new EquipAbility(5)); + } + + private AettirAndPriwen(final AettirAndPriwen card) { + super(card); + } + + @Override + public AettirAndPriwen copy() { + return new AettirAndPriwen(this); + } +} + +class AettirAndPriwenEffect extends ContinuousEffectImpl { + + AettirAndPriwenEffect() { + super(Duration.WhileOnBattlefield, Layer.PTChangingEffects_7, SubLayer.SetPT_7b, Outcome.Benefit); + staticText = "equipped creature has base power and toughness X/X, where X is your life total"; + } + + private AettirAndPriwenEffect(final AettirAndPriwenEffect effect) { + super(effect); + } + + @Override + public AettirAndPriwenEffect copy() { + return new AettirAndPriwenEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent == null) { + return false; + } + int life = Optional + .ofNullable(source) + .map(Controllable::getControllerId) + .map(game::getPlayer) + .map(Player::getLife) + .orElse(0); + permanent.getPower().setModifiedBaseValue(life); + permanent.getToughness().setModifiedBaseValue(life); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/a/AfterlifeFromTheLoam.java b/Mage.Sets/src/mage/cards/a/AfterlifeFromTheLoam.java index 6eb1514cc80..7b5f7357981 100644 --- a/Mage.Sets/src/mage/cards/a/AfterlifeFromTheLoam.java +++ b/Mage.Sets/src/mage/cards/a/AfterlifeFromTheLoam.java @@ -1,8 +1,5 @@ package mage.cards.a; -import java.util.UUID; - -import mage.abilities.Ability; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.abilities.effects.common.continuous.AddCardSubTypeTargetEffect; import mage.abilities.keyword.DelveAbility; @@ -11,14 +8,13 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.card.OwnerIdPredicate; -import mage.game.Game; -import mage.players.Player; +import mage.filter.StaticFilters; import mage.target.common.TargetCardInGraveyard; -import mage.target.targetadjustment.TargetAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; +import java.util.UUID; + /** * * @author Jmlundeen @@ -39,7 +35,8 @@ public final class AfterlifeFromTheLoam extends CardImpl { this.getSpellAbility().addEffect(new AddCardSubTypeTargetEffect(SubType.ZOMBIE, Duration.WhileOnBattlefield) .setTargetPointer(new EachTargetPointer()) .setText("they're Zombies in addition to their other types")); - this.getSpellAbility().setTargetAdjuster(AfterlifeFromTheLoamAdjuster.instance); + this.getSpellAbility().addTarget(new TargetCardInGraveyard(0, 1, StaticFilters.FILTER_CARD_CREATURE_A_GRAVEYARD)); + this.getSpellAbility().setTargetAdjuster(new ForEachPlayerTargetsAdjuster(true, false)); } private AfterlifeFromTheLoam(final AfterlifeFromTheLoam card) { @@ -51,22 +48,3 @@ public final class AfterlifeFromTheLoam extends CardImpl { return new AfterlifeFromTheLoam(this); } } - -enum AfterlifeFromTheLoamAdjuster implements TargetAdjuster { - instance; - - @Override - public void adjustTargets(Ability ability, Game game) { - ability.getTargets().clear(); - for (UUID playerId : game.getState().getPlayersInRange(ability.getControllerId(), game)) { - Player player = game.getPlayer(playerId); - if (player == null) { - continue; - } - String playerName = ability.isControlledBy(playerId) ? "your" : player.getName() + "'s"; - FilterCreatureCard filter = new FilterCreatureCard("creature card in " + playerName + " graveyard"); - filter.add(new OwnerIdPredicate(playerId)); - ability.addTarget(new TargetCardInGraveyard(0, 1, filter)); - } - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/a/AgathasChampion.java b/Mage.Sets/src/mage/cards/a/AgathasChampion.java index 7b7fdbaaa69..409195e2c24 100644 --- a/Mage.Sets/src/mage/cards/a/AgathasChampion.java +++ b/Mage.Sets/src/mage/cards/a/AgathasChampion.java @@ -1,10 +1,9 @@ package mage.cards.a; import mage.MageInt; -import mage.abilities.TriggeredAbility; +import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.BargainedCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.FightTargetSourceEffect; import mage.abilities.keyword.BargainAbility; import mage.abilities.keyword.TrampleAbility; @@ -37,14 +36,11 @@ public final class AgathasChampion extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // When Agatha's Champion enters the battlefield, if it was bargained, it fights up to one target creature you don't control. - TriggeredAbility trigger = new EntersBattlefieldTriggeredAbility(new FightTargetSourceEffect()); - trigger.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL, false)); - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - trigger, - BargainedCondition.instance, - "When {this} enters, if it was bargained, it fights up to one target creature you don't control." + - " (Each deals damage equal to its power to the other.)" - )); + Ability ability = new EntersBattlefieldTriggeredAbility( + new FightTargetSourceEffect().setText("it fights up to one target creature you don't control") + ).withInterveningIf(BargainedCondition.instance); + ability.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); + this.addAbility(ability); } private AgathasChampion(final AgathasChampion card) { diff --git a/Mage.Sets/src/mage/cards/a/AgentOfTreachery.java b/Mage.Sets/src/mage/cards/a/AgentOfTreachery.java index 4931519eaac..8c34c1dd047 100644 --- a/Mage.Sets/src/mage/cards/a/AgentOfTreachery.java +++ b/Mage.Sets/src/mage/cards/a/AgentOfTreachery.java @@ -2,14 +2,15 @@ package mage.cards.a; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; +import mage.abilities.hint.Hint; import mage.abilities.hint.ValueHint; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -24,12 +25,15 @@ import java.util.UUID; */ public final class AgentOfTreachery extends CardImpl { - private static final FilterPermanent filter = new FilterControlledPermanent(); + private static final FilterPermanent filter = new FilterControlledPermanent("you control three or more permanents you don't own"); static { filter.add(TargetController.NOT_YOU.getOwnerPredicate()); } + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 2); + private static final Hint hint = new ValueHint("Permanents you control but don't own", new PermanentsOnBattlefieldCount(filter)); + public AgentOfTreachery(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}{U}"); @@ -44,12 +48,7 @@ public final class AgentOfTreachery extends CardImpl { this.addAbility(ability); // At the beginning of your end step, if you control three or more permanents you don't own, draw three cards. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfEndStepTriggeredAbility( - new DrawCardSourceControllerEffect(3) - ), new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 2), "At the beginning of your end step, " + - "if you control three or more permanents you don't own, draw three cards." - ).addHint(new ValueHint("Permanents you control but don't own", new PermanentsOnBattlefieldCount(filter)))); + this.addAbility(new BeginningOfEndStepTriggeredAbility(new DrawCardSourceControllerEffect(3)).withInterveningIf(condition).addHint(hint)); } private AgentOfTreachery(final AgentOfTreachery card) { diff --git a/Mage.Sets/src/mage/cards/a/Aggression.java b/Mage.Sets/src/mage/cards/a/Aggression.java index 470f3b89da4..9d694b9ab7c 100644 --- a/Mage.Sets/src/mage/cards/a/Aggression.java +++ b/Mage.Sets/src/mage/cards/a/Aggression.java @@ -1,31 +1,26 @@ package mage.cards.a; -import java.util.UUID; -import mage.constants.SubType; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.condition.common.DidNotAttackThisTurnEnchantedCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.DestroyAttachedToEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; -import mage.constants.Outcome; -import mage.target.TargetPermanent; import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.FirstStrikeAbility; import mage.abilities.keyword.TrampleAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AttachmentType; -import mage.constants.CardType; -import mage.constants.TargetController; +import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.watchers.common.AttackedThisTurnWatcher; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class Aggression extends CardImpl { @@ -45,27 +40,25 @@ public final class Aggression extends CardImpl { TargetPermanent auraTarget = new TargetPermanent(filter); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget); - this.addAbility(ability); + this.addAbility(new EnchantAbility(auraTarget)); // Enchanted creature has first strike and trample. - Ability ability2 = new SimpleStaticAbility( - new GainAbilityAttachedEffect( - FirstStrikeAbility.getInstance(), - AttachmentType.AURA)); - ability2.addEffect(new GainAbilityAttachedEffect( - TrampleAbility.getInstance(), - AttachmentType.AURA).setText("and trample")); - this.addAbility(ability2); + Ability ability = new SimpleStaticAbility( + new GainAbilityAttachedEffect(FirstStrikeAbility.getInstance(), AttachmentType.AURA) + ); + ability.addEffect(new GainAbilityAttachedEffect( + TrampleAbility.getInstance(), AttachmentType.AURA + ).setText("and trample")); + this.addAbility(ability); // At the beginning of the end step of enchanted creature's controller, destroy that creature if it didn't attack this turn. - this.addAbility(new ConditionalTriggeredAbility( - new AtTheBeginOfNextEndStepDelayedTriggeredAbility( - new DestroyAttachedToEffect("enchanted"), - TargetController.CONTROLLER_ATTACHED_TO), - DidNotAttackThisTurnEnchantedCondition.instance, - "At the beginning of the end step of enchanted creature's controller, destroy that creature if it didn't attack this turn.")); - + this.addAbility(new BeginningOfEndStepTriggeredAbility( + TargetController.CONTROLLER_ATTACHED_TO, + new ConditionalOneShotEffect( + new DestroyAttachedToEffect(""), DidNotAttackThisTurnEnchantedCondition.instance, + "destroy that creature if it didn't attack this turn" + ), false + )); } private Aggression(final Aggression card) { diff --git a/Mage.Sets/src/mage/cards/a/Ahriman.java b/Mage.Sets/src/mage/cards/a/Ahriman.java new file mode 100644 index 00000000000..95df7d01aa7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/Ahriman.java @@ -0,0 +1,52 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Ahriman extends CardImpl { + + public Ahriman(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.EYE); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // {3}, Sacrifice another creature or artifact: Draw a card. + Ability ability = new SimpleActivatedAbility(new DrawCardSourceControllerEffect(1), new GenericManaCost(3)); + ability.addCost(new SacrificeTargetCost(StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE_OR_ARTIFACT)); + this.addAbility(ability); + } + + private Ahriman(final Ahriman card) { + super(card); + } + + @Override + public Ahriman copy() { + return new Ahriman(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AidFromTheCowl.java b/Mage.Sets/src/mage/cards/a/AidFromTheCowl.java index 4fc6fa05bc5..3cd45e595ad 100644 --- a/Mage.Sets/src/mage/cards/a/AidFromTheCowl.java +++ b/Mage.Sets/src/mage/cards/a/AidFromTheCowl.java @@ -76,7 +76,7 @@ class AidFromTheCowlEffect extends OneShotEffect { if (card.isPermanent(game) && controller.chooseUse(Outcome.Neutral, "Put " + card.getIdName() + " onto the battlefield?", source, game)) { controller.moveCards(card, Zone.BATTLEFIELD, source, game); } else if (controller.chooseUse(Outcome.Neutral, "Put " + card.getIdName() + " on the bottom of your library?", source, game)) { - controller.putCardsOnBottomOfLibrary(card, game, source, false); + controller.putCardsOnBottomOfLibrary(card, game, source); } else { game.informPlayers(controller.getLogName() + " puts the revealed card back to the top of the library."); } diff --git a/Mage.Sets/src/mage/cards/a/AirshipCrash.java b/Mage.Sets/src/mage/cards/a/AirshipCrash.java new file mode 100644 index 00000000000..9e65c4a46ce --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AirshipCrash.java @@ -0,0 +1,55 @@ +package mage.cards.a; + +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AirshipCrash extends CardImpl { + + private static final FilterPermanent filter + = new FilterPermanent("artifact, enchantment, or creature with flying"); + + static { + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.ENCHANTMENT.getPredicate(), + Predicates.and( + CardType.CREATURE.getPredicate(), + new AbilityPredicate(FlyingAbility.class) + ) + )); + } + + public AirshipCrash(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}"); + + // Destroy target artifact, enchantment, or creature with flying. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl<>("{2}"))); + } + + private AirshipCrash(final AirshipCrash card) { + super(card); + } + + @Override + public AirshipCrash copy() { + return new AirshipCrash(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AjaniSleeperAgent.java b/Mage.Sets/src/mage/cards/a/AjaniSleeperAgent.java index 0ac62db118b..c70844e96ab 100644 --- a/Mage.Sets/src/mage/cards/a/AjaniSleeperAgent.java +++ b/Mage.Sets/src/mage/cards/a/AjaniSleeperAgent.java @@ -90,7 +90,7 @@ class AjaniSleeperAgentEffect extends OneShotEffect { if (card.isCreature(game) || card.isPlaneswalker(game)) { controller.moveCards(card, Zone.HAND, source, game); } else if (controller.chooseUse(Outcome.Neutral, "Put " + card.getName() + " on the bottom of your library?", source, game)) { - controller.putCardsOnBottomOfLibrary(card, game, source, true); + controller.putCardsOnBottomOfLibrary(card, game, source); } return true; } diff --git a/Mage.Sets/src/mage/cards/a/AjanisAid.java b/Mage.Sets/src/mage/cards/a/AjanisAid.java index 51c07ba7b8e..2d2fd726353 100644 --- a/Mage.Sets/src/mage/cards/a/AjanisAid.java +++ b/Mage.Sets/src/mage/cards/a/AjanisAid.java @@ -1,29 +1,29 @@ package mage.cards.a; -import java.util.UUID; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.effects.common.PreventDamageByChosenSourceEffect; import mage.abilities.effects.common.search.SearchLibraryGraveyardPutInHandEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Zone; import mage.filter.FilterCard; +import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.NamePredicate; +import java.util.UUID; + /** - * * @author fireshoes */ public final class AjanisAid extends CardImpl { private static final FilterCard filter = new FilterCard("Ajani, Valiant Protector"); + private static final FilterPermanent filterPrevent = new FilterCreaturePermanent("creature of your choice"); static { filter.add(new NamePredicate("Ajani, Valiant Protector")); @@ -37,8 +37,7 @@ public final class AjanisAid extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryGraveyardPutInHandEffect(filter), true)); // Sacrifice Ajani's Aid: Prevent all combat damage a creature of your choice would deal this turn. - Effect effect = new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn, new FilterCreaturePermanent("creature of your choice"), true); - effect.setText("Prevent all combat damage a creature of your choice would deal this turn"); + Effect effect = new PreventDamageByChosenSourceEffect(filterPrevent, true); this.addAbility(new SimpleActivatedAbility(effect, new SacrificeSourceCost())); } diff --git a/Mage.Sets/src/mage/cards/a/AjanisComrade.java b/Mage.Sets/src/mage/cards/a/AjanisComrade.java index fa08f8af6f3..e00d50924a9 100644 --- a/Mage.Sets/src/mage/cards/a/AjanisComrade.java +++ b/Mage.Sets/src/mage/cards/a/AjanisComrade.java @@ -1,32 +1,28 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.keyword.TrampleAbility; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; -import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterControlledPlaneswalkerPermanent; + +import java.util.UUID; /** - * * @author fireshoes */ public final class AjanisComrade extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent(); - - static { - filter.add(CardType.PLANESWALKER.getPredicate()); - filter.add(SubType.AJANI.getPredicate()); - } + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + new FilterControlledPlaneswalkerPermanent(SubType.AJANI, "you control an Ajani planeswalker") + ); public AjanisComrade(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); @@ -40,10 +36,7 @@ public final class AjanisComrade extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // At the beginning of combat on your turn, if you control an Ajani planeswalker, put a +1/+1 counter on Ajani's Comrade. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfCombatTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance())), - new PermanentsOnTheBattlefieldCondition(filter), - "At the beginning of combat on your turn, if you control an Ajani planeswalker, put a +1/+1 counter on {this}.")); + this.addAbility(new BeginningOfCombatTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance())).withInterveningIf(condition)); } private AjanisComrade(final AjanisComrade card) { diff --git a/Mage.Sets/src/mage/cards/a/AjanisLastStand.java b/Mage.Sets/src/mage/cards/a/AjanisLastStand.java index a2697f89c90..e6137f7e1d3 100644 --- a/Mage.Sets/src/mage/cards/a/AjanisLastStand.java +++ b/Mage.Sets/src/mage/cards/a/AjanisLastStand.java @@ -1,51 +1,42 @@ package mage.cards.a; -import java.util.UUID; - -import mage.MageObject; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.common.DiscardedByOpponentTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.costs.common.SacrificeSourceCost; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DoIfCostPaid; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledPermanent; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; import mage.game.permanent.token.AvatarToken2; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class AjanisLastStand extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent(); - - static { - filter.add(SubType.PLAINS.getPredicate()); - } + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + new FilterControlledPermanent(SubType.PLAINS, "you control a Plains") + ); public AjanisLastStand(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}{W}"); // Whenever a creature or planeswalker you control dies, you may sacrifice Ajani's Last Stand. If you do, create a 4/4 white Avatar creature token with flying. - this.addAbility(new AjanisLastStandTriggeredAbility()); + this.addAbility(new DiesCreatureTriggeredAbility( + new DoIfCostPaid(new CreateTokenEffect(new AvatarToken2()), new SacrificeSourceCost()), + false, StaticFilters.FILTER_CONTROLLED_PERMANENT_CREATURE_OR_PLANESWALKER + )); // When a spell or ability an opponent controls causes you to discard this card, if you control a Plains, create a 4/4 white Avatar creature token with flying. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new DiscardedByOpponentTriggeredAbility(new CreateTokenEffect(new AvatarToken2())), - new PermanentsOnTheBattlefieldCondition(filter), - "When a spell or ability an opponent controls causes you to discard this card, " - + "if you control a Plains, create a 4/4 white Avatar creature token with flying." - )); + this.addAbility(new DiscardedByOpponentTriggeredAbility(new CreateTokenEffect(new AvatarToken2())).withInterveningIf(condition)); } private AjanisLastStand(final AjanisLastStand card) { @@ -57,53 +48,3 @@ public final class AjanisLastStand extends CardImpl { return new AjanisLastStand(this); } } - -class AjanisLastStandTriggeredAbility extends TriggeredAbilityImpl { - - public AjanisLastStandTriggeredAbility() { - super(Zone.BATTLEFIELD, new DoIfCostPaid( - new CreateTokenEffect(new AvatarToken2()), - new SacrificeSourceCost() - ), false); - setLeavesTheBattlefieldTrigger(true); - } - - private AjanisLastStandTriggeredAbility(final AjanisLastStandTriggeredAbility ability) { - super(ability); - } - - @Override - public AjanisLastStandTriggeredAbility copy() { - return new AjanisLastStandTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ZONE_CHANGE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.isDiesEvent()) { - if (zEvent.getTarget().isControlledBy(controllerId) - && (zEvent.getTarget().isCreature(game) - || zEvent.getTarget().isPlaneswalker(game))) { - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever a creature or planeswalker you control dies, " - + "you may sacrifice {this}. " - + "If you do, create a 4/4 white Avatar creature token with flying."; - } - - @Override - public boolean isInUseableZone(Game game, MageObject sourceObject, GameEvent event) { - return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, sourceObject, event, game); - } -} diff --git a/Mage.Sets/src/mage/cards/a/AkiriFearlessVoyager.java b/Mage.Sets/src/mage/cards/a/AkiriFearlessVoyager.java index 5f232322f39..dbd0d2576c2 100644 --- a/Mage.Sets/src/mage/cards/a/AkiriFearlessVoyager.java +++ b/Mage.Sets/src/mage/cards/a/AkiriFearlessVoyager.java @@ -13,9 +13,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterPermanent; -import mage.filter.common.FilterEquipmentPermanent; -import mage.filter.predicate.ObjectSourcePlayer; -import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.filter.StaticFilters; +import mage.filter.predicate.permanent.AttachedToPredicate; import mage.game.Game; import mage.game.events.DefenderAttackedEvent; import mage.game.events.GameEvent; @@ -103,21 +102,11 @@ class AkiriFearlessVoyagerTriggeredAbility extends TriggeredAbilityImpl { class AkiriFearlessVoyagerEffect extends OneShotEffect { - private static enum AkiriFearlessVoyagerPredicate implements ObjectSourcePlayerPredicate { - instance; - - @Override - public boolean apply(ObjectSourcePlayer input, Game game) { - return game.getPermanent(input.getObject().getAttachedTo()) != null - && game.getControllerId(input.getObject().getAttachedTo()).equals(input.getPlayerId()); - } - } - private static final FilterPermanent filter - = new FilterEquipmentPermanent("equipment attached to a creature you control"); + = new FilterPermanent(SubType.EQUIPMENT, "equipment attached to a creature you control"); static { - filter.add(AkiriFearlessVoyagerPredicate.instance); + filter.add(new AttachedToPredicate(StaticFilters.FILTER_CONTROLLED_CREATURE)); } AkiriFearlessVoyagerEffect() { diff --git a/Mage.Sets/src/mage/cards/a/AkromasWill.java b/Mage.Sets/src/mage/cards/a/AkromasWill.java index afddbde25d3..e6e6de26b4d 100644 --- a/Mage.Sets/src/mage/cards/a/AkromasWill.java +++ b/Mage.Sets/src/mage/cards/a/AkromasWill.java @@ -61,7 +61,7 @@ public final class AkromasWill extends CardImpl { mode.addEffect(new GainAbilityControlledEffect( new ProtectionAbility(filter), Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURE - ).setText(", and protection from all colors until end of turn")); + ).setText(", and protection from each color until end of turn")); this.getSpellAbility().addMode(mode); } diff --git a/Mage.Sets/src/mage/cards/a/AlaniaDivergentStorm.java b/Mage.Sets/src/mage/cards/a/AlaniaDivergentStorm.java index 9edcef7913c..693347ae678 100644 --- a/Mage.Sets/src/mage/cards/a/AlaniaDivergentStorm.java +++ b/Mage.Sets/src/mage/cards/a/AlaniaDivergentStorm.java @@ -1,8 +1,5 @@ package mage.cards.a; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; @@ -11,12 +8,11 @@ import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.costs.Cost; import mage.abilities.costs.CostImpl; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.CopyTargetStackObjectEffect; import mage.abilities.effects.common.DoIfCostPaid; -import mage.constants.*; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -25,15 +21,18 @@ import mage.players.Player; import mage.target.common.TargetOpponent; import mage.watchers.Watcher; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + /** - * * @author jimga150 */ public final class AlaniaDivergentStorm extends CardImpl { public AlaniaDivergentStorm(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{R}"); - + this.supertype.add(SuperType.LEGENDARY); this.subtype.add(SubType.OTTER); this.subtype.add(SubType.WIZARD); @@ -43,18 +42,10 @@ public final class AlaniaDivergentStorm extends CardImpl { // Whenever you cast a spell, if it's the first instant spell, the first sorcery spell, or the first Otter // spell other than Alania you've cast this turn, you may have target opponent draw a card. If you do, copy // that spell. You may choose new targets for the copy. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new SpellCastControllerTriggeredAbility(new DoIfCostPaid( - new CopyTargetStackObjectEffect(true), - new AlaniaDivergentStormCost() - ), null, false, SetTargetPointer.SPELL) - .setTriggerPhrase("Whenever you cast a spell, if it's the first instant spell, the first sorcery " + - "spell, or the first Otter spell other than Alania you've cast this turn, "), - AlaniaDivergentStormCondition.instance, "" - ); - ability.addWatcher(new AlaniaDivergentStormWatcher()); - this.addAbility(ability); - + this.addAbility(new SpellCastControllerTriggeredAbility( + new DoIfCostPaid(new CopyTargetStackObjectEffect(true), new AlaniaDivergentStormCost()), + null, false, SetTargetPointer.SPELL + ).withInterveningIf(AlaniaDivergentStormCondition.instance), new AlaniaDivergentStormWatcher()); } private AlaniaDivergentStorm(final AlaniaDivergentStorm card) { @@ -85,7 +76,7 @@ class AlaniaDivergentStormCost extends CostImpl { if (player == null) { return false; } - for (UUID opponentID : game.getOpponents(controllerId)){ + for (UUID opponentID : game.getOpponents(controllerId)) { Player opponent = game.getPlayer(opponentID); if (opponent == null) { continue; @@ -103,7 +94,7 @@ class AlaniaDivergentStormCost extends CostImpl { paid = false; if (this.getTargets().choose(Outcome.DrawCard, controllerId, source.getSourceId(), source, game)) { Player opponent = game.getPlayer(this.getTargets().getFirstTarget()); - if (opponent == null || !opponent.canRespond()){ + if (opponent == null || !opponent.canRespond()) { return false; } paid = opponent.drawCards(1, source, game) > 0; @@ -137,6 +128,11 @@ enum AlaniaDivergentStormCondition implements Condition { MageObjectReference spellMOR = new MageObjectReference(spell, game); return watcher.spellIsFirstISOCast(spellControllerID, spellMOR, sourceSpellMOR); } + + @Override + public String toString() { + return "it's the first instant spell, the first sorcery spell, or the first Otter spell other than {this} you've cast this turn"; + } } // Based on FirstSpellCastThisTurnWatcher @@ -168,7 +164,7 @@ class AlaniaDivergentStormWatcher extends Watcher { if (spell.getCardType(game).contains(CardType.SORCERY)) { playerFirstSorceryCast.putIfAbsent(spellControllerID, spellMOR); } - if (spell.getSubtype(game).contains(SubType.OTTER)){ + if (spell.hasSubtype(SubType.OTTER, game)) { if (playerFirstOtterCast.containsKey(spellControllerID)) { // We already cast an otter this turn, put it on the second otter list playerSecondOtterCast.putIfAbsent(spellControllerID, spellMOR); diff --git a/Mage.Sets/src/mage/cards/a/AlchemistsTalent.java b/Mage.Sets/src/mage/cards/a/AlchemistsTalent.java index 606b16c5bb1..65dfb59a6c2 100644 --- a/Mage.Sets/src/mage/cards/a/AlchemistsTalent.java +++ b/Mage.Sets/src/mage/cards/a/AlchemistsTalent.java @@ -7,7 +7,6 @@ import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateTokenEffect; @@ -52,18 +51,24 @@ public final class AlchemistsTalent extends CardImpl { // Treasures you control have "{T}, Sacrifice this artifact: Add two mana of any one color." Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaOfAnyColorEffect(2), new TapSourceCost()); ability.addCost(new SacrificeSourceCost().setText("sacrifice this artifact")); - this.addAbility(new SimpleStaticAbility(new GainClassAbilitySourceEffect(new GainAbilityControlledEffect(ability, Duration.WhileOnBattlefield, new FilterPermanent(SubType.TREASURE, "Treasures")), 2))); + this.addAbility(new SimpleStaticAbility(new GainClassAbilitySourceEffect( + new GainAbilityControlledEffect( + ability, Duration.WhileOnBattlefield, + new FilterPermanent(SubType.TREASURE, "Treasures") + ), 2 + ))); // {4}{R}: Level 3 this.addAbility(new ClassLevelAbility(3, "{4}{R}")); // Whenever you cast a spell, if mana from a Treasure was spent to cast it, this Class deals damage equal to that spell's mana value to each opponent. - this.addAbility(new SimpleStaticAbility(new GainClassAbilitySourceEffect(new ConditionalInterveningIfTriggeredAbility( + this.addAbility(new SimpleStaticAbility(new GainClassAbilitySourceEffect( new SpellCastControllerTriggeredAbility( - new DamagePlayersEffect(AlchemistsTalentValue.instance, TargetController.OPPONENT), StaticFilters.FILTER_SPELL, false, SetTargetPointer.SPELL - ), AlchemistsTalentCondition.instance, "Whenever you cast a spell, if mana from a Treasure " + - "was spent to cast it, this Class deals damage equal to that spell's mana value to each opponent" - ), 3))); + new DamagePlayersEffect(AlchemistsTalentValue.instance, TargetController.OPPONENT) + .setText("{this} deals damage equal to that spell's mana value to each opponent"), + StaticFilters.FILTER_SPELL, false, SetTargetPointer.SPELL + ).withInterveningIf(AlchemistsTalentCondition.instance), 3 + ))); } private AlchemistsTalent(final AlchemistsTalent card) { @@ -84,6 +89,11 @@ enum AlchemistsTalentCondition implements Condition { Spell spell = (Spell) source.getEffects().get(0).getValue("spellCast"); return spell != null && ManaPaidSourceWatcher.getTreasurePaid(spell.getSourceId(), game) > 0; } + + @Override + public String toString() { + return "mana from a Treasure was spent to cast it"; + } } enum AlchemistsTalentValue implements DynamicValue { @@ -111,6 +121,6 @@ enum AlchemistsTalentValue implements DynamicValue { @Override public String toString() { - return ""; + return "1"; } } diff --git a/Mage.Sets/src/mage/cards/a/AlelaCunningConqueror.java b/Mage.Sets/src/mage/cards/a/AlelaCunningConqueror.java index 35a70741ddc..64e623d1928 100644 --- a/Mage.Sets/src/mage/cards/a/AlelaCunningConqueror.java +++ b/Mage.Sets/src/mage/cards/a/AlelaCunningConqueror.java @@ -14,7 +14,7 @@ import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; import mage.game.permanent.token.FaerieRogueToken; import mage.target.TargetPermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -48,7 +48,7 @@ public final class AlelaCunningConqueror extends CardImpl { Effect effect = new GoadTargetEffect().setText("goad target creature that player controls"); Ability ability = new OneOrMoreCombatDamagePlayerTriggeredAbility(Zone.BATTLEFIELD, effect, faerieFilter, SetTargetPointer.PLAYER, false); ability.addTarget(new TargetPermanent(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/AlenaKessigTrapper.java b/Mage.Sets/src/mage/cards/a/AlenaKessigTrapper.java index 42d07882554..5fa4480be29 100644 --- a/Mage.Sets/src/mage/cards/a/AlenaKessigTrapper.java +++ b/Mage.Sets/src/mage/cards/a/AlenaKessigTrapper.java @@ -1,34 +1,38 @@ package mage.cards.a; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; -import java.util.UUID; - import mage.MageInt; -import mage.MageObject; -import mage.MageObjectReference; import mage.Mana; -import mage.abilities.Ability; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; +import mage.abilities.hint.Hint; +import mage.abilities.keyword.FirstStrikeAbility; import mage.abilities.keyword.PartnerAbility; import mage.abilities.mana.DynamicManaAbility; -import mage.constants.*; -import mage.abilities.keyword.FirstStrikeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; -import mage.watchers.Watcher; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.EnteredThisTurnPredicate; + +import java.util.UUID; /** * @author TheElk801 */ public final class AlenaKessigTrapper extends CardImpl { + private static final FilterPermanent filter = new FilterControlledCreaturePermanent("creatures you control that entered this turn"); + + static { + filter.add(EnteredThisTurnPredicate.instance); + } + + private static final GreatestAmongPermanentsValue xValue = new GreatestAmongPermanentsValue(GreatestAmongPermanentsValue.Quality.Power, filter); + private static final Hint hint = xValue.getHint(); + public AlenaKessigTrapper(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}"); @@ -43,9 +47,9 @@ public final class AlenaKessigTrapper extends CardImpl { // {T}: Add an amount of {R} equal to the greatest power among creatures you control that entered the battlefield this turn. this.addAbility(new DynamicManaAbility( - Mana.RedMana(1), AlenaKessigTrapperValue.instance, new TapSourceCost(), "Add an amount of {R} " + - "equal to the greatest power among creatures you control that entered the battlefield this turn." - ), new AlenaKessigTrapperWatcher()); + Mana.RedMana(1), xValue, new TapSourceCost(), + "Add an amount of {R} equal to the greatest power among creatures you control that entered this turn." + ).addHint(hint)); // Partner this.addAbility(PartnerAbility.getInstance()); @@ -59,64 +63,4 @@ public final class AlenaKessigTrapper extends CardImpl { public AlenaKessigTrapper copy() { return new AlenaKessigTrapper(this); } -} - -enum AlenaKessigTrapperValue implements DynamicValue { - instance; - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - AlenaKessigTrapperWatcher watcher = game.getState().getWatcher(AlenaKessigTrapperWatcher.class); - if (watcher == null) { - return 0; - } - return watcher.getPower(sourceAbility.getControllerId(), game); - } - - @Override - public AlenaKessigTrapperValue copy() { - return instance; - } - - @Override - public String getMessage() { - return ""; - } -} - -class AlenaKessigTrapperWatcher extends Watcher { - - private final Set enteredThisTurn = new HashSet<>(); - - public AlenaKessigTrapperWatcher() { - super(WatcherScope.GAME); - } - - @Override - public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.ZONE_CHANGE - && ((ZoneChangeEvent) event).getToZone() == Zone.BATTLEFIELD) { - enteredThisTurn.add(new MageObjectReference(event.getTargetId(), game)); - } - } - - @Override - public void reset() { - enteredThisTurn.clear(); - super.reset(); - } - - int getPower(UUID playerId, Game game) { - return enteredThisTurn - .stream() - .filter(Objects::nonNull) - .map(mor -> mor.getPermanent(game)) - .filter(Objects::nonNull) - .filter(permanent1 -> permanent1.isCreature(game)) - .filter(permanent -> permanent.isControlledBy(playerId)) - .map(MageObject::getPower) - .mapToInt(MageInt::getValue) - .max() - .orElse(0); - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/a/AlhammarretsArchive.java b/Mage.Sets/src/mage/cards/a/AlhammarretsArchive.java index 1ce92cda1b2..8c76d02891c 100644 --- a/Mage.Sets/src/mage/cards/a/AlhammarretsArchive.java +++ b/Mage.Sets/src/mage/cards/a/AlhammarretsArchive.java @@ -3,13 +3,13 @@ package mage.cards.a; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.replacement.GainDoubleLifeReplacementEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; -import mage.util.CardUtil; import mage.watchers.common.CardsDrawnDuringDrawStepWatcher; import java.util.UUID; @@ -24,7 +24,7 @@ public final class AlhammarretsArchive extends CardImpl { this.supertype.add(SuperType.LEGENDARY); // If you would gain life, you gain twice that much life instead. - this.addAbility(new SimpleStaticAbility(new AlhammarretsArchiveEffect())); + this.addAbility(new SimpleStaticAbility(new GainDoubleLifeReplacementEffect())); // If you draw a card except the first one you draw in each of your draw steps, draw two cards instead. this.addAbility(new SimpleStaticAbility(new AlhammarretsArchiveReplacementEffect()), new CardsDrawnDuringDrawStepWatcher()); @@ -40,39 +40,6 @@ public final class AlhammarretsArchive extends CardImpl { } } -class AlhammarretsArchiveEffect extends ReplacementEffectImpl { - - AlhammarretsArchiveEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "If you would gain life, you gain twice that much life instead"; - } - - private AlhammarretsArchiveEffect(final AlhammarretsArchiveEffect effect) { - super(effect); - } - - @Override - public AlhammarretsArchiveEffect copy() { - return new AlhammarretsArchiveEffect(this); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - event.setAmount(CardUtil.overflowMultiply(event.getAmount(), 2)); - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.GAIN_LIFE; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - return event.getPlayerId().equals(source.getControllerId()) && (source.getControllerId() != null); - } -} - class AlhammarretsArchiveReplacementEffect extends ReplacementEffectImpl { AlhammarretsArchiveReplacementEffect() { diff --git a/Mage.Sets/src/mage/cards/a/AlibouAncientWitness.java b/Mage.Sets/src/mage/cards/a/AlibouAncientWitness.java index df3188f77db..4517e2ec79c 100644 --- a/Mage.Sets/src/mage/cards/a/AlibouAncientWitness.java +++ b/Mage.Sets/src/mage/cards/a/AlibouAncientWitness.java @@ -4,22 +4,24 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.keyword.ScryEffect; import mage.abilities.hint.Hint; import mage.abilities.hint.ValueHint; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledArtifactPermanent; import mage.filter.predicate.permanent.TappedPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetAnyTarget; import java.util.UUID; @@ -29,6 +31,15 @@ import java.util.UUID; */ public final class AlibouAncientWitness extends CardImpl { + private static final FilterPermanent filter = new FilterControlledArtifactPermanent("tapped artifacts you control"); + + static { + filter.add(TappedPredicate.TAPPED); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, null); + private static final Hint hint = new ValueHint("Tapped artifacts you control", xValue); + public AlibouAncientWitness(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}{R}{W}"); @@ -45,11 +56,12 @@ public final class AlibouAncientWitness extends CardImpl { // Whenever one or more artifact creatures you control attack, Alibou, Ancient Witness deals X damage to any target and you scry X, where X is the number of tapped artifacts you control. Ability ability = new AttacksWithCreaturesTriggeredAbility( - new AlibouAncientWitnessEffect(), 1, - StaticFilters.FILTER_PERMANENTS_ARTIFACT_CREATURE + new DamageTargetEffect(xValue).setText("{this} deals X damage to any target"), + 1, StaticFilters.FILTER_PERMANENTS_ARTIFACT_CREATURE ).setTriggerPhrase("Whenever one or more artifact creatures you control attack, "); + ability.addEffect(new ScryEffect(xValue).concatBy("and you")); ability.addTarget(new TargetAnyTarget()); - this.addAbility(ability.addHint(AlibouAncientWitnessEffect.getHint())); + this.addAbility(ability.addHint(hint)); } private AlibouAncientWitness(final AlibouAncientWitness card) { @@ -60,57 +72,4 @@ public final class AlibouAncientWitness extends CardImpl { public AlibouAncientWitness copy() { return new AlibouAncientWitness(this); } -} - -class AlibouAncientWitnessEffect extends OneShotEffect { - - private static final FilterPermanent filter = new FilterControlledArtifactPermanent(); - - static { - filter.add(TappedPredicate.TAPPED); - } - - private static final Hint hint = new ValueHint( - "Tapped artifacts you control", new PermanentsOnBattlefieldCount(filter) - ); - - AlibouAncientWitnessEffect() { - super(Outcome.Benefit); - staticText = "{this} deals X damage to any target and you scry X, " + - "where X is the number of tapped artifacts you control"; - } - - private AlibouAncientWitnessEffect(final AlibouAncientWitnessEffect effect) { - super(effect); - } - - @Override - public AlibouAncientWitnessEffect copy() { - return new AlibouAncientWitnessEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - int xValue = game.getBattlefield().count(filter, source.getControllerId(), source, game); - if (xValue < 1) { - return false; - } - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent != null) { - permanent.damage(xValue, source.getSourceId(), source, game); - } - Player player = game.getPlayer(source.getFirstTarget()); - if (player != null) { - player.damage(xValue, source.getSourceId(), source, game); - } - player = game.getPlayer(source.getControllerId()); - if (player != null) { - player.scry(xValue, source, game); - } - return true; - } - - public static Hint getHint() { - return hint; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/a/AlistairTheBrigadier.java b/Mage.Sets/src/mage/cards/a/AlistairTheBrigadier.java index 0f5254018f9..1bb1e497add 100644 --- a/Mage.Sets/src/mage/cards/a/AlistairTheBrigadier.java +++ b/Mage.Sets/src/mage/cards/a/AlistairTheBrigadier.java @@ -18,9 +18,8 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.FilterPermanent; -import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledPermanent; -import mage.filter.common.FilterHistoricSpell; import mage.filter.predicate.mageobject.HistoricPredicate; import mage.game.permanent.token.SoldierToken; @@ -31,7 +30,6 @@ import java.util.UUID; */ public final class AlistairTheBrigadier extends CardImpl { - private static final FilterSpell filter = new FilterHistoricSpell(); private static final FilterPermanent filter2 = new FilterControlledPermanent("historic permanents you control"); static { @@ -52,7 +50,7 @@ public final class AlistairTheBrigadier extends CardImpl { // Whenever you cast a historic spell, create a 1/1 white Soldier creature token. this.addAbility(new SpellCastControllerTriggeredAbility( - new CreateTokenEffect(new SoldierToken()), filter, false + new CreateTokenEffect(new SoldierToken()), StaticFilters.FILTER_SPELL_HISTORIC, false )); // Whenever Alistair attacks, you may pay {8}. If you do, creatures you control get +X/+X until end of turn, where X is the number of historic permanents you control. diff --git a/Mage.Sets/src/mage/cards/a/AllosaurusShepherd.java b/Mage.Sets/src/mage/cards/a/AllosaurusShepherd.java index 803dec39d7c..a151fe7e1bf 100644 --- a/Mage.Sets/src/mage/cards/a/AllosaurusShepherd.java +++ b/Mage.Sets/src/mage/cards/a/AllosaurusShepherd.java @@ -12,13 +12,15 @@ import mage.abilities.effects.common.continuous.CreaturesBecomeOtherTypeEffect; import mage.abilities.effects.common.continuous.SetBasePowerToughnessAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; import mage.filter.FilterPermanent; +import mage.filter.FilterSpell; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; import java.util.UUID; -import mage.filter.FilterSpell; public class AllosaurusShepherd extends CardImpl { @@ -43,7 +45,7 @@ public class AllosaurusShepherd extends CardImpl { //Green spells you control can't be countered. this.addAbility(new SimpleStaticAbility( - new CantBeCounteredControlledEffect(greenSpellsFilter, null, Duration.WhileOnBattlefield))); + new CantBeCounteredControlledEffect(greenSpellsFilter, Duration.WhileOnBattlefield))); //4GG: Until end of turn, each Elf creature you control has base power and toughness 5/5 // and becomes a Dinosaur in addition to its other creature types. diff --git a/Mage.Sets/src/mage/cards/a/AltarOfShadows.java b/Mage.Sets/src/mage/cards/a/AltarOfShadows.java index efaa95dce93..e885492a63c 100644 --- a/Mage.Sets/src/mage/cards/a/AltarOfShadows.java +++ b/Mage.Sets/src/mage/cards/a/AltarOfShadows.java @@ -57,7 +57,7 @@ class AltarOfShadowsEffect extends OneShotEffect { AltarOfShadowsEffect() { super(Outcome.PutManaInPool); - this.staticText = "add {B} for each charge counter on Altar of Shadows"; + this.staticText = "add {B} for each charge counter on {this}"; } private AltarOfShadowsEffect(final AltarOfShadowsEffect effect) { diff --git a/Mage.Sets/src/mage/cards/a/AlteredEgo.java b/Mage.Sets/src/mage/cards/a/AlteredEgo.java index 8c8d41448b4..170040a0cb5 100644 --- a/Mage.Sets/src/mage/cards/a/AlteredEgo.java +++ b/Mage.Sets/src/mage/cards/a/AlteredEgo.java @@ -54,7 +54,7 @@ class AlteredEgoCopyApplier extends CopyApplier { @Override public String getText() { - return ", except it enters with an additional X +1/+1 counters on it"; + return ", except it enters with X additional +1/+1 counters on it"; } @Override @@ -85,4 +85,4 @@ class AlteredEgoCopyApplier extends CopyApplier { return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/a/AmassTheComponents.java b/Mage.Sets/src/mage/cards/a/AmassTheComponents.java index a281f3134ff..c336c277b09 100644 --- a/Mage.Sets/src/mage/cards/a/AmassTheComponents.java +++ b/Mage.Sets/src/mage/cards/a/AmassTheComponents.java @@ -71,7 +71,7 @@ class AmassTheComponentsEffect extends OneShotEffect { if (player.choose(Outcome.Detriment, player.getHand(), target, source, game)) { Card card = player.getHand().get(target.getFirstTarget(), game); if (card != null) { - return player.putCardsOnBottomOfLibrary(card, game, source, true); + return player.putCardsOnBottomOfLibrary(card, game, source); } } } diff --git a/Mage.Sets/src/mage/cards/a/AmbitiousDragonborn.java b/Mage.Sets/src/mage/cards/a/AmbitiousDragonborn.java index a306041ae54..5b393e6fca5 100644 --- a/Mage.Sets/src/mage/cards/a/AmbitiousDragonborn.java +++ b/Mage.Sets/src/mage/cards/a/AmbitiousDragonborn.java @@ -39,10 +39,12 @@ public final class AmbitiousDragonborn extends CardImpl { this.toughness = new MageInt(0); // Ambitious Dragonborn enters the battlefield with X +1/+1 counters on it, where X is the greatest power among creatures you control and creature cards in your graveyard. - this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect( - CounterType.P1P1.createInstance(), AmbitiousDragonbornValue.instance, false - ), "with X +1/+1 counters on it, where X is the greatest power " + - "among creatures you control and creature cards in your graveyard")); + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect( + CounterType.P1P1.createInstance(), AmbitiousDragonbornValue.instance, false + ), "with X +1/+1 counters on it, where X is the greatest power " + + "among creatures you control and creature cards in your graveyard" + ).addHint(hint)); } private AmbitiousDragonborn(final AmbitiousDragonborn card) { diff --git a/Mage.Sets/src/mage/cards/a/AmbrosiaWhiteheart.java b/Mage.Sets/src/mage/cards/a/AmbrosiaWhiteheart.java new file mode 100644 index 00000000000..e9809e1eca7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AmbrosiaWhiteheart.java @@ -0,0 +1,88 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.LandfallAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetImpl; +import mage.target.TargetPermanent; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AmbrosiaWhiteheart extends CardImpl { + + public AmbrosiaWhiteheart(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.BIRD); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // When Ambrosia Whiteheart enters, you may return another permanent you control to its owner's hand. + this.addAbility(new EntersBattlefieldTriggeredAbility(new AmbrosiaWhiteheartEffect())); + + // Landfall -- Whenever a land you control enters, Ambrosia Whiteheart gets +1/+0 until end of turn. + this.addAbility(new LandfallAbility(new BoostSourceEffect(1, 0, Duration.EndOfTurn))); + } + + private AmbrosiaWhiteheart(final AmbrosiaWhiteheart card) { + super(card); + } + + @Override + public AmbrosiaWhiteheart copy() { + return new AmbrosiaWhiteheart(this); + } +} + +class AmbrosiaWhiteheartEffect extends OneShotEffect { + + AmbrosiaWhiteheartEffect() { + super(Outcome.Benefit); + staticText = "you may return another permanent you control to its owner's hand"; + } + + private AmbrosiaWhiteheartEffect(final AmbrosiaWhiteheartEffect effect) { + super(effect); + } + + @Override + public AmbrosiaWhiteheartEffect copy() { + return new AmbrosiaWhiteheartEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + TargetPermanent target = new TargetPermanent( + 0, 1, StaticFilters.FILTER_CONTROLLED_ANOTHER_PERMANENT, true + ); + player.choose(outcome, target, source, game); + return Optional + .ofNullable(target) + .map(TargetImpl::getFirstTarget) + .map(game::getCard) + .map(card -> player.moveCards(card, Zone.HAND, source, game)) + .orElse(false); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AmyPond.java b/Mage.Sets/src/mage/cards/a/AmyPond.java index c9313e55bdd..c64b3fa0cc1 100644 --- a/Mage.Sets/src/mage/cards/a/AmyPond.java +++ b/Mage.Sets/src/mage/cards/a/AmyPond.java @@ -1,23 +1,26 @@ package mage.cards.a; -import java.util.UUID; - import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.SavedDamageValue; import mage.abilities.effects.OneShotEffect; -import mage.abilities.keyword.PartnerWithAbility; -import mage.cards.*; -import mage.constants.*; +import mage.abilities.effects.OneShotNonTargetEffect; import mage.abilities.keyword.DoctorsCompanionAbility; +import mage.abilities.keyword.PartnerWithAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; import mage.counters.CounterType; import mage.filter.common.FilterSuspendedCard; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInExile; +import java.util.UUID; + /** * * @author Skiwkr @@ -41,10 +44,9 @@ public final class AmyPond extends CardImpl { this.addAbility(new PartnerWithAbility("Rory Williams")); // Whenever Amy Pond deals combat damage to a player, choose a suspended card you own and remove that many time counters from it. - Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new AmyPondEffect(SavedDamageValue.MANY), - false, true); - ability.addTarget(new TargetCardInExile(filter).withNotTarget(true)); - this.addAbility(ability); + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new OneShotNonTargetEffect( + new AmyPondEffect(SavedDamageValue.MANY), new TargetCardInExile(filter)), + false, true)); // Doctor's companion this.addAbility(DoctorsCompanionAbility.getInstance()); @@ -68,7 +70,7 @@ class AmyPondEffect extends OneShotEffect { AmyPondEffect(DynamicValue numberCounters) { super(Outcome.Benefit); this.numberCounters = numberCounters; - this.staticText= "choose a suspended card you own and remove that many time counters from it"; + this.staticText = "choose a suspended card you own and remove that many time counters from it"; } private AmyPondEffect(final AmyPondEffect effect) { diff --git a/Mage.Sets/src/mage/cards/a/AnaBattlemage.java b/Mage.Sets/src/mage/cards/a/AnaBattlemage.java index 42bbe7073ef..f33df1df178 100644 --- a/Mage.Sets/src/mage/cards/a/AnaBattlemage.java +++ b/Mage.Sets/src/mage/cards/a/AnaBattlemage.java @@ -2,11 +2,11 @@ package mage.cards.a; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.KickedCostCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.TapTargetEffect; import mage.abilities.effects.common.discard.DiscardTargetEffect; import mage.abilities.keyword.KickerAbility; import mage.cards.CardImpl; @@ -35,6 +35,9 @@ public final class AnaBattlemage extends CardImpl { filter.add(TappedPredicate.UNTAPPED); } + private static final Condition condition = new KickedCostCondition("{2}{U}"); + private static final Condition condition2 = new KickedCostCondition("{1}{B}"); + public AnaBattlemage(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); this.subtype.add(SubType.HUMAN); @@ -46,16 +49,17 @@ public final class AnaBattlemage extends CardImpl { KickerAbility kickerAbility = new KickerAbility("{2}{U}"); kickerAbility.addKickerCost("{1}{B}"); this.addAbility(kickerAbility); + // When Ana Battlemage enters the battlefield, if it was kicked with its {2}{U} kicker, target player discards three cards. - TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new DiscardTargetEffect(3)); + Ability ability = new EntersBattlefieldTriggeredAbility(new DiscardTargetEffect(3)).withInterveningIf(condition); ability.addTarget(new TargetPlayer()); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new KickedCostCondition("{2}{U}"), - "When {this} enters, if it was kicked with its {2}{U} kicker, target player discards three cards.")); + this.addAbility(ability); + // When Ana Battlemage enters the battlefield, if it was kicked with its {1}{B} kicker, tap target untapped creature and that creature deals damage equal to its power to its controller. - ability = new EntersBattlefieldTriggeredAbility(new AnaBattlemageKickerEffect()); + ability = new EntersBattlefieldTriggeredAbility(new TapTargetEffect()).withInterveningIf(condition2); + ability.addEffect(new AnaBattlemageEffect()); ability.addTarget(new TargetCreaturePermanent(filter)); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new KickedCostCondition("{1}{B}"), - "When {this} enters, if it was kicked with its {1}{B} kicker, tap target untapped creature and that creature deals damage equal to its power to its controller.")); + this.addAbility(ability); } private AnaBattlemage(final AnaBattlemage card) { @@ -68,34 +72,31 @@ public final class AnaBattlemage extends CardImpl { } } -class AnaBattlemageKickerEffect extends OneShotEffect { +class AnaBattlemageEffect extends OneShotEffect { - AnaBattlemageKickerEffect() { + AnaBattlemageEffect() { super(Outcome.Detriment); - this.staticText = "tap target untapped creature and it deals damage equal to its power to its controller"; + this.staticText = "and it deals damage equal to its power to its controller"; } - private AnaBattlemageKickerEffect(final AnaBattlemageKickerEffect effect) { + private AnaBattlemageEffect(final AnaBattlemageEffect effect) { super(effect); } @Override - public AnaBattlemageKickerEffect copy() { - return new AnaBattlemageKickerEffect(this); + public AnaBattlemageEffect copy() { + return new AnaBattlemageEffect(this); } @Override public boolean apply(Game game, Ability source) { - boolean applied = false; Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (targetCreature != null) { - applied = targetCreature.tap(source, game); - Player controller = game.getPlayer(targetCreature.getControllerId()); - if (controller != null) { - controller.damage(targetCreature.getPower().getValue(), source.getSourceId(), source, game); - applied = true; - } + if (targetCreature == null) { + return false; } - return applied; + Player controller = game.getPlayer(targetCreature.getControllerId()); + return controller != null && controller.damage( + targetCreature.getPower().getValue(), source.getSourceId(), source, game + ) > 0; } } diff --git a/Mage.Sets/src/mage/cards/a/AncientAdamantoise.java b/Mage.Sets/src/mage/cards/a/AncientAdamantoise.java new file mode 100644 index 00000000000..cc981d2cd77 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AncientAdamantoise.java @@ -0,0 +1,148 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.ExileSourceEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.abilities.keyword.WardAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.game.events.DamageEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.TreasureToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AncientAdamantoise extends CardImpl { + + public AncientAdamantoise(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{G}{G}{G}"); + + this.subtype.add(SubType.TURTLE); + this.power = new MageInt(8); + this.toughness = new MageInt(20); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Ward {3} + this.addAbility(new WardAbility(new ManaCostsImpl<>("{3}"))); + + // Damage isn't removed from this creature during cleanup steps. + this.addAbility(new SimpleStaticAbility(new AncientAdamantoiseDamageEffect())); + + // All damage that would be dealt to you and other permanents you control is dealt to this creature instead. + this.addAbility(new SimpleStaticAbility(new AncientAdamantoiseRedirectEffect())); + + // When this creature dies, exile it and create ten tapped Treasure tokens. + Ability ability = new DiesSourceTriggeredAbility(new ExileSourceEffect().setText("exile it")); + ability.addEffect(new CreateTokenEffect(new TreasureToken(), 10, true).concatBy("and")); + this.addAbility(ability); + } + + private AncientAdamantoise(final AncientAdamantoise card) { + super(card); + } + + @Override + public AncientAdamantoise copy() { + return new AncientAdamantoise(this); + } +} + +class AncientAdamantoiseDamageEffect extends ContinuousRuleModifyingEffectImpl { + + AncientAdamantoiseDamageEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "damage isn't removed from {this} during cleanup steps"; + } + + private AncientAdamantoiseDamageEffect(final AncientAdamantoiseDamageEffect effect) { + super(effect); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.REMOVE_DAMAGE_EOT; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return event.getTargetId().equals(source.getSourceId()); + } + + @Override + public AncientAdamantoiseDamageEffect copy() { + return new AncientAdamantoiseDamageEffect(this); + } +} + +class AncientAdamantoiseRedirectEffect extends ReplacementEffectImpl { + + AncientAdamantoiseRedirectEffect() { + super(Duration.WhileOnBattlefield, Outcome.RedirectDamage); + staticText = "all damage that would be dealt to you and other " + + "permanents you control is dealt to this creature instead"; + } + + private AncientAdamantoiseRedirectEffect(final AncientAdamantoiseRedirectEffect effect) { + super(effect); + } + + @Override + public AncientAdamantoiseRedirectEffect copy() { + return new AncientAdamantoiseRedirectEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + DamageEvent damageEvent = (DamageEvent) event; + Permanent permanent = source.getSourcePermanentOrLKI(game); + if (permanent != null) { + permanent.damage( + damageEvent.getAmount(), event.getSourceId(), source, game, + damageEvent.isCombatDamage(), damageEvent.isPreventable() + ); + } + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + switch (event.getType()) { + case DAMAGE_PLAYER: + case DAMAGE_PERMANENT: + return true; + default: + return false; + } + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + switch (event.getType()) { + case DAMAGE_PLAYER: + return source.isControlledBy(event.getTargetId()); + case DAMAGE_PERMANENT: + return !event.getTargetId().equals(source.getSourceId()) + && source.isControlledBy(game.getControllerId(event.getTargetId())); + default: + return false; + } + } +} diff --git a/Mage.Sets/src/mage/cards/a/AngelOfDestiny.java b/Mage.Sets/src/mage/cards/a/AngelOfDestiny.java index c1c14a34957..bf0df6ed71b 100644 --- a/Mage.Sets/src/mage/cards/a/AngelOfDestiny.java +++ b/Mage.Sets/src/mage/cards/a/AngelOfDestiny.java @@ -3,13 +3,12 @@ package mage.cards.a; import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility; import mage.abilities.condition.common.MoreThanStartingLifeTotalCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.DoubleStrikeAbility; import mage.abilities.keyword.FlyingAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -47,13 +46,8 @@ public final class AngelOfDestiny extends CardImpl { )); // At the beginning of your end step, if you have at least 15 life more than your starting life total, each player Angel of Destiny attacked this turn loses the game. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfEndStepTriggeredAbility( - new AngelOfDestinyLoseEffect() - ), MoreThanStartingLifeTotalCondition.FIFTEEN, "At the beginning of your end step, " + - "if you have at least 15 life more than your starting life total, " + - "each player {this} attacked this turn loses the game." - ), new AngelOfDestinyWatcher()); + this.addAbility(new BeginningOfEndStepTriggeredAbility(new AngelOfDestinyLoseEffect()) + .withInterveningIf(MoreThanStartingLifeTotalCondition.FIFTEEN), new AngelOfDestinyWatcher()); } private AngelOfDestiny(final AngelOfDestiny card) { @@ -101,6 +95,7 @@ class AngelOfDestinyLoseEffect extends OneShotEffect { AngelOfDestinyLoseEffect() { super(Outcome.Benefit); + staticText = "each player {this} attacked this turn loses the game"; } private AngelOfDestinyLoseEffect(final AngelOfDestinyLoseEffect effect) { diff --git a/Mage.Sets/src/mage/cards/a/AngelicObserver.java b/Mage.Sets/src/mage/cards/a/AngelicObserver.java index 9829ad683ef..845b67428cd 100644 --- a/Mage.Sets/src/mage/cards/a/AngelicObserver.java +++ b/Mage.Sets/src/mage/cards/a/AngelicObserver.java @@ -1,18 +1,13 @@ package mage.cards.a; import mage.MageInt; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.common.AffinityEffect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.AffinityAbility; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AffinityType; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterControlledPermanent; import java.util.UUID; @@ -21,9 +16,6 @@ import java.util.UUID; */ public final class AngelicObserver extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.CITIZEN, "Citizens"); - private static final Hint hint = new ValueHint("Citizens you control", new PermanentsOnBattlefieldCount(filter)); - public AngelicObserver(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}"); @@ -32,8 +24,8 @@ public final class AngelicObserver extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); - // This spell costs {1} less to cast for each Citizen you control. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filter)).setRuleAtTheTop(true).addHint(hint)); + // Affinity for Citizens + this.addAbility(new AffinityAbility(AffinityType.CITIZENS)); // Flying this.addAbility(FlyingAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/a/AngelicSellSword.java b/Mage.Sets/src/mage/cards/a/AngelicSellSword.java index b7028153417..380bf6633cf 100644 --- a/Mage.Sets/src/mage/cards/a/AngelicSellSword.java +++ b/Mage.Sets/src/mage/cards/a/AngelicSellSword.java @@ -6,7 +6,6 @@ import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.FlyingAbility; @@ -48,11 +47,8 @@ public final class AngelicSellSword extends CardImpl { )); // Whenever Angelic Sell-Sword attacks, if its power is 6 or greater, draw a card. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new AttacksTriggeredAbility(new DrawCardSourceControllerEffect(1)), - AngelicSellSwordCondition.instance, "Whenever {this} attacks, " + - "if its power is 6 or greater, draw a card." - )); + this.addAbility(new AttacksTriggeredAbility(new DrawCardSourceControllerEffect(1)) + .withInterveningIf(AngelicSellSwordCondition.instance)); } private AngelicSellSword(final AngelicSellSword card) { @@ -76,4 +72,9 @@ enum AngelicSellSwordCondition implements Condition { .map(MageInt::getValue) .orElse(0) >= 6; } + + @Override + public String toString() { + return "its power is 6 or greater"; + } } diff --git a/Mage.Sets/src/mage/cards/a/Anger.java b/Mage.Sets/src/mage/cards/a/Anger.java index 8b589c68fc7..8994291cd55 100644 --- a/Mage.Sets/src/mage/cards/a/Anger.java +++ b/Mage.Sets/src/mage/cards/a/Anger.java @@ -1,12 +1,11 @@ package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; @@ -15,26 +14,20 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledPermanent; -import mage.filter.common.FilterCreaturePermanent; + +import java.util.UUID; /** - * * @author Backfir3 */ public final class Anger extends CardImpl { - private static final String ruleText = "As long as Anger is in your graveyard and you control a Mountain, creatures you control have haste"; - - private static final FilterControlledPermanent filter = new FilterControlledPermanent("Mountain"); - - static { - filter.add(CardType.LAND.getPredicate()); - filter.add(SubType.MOUNTAIN.getPredicate()); - } + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(new FilterControlledPermanent(SubType.MOUNTAIN)); public Anger(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); this.subtype.add(SubType.INCARNATION); this.power = new MageInt(2); @@ -44,11 +37,16 @@ public final class Anger extends CardImpl { this.addAbility(HasteAbility.getInstance()); // As long as Anger is in your graveyard and you control a Mountain, creatures you control have haste - ContinuousEffect effect = new GainAbilityControlledEffect(HasteAbility.getInstance(), - Duration.WhileOnBattlefield, new FilterCreaturePermanent()); - ConditionalContinuousEffect angerEffect = new ConditionalContinuousEffect(effect, - new PermanentsOnTheBattlefieldCondition(filter), ruleText); - this.addAbility(new SimpleStaticAbility(Zone.GRAVEYARD, angerEffect)); + this.addAbility(new SimpleStaticAbility( + Zone.GRAVEYARD, + new ConditionalContinuousEffect( + new GainAbilityControlledEffect( + HasteAbility.getInstance(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENT_CREATURE + ), condition, "as long as this card is in your graveyard " + + "and you control a Mountain, creatures you control have haste" + ) + )); } private Anger(final Anger card) { diff --git a/Mage.Sets/src/mage/cards/a/AnimistsAwakening.java b/Mage.Sets/src/mage/cards/a/AnimistsAwakening.java index 28202eee797..e86e878dd0f 100644 --- a/Mage.Sets/src/mage/cards/a/AnimistsAwakening.java +++ b/Mage.Sets/src/mage/cards/a/AnimistsAwakening.java @@ -77,7 +77,7 @@ class AnimistsAwakeningEffect extends OneShotEffect { if (SpellMasteryCondition.instance.apply(game, source)) { for (Card card : toBattlefield) { - Permanent land = game.getPermanent(card.getId()); + Permanent land = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (land != null) { land.untap(game); } diff --git a/Mage.Sets/src/mage/cards/a/AnuridBarkripper.java b/Mage.Sets/src/mage/cards/a/AnuridBarkripper.java index 4bc780ed368..a9bbe12f48e 100644 --- a/Mage.Sets/src/mage/cards/a/AnuridBarkripper.java +++ b/Mage.Sets/src/mage/cards/a/AnuridBarkripper.java @@ -29,7 +29,7 @@ public final class AnuridBarkripper extends CardImpl { // Threshold - Anurid Barkripper gets +2/+2 as long as seven or more cards are in your graveyard. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), ThresholdCondition.instance, - "as long as seven or more cards are in your graveyard, {this} gets +2/+2" + "{this} gets +2/+2 as long as seven or more cards are in your graveyard" )).setAbilityWord(AbilityWord.THRESHOLD)); } diff --git a/Mage.Sets/src/mage/cards/a/AnzragsRampage.java b/Mage.Sets/src/mage/cards/a/AnzragsRampage.java index c44f0cf95af..952af58845d 100644 --- a/Mage.Sets/src/mage/cards/a/AnzragsRampage.java +++ b/Mage.Sets/src/mage/cards/a/AnzragsRampage.java @@ -157,7 +157,7 @@ class AnzragsRampageEffect extends OneShotEffect { } if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return true; } diff --git a/Mage.Sets/src/mage/cards/a/ApexObservatory.java b/Mage.Sets/src/mage/cards/a/ApexObservatory.java index 2e6cbf27f24..47f9acc8c89 100644 --- a/Mage.Sets/src/mage/cards/a/ApexObservatory.java +++ b/Mage.Sets/src/mage/cards/a/ApexObservatory.java @@ -78,7 +78,8 @@ class ChooseCardTypeEffect extends OneShotEffect { if (permanent == null) { return false; } - ExileZone exileZone = (ExileZone) game.getExile().getExileZone(CardUtil.getExileZoneId(game, source, +1)); + // chase the exile zone down... + ExileZone exileZone = game.getState().getExile().getExileZone(CardUtil.getExileZoneId(game, source, game.getState().getZoneChangeCounter(mageObject.getId()) - 1)); if (exileZone == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/a/ApheliaViperWhisperer.java b/Mage.Sets/src/mage/cards/a/ApheliaViperWhisperer.java new file mode 100644 index 00000000000..fa6cad0afba --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/ApheliaViperWhisperer.java @@ -0,0 +1,112 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.BatchTriggeredAbility; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.LoseHalfLifeTargetEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.game.Controllable; +import mage.game.Game; +import mage.game.events.DamagedBatchForOnePlayerEvent; +import mage.game.events.DamagedPlayerEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.token.OphiomancerSnakeToken; +import mage.target.targetpointer.FixedTarget; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ApheliaViperWhisperer extends CardImpl { + + public ApheliaViperWhisperer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.GORGON); + this.subtype.add(SubType.ASSASSIN); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // Whenever Aphelia attacks, you may pay {1}{B/G}. If you do, create a 1/1 black Snake creature token with deathtouch. + this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid( + new CreateTokenEffect(new OphiomancerSnakeToken()), new ManaCostsImpl<>("{1}{B/G}") + ))); + + // {4}{B}: Until end of turn, whenever one or more Gorgons and/or Snakes you control deal combat damage to a player, that player loses half their life, rounded up. + this.addAbility(new SimpleActivatedAbility( + new CreateDelayedTriggeredAbilityEffect(new ApheliaViperWhispererTriggeredAbility()), new ManaCostsImpl<>("{4}{B}") + )); + } + + private ApheliaViperWhisperer(final ApheliaViperWhisperer card) { + super(card); + } + + @Override + public ApheliaViperWhisperer copy() { + return new ApheliaViperWhisperer(this); + } +} + +class ApheliaViperWhispererTriggeredAbility extends DelayedTriggeredAbility implements BatchTriggeredAbility { + + ApheliaViperWhispererTriggeredAbility() { + super(new LoseHalfLifeTargetEffect(), Duration.EndOfTurn, false, false); + setTriggerPhrase("Until end of turn, whenever one or more Gorgons and/or Snakes you control deal combat damage to a player, "); + } + + private ApheliaViperWhispererTriggeredAbility(final ApheliaViperWhispererTriggeredAbility ability) { + super(ability); + } + + @Override + public ApheliaViperWhispererTriggeredAbility copy() { + return new ApheliaViperWhispererTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_BATCH_FOR_ONE_PLAYER; + } + + @Override + public boolean checkEvent(DamagedPlayerEvent event, Game game) { + return event.isCombatDamage() + && Optional + .ofNullable(event) + .map(GameEvent::getSourceId) + .map(game::getPermanentOrLKIBattlefield) + .filter(permanent -> permanent.hasSubtype(SubType.GORGON, game) + || permanent.hasSubtype(SubType.SNAKE, game)) + .map(Controllable::getControllerId) + .filter(this::isControlledBy) + .isPresent(); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (this.getFilteredEvents((DamagedBatchForOnePlayerEvent) event, game).isEmpty()) { + return false; + } + this.getAllEffects().setTargetPointer(new FixedTarget(event.getTargetId())); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/a/ApothecaryGeist.java b/Mage.Sets/src/mage/cards/a/ApothecaryGeist.java index 42571f0ebf6..9f4ae930dc2 100644 --- a/Mage.Sets/src/mage/cards/a/ApothecaryGeist.java +++ b/Mage.Sets/src/mage/cards/a/ApothecaryGeist.java @@ -1,50 +1,45 @@ - package mage.cards.a; import mage.MageInt; -import mage.abilities.TriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.AnotherPredicate; import java.util.UUID; /** - * * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ public final class ApothecaryGeist extends CardImpl { - - private static final FilterControlledPermanent filter = new FilterControlledPermanent("another Elf"); + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.SPIRIT, "you control another Spirit"); static { filter.add(AnotherPredicate.instance); - filter.add(SubType.SPIRIT.getPredicate()); } + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + public ApothecaryGeist(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); this.subtype.add(SubType.SPIRIT); this.power = new MageInt(2); this.toughness = new MageInt(3); // Flying this.addAbility(FlyingAbility.getInstance()); - + // When Apothecary Geist enters the battlefield, if you control another Spirit, you gain 3 life. - TriggeredAbility triggeredAbility = new EntersBattlefieldTriggeredAbility(new GainLifeEffect(3)); - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - triggeredAbility, - new PermanentsOnTheBattlefieldCondition(filter), - "When {this} enters, if you control another Spirit, you gain 3 life.")); + this.addAbility(new EntersBattlefieldTriggeredAbility(new GainLifeEffect(3)).withInterveningIf(condition)); } private ApothecaryGeist(final ApothecaryGeist card) { diff --git a/Mage.Sets/src/mage/cards/a/ApprenticeNecromancer.java b/Mage.Sets/src/mage/cards/a/ApprenticeNecromancer.java index a34f82b88ec..ee20c52dacd 100644 --- a/Mage.Sets/src/mage/cards/a/ApprenticeNecromancer.java +++ b/Mage.Sets/src/mage/cards/a/ApprenticeNecromancer.java @@ -25,6 +25,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @@ -80,7 +81,7 @@ class ApprenticeNecromancerEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null && card != null) { if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { - Permanent creature = game.getPermanent(card.getId()); + Permanent creature = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (creature != null) { // Gains haste ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); diff --git a/Mage.Sets/src/mage/cards/a/ArbiterOfTheIdeal.java b/Mage.Sets/src/mage/cards/a/ArbiterOfTheIdeal.java index daa5840197f..5a07a7bfc80 100644 --- a/Mage.Sets/src/mage/cards/a/ArbiterOfTheIdeal.java +++ b/Mage.Sets/src/mage/cards/a/ArbiterOfTheIdeal.java @@ -19,6 +19,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @@ -86,7 +87,7 @@ class ArbiterOfTheIdealEffect extends OneShotEffect { controller.revealCards(sourceObject.getIdName(), new CardsImpl(card), game); if (filter.match(card, game) && controller.chooseUse(outcome, "Put " + card.getName() + " onto the battlefield?", source, game)) { controller.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { permanent.addCounters(CounterType.MANIFESTATION.createInstance(), source.getControllerId(), source, game); ContinuousEffect effect = new AddCardTypeTargetEffect(Duration.Custom, CardType.ENCHANTMENT); diff --git a/Mage.Sets/src/mage/cards/a/ArborAdherent.java b/Mage.Sets/src/mage/cards/a/ArborAdherent.java index 686eb403b1b..4a54e162e5c 100644 --- a/Mage.Sets/src/mage/cards/a/ArborAdherent.java +++ b/Mage.Sets/src/mage/cards/a/ArborAdherent.java @@ -3,7 +3,7 @@ package mage.cards.a; import mage.MageInt; import mage.Mana; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.dynamicvalue.common.GreatestToughnessAmongControlledCreaturesValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.mana.AnyColorManaAbility; import mage.abilities.mana.DynamicManaAbility; import mage.cards.CardImpl; @@ -31,10 +31,10 @@ public final class ArborAdherent extends CardImpl { // {T}: Add X mana of any one color, where X is the greatest toughness among other creatures you control. this.addAbility(new DynamicManaAbility( - Mana.AnyMana(1), GreatestToughnessAmongControlledCreaturesValue.OTHER, + Mana.AnyMana(1), GreatestAmongPermanentsValue.TOUGHNESS_OTHER_CONTROLLED_CREATURES, new TapSourceCost(), "add X mana of any one color, where X is the " + "greatest toughness among other creatures you control", true - ).addHint(GreatestToughnessAmongControlledCreaturesValue.OTHER.getHint())); + ).addHint(GreatestAmongPermanentsValue.TOUGHNESS_OTHER_CONTROLLED_CREATURES.getHint())); } private ArborAdherent(final ArborAdherent card) { diff --git a/Mage.Sets/src/mage/cards/a/ArborElf.java b/Mage.Sets/src/mage/cards/a/ArborElf.java index 622a0479758..7bec499d84e 100644 --- a/Mage.Sets/src/mage/cards/a/ArborElf.java +++ b/Mage.Sets/src/mage/cards/a/ArborElf.java @@ -1,8 +1,5 @@ - - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -12,30 +9,29 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterLandPermanent; -import mage.target.common.TargetLandPermanent; +import mage.filter.FilterPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author BetaSteward_at_googlemail.com */ public final class ArborElf extends CardImpl { - private static final FilterLandPermanent filter = new FilterLandPermanent(SubType.FOREST, "Forest"); + private static final FilterPermanent filter = new FilterPermanent(SubType.FOREST, "Forest"); public ArborElf(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}"); this.subtype.add(SubType.ELF); this.subtype.add(SubType.DRUID); this.power = new MageInt(1); this.toughness = new MageInt(1); - + // (T): Untap target Forest. Ability ability = new SimpleActivatedAbility(new UntapTargetEffect(), new TapSourceCost()); - TargetLandPermanent target = new TargetLandPermanent(filter); - ability.addTarget(target); + ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); } @@ -47,5 +43,4 @@ public final class ArborElf extends CardImpl { public ArborElf copy() { return new ArborElf(this); } - } diff --git a/Mage.Sets/src/mage/cards/a/ArcadeGannon.java b/Mage.Sets/src/mage/cards/a/ArcadeGannon.java index f70ee03061b..28400d471cb 100644 --- a/Mage.Sets/src/mage/cards/a/ArcadeGannon.java +++ b/Mage.Sets/src/mage/cards/a/ArcadeGannon.java @@ -2,7 +2,7 @@ package mage.cards.a; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.CastFromGraveyardOnceEachTurnAbility; +import mage.abilities.common.CastFromGraveyardOnceDuringEachOfYourTurnAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.common.DrawDiscardControllerEffect; @@ -24,14 +24,13 @@ import mage.game.permanent.Permanent; import java.util.UUID; /** - * * @author justinjohnson14 */ public final class ArcadeGannon extends CardImpl { private static final FilterCard filter = new FilterCard("an artifact or Human spell from your graveyard with mana value less than or equal to the number of quest counters on {this}"); - static{ + static { filter.add(Predicates.or( CardType.ARTIFACT.getPredicate(), SubType.HUMAN.getPredicate() @@ -41,7 +40,7 @@ public final class ArcadeGannon extends CardImpl { public ArcadeGannon(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{U}"); - + this.supertype.add(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.DOCTOR); @@ -49,12 +48,12 @@ public final class ArcadeGannon extends CardImpl { this.toughness = new MageInt(3); // {T}: Draw a card, then discard a card. Put a quest counter on Arcade Gannon. - Ability ability = (new SimpleActivatedAbility(new DrawDiscardControllerEffect(1,1), new TapSourceCost())); + Ability ability = (new SimpleActivatedAbility(new DrawDiscardControllerEffect(1, 1), new TapSourceCost())); ability.addEffect(new AddCountersSourceEffect(CounterType.QUEST.createInstance(1))); this.addAbility(ability); // For Auld Lang Syne -- Once during each of your turns, you may cast an artifact or Human spell from your graveyard with mana value less than or equal to the number of quest counters on Arcade Gannon. - this.addAbility(new CastFromGraveyardOnceEachTurnAbility(filter).withFlavorWord("For Auld Lang Syne")); + this.addAbility(new CastFromGraveyardOnceDuringEachOfYourTurnAbility(filter).withFlavorWord("For Auld Lang Syne")); } private ArcadeGannon(final ArcadeGannon card) { diff --git a/Mage.Sets/src/mage/cards/a/ArcaneProxy.java b/Mage.Sets/src/mage/cards/a/ArcaneProxy.java index 60319104e0f..c986b141b14 100644 --- a/Mage.Sets/src/mage/cards/a/ArcaneProxy.java +++ b/Mage.Sets/src/mage/cards/a/ArcaneProxy.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.CastFromEverywhereSourceCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.ExileTargetCardCopyAndCastEffect; import mage.abilities.keyword.PrototypeAbility; import mage.cards.CardImpl; @@ -42,12 +41,11 @@ public final class ArcaneProxy extends CardImpl { this.addAbility(new PrototypeAbility(this, "{1}{U}{U}", 2, 1)); // When Arcane Proxy enters the battlefield, if you cast it, exile target instant or sorcery card with mana value less than or equal to Arcane Proxy's power from your graveyard. Copy that card. You may cast the copy without paying its mana cost. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new ExileTargetCardCopyAndCastEffect(true)), - CastFromEverywhereSourceCondition.instance, "When {this} enters, " + - "if you cast it, exile target instant or sorcery card with mana value less than or equal to {this}'s " + - "power from your graveyard. Copy that card. You may cast the copy without paying its mana cost." - ); + Ability ability = new EntersBattlefieldTriggeredAbility( + new ExileTargetCardCopyAndCastEffect(true) + .setText("exile target instant or sorcery card with mana value less than or equal to {this}'s " + + "power from your graveyard. Copy that card. You may cast the copy without paying its mana cost") + ).withInterveningIf(CastFromEverywhereSourceCondition.instance); ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/ArcboundOverseer.java b/Mage.Sets/src/mage/cards/a/ArcboundOverseer.java index df81ba97c48..2fd4ffb9a12 100644 --- a/Mage.Sets/src/mage/cards/a/ArcboundOverseer.java +++ b/Mage.Sets/src/mage/cards/a/ArcboundOverseer.java @@ -24,7 +24,7 @@ public final class ArcboundOverseer extends CardImpl { private static final FilterControlledCreaturePermanent filter; static { - filter = new FilterControlledCreaturePermanent("creature with modular you control"); + filter = new FilterControlledCreaturePermanent("creature you control with modular"); filter.add(new AbilityPredicate(ModularAbility.class)); } diff --git a/Mage.Sets/src/mage/cards/a/ArchaeomancersMap.java b/Mage.Sets/src/mage/cards/a/ArchaeomancersMap.java index 27880770ad5..27cfcac06b0 100644 --- a/Mage.Sets/src/mage/cards/a/ArchaeomancersMap.java +++ b/Mage.Sets/src/mage/cards/a/ArchaeomancersMap.java @@ -4,7 +4,6 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.PutCardFromHandOntoBattlefieldEffect; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.cards.CardImpl; @@ -28,7 +27,7 @@ import java.util.UUID; public final class ArchaeomancersMap extends CardImpl { private static final FilterCard filter = new FilterCard("basic Plains cards"); - private static final FilterPermanent filter2 = new FilterLandPermanent(); + private static final FilterPermanent filter2 = new FilterLandPermanent("a land an opponent controls"); static { filter.add(SubType.PLAINS.getPredicate()); @@ -45,13 +44,9 @@ public final class ArchaeomancersMap extends CardImpl { )); // Whenever a land enters the battlefield under an opponent's control, if that player controls more lands than you, you may put a land card from your hand onto the battlefield. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldAllTriggeredAbility( - new PutCardFromHandOntoBattlefieldEffect(StaticFilters.FILTER_CARD_LAND_A), filter2 - ), ArchaeomancersMapCondition.instance, "Whenever a land enters the battlefield " + - "under an opponent's control, if that player controls more lands than you, " + - "you may put a land card from your hand onto the battlefield." - )); + this.addAbility(new EntersBattlefieldAllTriggeredAbility( + new PutCardFromHandOntoBattlefieldEffect(StaticFilters.FILTER_CARD_LAND_A), filter2 + ).withInterveningIf(ArchaeomancersMapCondition.instance)); } private ArchaeomancersMap(final ArchaeomancersMap card) { @@ -78,4 +73,9 @@ enum ArchaeomancersMapCondition implements Condition { source.getControllerId(), source, game ); } + + @Override + public String toString() { + return "that player controls more lands than you"; + } } diff --git a/Mage.Sets/src/mage/cards/a/ArchangelOfWrath.java b/Mage.Sets/src/mage/cards/a/ArchangelOfWrath.java index aa663cf4f2f..f472cff0b5a 100644 --- a/Mage.Sets/src/mage/cards/a/ArchangelOfWrath.java +++ b/Mage.Sets/src/mage/cards/a/ArchangelOfWrath.java @@ -1,10 +1,9 @@ package mage.cards.a; import mage.MageInt; -import mage.abilities.TriggeredAbility; +import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.KickedCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.KickerAbility; @@ -40,19 +39,19 @@ public final class ArchangelOfWrath extends CardImpl { // Lifelink this.addAbility(LifelinkAbility.getInstance()); - TriggeredAbility triggeredAbility = new EntersBattlefieldTriggeredAbility(new DamageTargetEffect(2)); - triggeredAbility.addTarget(new TargetAnyTarget()); // When Archangel of Wrath enters the battlefield, if it was kicked, it deals 2 damage to any target. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - triggeredAbility, KickedCondition.ONCE, "When {this} enters, " + - "if it was kicked, it deals 2 damage to any target." - )); + Ability ability = new EntersBattlefieldTriggeredAbility( + new DamageTargetEffect(2, "it") + ).withInterveningIf(KickedCondition.ONCE); + ability.addTarget(new TargetAnyTarget()); + this.addAbility(ability); // When Archangel of Wrath enters the battlefield, if it was kicked twice, it deals 2 damage to any target. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - triggeredAbility.copy(), KickedCondition.TWICE, "When {this} enters, " + - "if it was kicked twice, it deals 2 damage to any target." - )); + ability = new EntersBattlefieldTriggeredAbility( + new DamageTargetEffect(2, "it") + ).withInterveningIf(KickedCondition.TWICE); + ability.addTarget(new TargetAnyTarget()); + this.addAbility(ability); } private ArchangelOfWrath(final ArchangelOfWrath card) { diff --git a/Mage.Sets/src/mage/cards/a/ArchfiendOfTheDross.java b/Mage.Sets/src/mage/cards/a/ArchfiendOfTheDross.java index d3583388e8f..4f44292aeb8 100644 --- a/Mage.Sets/src/mage/cards/a/ArchfiendOfTheDross.java +++ b/Mage.Sets/src/mage/cards/a/ArchfiendOfTheDross.java @@ -2,7 +2,6 @@ package mage.cards.a; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.condition.Condition; @@ -13,9 +12,11 @@ import mage.abilities.effects.common.LoseGameSourceControllerEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; import mage.abilities.keyword.FlyingAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.Outcome; import mage.constants.SubType; import mage.counters.CounterType; @@ -33,7 +34,7 @@ import java.util.UUID; */ public final class ArchfiendOfTheDross extends CardImpl { - private static final Condition condition = new SourceHasCounterCondition(CounterType.OIL, 0, 0); + private static final Condition condition = new SourceHasCounterCondition(CounterType.OIL, ComparisonType.EQUAL_TO, 0); public ArchfiendOfTheDross(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}"); diff --git a/Mage.Sets/src/mage/cards/a/ArchivistOfGondor.java b/Mage.Sets/src/mage/cards/a/ArchivistOfGondor.java index dc042152bab..14b7f10242a 100644 --- a/Mage.Sets/src/mage/cards/a/ArchivistOfGondor.java +++ b/Mage.Sets/src/mage/cards/a/ArchivistOfGondor.java @@ -1,12 +1,13 @@ package mage.cards.a; import mage.MageInt; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.abilities.Ability; import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.effects.common.BecomesMonarchSourceEffect; import mage.abilities.effects.common.DrawCardTargetEffect; import mage.abilities.hint.common.MonarchHint; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -15,6 +16,7 @@ import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.FilterPermanent; import mage.filter.predicate.mageobject.CommanderPredicate; +import mage.game.Game; import java.util.UUID; @@ -39,13 +41,11 @@ public final class ArchivistOfGondor extends CardImpl { this.toughness = new MageInt(3); // When your commander deals combat damage to a player, if there is no monarch, you become the monarch. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new DealsDamageToAPlayerAllTriggeredAbility( - new BecomesMonarchSourceEffect(), filter, false, - SetTargetPointer.NONE, true - ), (game, source) -> game.getMonarchId() == null, "When your commander " + - "deals combat damage to a player, if there is no monarch, you become the monarch." - ).addHint(MonarchHint.instance)); + this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility( + new BecomesMonarchSourceEffect(), filter, false, + SetTargetPointer.NONE, true + ).setTriggerPhrase("When your commander deals combat damage to a player, ") + .withInterveningIf(ArchivistOfGondorCondition.instance).addHint(MonarchHint.instance)); // At the beginning of the monarch's end step, that player draws a card. this.addAbility(new BeginningOfEndStepTriggeredAbility( @@ -62,3 +62,17 @@ public final class ArchivistOfGondor extends CardImpl { return new ArchivistOfGondor(this); } } + +enum ArchivistOfGondorCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + return game.getMonarchId() == null; + } + + @Override + public String toString() { + return "there is no monarch"; + } +} diff --git a/Mage.Sets/src/mage/cards/a/ArchmageAscension.java b/Mage.Sets/src/mage/cards/a/ArchmageAscension.java index 76c4e4c2f31..2ae3ec86cfd 100644 --- a/Mage.Sets/src/mage/cards/a/ArchmageAscension.java +++ b/Mage.Sets/src/mage/cards/a/ArchmageAscension.java @@ -1,12 +1,11 @@ package mage.cards.a; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -30,12 +29,9 @@ public final class ArchmageAscension extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); // At the beginning of each end step, if you drew two or more cards this turn, you may put a quest counter on Archmage Ascension. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfEndStepTriggeredAbility( - TargetController.EACH_PLAYER, new AddCountersSourceEffect(CounterType.QUEST.createInstance(1)), - true - ), ArchmageAscensionCondition.instance, "At the beginning of each end step, " + - "if you drew two or more cards this turn, you may put a quest counter on {this}." + this.addAbility(new BeginningOfEndStepTriggeredAbility( + TargetController.EACH_PLAYER, new AddCountersSourceEffect(CounterType.QUEST.createInstance()), + true, ArchmageAscensionCondition.instance ), new CardsAmountDrawnThisTurnWatcher()); // As long as Archmage Ascension has six or more quest counters on it, if you would draw a card, @@ -61,6 +57,11 @@ enum ArchmageAscensionCondition implements Condition { CardsAmountDrawnThisTurnWatcher watcher = game.getState().getWatcher(CardsAmountDrawnThisTurnWatcher.class); return watcher != null && watcher.getAmountCardsDrawn(source.getControllerId()) >= 2; } + + @Override + public String toString() { + return "you drew two or more cards this turn"; + } } class ArchmageAscensionReplacementEffect extends ReplacementEffectImpl { diff --git a/Mage.Sets/src/mage/cards/a/ArchonOfTheTriumvirate.java b/Mage.Sets/src/mage/cards/a/ArchonOfTheTriumvirate.java index 22645ecd544..643ba014325 100644 --- a/Mage.Sets/src/mage/cards/a/ArchonOfTheTriumvirate.java +++ b/Mage.Sets/src/mage/cards/a/ArchonOfTheTriumvirate.java @@ -1,7 +1,6 @@ package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; @@ -13,22 +12,23 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.common.FilterNonlandPermanent; -import mage.target.common.TargetNonlandPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class ArchonOfTheTriumvirate extends CardImpl { private static final FilterNonlandPermanent filter = new FilterNonlandPermanent("nonland permanents your opponents control"); - + static { filter.add(TargetController.OPPONENT.getControllerPredicate()); } - + public ArchonOfTheTriumvirate(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{W}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}{U}"); this.subtype.add(SubType.ARCHON); this.power = new MageInt(4); @@ -36,11 +36,11 @@ public final class ArchonOfTheTriumvirate extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - + // Whenever Archon of the Triumvirate attacks, detain up to two target nonland permanents your opponents control. // (Until your next turn, those permanents can't attack or block and their activated abilities can't be activated.) Ability ability = new AttacksTriggeredAbility(new DetainTargetEffect(), false); - ability.addTarget(new TargetNonlandPermanent(0,2,filter, false)); + ability.addTarget(new TargetPermanent(0, 2, filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/ArchpriestOfIona.java b/Mage.Sets/src/mage/cards/a/ArchpriestOfIona.java index dc165494fb4..8409984d5d9 100644 --- a/Mage.Sets/src/mage/cards/a/ArchpriestOfIona.java +++ b/Mage.Sets/src/mage/cards/a/ArchpriestOfIona.java @@ -2,19 +2,20 @@ package mage.cards.a; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.FullPartyCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.dynamicvalue.common.PartyCount; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.effects.common.continuous.SetBasePowerSourceEffect; import mage.abilities.hint.common.PartyCountHint; import mage.abilities.keyword.FlyingAbility; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -35,19 +36,16 @@ public final class ArchpriestOfIona extends CardImpl { // Archpriest of Iona's power is equal to the number of creatures in your party. this.addAbility(new SimpleStaticAbility( Zone.ALL, - new SetBasePowerSourceEffect( - PartyCount.instance - ).setText("{this}'s power is equal to the number of creatures in your party. " + PartyCount.getReminder()) + new SetBasePowerSourceEffect(PartyCount.instance) + .setText("{this}'s power is equal to the number of creatures in your party. " + PartyCount.getReminder()) ).addHint(PartyCountHint.instance)); // At the beginning of combat on your turn, if you have a full party, target creature gets +1/+1 and gains flying until end of turn. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new BeginningOfCombatTriggeredAbility( - new BoostTargetEffect(1, 1, Duration.EndOfTurn) - ), FullPartyCondition.instance, "At the beginning of combat on your turn, " + - "if you have a full party, target creature gets +1/+1 and gains flying until end of turn." - ); - ability.addEffect(new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn)); + Ability ability = new BeginningOfCombatTriggeredAbility( + new BoostTargetEffect(1, 1).setText("target creature gets +1/+1") + ).withInterveningIf(FullPartyCondition.instance); + ability.addEffect(new GainAbilityTargetEffect(FlyingAbility.getInstance()) + .setText("and gains flying until end of turn")); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/ArchwayAngel.java b/Mage.Sets/src/mage/cards/a/ArchwayAngel.java index 03ffedcede5..4f7ef212252 100644 --- a/Mage.Sets/src/mage/cards/a/ArchwayAngel.java +++ b/Mage.Sets/src/mage/cards/a/ArchwayAngel.java @@ -5,7 +5,7 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.GainLifeEffect; -import mage.abilities.hint.common.GateYouControlHint; +import mage.abilities.hint.common.GatesYouControlHint; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -39,7 +39,7 @@ public final class ArchwayAngel extends CardImpl { // When Archway Angel enters the battlefield, you gain 2 life for each Gate you control. Ability ability = new EntersBattlefieldTriggeredAbility(new GainLifeEffect(new PermanentsOnBattlefieldCount(filter, 2))); - ability.addHint(GateYouControlHint.instance); + ability.addHint(GatesYouControlHint.instance); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/ArclightPhoenix.java b/Mage.Sets/src/mage/cards/a/ArclightPhoenix.java index e4000613e0f..2332e95e80d 100644 --- a/Mage.Sets/src/mage/cards/a/ArclightPhoenix.java +++ b/Mage.Sets/src/mage/cards/a/ArclightPhoenix.java @@ -2,18 +2,18 @@ package mage.cards.a; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.HasteAbility; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; +import mage.util.CardUtil; import mage.watchers.Watcher; import java.util.HashMap; @@ -39,17 +39,10 @@ public final class ArclightPhoenix extends CardImpl { this.addAbility(HasteAbility.getInstance()); // At the beginning of combat on your turn, if you cast 3 or more instants and/or sorceries this turn, return Arclight Phoenix from your graveyard to the battlefield. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfCombatTriggeredAbility( - Zone.GRAVEYARD, - TargetController.YOU, new ReturnSourceFromGraveyardToBattlefieldEffect(), - false - ), ArclightPhoenixCondition.instance, - "At the beginning of combat on your turn, " - + "if you've cast three or more instant " - + "and sorcery spells this turn, return {this} " - + "from your graveyard to the battlefield." - ), new ArclightPhoenixWatcher()); + this.addAbility(new BeginningOfCombatTriggeredAbility( + Zone.GRAVEYARD, TargetController.YOU, + new ReturnSourceFromGraveyardToBattlefieldEffect(), false + ).withInterveningIf(ArclightPhoenixCondition.instance), new ArclightPhoenixWatcher()); } private ArclightPhoenix(final ArclightPhoenix card) { @@ -67,8 +60,12 @@ enum ArclightPhoenixCondition implements Condition { @Override public boolean apply(Game game, Ability source) { - ArclightPhoenixWatcher watcher = game.getState().getWatcher(ArclightPhoenixWatcher.class); - return watcher != null && watcher.getInstantSorceryCount(source.getControllerId()) > 2; + return ArclightPhoenixWatcher.getInstantSorceryCount(game, source); + } + + @Override + public String toString() { + return "you've cast three or more instant and sorcery spells this turn"; } } @@ -82,15 +79,12 @@ class ArclightPhoenixWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.SPELL_CAST) { - Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell == null || !spell.isInstantOrSorcery(game)) { - return; - } - this.instantSorceryCount.putIfAbsent(spell.getControllerId(), 0); - this.instantSorceryCount.compute( - spell.getControllerId(), (k, a) -> a + 1 - ); + if (event.getType() != GameEvent.EventType.SPELL_CAST) { + return; + } + Spell spell = game.getStack().getSpell(event.getTargetId()); + if (spell != null && spell.isInstantOrSorcery(game)) { + this.instantSorceryCount.compute(spell.getControllerId(), CardUtil::setOrIncrementValue); } } @@ -100,7 +94,11 @@ class ArclightPhoenixWatcher extends Watcher { this.instantSorceryCount.clear(); } - int getInstantSorceryCount(UUID playerId) { - return this.instantSorceryCount.getOrDefault(playerId, 0); + static boolean getInstantSorceryCount(Game game, Ability source) { + return game + .getState() + .getWatcher(ArclightPhoenixWatcher.class) + .instantSorceryCount + .getOrDefault(source.getControllerId(), 0) >= 3; } } diff --git a/Mage.Sets/src/mage/cards/a/ArcumDagsson.java b/Mage.Sets/src/mage/cards/a/ArcumDagsson.java index 4bf9142e155..82a5bfb9ee0 100644 --- a/Mage.Sets/src/mage/cards/a/ArcumDagsson.java +++ b/Mage.Sets/src/mage/cards/a/ArcumDagsson.java @@ -12,7 +12,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterCard; import mage.filter.StaticFilters; -import mage.filter.common.FilterNoncreatureCard; +import mage.filter.predicate.Predicates; import mage.target.TargetPermanent; import mage.target.common.TargetCardInLibrary; @@ -22,9 +22,10 @@ import mage.target.common.TargetCardInLibrary; */ public final class ArcumDagsson extends CardImpl { - private static final FilterCard filter = new FilterNoncreatureCard("noncreature artifact card"); + private static final FilterCard filter = new FilterCard("noncreature artifact card"); static { + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); filter.add(CardType.ARTIFACT.getPredicate()); } diff --git a/Mage.Sets/src/mage/cards/a/ArdentDustspeaker.java b/Mage.Sets/src/mage/cards/a/ArdentDustspeaker.java index 91236a3ea8c..4b4ace434dc 100644 --- a/Mage.Sets/src/mage/cards/a/ArdentDustspeaker.java +++ b/Mage.Sets/src/mage/cards/a/ArdentDustspeaker.java @@ -88,7 +88,7 @@ class ArdentDustspeakerCost extends CostImpl { if (controller != null && (controller.chooseTarget(Outcome.Benefit, this.getTargets().get(0), source, game))) { Card card = game.getCard(this.getTargets().get(0).getFirstTarget()); if (card != null) { - controller.putCardsOnBottomOfLibrary(card, game, source, true); + controller.putCardsOnBottomOfLibrary(card, game, source); paid = true; } } diff --git a/Mage.Sets/src/mage/cards/a/ArdynTheUsurper.java b/Mage.Sets/src/mage/cards/a/ArdynTheUsurper.java index e0f20d5da57..f2b2dc4ed4a 100644 --- a/Mage.Sets/src/mage/cards/a/ArdynTheUsurper.java +++ b/Mage.Sets/src/mage/cards/a/ArdynTheUsurper.java @@ -75,8 +75,8 @@ class ArdynTheUsurperEffect extends OneShotEffect { ArdynTheUsurperEffect() { super(Outcome.Benefit); - staticText = "at the beginning of combat on your turn, exile up to one target creature card from a graveyard. " + - "If you exiled a card this way, create a token that's a copy of that card, except it's a 5/5 black Demon"; + staticText = "exile up to one target creature card from a graveyard. If you exiled a card this way, " + + "create a token that's a copy of that card, except it's a 5/5 black Demon"; } private ArdynTheUsurperEffect(final ArdynTheUsurperEffect effect) { diff --git a/Mage.Sets/src/mage/cards/a/ArgentDais.java b/Mage.Sets/src/mage/cards/a/ArgentDais.java index fd1818417b3..7817be0dd03 100644 --- a/Mage.Sets/src/mage/cards/a/ArgentDais.java +++ b/Mage.Sets/src/mage/cards/a/ArgentDais.java @@ -19,7 +19,7 @@ import mage.filter.common.FilterNonlandPermanent; import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.target.common.TargetNonlandPermanent; +import mage.target.TargetPermanent; import java.util.UUID; @@ -53,7 +53,7 @@ public final class ArgentDais extends CardImpl { ); ability.addCost(new TapSourceCost()); ability.addCost(new RemoveCountersSourceCost(CounterType.OIL.createInstance(2))); - ability.addTarget(new TargetNonlandPermanent(filter)); + ability.addTarget(new TargetPermanent(filter)); ability.addEffect(new DrawCardTargetControllerEffect(2)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/ArgivianPhalanx.java b/Mage.Sets/src/mage/cards/a/ArgivianPhalanx.java index f8c6e515b47..e8d4f6da868 100644 --- a/Mage.Sets/src/mage/cards/a/ArgivianPhalanx.java +++ b/Mage.Sets/src/mage/cards/a/ArgivianPhalanx.java @@ -1,17 +1,13 @@ package mage.cards.a; import mage.MageInt; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.AffinityEffect; -import mage.abilities.hint.common.CreaturesYouControlHint; +import mage.abilities.keyword.AffinityAbility; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AffinityType; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterControlledPermanent; import java.util.UUID; @@ -20,8 +16,6 @@ import java.util.UUID; */ public final class ArgivianPhalanx extends CardImpl { - static final FilterControlledPermanent filter = new FilterControlledCreaturePermanent("creatures"); - public ArgivianPhalanx(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}"); @@ -31,8 +25,8 @@ public final class ArgivianPhalanx extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - // This spell costs {1} less to cast for each creature you control. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filter)).addHint(CreaturesYouControlHint.instance)); + // Affinity for creatures + this.addAbility(new AffinityAbility(AffinityType.CREATURES)); // Vigilance this.addAbility(VigilanceAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/a/ArixmethesSlumberingIsle.java b/Mage.Sets/src/mage/cards/a/ArixmethesSlumberingIsle.java index a05326bb38e..291e5a65642 100644 --- a/Mage.Sets/src/mage/cards/a/ArixmethesSlumberingIsle.java +++ b/Mage.Sets/src/mage/cards/a/ArixmethesSlumberingIsle.java @@ -29,8 +29,7 @@ import java.util.UUID; */ public final class ArixmethesSlumberingIsle extends CardImpl { - private static final Condition condition - = new SourceHasCounterCondition(CounterType.SLUMBER, 1, Integer.MAX_VALUE); + private static final Condition condition = new SourceHasCounterCondition(CounterType.SLUMBER); public ArixmethesSlumberingIsle(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{U}"); diff --git a/Mage.Sets/src/mage/cards/a/ArmWithAether.java b/Mage.Sets/src/mage/cards/a/ArmWithAether.java index 5c8eacb5aac..56864b7214a 100644 --- a/Mage.Sets/src/mage/cards/a/ArmWithAether.java +++ b/Mage.Sets/src/mage/cards/a/ArmWithAether.java @@ -12,7 +12,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.filter.common.FilterCreaturePermanent; import mage.target.TargetPermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -30,7 +30,7 @@ public final class ArmWithAether extends CardImpl { // Until end of turn, creatures you control gain "Whenever this creature deals damage to an opponent, you may return target creature that player controls to its owner's hand." Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new ReturnToHandTargetEffect(), false, true); ability.addTarget(new TargetPermanent(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); Effect effect = new GainAbilityControlledEffect(ability, Duration.EndOfTurn, new FilterCreaturePermanent()); effect.setText("Until end of turn, creatures you control gain \"Whenever this creature deals damage to an opponent, you may return target creature that player controls to its owner's hand.\""); diff --git a/Mage.Sets/src/mage/cards/a/ArmedResponse.java b/Mage.Sets/src/mage/cards/a/ArmedResponse.java index de6f6701d32..62ae98386f2 100644 --- a/Mage.Sets/src/mage/cards/a/ArmedResponse.java +++ b/Mage.Sets/src/mage/cards/a/ArmedResponse.java @@ -1,35 +1,27 @@ - package mage.cards.a; -import java.util.UUID; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; -import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.StaticFilters; import mage.target.common.TargetAttackingCreature; +import java.util.UUID; + /** - * * @author Plopman */ public final class ArmedResponse extends CardImpl { - - private static final FilterControlledArtifactPermanent filter = new FilterControlledArtifactPermanent("Equipment you control"); - - static { - filter.add(SubType.EQUIPMENT.getPredicate()); - } public ArmedResponse(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}"); // Armed Response deals damage to target attacking creature equal to the number of Equipment you control. - Effect effect = new DamageTargetEffect(new PermanentsOnBattlefieldCount(filter)); + Effect effect = new DamageTargetEffect(new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT)); effect.setText("{this} deals damage to target attacking creature equal to the number of Equipment you control"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetAttackingCreature()); diff --git a/Mage.Sets/src/mage/cards/a/ArmoredSkyhunter.java b/Mage.Sets/src/mage/cards/a/ArmoredSkyhunter.java index c42b1cb811b..42e84e7e205 100644 --- a/Mage.Sets/src/mage/cards/a/ArmoredSkyhunter.java +++ b/Mage.Sets/src/mage/cards/a/ArmoredSkyhunter.java @@ -18,6 +18,7 @@ import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetControlledCreaturePermanent; +import mage.util.CardUtil; import java.util.UUID; @@ -94,7 +95,7 @@ class ArmoredSkyhunterEffect extends OneShotEffect { } player.moveCards(card, Zone.BATTLEFIELD, source, game); cards.removeIf(uuid -> game.getState().getZone(uuid) != Zone.LIBRARY); - Permanent equipment = game.getPermanent(card.getId()); + Permanent equipment = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (equipment == null || !equipment.hasSubtype(SubType.EQUIPMENT, game)) { return player.putCardsOnBottomOfLibrary(cards, game, source, false); } diff --git a/Mage.Sets/src/mage/cards/a/ArmoryAutomaton.java b/Mage.Sets/src/mage/cards/a/ArmoryAutomaton.java index a56ef46f944..d3d1c6ac34c 100644 --- a/Mage.Sets/src/mage/cards/a/ArmoryAutomaton.java +++ b/Mage.Sets/src/mage/cards/a/ArmoryAutomaton.java @@ -9,7 +9,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; @@ -21,12 +21,6 @@ import java.util.UUID; */ public final class ArmoryAutomaton extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("Equipment"); - - static { - filter.add(SubType.EQUIPMENT.getPredicate()); - } - public ArmoryAutomaton(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); @@ -36,7 +30,7 @@ public final class ArmoryAutomaton extends CardImpl { // Whenever Armory Automaton enters or attacks, you may attach any number of target Equipment to it. Ability ability = new EntersBattlefieldOrAttacksSourceTriggeredAbility(new ArmoryAutomatonEffect(), true); - ability.addTarget(new TargetPermanent(0, Integer.MAX_VALUE, filter)); + ability.addTarget(new TargetPermanent(0, Integer.MAX_VALUE, StaticFilters.FILTER_PERMANENT_EQUIPMENT)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/ArmoryGuard.java b/Mage.Sets/src/mage/cards/a/ArmoryGuard.java index 6b20c41adaa..ea8a354fc45 100644 --- a/Mage.Sets/src/mage/cards/a/ArmoryGuard.java +++ b/Mage.Sets/src/mage/cards/a/ArmoryGuard.java @@ -1,9 +1,9 @@ package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; @@ -12,25 +12,19 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; + +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class ArmoryGuard extends CardImpl { - - private static final String rule = "Armory Guard has vigilance as long as you control a Gate"; - - private static final FilterPermanent filter = new FilterPermanent("Gate"); - - static { - filter.add(SubType.GATE.getPredicate()); - } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(new FilterControlledPermanent(SubType.GATE)); public ArmoryGuard(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); this.subtype.add(SubType.GIANT); this.subtype.add(SubType.SOLDIER); @@ -38,8 +32,10 @@ public final class ArmoryGuard extends CardImpl { this.toughness = new MageInt(5); // Armory Guard has vigilance as long as you control a Gate. - ConditionalContinuousEffect effect = new ConditionalContinuousEffect(new GainAbilitySourceEffect(VigilanceAbility.getInstance()), new PermanentsOnTheBattlefieldCondition(filter), rule); - this.addAbility(new SimpleStaticAbility(effect)); + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(VigilanceAbility.getInstance()), + condition, "{this} has vigilance as long as you control a Gate" + ))); } private ArmoryGuard(final ArmoryGuard card) { diff --git a/Mage.Sets/src/mage/cards/a/ArniBrokenbrow.java b/Mage.Sets/src/mage/cards/a/ArniBrokenbrow.java index eda3f368e66..96fa8919ea6 100644 --- a/Mage.Sets/src/mage/cards/a/ArniBrokenbrow.java +++ b/Mage.Sets/src/mage/cards/a/ArniBrokenbrow.java @@ -4,6 +4,7 @@ import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.SetBasePowerSourceEffect; import mage.abilities.keyword.BoastAbility; @@ -11,11 +12,9 @@ import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; -import java.util.Objects; import java.util.UUID; /** @@ -36,7 +35,8 @@ public final class ArniBrokenbrow extends CardImpl { this.addAbility(HasteAbility.getInstance()); // Boast — {1}: You may change Arni Brokenbrow's base power to 1 plus the greatest power among other creatures you control until end of turn. - this.addAbility(new BoastAbility(new ArniBrokenbrowEffect(), new GenericManaCost(1))); + this.addAbility(new BoastAbility(new ArniBrokenbrowEffect(), new GenericManaCost(1)) + .addHint(GreatestAmongPermanentsValue.POWER_OTHER_CONTROLLED_CREATURES.getHint())); } private ArniBrokenbrow(final ArniBrokenbrow card) { @@ -72,17 +72,7 @@ class ArniBrokenbrowEffect extends OneShotEffect { if (controller == null || mageObject == null) { return false; } - int power = game - .getBattlefield() - .getActivePermanents(StaticFilters.FILTER_CONTROLLED_CREATURE, source.getControllerId(), game) - .stream() - .filter(Objects::nonNull) - .filter(permanent -> !permanent.getId().equals(source.getSourceId()) - || permanent.getZoneChangeCounter(game) != source.getSourceObjectZoneChangeCounter()) - .map(MageObject::getPower) - .mapToInt(MageInt::getValue) - .max() - .orElse(0); + int power = GreatestAmongPermanentsValue.POWER_OTHER_CONTROLLED_CREATURES.calculate(game, source, this); power += 1; if (controller.chooseUse(outcome, "Change base power of " + mageObject.getLogName() + " to " + power + " until end of turn?", source, game diff --git a/Mage.Sets/src/mage/cards/a/ArniMetalbrow.java b/Mage.Sets/src/mage/cards/a/ArniMetalbrow.java index 1c7dd738f22..f0aa7bf6029 100644 --- a/Mage.Sets/src/mage/cards/a/ArniMetalbrow.java +++ b/Mage.Sets/src/mage/cards/a/ArniMetalbrow.java @@ -16,6 +16,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetCardInHand; +import mage.util.CardUtil; import java.util.UUID; @@ -131,7 +132,7 @@ class ArniMetalbrowEffect extends OneShotEffect { return false; } player.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null); - Permanent creature = game.getPermanent(card.getId()); + Permanent creature = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (creature != null) { game.getCombat().addAttackingCreature(creature.getId(), game); } diff --git a/Mage.Sets/src/mage/cards/a/ArniSlaysTheTroll.java b/Mage.Sets/src/mage/cards/a/ArniSlaysTheTroll.java index 23a599352f2..5e937c7469f 100644 --- a/Mage.Sets/src/mage/cards/a/ArniSlaysTheTroll.java +++ b/Mage.Sets/src/mage/cards/a/ArniSlaysTheTroll.java @@ -2,7 +2,7 @@ package mage.cards.a; import mage.Mana; import mage.abilities.common.SagaAbility; -import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.FightTargetsEffect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; @@ -58,11 +58,11 @@ public final class ArniSlaysTheTroll extends CardImpl { // III — You gain life equal to the greatest power among creatures you control. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, - new GainLifeEffect(GreatestPowerAmongControlledCreaturesValue.instance, + new GainLifeEffect(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES, "You gain life equal to the greatest power among creatures you control" ) ); - sagaAbility.addHint(GreatestPowerAmongControlledCreaturesValue.getHint()); + sagaAbility.addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint()); this.addAbility(sagaAbility); } diff --git a/Mage.Sets/src/mage/cards/a/ArrogantOutlaw.java b/Mage.Sets/src/mage/cards/a/ArrogantOutlaw.java index fd815efc8d8..434a8048025 100644 --- a/Mage.Sets/src/mage/cards/a/ArrogantOutlaw.java +++ b/Mage.Sets/src/mage/cards/a/ArrogantOutlaw.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.OpponentsLostLifeCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.LoseLifeOpponentsEffect; import mage.abilities.hint.common.OpponentsLostLifeHint; @@ -29,13 +28,10 @@ public final class ArrogantOutlaw extends CardImpl { this.toughness = new MageInt(2); // When Arrogant Outlaw enters the battlefield, if an opponent lost life this turn, each opponent loses 2 life and you gain 2 life. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility( - new LoseLifeOpponentsEffect(2), false - ), OpponentsLostLifeCondition.instance, "When {this} enters, " + - "if an opponent lost life this turn, each opponent loses 2 life and you gain 2 life." - ); - ability.addEffect(new GainLifeEffect(2)); + Ability ability = new EntersBattlefieldTriggeredAbility( + new LoseLifeOpponentsEffect(2), false + ).withInterveningIf(OpponentsLostLifeCondition.instance); + ability.addEffect(new GainLifeEffect(2).concatBy("and")); this.addAbility(ability.addHint(OpponentsLostLifeHint.instance)); } diff --git a/Mage.Sets/src/mage/cards/a/ArthurMarigoldKnight.java b/Mage.Sets/src/mage/cards/a/ArthurMarigoldKnight.java index 19f76cb9ee1..d6c99fba983 100644 --- a/Mage.Sets/src/mage/cards/a/ArthurMarigoldKnight.java +++ b/Mage.Sets/src/mage/cards/a/ArthurMarigoldKnight.java @@ -19,6 +19,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInLibrary; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @@ -111,7 +112,7 @@ class ArthurMarigoldKnightEffect extends OneShotEffect { )) { return player.putCardsOnBottomOfLibrary(cards, game, source, false); } - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return player.putCardsOnBottomOfLibrary(cards, game, source, false); } @@ -129,4 +130,4 @@ class ArthurMarigoldKnightEffect extends OneShotEffect { public ArthurMarigoldKnightEffect copy() { return new ArthurMarigoldKnightEffect(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/a/ArtifactBlast.java b/Mage.Sets/src/mage/cards/a/ArtifactBlast.java index 7de47c81c49..f1a79cc3ac8 100644 --- a/Mage.Sets/src/mage/cards/a/ArtifactBlast.java +++ b/Mage.Sets/src/mage/cards/a/ArtifactBlast.java @@ -1,24 +1,30 @@ - package mage.cards.a; -import java.util.UUID; import mage.abilities.effects.common.CounterTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterArtifactSpell; +import mage.filter.FilterSpell; import mage.target.TargetSpell; +import java.util.UUID; + /** * * @author Jgod */ public final class ArtifactBlast extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("artifact spell"); + static { + filter.add(CardType.ARTIFACT.getPredicate()); + } + public ArtifactBlast(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{R}"); // Counter target artifact spell. - this.getSpellAbility().addTarget(new TargetSpell(new FilterArtifactSpell())); + this.getSpellAbility().addTarget(new TargetSpell(filter)); this.getSpellAbility().addEffect(new CounterTargetEffect()); } diff --git a/Mage.Sets/src/mage/cards/a/ArtificersHex.java b/Mage.Sets/src/mage/cards/a/ArtificersHex.java index 3f1308f1ac3..88f42cdff68 100644 --- a/Mage.Sets/src/mage/cards/a/ArtificersHex.java +++ b/Mage.Sets/src/mage/cards/a/ArtificersHex.java @@ -1,39 +1,34 @@ - package mage.cards.a; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.keyword.EnchantAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.FilterPermanent; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class ArtificersHex extends CardImpl { - - private static final FilterPermanent filter = new FilterPermanent("Equipment"); - static { - filter.add(CardType.ARTIFACT.getPredicate()); - filter.add(SubType.EQUIPMENT.getPredicate()); - } public ArtificersHex(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{B}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{B}"); this.subtype.add(SubType.AURA); // Enchant Equipment - TargetPermanent auraTarget = new TargetPermanent(filter); + TargetPermanent auraTarget = new TargetPermanent(StaticFilters.FILTER_PERMANENT_EQUIPMENT); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); Ability ability = new EnchantAbility(auraTarget); diff --git a/Mage.Sets/src/mage/cards/a/ArtistsTalent.java b/Mage.Sets/src/mage/cards/a/ArtistsTalent.java index 6e7235eb89c..3eb826b7ab6 100644 --- a/Mage.Sets/src/mage/cards/a/ArtistsTalent.java +++ b/Mage.Sets/src/mage/cards/a/ArtistsTalent.java @@ -19,7 +19,7 @@ import mage.constants.Outcome; import mage.constants.SubType; import mage.filter.FilterCard; import mage.filter.StaticFilters; -import mage.filter.common.FilterNoncreatureCard; +import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.events.DamageEvent; import mage.game.events.GameEvent; @@ -33,7 +33,11 @@ import java.util.UUID; */ public final class ArtistsTalent extends CardImpl { - private static final FilterCard filter = new FilterNoncreatureCard("noncreature spells"); + private static final FilterCard filter = new FilterCard("noncreature spells"); + + static { + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); + } public ArtistsTalent(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{R}"); diff --git a/Mage.Sets/src/mage/cards/a/AsajjVentress.java b/Mage.Sets/src/mage/cards/a/AsajjVentress.java index 139d20b5f30..4384a66cd0b 100644 --- a/Mage.Sets/src/mage/cards/a/AsajjVentress.java +++ b/Mage.Sets/src/mage/cards/a/AsajjVentress.java @@ -1,13 +1,11 @@ package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.BecomesBlockedSourceTriggeredAbility; import mage.abilities.condition.common.HateCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.dynamicvalue.common.BlockingCreatureCount; import mage.abilities.effects.Effect; import mage.abilities.effects.common.combat.BlocksIfAbleTargetEffect; @@ -15,13 +13,12 @@ import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.DoubleStrikeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.SuperType; +import mage.constants.*; import mage.target.common.TargetCreaturePermanent; import mage.watchers.common.LifeLossOtherFromCombatWatcher; +import java.util.UUID; + /** * @author Styxo */ @@ -43,10 +40,8 @@ public final class AsajjVentress extends CardImpl { this.addAbility(new BecomesBlockedSourceTriggeredAbility(effect, false)); // Hate — Whenever Asajj Ventress attacks, if an opponent lost life from a source other than combat damage this turn, target creature blocks this turn if able. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new AttacksTriggeredAbility(new BlocksIfAbleTargetEffect(Duration.EndOfTurn), false), - HateCondition.instance, - "Hate — Whenever Asajj Ventress attacks, if an opponent lost life from a source other than combat damage this turn, target creature blocks this turn if able"); + Ability ability = new AttacksTriggeredAbility(new BlocksIfAbleTargetEffect(Duration.EndOfTurn), false) + .withInterveningIf(HateCondition.instance).setAbilityWord(AbilityWord.HATE); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability, new LifeLossOtherFromCombatWatcher()); } diff --git a/Mage.Sets/src/mage/cards/a/AscentOfTheWorthy.java b/Mage.Sets/src/mage/cards/a/AscentOfTheWorthy.java index 0148dab9270..f3fdf2d8b6c 100644 --- a/Mage.Sets/src/mage/cards/a/AscentOfTheWorthy.java +++ b/Mage.Sets/src/mage/cards/a/AscentOfTheWorthy.java @@ -22,6 +22,7 @@ import mage.target.TargetPermanent; import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -172,7 +173,7 @@ class AscentOfTheWorthyReturnEffect extends OneShotEffect { countersToAdd.addCounter(CounterType.FLYING.createInstance()); game.setEnterWithCounters(card.getId(), countersToAdd); player.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/a/AshPartyCrasher.java b/Mage.Sets/src/mage/cards/a/AshPartyCrasher.java index 7deb6eea062..6736bad9f80 100644 --- a/Mage.Sets/src/mage/cards/a/AshPartyCrasher.java +++ b/Mage.Sets/src/mage/cards/a/AshPartyCrasher.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.condition.common.CelebrationCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; @@ -36,11 +35,8 @@ public final class AshPartyCrasher extends CardImpl { this.addAbility(HasteAbility.getInstance()); // Celebration -- Whenever Ash, Party Crasher attacks, if two or more nonland permanents entered the battlefield under your control this turn, put a +1/+1 counter on Ash. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new AttacksTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(1)), false), - CelebrationCondition.instance, "Whenever {this} attacks, if two or more nonland permanents " + - "entered the battlefield under your control this turn, put a +1/+1 counter on {this}." - ); + Ability ability = new AttacksTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(1)), false) + .withInterveningIf(CelebrationCondition.instance); ability.setAbilityWord(AbilityWord.CELEBRATION); ability.addHint(CelebrationCondition.getHint()); this.addAbility(ability, new PermanentsEnteredBattlefieldWatcher()); diff --git a/Mage.Sets/src/mage/cards/a/AshadTheLoneCyberman.java b/Mage.Sets/src/mage/cards/a/AshadTheLoneCyberman.java new file mode 100644 index 00000000000..ac01c32db50 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AshadTheLoneCyberman.java @@ -0,0 +1,127 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.common.SacrificePermanentTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.GainAbilityControlledSpellsEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.CasualtyAbility; +import mage.constants.*; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.counters.CounterType; +import mage.filter.common.FilterNonlandCard; +import mage.filter.StaticFilters; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author padfoothelix + */ +public final class AshadTheLoneCyberman extends CardImpl { + + private static final String rule = "The first nonlegendary artifact spell you cast each turn has casualty 2. " + + "(As you cast it, you may sacrifice a creature with power 2 or greater. When you do, copy it. " + + "A copy of an artifact spell becomes a token.)"; + private static final FilterNonlandCard filter = new FilterNonlandCard("the first nonlegendary artifact you cast each turn"); + static { + filter.add(AshadTheLoneCybermanSpellPredicate.instance); + } + + public AshadTheLoneCyberman(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}{U}{B}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.CYBERMAN); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // The first nonlegendary artifact spell you cast each turn has casualty 2. + this.addAbility(new SimpleStaticAbility( + new GainAbilityControlledSpellsEffect( + new CasualtyAbility(2), + filter + ).setText(rule)), + new AshadTheLoneCybermanWatcher() + ); + + // Whenever you sacrifice another creature, put a +1/+1 counter on Ashad, the Lone Cyberman. + this.addAbility(new SacrificePermanentTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), + StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE + )); + } + + private AshadTheLoneCyberman(final AshadTheLoneCyberman card) { + super(card); + } + + @Override + public AshadTheLoneCyberman copy() { + return new AshadTheLoneCyberman(this); + } +} + +class AshadTheLoneCybermanWatcher extends Watcher { + + // Based on Peri Brown which is based on Conduit of Ruin + + private final Map nonlegendaryArtifactSpells; // player id -> number + + public AshadTheLoneCybermanWatcher() { + super(WatcherScope.GAME); + nonlegendaryArtifactSpells = new HashMap<>(); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.SPELL_CAST) { + Spell spell = (Spell) game.getObject(event.getTargetId()); + if (spell != null + && !spell.isLegendary(game) + && spell.isArtifact(game)) { + nonlegendaryArtifactSpells.put(event.getPlayerId(), nonlegendaryArtifactSpellsCastThisTurn(event.getPlayerId()) + 1); + } + } + } + + public int nonlegendaryArtifactSpellsCastThisTurn(UUID playerId) { + return nonlegendaryArtifactSpells.getOrDefault(playerId, 0); + } + + @Override + public void reset() { + super.reset(); + nonlegendaryArtifactSpells.clear(); + } +} + + +enum AshadTheLoneCybermanSpellPredicate implements ObjectSourcePlayerPredicate { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + if (input.getObject() != null + && !input.getObject().isLegendary(game) + && input.getObject().isArtifact(game)) { + AshadTheLoneCybermanWatcher watcher = game.getState().getWatcher(AshadTheLoneCybermanWatcher.class); + return watcher != null && watcher.nonlegendaryArtifactSpellsCastThisTurn(input.getPlayerId()) == 0; + } + return false; + } + + @Override + public String toString() { + return "The first nonlegendary artifact spell you cast each turn"; + } +} diff --git a/Mage.Sets/src/mage/cards/a/AshePrincessOfDalmasca.java b/Mage.Sets/src/mage/cards/a/AshePrincessOfDalmasca.java new file mode 100644 index 00000000000..af7e69dca6b --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AshePrincessOfDalmasca.java @@ -0,0 +1,46 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.PutCards; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AshePrincessOfDalmasca extends CardImpl { + + public AshePrincessOfDalmasca(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.REBEL); + this.subtype.add(SubType.NOBLE); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Whenever Ashe attacks, look at the top five cards of your library. You may reveal an artifact card from among them and put it into your hand. Put the rest on the bottom of your library in a random order. + this.addAbility(new AttacksTriggeredAbility(new LookLibraryAndPickControllerEffect( + 5, 1, StaticFilters.FILTER_CARD_ARTIFACT_AN, + PutCards.HAND, PutCards.BOTTOM_RANDOM + ))); + } + + private AshePrincessOfDalmasca(final AshePrincessOfDalmasca card) { + super(card); + } + + @Override + public AshePrincessOfDalmasca copy() { + return new AshePrincessOfDalmasca(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AshiokNightmareWeaver.java b/Mage.Sets/src/mage/cards/a/AshiokNightmareWeaver.java index d23eb46706a..5f218c29fc8 100644 --- a/Mage.Sets/src/mage/cards/a/AshiokNightmareWeaver.java +++ b/Mage.Sets/src/mage/cards/a/AshiokNightmareWeaver.java @@ -125,7 +125,7 @@ class AshiokNightmareWeaverPutIntoPlayEffect extends OneShotEffect { return true; } controller.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { game.addEffect(new AddCardSubTypeTargetEffect( SubType.NIGHTMARE, Duration.EndOfTurn diff --git a/Mage.Sets/src/mage/cards/a/AshlingTheExtinguisher.java b/Mage.Sets/src/mage/cards/a/AshlingTheExtinguisher.java index 87c9ed6ce85..3b110555383 100644 --- a/Mage.Sets/src/mage/cards/a/AshlingTheExtinguisher.java +++ b/Mage.Sets/src/mage/cards/a/AshlingTheExtinguisher.java @@ -11,7 +11,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -32,7 +32,7 @@ public final class AshlingTheExtinguisher extends CardImpl { Effect effect = new SacrificeTargetEffect().setText("choose target creature that player controls. The player sacrifices that creature"); Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(effect, false, true); ability.addTarget(new TargetCreaturePermanent()); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/AspiringChampion.java b/Mage.Sets/src/mage/cards/a/AspiringChampion.java index efe48abeebf..69a845b6374 100644 --- a/Mage.Sets/src/mage/cards/a/AspiringChampion.java +++ b/Mage.Sets/src/mage/cards/a/AspiringChampion.java @@ -13,6 +13,7 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import mage.util.CardUtil; import java.util.UUID; @@ -81,7 +82,8 @@ class AspiringChampionEffect extends OneShotEffect { toReveal.retainZone(Zone.LIBRARY, game); player.putCardsOnBottomOfLibrary(toReveal, game, source, false); player.shuffleLibrary(source, game); - Permanent creature = game.getPermanent(card.getId()); + game.processAction(); + Permanent creature = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (creature == null || !creature.hasSubtype(SubType.DEMON, game) || creature.getPower().getValue() < 1) { diff --git a/Mage.Sets/src/mage/cards/a/AstrologiansPlanisphere.java b/Mage.Sets/src/mage/cards/a/AstrologiansPlanisphere.java new file mode 100644 index 00000000000..f0795a9e38c --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AstrologiansPlanisphere.java @@ -0,0 +1,62 @@ +package mage.cards.a; + +import mage.abilities.Ability; +import mage.abilities.common.DrawNthCardTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.continuous.AddCardSubtypeAttachedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.EquipAbility; +import mage.abilities.keyword.JobSelectAbility; +import mage.abilities.meta.OrTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AstrologiansPlanisphere extends CardImpl { + + public AstrologiansPlanisphere(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{U}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Job select + this.addAbility(new JobSelectAbility()); + + // Equipped creature is a Wizard in addition to its other types and has "Whenever you cast a noncreature spell and whenever you draw your third card each turn, put a +1/+1 counter on this creature." + Ability ability = new SimpleStaticAbility( + new AddCardSubtypeAttachedEffect(SubType.WIZARD, AttachmentType.EQUIPMENT) + .setText("equipped creature is a Wizard in addition to its other types") + ); + ability.addEffect(new GainAbilityAttachedEffect(new OrTriggeredAbility( + Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance()), + new SpellCastControllerTriggeredAbility(null, StaticFilters.FILTER_SPELL_A_NON_CREATURE, false), + new DrawNthCardTriggeredAbility(null, false, 3) + ), AttachmentType.EQUIPMENT).setText("and has \"Whenever you cast a noncreature spell " + + "and whenever you draw your third card each turn, put a +1/+1 counter on this creature.\"")); + this.addAbility(ability); + + // Diana -- Equip {2} + this.addAbility(new EquipAbility(2).withFlavorWord("Diana")); + } + + private AstrologiansPlanisphere(final AstrologiansPlanisphere card) { + super(card); + } + + @Override + public AstrologiansPlanisphere copy() { + return new AstrologiansPlanisphere(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AsylumVisitor.java b/Mage.Sets/src/mage/cards/a/AsylumVisitor.java index 558a2ecc4bf..3d01de1043e 100644 --- a/Mage.Sets/src/mage/cards/a/AsylumVisitor.java +++ b/Mage.Sets/src/mage/cards/a/AsylumVisitor.java @@ -2,14 +2,13 @@ package mage.cards.a; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.CardsInHandCondition; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.LoseLifeSourceControllerEffect; import mage.abilities.keyword.MadnessAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -24,6 +23,8 @@ import java.util.UUID; */ public final class AsylumVisitor extends CardImpl { + private static final Condition condition = new CardsInHandCondition(ComparisonType.EQUAL_TO, 0, TargetController.ACTIVE); + public AsylumVisitor(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); this.subtype.add(SubType.VAMPIRE); @@ -32,12 +33,10 @@ public final class AsylumVisitor extends CardImpl { this.toughness = new MageInt(1); // At the beginning of each player's upkeep, if that player has no cards in hand, you draw a card and you lose 1 life. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility(TargetController.ANY, new DrawCardSourceControllerEffect(1, true), false), - new CardsInHandCondition(ComparisonType.EQUAL_TO, 0, TargetController.ACTIVE), - "At the beginning of each player's upkeep, if that player has no cards in hand, you draw a card and you lose 1 life."); - Effect effect = new LoseLifeSourceControllerEffect(1); - ability.addEffect(effect); + Ability ability = new BeginningOfUpkeepTriggeredAbility( + TargetController.ANY, new DrawCardSourceControllerEffect(1, true), false + ).withInterveningIf(condition); + ability.addEffect(new LoseLifeSourceControllerEffect(1).concatBy("and")); this.addAbility(ability); // Madness {1}{B} diff --git a/Mage.Sets/src/mage/cards/a/AttendantOfVraska.java b/Mage.Sets/src/mage/cards/a/AttendantOfVraska.java index d4f6dd732c7..fd25f2b0cc8 100644 --- a/Mage.Sets/src/mage/cards/a/AttendantOfVraska.java +++ b/Mage.Sets/src/mage/cards/a/AttendantOfVraska.java @@ -2,32 +2,26 @@ package mage.cards.a; import mage.MageInt; import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.dynamicvalue.common.SourcePermanentPowerValue; import mage.abilities.effects.common.GainLifeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPlaneswalkerPermanent; import java.util.UUID; /** - * * @author TheElk801 */ public final class AttendantOfVraska extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("a Vraska planeswalker"); - - static { - filter.add(TargetController.YOU.getControllerPredicate()); - filter.add(CardType.PLANESWALKER.getPredicate()); - filter.add(SubType.VRASKA.getPredicate()); - } + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + new FilterControlledPlaneswalkerPermanent(SubType.VRASKA, "you control a Vraska planeswalker") + ); public AttendantOfVraska(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{G}"); @@ -38,13 +32,8 @@ public final class AttendantOfVraska extends CardImpl { this.toughness = new MageInt(3); // When Attendant of Vraska dies, if you control a Vraska planeswalker, you gain life equal to Attendant of Vraska's power. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new DiesSourceTriggeredAbility(new GainLifeEffect( - SourcePermanentPowerValue.NOT_NEGATIVE - ), false), new PermanentsOnTheBattlefieldCondition(filter), - "When {this} dies, if you control a Vraska planeswalker, " - + "you gain life equal to {this}'s power." - )); + this.addAbility(new DiesSourceTriggeredAbility(new GainLifeEffect(SourcePermanentPowerValue.NOT_NEGATIVE) + .setText("you gain life equal to {this}'s power"), false).withInterveningIf(condition)); } private AttendantOfVraska(final AttendantOfVraska card) { diff --git a/Mage.Sets/src/mage/cards/a/AuraFinesse.java b/Mage.Sets/src/mage/cards/a/AuraFinesse.java index d816f5d42c9..ce531099727 100644 --- a/Mage.Sets/src/mage/cards/a/AuraFinesse.java +++ b/Mage.Sets/src/mage/cards/a/AuraFinesse.java @@ -1,42 +1,30 @@ - package mage.cards.a; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachTargetToTargetEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterEnchantmentPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.Target; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author North */ public final class AuraFinesse extends CardImpl { - private static final FilterEnchantmentPermanent filter = new FilterEnchantmentPermanent("Aura you control"); - - static { - filter.add(TargetController.YOU.getControllerPredicate()); - filter.add(SubType.AURA.getPredicate()); - } + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.AURA); public AuraFinesse(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}"); // Attach target Aura you control to target creature. - this.getSpellAbility().addEffect(new AuraFinesseEffect()); + this.getSpellAbility().addEffect(new AttachTargetToTargetEffect()); this.getSpellAbility().addTarget(new TargetPermanent(filter)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); @@ -53,47 +41,3 @@ public final class AuraFinesse extends CardImpl { return new AuraFinesse(this); } } - -class AuraFinesseEffect extends OneShotEffect { - - AuraFinesseEffect() { - super(Outcome.BoostCreature); - this.staticText = "Attach target Aura you control to target creature"; - } - - private AuraFinesseEffect(final AuraFinesseEffect effect) { - super(effect); - } - - @Override - public AuraFinesseEffect copy() { - return new AuraFinesseEffect(this); - } - - // 15/06/2010 As Aura Finesse resolves, if either target is illegal, - // the spell resolves but the Aura doesn’t move. You still draw a card. - // If both targets are illegal, Aura Finesse doesn’t resolve and you don’t draw a card. - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { - return false; - } - - Permanent aura = game.getPermanent(source.getFirstTarget()); - Permanent creature = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if (aura != null && creature != null) { - Permanent oldCreature = game.getPermanent(aura.getAttachedTo()); - if (oldCreature != null && !oldCreature.equals(creature)) { - Target auraTarget = aura.getSpellAbility().getTargets().get(0); - if (!auraTarget.canTarget(creature.getId(), game)) { - game.informPlayers(aura.getLogName() + " was not attched to " +creature.getLogName() + " because it's no legal target for the aura" ); - } else if (oldCreature.removeAttachment(aura.getId(), source, game)) { - game.informPlayers(aura.getLogName() + " was unattached from " + oldCreature.getLogName() + " and attached to " + creature.getLogName()); - creature.addAttachment(aura.getId(), source, game); - } - } - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/a/AuramancersGuise.java b/Mage.Sets/src/mage/cards/a/AuramancersGuise.java index a63e6660004..c2f037a973d 100644 --- a/Mage.Sets/src/mage/cards/a/AuramancersGuise.java +++ b/Mage.Sets/src/mage/cards/a/AuramancersGuise.java @@ -1,4 +1,3 @@ - package mage.cards.a; import mage.abilities.Ability; @@ -18,11 +17,11 @@ import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; -import java.util.List; +import java.util.Objects; +import java.util.Optional; import java.util.UUID; /** - * * @author spjspj */ public final class AuramancersGuise extends CardImpl { @@ -36,16 +35,16 @@ public final class AuramancersGuise extends CardImpl { TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget); - this.addAbility(ability); + this.addAbility(new EnchantAbility(auraTarget)); // Enchanted creature gets +2/+2 for each Aura attached to it and has vigilance. - DynamicValue ptBoost = new EnchantedCreatureAurasCount(); - BoostEnchantedEffect effect = new BoostEnchantedEffect(ptBoost, ptBoost, Duration.WhileOnBattlefield); - effect.setText("Enchanted creature gets +2/+2 for each Aura attached to it"); - SimpleStaticAbility ability2 = new SimpleStaticAbility(effect); - ability2.addEffect(new GainAbilityAttachedEffect(VigilanceAbility.getInstance(), AttachmentType.AURA).setText("and has vigilance")); - this.addAbility(ability2); + Ability ability = new SimpleStaticAbility(new BoostEnchantedEffect( + AuramancersGuiseValue.instance, AuramancersGuiseValue.instance, Duration.WhileOnBattlefield + )); + ability.addEffect(new GainAbilityAttachedEffect( + VigilanceAbility.getInstance(), AttachmentType.AURA + ).setText("and has vigilance")); + this.addAbility(ability); } private AuramancersGuise(final AuramancersGuise card) { @@ -58,48 +57,40 @@ public final class AuramancersGuise extends CardImpl { } } -class EnchantedCreatureAurasCount implements DynamicValue { - - public EnchantedCreatureAurasCount() { - } - - private EnchantedCreatureAurasCount(final EnchantedCreatureAurasCount dynamicValue) { - } +enum AuramancersGuiseValue implements DynamicValue { + instance; @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { - int count = 0; - Permanent aura = game.getPermanent(sourceAbility.getSourceId()); - if (aura != null) { - Permanent permanent = game.getPermanent(aura.getAttachedTo()); - if (permanent != null) { - List attachments = permanent.getAttachments(); - for (UUID attachmentId : attachments) { - Permanent attached = game.getPermanent(attachmentId); - if (attached != null && attached.hasSubtype(SubType.AURA, game)) { - count++; - } - - } - return 2 * count; - } - } - return count; + Permanent permanent = Optional + .ofNullable(sourceAbility.getSourcePermanentIfItStillExists(game)) + .map(Permanent::getAttachedTo) + .map(game::getPermanent) + .orElse(null); + return permanent != null + ? 2 * permanent + .getAttachments() + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .filter(p -> p.hasSubtype(SubType.AURA, game)) + .mapToInt(x -> 1) + .sum() + : 0; } @Override - public EnchantedCreatureAurasCount copy() { - return new EnchantedCreatureAurasCount(this); - } - - @Override - public String toString() { - return "1"; + public AuramancersGuiseValue copy() { + return this; } @Override public String getMessage() { - return "of its auras"; + return "for each Aura attached to it"; } + @Override + public String toString() { + return "2"; + } } diff --git a/Mage.Sets/src/mage/cards/a/AuriokWindwalker.java b/Mage.Sets/src/mage/cards/a/AuriokWindwalker.java index 1af05214e72..84273371e0c 100644 --- a/Mage.Sets/src/mage/cards/a/AuriokWindwalker.java +++ b/Mage.Sets/src/mage/cards/a/AuriokWindwalker.java @@ -1,39 +1,28 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachTargetToTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterControlledPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; -import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; /** - * * @author Plopman */ public final class AuriokWindwalker extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("Equipment you control"); - - static { - filter.add(SubType.EQUIPMENT.getPredicate()); - } - public AuriokWindwalker(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WIZARD); this.power = new MageInt(2); @@ -41,9 +30,10 @@ public final class AuriokWindwalker extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); + // {T}: Attach target Equipment you control to target creature you control. - Ability ability = new SimpleActivatedAbility(new AttachTargetEquipmentEffect(), new TapSourceCost()); - ability.addTarget(new TargetControlledPermanent(filter)); + Ability ability = new SimpleActivatedAbility(new AttachTargetToTargetEffect(), new TapSourceCost()); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT)); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); } @@ -57,30 +47,3 @@ public final class AuriokWindwalker extends CardImpl { return new AuriokWindwalker(this); } } - -class AttachTargetEquipmentEffect extends OneShotEffect { - - AttachTargetEquipmentEffect() { - super(Outcome.BoostCreature); - staticText = "Attach target Equipment you control to target creature you control"; - } - - private AttachTargetEquipmentEffect(final AttachTargetEquipmentEffect effect) { - super(effect); - } - - @Override - public AttachTargetEquipmentEffect copy() { - return new AttachTargetEquipmentEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent equipment = game.getPermanent(source.getFirstTarget()); - Permanent creature = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if (creature != null && equipment != null) { - return creature.addAttachment(equipment.getId(), source, game); - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/a/AuroraChampion.java b/Mage.Sets/src/mage/cards/a/AuroraChampion.java index c21eefb425c..d712378e719 100644 --- a/Mage.Sets/src/mage/cards/a/AuroraChampion.java +++ b/Mage.Sets/src/mage/cards/a/AuroraChampion.java @@ -1,33 +1,35 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.TapTargetEffect; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; import mage.filter.common.FilterTeamPermanent; import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class AuroraChampion extends CardImpl { - private static final FilterTeamPermanent filter = new FilterTeamPermanent(SubType.WARRIOR, "another Warrior"); + private static final FilterPermanent filter = new FilterTeamPermanent(SubType.WARRIOR, "your team controls another Warrior"); static { filter.add(AnotherPredicate.instance); } + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter, false); + public AuroraChampion(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); @@ -37,11 +39,7 @@ public final class AuroraChampion extends CardImpl { this.toughness = new MageInt(2); // Whenever Aurora Champion attacks, if your team controls another Warrior, tap target creature. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new AttacksTriggeredAbility(new TapTargetEffect(), false), - new PermanentsOnTheBattlefieldCondition(filter), - "Whenever {this} attacks, if your team controls another Warrior, tap target creature." - ); + Ability ability = new AttacksTriggeredAbility(new TapTargetEffect(), false).withInterveningIf(condition); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/AwakenTheAncient.java b/Mage.Sets/src/mage/cards/a/AwakenTheAncient.java index e4ffb5e9d66..92c4091e8f0 100644 --- a/Mage.Sets/src/mage/cards/a/AwakenTheAncient.java +++ b/Mage.Sets/src/mage/cards/a/AwakenTheAncient.java @@ -1,9 +1,6 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.BecomesCreatureAttachedEffect; @@ -11,37 +8,38 @@ import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.common.FilterLandPermanent; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterPermanent; import mage.game.permanent.token.TokenImpl; import mage.target.TargetPermanent; -import mage.target.common.TargetLandPermanent; + +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class AwakenTheAncient extends CardImpl { - private static final FilterLandPermanent filter = new FilterLandPermanent(SubType.MOUNTAIN, "Mountain"); + private static final FilterPermanent filter = new FilterPermanent(SubType.MOUNTAIN, "Mountain"); public AwakenTheAncient(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{R}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{R}{R}{R}"); this.subtype.add(SubType.AURA); - // Enchant Mountain - TargetPermanent auraTarget = new TargetLandPermanent(filter); + TargetPermanent auraTarget = new TargetPermanent(filter); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.PutCreatureInPlay)); - Ability ability = new EnchantAbility(auraTarget); - this.addAbility(ability); + this.addAbility(new EnchantAbility(auraTarget)); // Enchanted Mountain is a 7/7 red Giant creature with haste. It's still a land. - Ability ability2 = new SimpleStaticAbility(new BecomesCreatureAttachedEffect( - new GiantToken(), "Enchanted Mountain is a 7/7 red Giant creature with haste. It's still a land", Duration.WhileOnBattlefield, BecomesCreatureAttachedEffect.LoseType.COLOR)); - this.addAbility(ability2); - + this.addAbility(new SimpleStaticAbility(new BecomesCreatureAttachedEffect( + new GiantToken(), "Enchanted Mountain is a 7/7 red Giant creature with haste. It's still a land", + Duration.WhileOnBattlefield, BecomesCreatureAttachedEffect.LoseType.COLOR + ))); } private AwakenTheAncient(final AwakenTheAncient card) { diff --git a/Mage.Sets/src/mage/cards/a/AwakenerDruid.java b/Mage.Sets/src/mage/cards/a/AwakenerDruid.java index f596463698e..3fbe8362f5f 100644 --- a/Mage.Sets/src/mage/cards/a/AwakenerDruid.java +++ b/Mage.Sets/src/mage/cards/a/AwakenerDruid.java @@ -1,32 +1,29 @@ - - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.Mode; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterLandPermanent; +import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.TokenImpl; -import mage.target.common.TargetLandPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author BetaSteward_at_googlemail.com */ public final class AwakenerDruid extends CardImpl { - private static final FilterLandPermanent filter = new FilterLandPermanent(SubType.FOREST, "Forest"); + private static final FilterPermanent filter = new FilterPermanent(SubType.FOREST, "Forest"); public AwakenerDruid(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.DRUID); @@ -35,7 +32,7 @@ public final class AwakenerDruid extends CardImpl { // When Awakener Druid enters the battlefield, target Forest becomes a 4/5 green Treefolk creature for as long as Awakener Druid remains on the battlefield. It's still a land. Ability ability = new EntersBattlefieldTriggeredAbility(new AwakenerDruidBecomesCreatureEffect(), false); - ability.addTarget(new TargetLandPermanent(filter)); + ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); } @@ -87,6 +84,7 @@ class AwakenerDruidToken extends TokenImpl { power = new MageInt(4); toughness = new MageInt(5); } + private AwakenerDruidToken(final AwakenerDruidToken token) { super(token); } diff --git a/Mage.Sets/src/mage/cards/a/AyaraFurnaceQueen.java b/Mage.Sets/src/mage/cards/a/AyaraFurnaceQueen.java index d8451ecda69..686133de413 100644 --- a/Mage.Sets/src/mage/cards/a/AyaraFurnaceQueen.java +++ b/Mage.Sets/src/mage/cards/a/AyaraFurnaceQueen.java @@ -15,9 +15,11 @@ import mage.constants.*; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -90,17 +92,18 @@ class AyaraFurnaceQueenEffect extends OneShotEffect { if (player == null || card == null) { return false; } - player.moveCards(card, Zone.BATTLEFIELD, source, game); - if (game.getPermanent(card.getId()) == null) { + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); + if (permanent == null) { return false; } game.addEffect(new GainAbilityTargetEffect( HasteAbility.getInstance(), Duration.Custom - ).setTargetPointer(new FixedTarget(card.getId(), game)), source); + ).setTargetPointer(new FixedTarget(permanent.getId(), game)), source); game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility( new ExileTargetEffect().setText("exile it") - .setTargetPointer(new FixedTarget(card.getId(), game)), + .setTargetPointer(new FixedTarget(permanent.getId(), game)), TargetController.ANY ), source); - return true;} + return true; + } } diff --git a/Mage.Sets/src/mage/cards/a/AyarasOathsworn.java b/Mage.Sets/src/mage/cards/a/AyarasOathsworn.java index 25c7360f97b..28783a8319b 100644 --- a/Mage.Sets/src/mage/cards/a/AyarasOathsworn.java +++ b/Mage.Sets/src/mage/cards/a/AyarasOathsworn.java @@ -5,7 +5,6 @@ import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; @@ -13,6 +12,7 @@ import mage.abilities.keyword.MenaceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.SubType; import mage.counters.CounterType; import mage.target.common.TargetCardInLibrary; @@ -24,8 +24,8 @@ import java.util.UUID; */ public final class AyarasOathsworn extends CardImpl { - private static final Condition condition1 = new SourceHasCounterCondition(CounterType.P1P1, 0, 3); - private static final Condition condition2 = new SourceHasCounterCondition(CounterType.P1P1, 4, 4); + private static final Condition condition1 = new SourceHasCounterCondition(CounterType.P1P1, ComparisonType.FEWER_THAN, 4); + private static final Condition condition2 = new SourceHasCounterCondition(CounterType.P1P1, ComparisonType.EQUAL_TO, 4); public AyarasOathsworn(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); @@ -39,15 +39,14 @@ public final class AyarasOathsworn extends CardImpl { this.addAbility(new MenaceAbility(false)); // Whenever Ayara's Oathsworn deals combat damage to a player, if it has fewer than four +1/+1 counters on it, put a +1/+1 counter on it. Then if it has exactly four +1/+1 counters on it, search your library for a card, put it into your hand, then shuffle. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new DealsCombatDamageToAPlayerTriggeredAbility( - new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false - ), condition1, "Whenever {this} deals combat damage to a player, if it has fewer than four " + - "+1/+1 counters on it, put a +1/+1 counter on it. Then if it has exactly four +1/+1 counters on it, " + - "search your library for a card, put it into your hand, then shuffle." - ); + Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()) + .setText("put a +1/+1 counter on it"), false + ).withInterveningIf(condition1); ability.addEffect(new ConditionalOneShotEffect( - new SearchLibraryPutInHandEffect(new TargetCardInLibrary(), false), condition2 + new SearchLibraryPutInHandEffect(new TargetCardInLibrary(), false), + condition2, "Then if it has exactly four +1/+1 counters on it, " + + "search your library for a card, put it into your hand, then shuffle" )); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/AzorsElocutors.java b/Mage.Sets/src/mage/cards/a/AzorsElocutors.java index d1ab1470252..633d1c6f776 100644 --- a/Mage.Sets/src/mage/cards/a/AzorsElocutors.java +++ b/Mage.Sets/src/mage/cards/a/AzorsElocutors.java @@ -1,28 +1,33 @@ - package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.SourceHasCounterCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.WinGameSourceControllerEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class AzorsElocutors extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.FILIBUSTER, 5); + public AzorsElocutors(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W/U}{W/U}"); this.subtype.add(SubType.HUMAN); @@ -32,7 +37,13 @@ public final class AzorsElocutors extends CardImpl { this.toughness = new MageInt(5); // At the beginning of your upkeep, put a filibuster counter on Azor's Elocutors. Then if Azor's Elocutors has five or more filibuster counters on it, you win the game. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new AzorsElocutorsEffect())); + Ability ability = new BeginningOfUpkeepTriggeredAbility( + new AddCountersSourceEffect(CounterType.FILIBUSTER.createInstance()) + ); + ability.addEffect(new ConditionalOneShotEffect( + new WinGameSourceControllerEffect(), condition, + "Then if {this} has five or more filibuster counters on it, you win the game" + )); // Whenever a source deals damage to you, remove a filibuster counter from Azor's Elocutors. this.addAbility(new AzorsElocutorsTriggeredAbility()); @@ -71,39 +82,6 @@ class AzorsElocutorsTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - return event.getTargetId().equals(this.controllerId); - } -} - -class AzorsElocutorsEffect extends OneShotEffect { - - AzorsElocutorsEffect() { - super(Outcome.Benefit); - staticText = "put a filibuster counter on Azor's Elocutors. Then if Azor's Elocutors has five or more filibuster counters on it, you win the game"; - } - - private AzorsElocutorsEffect(final AzorsElocutorsEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - permanent.addCounters(CounterType.FILIBUSTER.createInstance(), source.getControllerId(), source, game); - if (permanent.getCounters(game).getCount(CounterType.FILIBUSTER) > 4) { - Player player = game.getPlayer(permanent.getControllerId()); - if (player != null) { - player.won(game); - } - } - return true; - } - return false; - } - - @Override - public AzorsElocutorsEffect copy() { - return new AzorsElocutorsEffect(this); + return isControlledBy(event.getTargetId()); } } diff --git a/Mage.Sets/src/mage/cards/b/BackForMore.java b/Mage.Sets/src/mage/cards/b/BackForMore.java index 6d2ee7840d9..36c549a9b10 100644 --- a/Mage.Sets/src/mage/cards/b/BackForMore.java +++ b/Mage.Sets/src/mage/cards/b/BackForMore.java @@ -19,6 +19,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCardInYourGraveyard; +import mage.util.CardUtil; import java.util.UUID; @@ -79,7 +80,7 @@ class BackForMoreEffect extends OneShotEffect { return false; } player.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/b/BahamutWardenOfLight.java b/Mage.Sets/src/mage/cards/b/BahamutWardenOfLight.java new file mode 100644 index 00000000000..ccd8cd3eb2a --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BahamutWardenOfLight.java @@ -0,0 +1,71 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.ExileSourceAndReturnFaceUpEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BahamutWardenOfLight extends CardImpl { + + public BahamutWardenOfLight(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SAGA); + this.subtype.add(SubType.DRAGON); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + this.nightCard = true; + this.color.setWhite(true); + + // (As this Saga enters and after your draw step, add a lore counter.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I, II -- Wings of Light -- Put a +1/+1 counter on each other creature you control. Those creatures gain flying until end of turn. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, ability -> { + ability.addEffect(new AddCountersAllEffect( + CounterType.P1P1.createInstance(), StaticFilters.FILTER_OTHER_CONTROLLED_CREATURE + )); + ability.addEffect(new GainAbilityControlledEffect( + FlyingAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_CONTROLLED_CREATURE + ).setText("Those creatures gain flying until end of turn")); + ability.withFlavorWord("Wings of Light"); + }); + + // III -- Gigaflare -- Destroy target permanent. Exile Bahamut, then return it to the battlefield. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, ability -> { + ability.addEffect(new DestroyTargetEffect()); + ability.addEffect(new ExileSourceAndReturnFaceUpEffect()); + ability.addTarget(new TargetPermanent()); + ability.withFlavorWord("Gigaflare"); + }); + this.addAbility(sagaAbility); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + } + + private BahamutWardenOfLight(final BahamutWardenOfLight card) { + super(card); + } + + @Override + public BahamutWardenOfLight copy() { + return new BahamutWardenOfLight(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BakuAltar.java b/Mage.Sets/src/mage/cards/b/BakuAltar.java index 5ef5bead80f..d74b22c6999 100644 --- a/Mage.Sets/src/mage/cards/b/BakuAltar.java +++ b/Mage.Sets/src/mage/cards/b/BakuAltar.java @@ -13,7 +13,6 @@ import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.game.permanent.token.SpiritToken; @@ -27,7 +26,7 @@ public final class BakuAltar extends CardImpl { public BakuAltar(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Baku Altar. - this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance(1)), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance(1)), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, true)); // {2}, {tap}, Remove a ki counter from Baku Altar: Create a 1/1 colorless Spirit creature token. Ability ability = new SimpleActivatedAbility(new CreateTokenEffect(new SpiritToken(), 1), new GenericManaCost(2)); ability.addCost(new TapSourceCost()); diff --git a/Mage.Sets/src/mage/cards/b/BalambGardenAirborne.java b/Mage.Sets/src/mage/cards/b/BalambGardenAirborne.java new file mode 100644 index 00000000000..7b34006109d --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BalambGardenAirborne.java @@ -0,0 +1,48 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.CrewAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BalambGardenAirborne extends CardImpl { + + public BalambGardenAirborne(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, ""); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(5); + this.toughness = new MageInt(4); + this.nightCard = true; + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever Balamb Garden attacks, draw a card. + this.addAbility(new AttacksTriggeredAbility(new DrawCardSourceControllerEffect(1))); + + // Crew 1 + this.addAbility(new CrewAbility(1)); + } + + private BalambGardenAirborne(final BalambGardenAirborne card) { + super(card); + } + + @Override + public BalambGardenAirborne copy() { + return new BalambGardenAirborne(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BalambGardenSeeDAcademy.java b/Mage.Sets/src/mage/cards/b/BalambGardenSeeDAcademy.java new file mode 100644 index 00000000000..723330f7b88 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BalambGardenSeeDAcademy.java @@ -0,0 +1,88 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.CostAdjuster; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.TransformAbility; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.game.Game; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BalambGardenSeeDAcademy extends CardImpl { + + public BalambGardenSeeDAcademy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.subtype.add(SubType.TOWN); + this.secondSideCardClazz = mage.cards.b.BalambGardenAirborne.class; + + // This land enters tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // Add {G} or {U}. + this.addAbility(new GreenManaAbility()); + this.addAbility(new BlueManaAbility()); + + // {5}{G}{U}, {T}: Transform this land. This ability costs {1} less to activate for each other Town you control. + this.addAbility(new TransformAbility()); + Ability ability = new SimpleActivatedAbility(new TransformSourceEffect(), new ManaCostsImpl<>("{5}{G}{U}")); + ability.addCost(new TapSourceCost()); + ability.addEffect(new InfoEffect("This ability costs {1} less to activate for each other Town you control")); + this.addAbility(ability + .setCostAdjuster(BalambGardenSeeDAcademyAdjuster.instance) + .addHint(BalambGardenSeeDAcademyAdjuster.getHint())); + } + + private BalambGardenSeeDAcademy(final BalambGardenSeeDAcademy card) { + super(card); + } + + @Override + public BalambGardenSeeDAcademy copy() { + return new BalambGardenSeeDAcademy(this); + } +} + +enum BalambGardenSeeDAcademyAdjuster implements CostAdjuster { + instance; + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.TOWN); + + static { + filter.add(AnotherPredicate.instance); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + private static final Hint hint = new ValueHint("Other Towns you control", xValue); + + static Hint getHint() { + return hint; + } + + @Override + public void reduceCost(Ability ability, Game game) { + int count = xValue.calculate(game, ability, null); + CardUtil.reduceCost(ability, count); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BalambTRexaur.java b/Mage.Sets/src/mage/cards/b/BalambTRexaur.java new file mode 100644 index 00000000000..a084698e396 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BalambTRexaur.java @@ -0,0 +1,46 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.keyword.ForestcyclingAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BalambTRexaur extends CardImpl { + + public BalambTRexaur(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}"); + + this.subtype.add(SubType.DINOSAUR); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // When this creature enters, you gain 3 life. + this.addAbility(new EntersBattlefieldTriggeredAbility(new GainLifeEffect(3))); + + // Forestcycling {2} + this.addAbility(new ForestcyclingAbility(new ManaCostsImpl<>("{2}"))); + } + + private BalambTRexaur(final BalambTRexaur card) { + super(card); + } + + @Override + public BalambTRexaur copy() { + return new BalambTRexaur(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BalanWanderingKnight.java b/Mage.Sets/src/mage/cards/b/BalanWanderingKnight.java index ce4ee20318c..a2eb572b840 100644 --- a/Mage.Sets/src/mage/cards/b/BalanWanderingKnight.java +++ b/Mage.Sets/src/mage/cards/b/BalanWanderingKnight.java @@ -4,7 +4,8 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.common.EquippedMultipleSourceCondition; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.OneShotEffect; @@ -15,6 +16,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.predicate.permanent.AttachedToSourcePredicate; import mage.game.Game; import mage.game.permanent.Permanent; @@ -25,7 +28,14 @@ import java.util.UUID; */ public final class BalanWanderingKnight extends CardImpl { - private static final String rule = "{this} has double strike as long as two or more Equipment are attached to it."; + private static final FilterPermanent filter = new FilterPermanent(SubType.EQUIPMENT, ""); + + static { + filter.add(AttachedToSourcePredicate.instance); + } + + private static final Condition condition + = new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 1, false); public BalanWanderingKnight(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}"); @@ -38,8 +48,10 @@ public final class BalanWanderingKnight extends CardImpl { this.addAbility(FirstStrikeAbility.getInstance()); // Balan, Wandering Knight has double strike as long as two or more Equipment are attached to it. - ConditionalContinuousEffect effect = new ConditionalContinuousEffect(new GainAbilitySourceEffect(DoubleStrikeAbility.getInstance()), EquippedMultipleSourceCondition.instance, rule); - this.addAbility(new SimpleStaticAbility(effect)); + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(DoubleStrikeAbility.getInstance()), condition, + "{this} has double strike as long as two or more Equipment are attached to it." + ))); // {1}{W}: Attach all Equipment you control to Balan. this.addAbility(new SimpleActivatedAbility(new BalanWanderingKnightEffect(), new ManaCostsImpl<>("{1}{W}"))); @@ -54,39 +66,35 @@ public final class BalanWanderingKnight extends CardImpl { return new BalanWanderingKnight(this); } - static class BalanWanderingKnightEffect extends OneShotEffect { +} - public BalanWanderingKnightEffect() { - super(Outcome.Benefit); - this.staticText = "Attach all Equipment you control to {this}."; - } +class BalanWanderingKnightEffect extends OneShotEffect { - private BalanWanderingKnightEffect(final BalanWanderingKnightEffect effect) { - super(effect); - } + BalanWanderingKnightEffect() { + super(Outcome.Benefit); + this.staticText = "attach all Equipment you control to {this}."; + } - @Override - public BalanWanderingKnightEffect copy() { - return new BalanWanderingKnightEffect(this); - } + private BalanWanderingKnightEffect(final BalanWanderingKnightEffect effect) { + super(effect); + } - @Override - public boolean apply(Game game, Ability source) { - Permanent balan = game.getPermanent(source.getSourceId()); - if (balan != null) { - FilterPermanent filter = new FilterPermanent(); - filter.add(SubType.EQUIPMENT.getPredicate()); - for (Permanent equipment : game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) { - if (equipment != null) { - //If an Equipment can't equip, it isn't attached, and it doesn't become unattached (if it's attached to a creature). - if (!balan.cantBeAttachedBy(equipment, source, game, false)) { - balan.addAttachment(equipment.getId(), source, game); - } - } - } - return true; - } + @Override + public BalanWanderingKnightEffect copy() { + return new BalanWanderingKnightEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent == null) { return false; } + for (Permanent equipment : game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT, source.getControllerId(), source, game + )) { + permanent.addAttachment(equipment.getId(), source, game); + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/b/BalduvianAtrocity.java b/Mage.Sets/src/mage/cards/b/BalduvianAtrocity.java index 581df06cbdd..1661d5b4ccd 100644 --- a/Mage.Sets/src/mage/cards/b/BalduvianAtrocity.java +++ b/Mage.Sets/src/mage/cards/b/BalduvianAtrocity.java @@ -1,22 +1,20 @@ package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.condition.common.KickedCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.SacrificeTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.HasteAbility; -import mage.cards.Card; -import mage.constants.*; import mage.abilities.keyword.KickerAbility; import mage.abilities.keyword.MenaceAbility; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.*; import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; @@ -24,9 +22,11 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author weirddan455 */ public final class BalduvianAtrocity extends CardImpl { @@ -52,11 +52,7 @@ public final class BalduvianAtrocity extends CardImpl { this.addAbility(new MenaceAbility(false)); // When Balduvian Atrocity enters the battlefield, if it was kicked, return target creature card with mana value 3 or less from your graveyard to the battlefield. It gains haste. Sacrifice it at the beginning of the next end step. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new BalduvianAtrocityEffect()), - KickedCondition.ONCE, - "When {this} enters, if it was kicked, return target creature card with mana value 3 or less from your graveyard to the battlefield. It gains haste. Sacrifice it at the beginning of the next end step." - ); + Ability ability = new EntersBattlefieldTriggeredAbility(new BalduvianAtrocityEffect()).withInterveningIf(KickedCondition.ONCE); ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); } @@ -75,7 +71,8 @@ class BalduvianAtrocityEffect extends OneShotEffect { BalduvianAtrocityEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "return target creature card with mana value 3 or less from your graveyard to the battlefield. It gains haste. Sacrifice it at the beginning of the next end step."; + this.staticText = "return target creature card with mana value 3 or less from your graveyard to the battlefield. " + + "It gains haste. Sacrifice it at the beginning of the next end step."; } private BalduvianAtrocityEffect(final BalduvianAtrocityEffect effect) { @@ -98,7 +95,7 @@ class BalduvianAtrocityEffect extends OneShotEffect { return false; } controller.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/b/BalladOfTheBlackFlag.java b/Mage.Sets/src/mage/cards/b/BalladOfTheBlackFlag.java index 8129e6229d3..a6a9df732da 100644 --- a/Mage.Sets/src/mage/cards/b/BalladOfTheBlackFlag.java +++ b/Mage.Sets/src/mage/cards/b/BalladOfTheBlackFlag.java @@ -9,7 +9,7 @@ import mage.constants.CardType; import mage.constants.SagaChapter; import mage.constants.SubType; import mage.filter.FilterCard; -import mage.filter.common.FilterHistoricCard; +import mage.filter.predicate.mageobject.HistoricPredicate; import java.util.UUID; @@ -18,7 +18,10 @@ import java.util.UUID; */ public final class BalladOfTheBlackFlag extends CardImpl { - private static final FilterCard filter = new FilterHistoricCard(); + private static final FilterCard filter = new FilterCard("historic card"); + static { + filter.add(HistoricPredicate.instance); + } public BalladOfTheBlackFlag(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}{U}"); diff --git a/Mage.Sets/src/mage/cards/b/BalthierAndFran.java b/Mage.Sets/src/mage/cards/b/BalthierAndFran.java new file mode 100644 index 00000000000..f3bb72fec8e --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BalthierAndFran.java @@ -0,0 +1,93 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksAllTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.FirstCombatPhaseCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.AdditionalCombatPhaseEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.ReachAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.watchers.common.CrewedVehicleWatcher; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BalthierAndFran extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.VEHICLE, "Vehicles"); + private static final FilterCreaturePermanent filter2 + = new FilterCreaturePermanent(SubType.VEHICLE, "a Vehicle crewed by {this} this turn"); + + static { + filter.add(BalthierAndFranPredicate.instance); + } + + public BalthierAndFran(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{G}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.RABBIT); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Vehicles you control get +1/+1 and have vigilance and reach. + Ability ability = new SimpleStaticAbility(new BoostControlledEffect( + 1, 1, Duration.WhileOnBattlefield, filter + )); + ability.addEffect(new GainAbilityControlledEffect( + VigilanceAbility.getInstance(), Duration.WhileOnBattlefield, filter + ).setText("and have vigilance")); + ability.addEffect(new GainAbilityControlledEffect( + ReachAbility.getInstance(), Duration.WhileOnBattlefield, filter + ).setText("and reach")); + this.addAbility(ability); + + // Whenever a Vehicle crewed by Balthier and Fran this turn attacks, if it's the first combat phase of the turn, you may pay {1}{R}{G}. If you do, after this phase, there is an additional combat phase. + this.addAbility(new AttacksAllTriggeredAbility( + new DoIfCostPaid(new AdditionalCombatPhaseEffect(), new ManaCostsImpl<>("{1}{R}{G}")), + false, filter2, SetTargetPointer.NONE, false + ).withInterveningIf(FirstCombatPhaseCondition.instance), new CrewedVehicleWatcher()); + } + + private BalthierAndFran(final BalthierAndFran card) { + super(card); + } + + @Override + public BalthierAndFran copy() { + return new BalthierAndFran(this); + } +} + +enum BalthierAndFranPredicate implements ObjectSourcePlayerPredicate { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + return Optional + .ofNullable(input.getSource().getSourcePermanentIfItStillExists(game)) + .map(permanent -> CrewedVehicleWatcher.checkIfCrewedThisTurn(permanent, input.getObject(), game)) + .orElse(false); + } +} diff --git a/Mage.Sets/src/mage/cards/b/Bamboozle.java b/Mage.Sets/src/mage/cards/b/Bamboozle.java index 96b9e5aadd5..b80905a772a 100644 --- a/Mage.Sets/src/mage/cards/b/Bamboozle.java +++ b/Mage.Sets/src/mage/cards/b/Bamboozle.java @@ -48,7 +48,7 @@ class BamboozleEffect extends OneShotEffect { BamboozleEffect() { super(Outcome.Discard); - staticText = "Target player reveals the top four cards of their library. You choose two of those cards and put them into their graveyard. Put the rest on top of their library in any order"; + staticText = "Target player reveals the top four cards of their library. You choose two of those cards and put them into that player's graveyard. Put the rest on top of their library in any order"; } private BamboozleEffect(final BamboozleEffect effect) { diff --git a/Mage.Sets/src/mage/cards/b/BanditsTalent.java b/Mage.Sets/src/mage/cards/b/BanditsTalent.java index f9e840fbc0c..3dce439ecf4 100644 --- a/Mage.Sets/src/mage/cards/b/BanditsTalent.java +++ b/Mage.Sets/src/mage/cards/b/BanditsTalent.java @@ -54,7 +54,7 @@ public final class BanditsTalent extends CardImpl { this.addAbility(new ClassLevelAbility(2, "{B}")); // At the beginning of each opponent's upkeep, if that player has one or fewer cards in hand, they lose 2 life. this.addAbility(new SimpleStaticAbility(new GainClassAbilitySourceEffect(new BeginningOfUpkeepTriggeredAbility( - TargetController.OPPONENT, new ConditionalOneShotEffect(new LoseLifeTargetEffect(2), new CardsInHandCondition(ComparisonType.OR_LESS, 1, TargetController.ACTIVE)), + TargetController.OPPONENT, new ConditionalOneShotEffect(new LoseLifeTargetEffect(2).setText("they lose 2 life"), new CardsInHandCondition(ComparisonType.OR_LESS, 1, TargetController.ACTIVE)), false), 2))); // {3}{B}: Level 3 diff --git a/Mage.Sets/src/mage/cards/b/BanishToAnotherUniverse.java b/Mage.Sets/src/mage/cards/b/BanishToAnotherUniverse.java index 50bfd5f4231..0b397fc34f0 100644 --- a/Mage.Sets/src/mage/cards/b/BanishToAnotherUniverse.java +++ b/Mage.Sets/src/mage/cards/b/BanishToAnotherUniverse.java @@ -2,19 +2,13 @@ package mage.cards.b; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.common.AffinityEffect; import mage.abilities.effects.common.ExileUntilSourceLeavesEffect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.AffinityAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AffinityType; import mage.constants.CardType; -import mage.constants.Zone; import mage.filter.StaticFilters; -import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.mageobject.HistoricPredicate; import mage.target.TargetPermanent; import java.util.UUID; @@ -24,19 +18,11 @@ import java.util.UUID; */ public final class BanishToAnotherUniverse extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("historic permanents"); - private static final Hint hint = new ValueHint("historic permanents you control", new PermanentsOnBattlefieldCount(filter)); - - static { - filter.add(HistoricPredicate.instance); - } - public BanishToAnotherUniverse(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{W}"); // Affinity for historic permanents - this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filter)) - .addHint(hint)); + this.addAbility(new AffinityAbility(AffinityType.HISTORIC)); // When Banish to Another Universe enters the battlefield, exile target nonland permanent an opponent controls until Banish to Another Universe leaves the battlefield. Ability ability = new EntersBattlefieldTriggeredAbility(new ExileUntilSourceLeavesEffect()); diff --git a/Mage.Sets/src/mage/cards/b/BanonTheReturnersLeader.java b/Mage.Sets/src/mage/cards/b/BanonTheReturnersLeader.java new file mode 100644 index 00000000000..81f8236d50e --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BanonTheReturnersLeader.java @@ -0,0 +1,79 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; +import mage.abilities.common.CastFromGraveyardOnceDuringEachOfYourTurnAbility; +import mage.abilities.costs.CompositeCost; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.watchers.common.CardsPutIntoGraveyardWatcher; + +import java.util.UUID; + +/** + * @author balazskristof + */ +public final class BanonTheReturnersLeader extends CardImpl { + + private static final FilterCreatureCard filter = new FilterCreatureCard( + "a creature spell from among cards in your graveyard that were put there from anywhere other than the battlefield this turn" + ); + + static { + filter.add(BanonTheReturnersLeaderPredicate.instance); + } + + public BanonTheReturnersLeader(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.subtype.add(SubType.REBEL); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Pray -- Once during each of your turns, you may cast a creature spell from among cards in your graveyard that were put there from anywhere other than the battlefield this turn. + Ability ability = new CastFromGraveyardOnceDuringEachOfYourTurnAbility(filter).withFlavorWord("Pray"); + ability.addWatcher(new CardsPutIntoGraveyardWatcher()); + this.addAbility(ability); + // Whenever you attack, you may pay {1} and discard a card. If you do, draw a card. + this.addAbility(new AttacksWithCreaturesTriggeredAbility( + new DoIfCostPaid( + new DrawCardSourceControllerEffect(1), + new CompositeCost(new ManaCostsImpl<>("{1}"), new DiscardCardCost(), "pay {1} and discard a card") + ), 1 + )); + } + + private BanonTheReturnersLeader(final BanonTheReturnersLeader card) { + super(card); + } + + @Override + public BanonTheReturnersLeader copy() { + return new BanonTheReturnersLeader(this); + } +} + +enum BanonTheReturnersLeaderPredicate implements Predicate { + instance; + + @Override + public boolean apply(Card input, Game game) { + CardsPutIntoGraveyardWatcher watcher = game.getState().getWatcher(CardsPutIntoGraveyardWatcher.class); + return watcher != null && watcher.checkCardNotFromBattlefield(input, game); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BanquetGuests.java b/Mage.Sets/src/mage/cards/b/BanquetGuests.java index 6f58fb799bc..66ec17774b3 100644 --- a/Mage.Sets/src/mage/cards/b/BanquetGuests.java +++ b/Mage.Sets/src/mage/cards/b/BanquetGuests.java @@ -4,26 +4,21 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.common.AffinityEffect; import mage.abilities.effects.common.EntersBattlefieldWithXCountersEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.AffinityAbility; import mage.abilities.keyword.IndestructibleAbility; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AffinityType; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.StaticFilters; -import mage.target.common.TargetControlledPermanent; import java.util.UUID; @@ -32,8 +27,6 @@ import java.util.UUID; */ public final class BanquetGuests extends CardImpl { - private static final Hint hint = new ValueHint("Food you control", new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_FOOD)); - public BanquetGuests(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{X}{G}{W}"); @@ -43,12 +36,7 @@ public final class BanquetGuests extends CardImpl { this.toughness = new MageInt(0); // Affinity for Food - this.addAbility( - new SimpleStaticAbility( - Zone.ALL, - new AffinityEffect(StaticFilters.FILTER_CONTROLLED_FOOD) - ).addHint(hint) - ); + this.addAbility(new AffinityAbility(AffinityType.FOOD)); // Trample this.addAbility(TrampleAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/b/BardsBow.java b/Mage.Sets/src/mage/cards/b/BardsBow.java new file mode 100644 index 00000000000..dbe45f7d274 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BardsBow.java @@ -0,0 +1,54 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.AddCardSubtypeAttachedEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.abilities.keyword.JobSelectAbility; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BardsBow extends CardImpl { + + public BardsBow(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{G}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Job select + this.addAbility(new JobSelectAbility()); + + // Equipped creature gets +2/+2, has reach, and is a Bard in addition to its other types. + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(2, 2)); + ability.addEffect(new GainAbilityAttachedEffect( + ReachAbility.getInstance(), AttachmentType.EQUIPMENT + ).setText(", has reach")); + ability.addEffect(new AddCardSubtypeAttachedEffect( + SubType.BARD, AttachmentType.EQUIPMENT + ).setText(", and is a Bard in addition to its other types")); + this.addAbility(ability); + + // Perseus's Bow-- Equip {6} + this.addAbility(new EquipAbility(6).withFlavorWord("Perseus's Bow")); + } + + private BardsBow(final BardsBow card) { + super(card); + } + + @Override + public BardsBow copy() { + return new BardsBow(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BaronAirshipKingdom.java b/Mage.Sets/src/mage/cards/b/BaronAirshipKingdom.java new file mode 100644 index 00000000000..038772cc63b --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BaronAirshipKingdom.java @@ -0,0 +1,39 @@ +package mage.cards.b; + +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.RedManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BaronAirshipKingdom extends CardImpl { + + public BaronAirshipKingdom(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.subtype.add(SubType.TOWN); + + // This land enters tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {U} or {R}. + this.addAbility(new BlueManaAbility()); + this.addAbility(new RedManaAbility()); + } + + private BaronAirshipKingdom(final BaronAirshipKingdom card) { + super(card); + } + + @Override + public BaronAirshipKingdom copy() { + return new BaronAirshipKingdom(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BarrenGlory.java b/Mage.Sets/src/mage/cards/b/BarrenGlory.java index 521770cda00..6fd39c760b5 100644 --- a/Mage.Sets/src/mage/cards/b/BarrenGlory.java +++ b/Mage.Sets/src/mage/cards/b/BarrenGlory.java @@ -1,42 +1,37 @@ - package mage.cards.b; -import java.util.UUID; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.condition.CompoundCondition; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.CardsInHandCondition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.WinGameSourceControllerEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.ComparisonType; -import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.StaticFilters; + +import java.util.UUID; /** * @author fireshoes */ public final class BarrenGlory extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent(); - - static { - filter.add(AnotherPredicate.instance); - } + private static final Condition condition = new CompoundCondition( + "you control no permanents other than this enchantment and have no cards in hand", + new CardsInHandCondition(ComparisonType.EQUAL_TO, 0), + new PermanentsOnTheBattlefieldCondition( + StaticFilters.FILTER_OTHER_CONTROLLED_PERMANENTS, ComparisonType.EQUAL_TO, 0 + ) + ); public BarrenGlory(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{W}{W}"); // At the beginning of your upkeep, if you control no permanents other than Barren Glory and have no cards in hand, you win the game. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility(new WinGameSourceControllerEffect()), - new CompoundCondition( - new CardsInHandCondition(ComparisonType.EQUAL_TO, 0), - new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.EQUAL_TO, 0) - ), - "At the beginning of your upkeep, if you control no permanents other than {this} and have no cards in hand, you win the game")); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new WinGameSourceControllerEffect()).withInterveningIf(condition)); } private BarrenGlory(final BarrenGlory card) { diff --git a/Mage.Sets/src/mage/cards/b/BarretAvalancheLeader.java b/Mage.Sets/src/mage/cards/b/BarretAvalancheLeader.java index 3759bcad629..c56cec00d42 100644 --- a/Mage.Sets/src/mage/cards/b/BarretAvalancheLeader.java +++ b/Mage.Sets/src/mage/cards/b/BarretAvalancheLeader.java @@ -3,35 +3,28 @@ package mage.cards.b; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachTargetToTargetEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.ReachAbility; import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.game.permanent.token.RebelRedToken; import mage.target.TargetPermanent; -import mage.target.targetpointer.EachTargetPointer; -import java.util.List; -import java.util.Objects; import java.util.UUID; -import java.util.stream.Collectors; /** * @author TheElk801 */ public final class BarretAvalancheLeader extends CardImpl { - private static final FilterPermanent filter = new FilterControlledPermanent(SubType.EQUIPMENT); private static final FilterPermanent filter2 = new FilterControlledPermanent(SubType.REBEL); public BarretAvalancheLeader(UUID ownerId, CardSetInfo setInfo) { @@ -48,12 +41,12 @@ public final class BarretAvalancheLeader extends CardImpl { // Avalanche! -- Whenever an Equipment you control enters, create a 2/2 red Rebel creature token. this.addAbility(new EntersBattlefieldAllTriggeredAbility( - new CreateTokenEffect(new RebelRedToken()), filter + new CreateTokenEffect(new RebelRedToken()), StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT ).withFlavorWord("Avalanche!")); // At the beginning of combat on your turn, attach up to one target Equipment you control to target Rebel you control. - Ability ability = new BeginningOfCombatTriggeredAbility(new BarretAvalancheLeaderEffect()); - ability.addTarget(new TargetPermanent(0, 1, filter)); + Ability ability = new BeginningOfCombatTriggeredAbility(new AttachTargetToTargetEffect()); + ability.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT)); ability.addTarget(new TargetPermanent(filter2)); this.addAbility(ability); } @@ -67,33 +60,3 @@ public final class BarretAvalancheLeader extends CardImpl { return new BarretAvalancheLeader(this); } } - -class BarretAvalancheLeaderEffect extends OneShotEffect { - - BarretAvalancheLeaderEffect() { - super(Outcome.Benefit); - staticText = "attach up to one target Equipment you control to target Rebel you control"; - this.setTargetPointer(new EachTargetPointer()); - } - - private BarretAvalancheLeaderEffect(final BarretAvalancheLeaderEffect effect) { - super(effect); - } - - @Override - public BarretAvalancheLeaderEffect copy() { - return new BarretAvalancheLeaderEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - List permanents = this - .getTargetPointer() - .getTargets(game, source) - .stream() - .map(game::getPermanent) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - return permanents.size() >= 2 && permanents.get(1).addAttachment(permanents.get(0).getId(), source, game); - } -} diff --git a/Mage.Sets/src/mage/cards/b/BarretWallace.java b/Mage.Sets/src/mage/cards/b/BarretWallace.java index 6f5c711394a..e040ec94df4 100644 --- a/Mage.Sets/src/mage/cards/b/BarretWallace.java +++ b/Mage.Sets/src/mage/cards/b/BarretWallace.java @@ -49,7 +49,8 @@ public final class BarretWallace extends CardImpl { // Whenever Barret Wallace attacks, it deals damage equal to the number of equipped creatures you control to defending player. this.addAbility(new AttacksTriggeredAbility( - new DamageTargetEffect(xValue, true, "it", true), + new DamageTargetEffect(xValue, true, "it", true) + .setText("it deals damage equal to the number of equipped creatures you control to defending player"), false, null, SetTargetPointer.PLAYER ).withRuleTextReplacement(true).addHint(hint)); } diff --git a/Mage.Sets/src/mage/cards/b/BarrierBreach.java b/Mage.Sets/src/mage/cards/b/BarrierBreach.java index 88835285968..3906df17271 100644 --- a/Mage.Sets/src/mage/cards/b/BarrierBreach.java +++ b/Mage.Sets/src/mage/cards/b/BarrierBreach.java @@ -6,8 +6,8 @@ import mage.abilities.keyword.CyclingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterEnchantmentPermanent; -import mage.target.common.TargetEnchantmentPermanent; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; import java.util.UUID; @@ -21,8 +21,9 @@ public final class BarrierBreach extends CardImpl { // Exile up to three target enchantments. this.getSpellAbility().addEffect(new ExileTargetEffect()); - this.getSpellAbility().addTarget(new TargetEnchantmentPermanent(0, 3, - new FilterEnchantmentPermanent("enchantments"), false)); + this.getSpellAbility().addTarget(new TargetPermanent( + 0, 3, StaticFilters.FILTER_PERMANENT_ENCHANTMENTS + )); // Cycling {2} this.addAbility(new CyclingAbility(new ManaCostsImpl<>("{2}"))); diff --git a/Mage.Sets/src/mage/cards/b/BarrinTolarianArchmage.java b/Mage.Sets/src/mage/cards/b/BarrinTolarianArchmage.java index 70da770dc14..e18e185f475 100644 --- a/Mage.Sets/src/mage/cards/b/BarrinTolarianArchmage.java +++ b/Mage.Sets/src/mage/cards/b/BarrinTolarianArchmage.java @@ -2,12 +2,11 @@ package mage.cards.b; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -50,12 +49,8 @@ public final class BarrinTolarianArchmage extends CardImpl { this.addAbility(ability); // At the beginning of your end step, if a permanent was put into your hand from the battlefield this turn, draw a card. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfEndStepTriggeredAbility( - new DrawCardSourceControllerEffect(1) - ), BarrinTolarianArchmageCondition.instance, "At the beginning of your end step, " + - "if a permanent was put into your hand from the battlefield this turn, draw a card." - ), new BarrinTolarianArchmageWatcher()); + this.addAbility(new BeginningOfEndStepTriggeredAbility(new DrawCardSourceControllerEffect(1)) + .withInterveningIf(BarrinTolarianArchmageCondition.instance), new BarrinTolarianArchmageWatcher()); } private BarrinTolarianArchmage(final BarrinTolarianArchmage card) { @@ -76,6 +71,11 @@ enum BarrinTolarianArchmageCondition implements Condition { BarrinTolarianArchmageWatcher watcher = game.getState().getWatcher(BarrinTolarianArchmageWatcher.class); return watcher != null && watcher.checkPlayer(source.getControllerId()); } + + @Override + public String toString() { + return "a permanent was put into your hand from the battlefield this turn"; + } } class BarrinTolarianArchmageWatcher extends Watcher { diff --git a/Mage.Sets/src/mage/cards/b/BarrowinOfClanUndurr.java b/Mage.Sets/src/mage/cards/b/BarrowinOfClanUndurr.java index 4179fe20e0f..e88193a920d 100644 --- a/Mage.Sets/src/mage/cards/b/BarrowinOfClanUndurr.java +++ b/Mage.Sets/src/mage/cards/b/BarrowinOfClanUndurr.java @@ -6,6 +6,7 @@ import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.CompletedDungeonCondition; import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.OneShotNonTargetEffect; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.abilities.effects.keyword.VentureIntoTheDungeonEffect; import mage.abilities.hint.common.CurrentDungeonHint; @@ -50,11 +51,11 @@ public final class BarrowinOfClanUndurr extends CardImpl { // Whenever Barrowin of Clan Undurr attacks, return up to one creature card with mana value 3 or less from your graveyard to the battlefield if you've completed a dungeon. Ability ability = new AttacksTriggeredAbility(new ConditionalOneShotEffect( - new ReturnFromGraveyardToBattlefieldTargetEffect(), - CompletedDungeonCondition.instance, "return up to one creature card " + - "with mana value 3 or less from your graveyard to the battlefield if you've completed a dungeon" - )); - ability.addTarget(new TargetCardInYourGraveyard(0, 1, filter, true)); + new OneShotNonTargetEffect(new ReturnFromGraveyardToBattlefieldTargetEffect() + .setText("return up to one creature card with mana value 3 or less from your graveyard to the battlefield"), + new TargetCardInYourGraveyard(0, 1, filter, true)), + CompletedDungeonCondition.instance + ).withConditionTextAtEnd(true)); this.addAbility(ability.addHint(CompletedDungeonCondition.getHint()), new CompletedDungeonWatcher()); } diff --git a/Mage.Sets/src/mage/cards/b/BartzAndBoko.java b/Mage.Sets/src/mage/cards/b/BartzAndBoko.java index 968359caf92..f7f76eb2b8e 100644 --- a/Mage.Sets/src/mage/cards/b/BartzAndBoko.java +++ b/Mage.Sets/src/mage/cards/b/BartzAndBoko.java @@ -3,12 +3,8 @@ package mage.cards.b; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.AffinityEffect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.AffinityAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -26,9 +22,6 @@ import java.util.UUID; */ public final class BartzAndBoko extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.BIRD, "Birds"); - private static final Hint hint = new ValueHint("Birds you control", new PermanentsOnBattlefieldCount(filter)); - public BartzAndBoko(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{G}"); @@ -39,7 +32,7 @@ public final class BartzAndBoko extends CardImpl { this.toughness = new MageInt(3); // Affinity for Birds - this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filter)).setRuleAtTheTop(true).addHint(hint)); + this.addAbility(new AffinityAbility(AffinityType.BIRDS)); // When Bartz and Boko enters, each other Bird you control deals damage equal to its power to target creature an opponent controls. Ability ability = new EntersBattlefieldTriggeredAbility(new BartzAndBokoEffect()); diff --git a/Mage.Sets/src/mage/cards/b/BaruWurmspeaker.java b/Mage.Sets/src/mage/cards/b/BaruWurmspeaker.java index c76fb6d540b..cd7ecf16294 100644 --- a/Mage.Sets/src/mage/cards/b/BaruWurmspeaker.java +++ b/Mage.Sets/src/mage/cards/b/BaruWurmspeaker.java @@ -1,28 +1,26 @@ package mage.cards.b; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.CostAdjuster; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.InfoEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.FilterPermanent; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.token.Wurm44Token; import mage.util.CardUtil; @@ -34,11 +32,9 @@ import java.util.UUID; */ public final class BaruWurmspeaker extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.WURM, "Wurms"); - private static final FilterPermanent filter2 = new FilterPermanent(SubType.WURM, ""); - private static final Hint hint = new ValueHint( - "Highest power among Wurms you control", BaruWurmspeakerValue.instance - ); + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(SubType.WURM, "Wurms you control"); + static final GreatestAmongPermanentsValue xValue = new GreatestAmongPermanentsValue(GreatestAmongPermanentsValue.Quality.Power, filter); + private static final Hint hint = xValue.getHint(); public BaruWurmspeaker(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}"); @@ -54,7 +50,7 @@ public final class BaruWurmspeaker extends CardImpl { 2, 2, Duration.WhileOnBattlefield, filter, false )); ability.addEffect(new GainAbilityControlledEffect( - TrampleAbility.getInstance(), Duration.WhileOnBattlefield, filter2 + TrampleAbility.getInstance(), Duration.WhileOnBattlefield, filter ).setText("and have trample")); this.addAbility(ability); @@ -64,6 +60,7 @@ public final class BaruWurmspeaker extends CardImpl { ability.addEffect(new InfoEffect("this ability costs {X} less to activate, " + "where X is the greatest power among Wurms you control")); ability.setCostAdjuster(BaruWurmspeakerAdjuster.instance); + ability.addHint(hint); this.addAbility(ability); } @@ -77,44 +74,12 @@ public final class BaruWurmspeaker extends CardImpl { } } -enum BaruWurmspeakerValue implements DynamicValue { - instance; - private static final FilterPermanent filter = new FilterControlledCreaturePermanent(SubType.WURM); - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - return game - .getBattlefield() - .getActivePermanents(filter, sourceAbility.getControllerId(), sourceAbility, game) - .stream() - .map(MageObject::getPower) - .mapToInt(MageInt::getValue) - .max() - .orElse(0); - } - - @Override - public BaruWurmspeakerValue copy() { - return this; - } - - @Override - public String getMessage() { - return ""; - } - - @Override - public String toString() { - return ""; - } -} - enum BaruWurmspeakerAdjuster implements CostAdjuster { instance; @Override public void reduceCost(Ability ability, Game game) { - int value = BaruWurmspeakerValue.instance.calculate(game, ability, null); + int value = BaruWurmspeaker.xValue.calculate(game, ability, null); if (value > 0) { CardUtil.reduceCost(ability, value); } diff --git a/Mage.Sets/src/mage/cards/b/BatWhisperer.java b/Mage.Sets/src/mage/cards/b/BatWhisperer.java index 0f7970ace24..236c103da93 100644 --- a/Mage.Sets/src/mage/cards/b/BatWhisperer.java +++ b/Mage.Sets/src/mage/cards/b/BatWhisperer.java @@ -3,7 +3,6 @@ package mage.cards.b; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.OpponentsLostLifeCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.hint.common.OpponentsLostLifeHint; import mage.cards.CardImpl; @@ -27,11 +26,8 @@ public final class BatWhisperer extends CardImpl { this.toughness = new MageInt(2); // When Bat Whisperer enters the battlefield, if an opponent lost life this turn, create a 1/1 black Bat creature token with flying. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new BatToken())), - OpponentsLostLifeCondition.instance, "When {this} enters, " + - "if an opponent lost life this turn, create a 1/1 black Bat creature token with flying." - ).addHint(OpponentsLostLifeHint.instance)); + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new BatToken())) + .withInterveningIf(OpponentsLostLifeCondition.instance).addHint(OpponentsLostLifeHint.instance)); } private BatWhisperer(final BatWhisperer card) { diff --git a/Mage.Sets/src/mage/cards/b/BatteryBearer.java b/Mage.Sets/src/mage/cards/b/BatteryBearer.java index 52363581bf5..96f2d2d5f96 100644 --- a/Mage.Sets/src/mage/cards/b/BatteryBearer.java +++ b/Mage.Sets/src/mage/cards/b/BatteryBearer.java @@ -14,7 +14,6 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.FilterSpell; import mage.filter.StaticFilters; -import mage.filter.common.FilterArtifactSpell; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.permanent.token.PowerstoneToken; @@ -26,9 +25,10 @@ import java.util.UUID; public final class BatteryBearer extends CardImpl { private static final FilterSpell filter - = new FilterArtifactSpell("an artifact spell with mana value 6 or greater"); + = new FilterSpell("an artifact spell with mana value 6 or greater"); static { + filter.add(CardType.ARTIFACT.getPredicate()); filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 5)); } diff --git a/Mage.Sets/src/mage/cards/b/BattleAtTheHelvault.java b/Mage.Sets/src/mage/cards/b/BattleAtTheHelvault.java index 8cf9d484ecd..f89e53e2ca9 100644 --- a/Mage.Sets/src/mage/cards/b/BattleAtTheHelvault.java +++ b/Mage.Sets/src/mage/cards/b/BattleAtTheHelvault.java @@ -1,7 +1,5 @@ package mage.cards.b; -import mage.MageObject; -import mage.abilities.Ability; import mage.abilities.common.SagaAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.ExileUntilSourceLeavesEffect; @@ -11,15 +9,10 @@ import mage.constants.CardType; import mage.constants.SagaChapter; import mage.constants.SubType; import mage.filter.FilterPermanent; -import mage.filter.common.FilterNonlandPermanent; -import mage.filter.predicate.Predicate; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.ControllerIdPredicate; -import mage.game.Game; import mage.game.permanent.token.AvacynToken; -import mage.players.Player; import mage.target.TargetPermanent; -import mage.target.targetadjustment.TargetAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; import java.util.UUID; @@ -28,6 +21,11 @@ import java.util.UUID; * @author TheElk801 */ public final class BattleAtTheHelvault extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent("non-Saga, nonland permanent"); + static { + filter.add(Predicates.not(CardType.LAND.getPredicate())); + filter.add(Predicates.not(SubType.SAGA.getPredicate())); + } public BattleAtTheHelvault(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{W}{W}"); @@ -45,7 +43,8 @@ public final class BattleAtTheHelvault extends CardImpl { .setText("for each player, exile up to one target non-Saga, " + "nonland permanent that player controls until {this} leaves the battlefield") .setTargetPointer(new EachTargetPointer())); - ability.setTargetAdjuster(BattleAtTheHelvaultAdjuster.instance); + ability.addTarget(new TargetPermanent(0, 1, filter)); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, false)); }); // III -- Create Avacyn, a legendary 8/8 white Angel creature token with flying, vigilance, and indestructible. @@ -65,26 +64,3 @@ public final class BattleAtTheHelvault extends CardImpl { return new BattleAtTheHelvault(this); } } - -enum BattleAtTheHelvaultAdjuster implements TargetAdjuster { - instance; - private static final Predicate predicate = Predicates.not(SubType.SAGA.getPredicate()); - - @Override - public void adjustTargets(Ability ability, Game game) { - ability.getTargets().clear(); - for (UUID playerId : game.getState().getPlayersInRange(ability.getControllerId(), game)) { - Player player = game.getPlayer(playerId); - if (player == null) { - continue; - } - FilterPermanent filter = new FilterNonlandPermanent( - "non-Saga, nonland permanent " - + (ability.isControlledBy(playerId) ? "you control" : "controlled by " + player.getName()) - ); - filter.add(predicate); - filter.add(new ControllerIdPredicate(playerId)); - ability.addTarget(new TargetPermanent(0, 1, filter)); - } - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/b/BattleMenu.java b/Mage.Sets/src/mage/cards/b/BattleMenu.java new file mode 100644 index 00000000000..16649937a09 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BattleMenu.java @@ -0,0 +1,62 @@ +package mage.cards.b; + +import mage.abilities.Mode; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.PowerPredicate; +import mage.game.permanent.token.WaylayToken; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BattleMenu extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("creature with power 4 or greater"); + + static { + filter.add(new PowerPredicate(ComparisonType.MORE_THAN, 3)); + } + + public BattleMenu(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); + + // Choose one -- + // * Attack -- Create a 2/2 white Knight creature token. + this.getSpellAbility().addEffect(new CreateTokenEffect(new WaylayToken())); + this.getSpellAbility().withFirstModeFlavorWord("Attack"); + + // * Ability -- Target creature gets +0/+4 until end of turn. + this.getSpellAbility().addMode(new Mode(new BoostTargetEffect(0, 4)) + .addTarget(new TargetCreaturePermanent()) + .withFlavorWord("Ability")); + + // * Magic -- Destroy target creature with power 4 or greater. + this.getSpellAbility().addMode(new Mode(new DestroyTargetEffect()) + .addTarget(new TargetPermanent(filter)) + .withFlavorWord("Magic")); + + // * Item -- You gain 4 life. + this.getSpellAbility().addMode(new Mode(new GainLifeEffect(4)).withFlavorWord("Item")); + } + + private BattleMenu(final BattleMenu card) { + super(card); + } + + @Override + public BattleMenu copy() { + return new BattleMenu(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BattleOfWits.java b/Mage.Sets/src/mage/cards/b/BattleOfWits.java index 054889aad3d..3603fad84ff 100644 --- a/Mage.Sets/src/mage/cards/b/BattleOfWits.java +++ b/Mage.Sets/src/mage/cards/b/BattleOfWits.java @@ -1,32 +1,31 @@ - package mage.cards.b; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.WinGameSourceControllerEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.game.Controllable; import mage.game.Game; +import mage.players.Library; import mage.players.Player; +import java.util.Optional; +import java.util.UUID; + /** - * * @author North */ public final class BattleOfWits extends CardImpl { public BattleOfWits(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{U}{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}{U}"); // At the beginning of your upkeep, if you have 200 or more cards in your library, you win the game. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new WinGameSourceControllerEffect()); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new BattleOfWitsCondition(), "At the beginning of your upkeep, if you have 200 or more cards in your library, you win the game.")); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new WinGameSourceControllerEffect()) + .withInterveningIf(BattleOfWitsCondition.instance)); } private BattleOfWits(final BattleOfWits card) { @@ -39,14 +38,22 @@ public final class BattleOfWits extends CardImpl { } } -class BattleOfWitsCondition implements Condition { +enum BattleOfWitsCondition implements Condition { + instance; @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null && player.getLibrary().size() >= 200) { - return true; - } - return false; + return Optional + .ofNullable(source) + .map(Controllable::getControllerId) + .map(game::getPlayer) + .map(Player::getLibrary) + .map(Library::size) + .orElse(0) >= 200; + } + + @Override + public String toString() { + return "you have 200 or more cards in your library"; } } diff --git a/Mage.Sets/src/mage/cards/b/BattlewingMystic.java b/Mage.Sets/src/mage/cards/b/BattlewingMystic.java index 27ee0950304..a9321405dca 100644 --- a/Mage.Sets/src/mage/cards/b/BattlewingMystic.java +++ b/Mage.Sets/src/mage/cards/b/BattlewingMystic.java @@ -1,21 +1,21 @@ package mage.cards.b; -import java.util.UUID; import mage.MageInt; +import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.KickedCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.discard.DiscardHandControllerEffect; -import mage.constants.SubType; -import mage.abilities.keyword.KickerAbility; import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.KickerAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; /** - * * @author weirddan455 */ public final class BattlewingMystic extends CardImpl { @@ -35,12 +35,9 @@ public final class BattlewingMystic extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Battlewing Mystic enters the battlefield, if it was kicked, discard your hand, then draw two cards. - EntersBattlefieldTriggeredAbility triggeredAbility = new EntersBattlefieldTriggeredAbility(new DiscardHandControllerEffect()); - triggeredAbility.addEffect(new DrawCardSourceControllerEffect(2)); - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - triggeredAbility, KickedCondition.ONCE, - "When {this} enters, if it was kicked, discard your hand, then draw two cards." - )); + Ability ability = new EntersBattlefieldTriggeredAbility(new DiscardHandControllerEffect()).withInterveningIf(KickedCondition.ONCE); + ability.addEffect(new DrawCardSourceControllerEffect(2).concatBy(", then")); + this.addAbility(ability); } private BattlewingMystic(final BattlewingMystic card) { diff --git a/Mage.Sets/src/mage/cards/b/BeastbondOutcaster.java b/Mage.Sets/src/mage/cards/b/BeastbondOutcaster.java index 9034f4f66da..a8da380d01b 100644 --- a/Mage.Sets/src/mage/cards/b/BeastbondOutcaster.java +++ b/Mage.Sets/src/mage/cards/b/BeastbondOutcaster.java @@ -3,7 +3,6 @@ package mage.cards.b; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.FerociousCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.hint.common.FerociousHint; import mage.abilities.keyword.PlotAbility; @@ -28,11 +27,8 @@ public final class BeastbondOutcaster extends CardImpl { this.toughness = new MageInt(3); // When Beastbond Outcaster enters the battlefield, if you control a creature with power 4 or greater, draw a card. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1)), - FerociousCondition.instance, "When {this} enters, " + - "if you control a creature with power 4 or greater, draw a card." - ).addHint(FerociousHint.instance)); + this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1)) + .withInterveningIf(FerociousCondition.instance).addHint(FerociousHint.instance)); // Plot {1}{G} this.addAbility(new PlotAbility("{1}{G}")); diff --git a/Mage.Sets/src/mage/cards/b/BelligerentOfTheBall.java b/Mage.Sets/src/mage/cards/b/BelligerentOfTheBall.java index 9fa2fe334d1..a1ceab481a7 100644 --- a/Mage.Sets/src/mage/cards/b/BelligerentOfTheBall.java +++ b/Mage.Sets/src/mage/cards/b/BelligerentOfTheBall.java @@ -2,12 +2,11 @@ package mage.cards.b; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.abilities.condition.common.CelebrationCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.MenaceAbility; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.AbilityWord; @@ -32,16 +31,10 @@ public final class BelligerentOfTheBall extends CardImpl { this.toughness = new MageInt(3); // Celebration -- At the beginning of combat on your turn, if two or more nonland permanents entered the battlefield under your control this turn, target creature you control gets +1/+0 and gains menace until end of turn. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new BeginningOfCombatTriggeredAbility( - new BoostTargetEffect(1, 0) - ), - CelebrationCondition.instance, "At the beginning of combat on your turn, if two or more nonland " - + "permanents entered the battlefield under your control this turn, target creature you control " - + "gets +1/+0 and gains menace until end of turn." - + " (It can't be blocked except by two or more creatures.)" - ); - ability.addEffect(new GainAbilityTargetEffect(new MenaceAbility(false))); + Ability ability = new BeginningOfCombatTriggeredAbility(new BoostTargetEffect(1, 0) + .setText("target creature you control gets +1/+0")).withInterveningIf(CelebrationCondition.instance); + ability.addEffect(new GainAbilityTargetEffect(new MenaceAbility(false)) + .setText("and gains menace until end of turn")); ability.addTarget(new TargetControlledCreaturePermanent()); ability.setAbilityWord(AbilityWord.CELEBRATION); ability.addHint(CelebrationCondition.getHint()); diff --git a/Mage.Sets/src/mage/cards/b/BelloBardOfTheBrambles.java b/Mage.Sets/src/mage/cards/b/BelloBardOfTheBrambles.java index 913f3190d0b..c823c0bf1a7 100644 --- a/Mage.Sets/src/mage/cards/b/BelloBardOfTheBrambles.java +++ b/Mage.Sets/src/mage/cards/b/BelloBardOfTheBrambles.java @@ -1,20 +1,17 @@ package mage.cards.b; -import java.util.UUID; - import mage.MageInt; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.common.MyTurnCondition; -import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.IndestructibleAbility; -import mage.constants.*; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; @@ -22,15 +19,17 @@ import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.Iterator; +import java.util.UUID; + /** - * * @author Grath */ public final class BelloBardOfTheBrambles extends CardImpl { public BelloBardOfTheBrambles(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{G}"); - + this.supertype.add(SuperType.LEGENDARY); this.subtype.add(SubType.RACCOON); this.subtype.add(SubType.BARD); @@ -38,13 +37,7 @@ public final class BelloBardOfTheBrambles extends CardImpl { this.toughness = new MageInt(3); // During your turn, each non-Equipment artifact and non-Aura enchantment you control with mana value 4 or greater is a 4/4 Elemental creature in addition to its other types and has indestructible, haste, and "Whenever this creature deals combat damage to a player, draw a card." - Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( - new BelloBardOfTheBramblesEffect(), MyTurnCondition.instance, "During your turn, each " + - "non-Equipment artifact and non-Aura enchantment you control with mana value 4 or greater is a 4/4 " + - "Elemental creature in addition to its other types and has indestructible, haste, and \"Whenever " + - "this creature deals combat damage to a player, draw a card.\"" - )); - this.addAbility(ability); + this.addAbility(new SimpleStaticAbility(new BelloBardOfTheBramblesEffect())); } private BelloBardOfTheBrambles(final BelloBardOfTheBrambles card) { @@ -62,20 +55,20 @@ class BelloBardOfTheBramblesEffect extends ContinuousEffectImpl { private static final FilterPermanent filter = new FilterControlledPermanent(); static { - filter.add(Predicates.and( - Predicates.or( - Predicates.and(CardType.ARTIFACT.getPredicate(), Predicates.not(SubType.EQUIPMENT.getPredicate())), - Predicates.and(CardType.ENCHANTMENT.getPredicate(), Predicates.not(SubType.AURA.getPredicate())) - ), - new ManaValuePredicate(ComparisonType.OR_GREATER, 4) + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.ENCHANTMENT.getPredicate() )); + filter.add(Predicates.not(SubType.EQUIPMENT.getPredicate())); + filter.add(Predicates.not(SubType.AURA.getPredicate())); + filter.add(new ManaValuePredicate(ComparisonType.OR_GREATER, 4)); } public BelloBardOfTheBramblesEffect() { super(Duration.WhileOnBattlefield, Outcome.BecomeCreature); - staticText = "each non-Equipment artifact and non-Aura enchantment you control with mana value 4 or greater " + - "is a 4/4 Elemental creature in addition to its other types and has indestructible, haste, and " + - "\"Whenever this creature deals combat damage to a player, draw a card.\""; + staticText = "during your turn, each non-Equipment artifact and non-Aura enchantment you control " + + "with mana value 4 or greater is a 4/4 Elemental creature in addition to its other types and has " + + "indestructible, haste, and \"Whenever this creature deals combat damage to a player, draw a card.\""; this.dependendToTypes.add(DependencyType.EnchantmentAddingRemoving); // Enchanted Evening this.dependendToTypes.add(DependencyType.ArtifactAddingRemoving); // March of the Machines @@ -89,26 +82,45 @@ class BelloBardOfTheBramblesEffect extends ContinuousEffectImpl { @Override public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source, game)) { - switch (layer) { - case TypeChangingEffects_4: + if (!source.isControlledBy(game.getActivePlayerId())) { + return false; + } + switch (layer) { + case TypeChangingEffects_4: + affectedObjectList.clear(); + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source, game)) { permanent.addCardType(game, CardType.CREATURE); permanent.addSubType(game, SubType.ELEMENTAL); - break; - case AbilityAddingRemovingEffects_6: - if (sublayer == SubLayer.NA) { - permanent.addAbility(IndestructibleAbility.getInstance(), source.getSourceId(), game); - permanent.addAbility(HasteAbility.getInstance(), source.getSourceId(), game); - permanent.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new DrawCardSourceControllerEffect(1), false, false), source.getSourceId(), game); + affectedObjectList.add(new MageObjectReference(permanent, game)); + } + return true; + case AbilityAddingRemovingEffects_6: + for (Iterator it = affectedObjectList.iterator(); it.hasNext(); ) { + Permanent permanent = it.next().getPermanent(game); + if (permanent == null) { + continue; } - case PTChangingEffects_7: - if (sublayer == SubLayer.SetPT_7b) { - permanent.getPower().setModifiedBaseValue(4); - permanent.getToughness().setModifiedBaseValue(4); + permanent.addAbility(IndestructibleAbility.getInstance(), source.getSourceId(), game); + permanent.addAbility(HasteAbility.getInstance(), source.getSourceId(), game); + permanent.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new DrawCardSourceControllerEffect(1)), source.getSourceId(), game); + } + return true; + case PTChangingEffects_7: + if (sublayer != SubLayer.SetPT_7b) { + return false; + } + for (Iterator it = affectedObjectList.iterator(); it.hasNext(); ) { + Permanent permanent = it.next().getPermanent(game); + if (permanent == null) { + continue; } - } + permanent.getPower().setModifiedBaseValue(4); + permanent.getToughness().setModifiedBaseValue(4); + } + return true; + default: + return false; } - return true; } @Override @@ -127,4 +139,4 @@ class BelloBardOfTheBramblesEffect extends ContinuousEffectImpl { public BelloBardOfTheBramblesEffect copy() { return new BelloBardOfTheBramblesEffect(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/b/BenalishEmissary.java b/Mage.Sets/src/mage/cards/b/BenalishEmissary.java index df1bc77cfd0..84854b58014 100644 --- a/Mage.Sets/src/mage/cards/b/BenalishEmissary.java +++ b/Mage.Sets/src/mage/cards/b/BenalishEmissary.java @@ -1,12 +1,9 @@ - package mage.cards.b; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; +import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.KickedCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.KickerAbility; import mage.cards.CardImpl; @@ -15,26 +12,26 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.target.common.TargetLandPermanent; -/** - * - * @author LoneFox +import java.util.UUID; +/** + * @author LoneFox */ public final class BenalishEmissary extends CardImpl { public BenalishEmissary(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); this.subtype.add(SubType.HUMAN, SubType.WIZARD); this.power = new MageInt(1); this.toughness = new MageInt(4); // Kicker {1}{G} this.addAbility(new KickerAbility("{1}{G}")); + // When Benalish Emissary enters the battlefield, if it was kicked, destroy target land. - TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect()); + Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect()).withInterveningIf(KickedCondition.ONCE); ability.addTarget(new TargetLandPermanent()); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, KickedCondition.ONCE, - "When {this} enters, if it was kicked, destroy target land.")); + this.addAbility(ability); } private BenalishEmissary(final BenalishEmissary card) { diff --git a/Mage.Sets/src/mage/cards/b/BenalishSleeper.java b/Mage.Sets/src/mage/cards/b/BenalishSleeper.java index 4a361f0ad34..84ac34e6243 100644 --- a/Mage.Sets/src/mage/cards/b/BenalishSleeper.java +++ b/Mage.Sets/src/mage/cards/b/BenalishSleeper.java @@ -3,7 +3,6 @@ package mage.cards.b; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.KickedCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.SacrificeAllEffect; import mage.abilities.keyword.KickerAbility; import mage.cards.CardImpl; @@ -32,10 +31,7 @@ public final class BenalishSleeper extends CardImpl { this.addAbility(new KickerAbility("{B}")); // When Benalish Sleeper enters the battlefield, if it was kicked, each player sacrifices a creature. - this.addAbility(new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility( - new SacrificeAllEffect(StaticFilters.FILTER_PERMANENT_CREATURE) - ), KickedCondition.ONCE, "When {this} enters, " + - "if it was kicked, each player sacrifices a creature.")); + this.addAbility(new EntersBattlefieldTriggeredAbility(new SacrificeAllEffect(StaticFilters.FILTER_PERMANENT_CREATURE)).withInterveningIf(KickedCondition.ONCE)); } private BenalishSleeper(final BenalishSleeper card) { diff --git a/Mage.Sets/src/mage/cards/b/BenevolentGeist.java b/Mage.Sets/src/mage/cards/b/BenevolentGeist.java index 814302e5078..d74cbeb9eb5 100644 --- a/Mage.Sets/src/mage/cards/b/BenevolentGeist.java +++ b/Mage.Sets/src/mage/cards/b/BenevolentGeist.java @@ -35,7 +35,7 @@ public final class BenevolentGeist extends CardImpl { // Noncreature spells you control can't be countered. this.addAbility(new SimpleStaticAbility(new CantBeCounteredControlledEffect( - StaticFilters.FILTER_SPELLS_NON_CREATURE, null, Duration.WhileOnBattlefield + StaticFilters.FILTER_SPELLS_NON_CREATURE, Duration.WhileOnBattlefield ))); // If Benevolent Geist would be put into a graveyard from anywhere, exile it instead. diff --git a/Mage.Sets/src/mage/cards/b/BenthicExplorers.java b/Mage.Sets/src/mage/cards/b/BenthicExplorers.java index 1323c0a376c..6ebb6fc6b7c 100644 --- a/Mage.Sets/src/mage/cards/b/BenthicExplorers.java +++ b/Mage.Sets/src/mage/cards/b/BenthicExplorers.java @@ -12,12 +12,13 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.choices.Choice; import mage.constants.*; +import mage.filter.FilterPermanent; import mage.filter.common.FilterLandPermanent; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.common.TargetLandPermanent; +import mage.target.TargetPermanent; import java.util.*; @@ -26,7 +27,7 @@ import java.util.*; */ public final class BenthicExplorers extends CardImpl { - private static final FilterLandPermanent filter = new FilterLandPermanent("tapped land an opponent controls"); + private static final FilterPermanent filter = new FilterLandPermanent("tapped land an opponent controls"); static { filter.add(TargetController.OPPONENT.getControllerPredicate()); @@ -43,9 +44,7 @@ public final class BenthicExplorers extends CardImpl { // {T}, Untap a tapped land an opponent controls: Add one mana of any type that land could produce. Ability ability = new BenthicExplorersManaAbility(); - ability.addCost(new UntapTargetCost( - new TargetLandPermanent(filter) - )); + ability.addCost(new UntapTargetCost(new TargetPermanent(filter))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/BeseechTheQueen.java b/Mage.Sets/src/mage/cards/b/BeseechTheQueen.java index 8880df9a4eb..e3b91476606 100644 --- a/Mage.Sets/src/mage/cards/b/BeseechTheQueen.java +++ b/Mage.Sets/src/mage/cards/b/BeseechTheQueen.java @@ -7,7 +7,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.FilterCard; -import mage.filter.predicate.card.ManaValueLessThanControlledLandCountPredicate; +import mage.filter.predicate.mageobject.ManaValueLessThanControlledLandCountPredicate; import mage.target.common.TargetCardInLibrary; /** diff --git a/Mage.Sets/src/mage/cards/b/BespokeBattlegarb.java b/Mage.Sets/src/mage/cards/b/BespokeBattlegarb.java index bad7c7acc0a..72ca2444fe4 100644 --- a/Mage.Sets/src/mage/cards/b/BespokeBattlegarb.java +++ b/Mage.Sets/src/mage/cards/b/BespokeBattlegarb.java @@ -1,17 +1,19 @@ package mage.cards.b; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.CelebrationCondition; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.BoostEquippedEffect; import mage.abilities.keyword.EquipAbility; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.AbilityWord; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; import mage.target.common.TargetControlledCreaturePermanent; import mage.watchers.common.PermanentsEnteredBattlefieldWatcher; @@ -28,19 +30,12 @@ public final class BespokeBattlegarb extends CardImpl { this.subtype.add(SubType.EQUIPMENT); // Equipped creature gets +2/+0. - this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(2, 0, Duration.WhileOnBattlefield))); + this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(2, 0))); // Celebration -- At the beginning of combat on your turn, if two or more nonland permanents entered the battlefield under your control this turn, attach Bespoke Battlegarb to up to one target creature you control. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new BeginningOfCombatTriggeredAbility( - new AttachEffect( - Outcome.BoostCreature, - "attach {this} to up to one target creature you control" - ) - ), CelebrationCondition.instance, "At the beginning of combat on your turn, if two " - + "or more nonland permanents entered the battlefield under your control this turn, " - + "attach {this} to up to one target creature you control" - ); + Ability ability = new BeginningOfCombatTriggeredAbility(new AttachEffect( + Outcome.BoostCreature, "attach {this} to up to one target creature you control" + )).withInterveningIf(CelebrationCondition.instance); ability.addTarget(new TargetControlledCreaturePermanent(0, 1)); ability.setAbilityWord(AbilityWord.CELEBRATION); ability.addHint(CelebrationCondition.getHint()); diff --git a/Mage.Sets/src/mage/cards/b/BezaTheBoundingSpring.java b/Mage.Sets/src/mage/cards/b/BezaTheBoundingSpring.java index cdf57adce26..d515076bf2e 100644 --- a/Mage.Sets/src/mage/cards/b/BezaTheBoundingSpring.java +++ b/Mage.Sets/src/mage/cards/b/BezaTheBoundingSpring.java @@ -10,17 +10,22 @@ import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.StaticFilters; +import mage.game.Controllable; import mage.game.Game; import mage.game.permanent.token.FishNoAbilityToken; import mage.game.permanent.token.TreasureToken; import mage.players.Player; +import java.util.Optional; +import java.util.Set; import java.util.UUID; /** @@ -28,6 +33,13 @@ import java.util.UUID; */ public final class BezaTheBoundingSpring extends CardImpl { + private static final Condition condition = new OpponentControlsMoreCondition(StaticFilters.FILTER_LANDS); + private static final Condition condition2 = new OpponentControlsMoreCondition(StaticFilters.FILTER_PERMANENT_CREATURES); + private static final Hint hint = new ConditionHint(condition); + private static final Hint hint2 = new ConditionHint(condition2); + private static final Hint hint3 = new ConditionHint(OpponentHasMoreLifeCondition.instance); + private static final Hint hint4 = new ConditionHint(BezaOpponentHasMoreCardsInHandThanYouCondition.instance); + public BezaTheBoundingSpring(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}"); @@ -39,14 +51,23 @@ public final class BezaTheBoundingSpring extends CardImpl { // When Beza, the Bounding Spring enters, create a Treasure token if an opponent controls more lands than you. You gain 4 life if an opponent has more life than you. Create two 1/1 blue Fish creature tokens if an opponent controls more creatures than you. Draw a card if an opponent has more cards in hand than you. Ability ability = new EntersBattlefieldTriggeredAbility(new ConditionalOneShotEffect( - new CreateTokenEffect(new TreasureToken()), new OpponentControlsMoreCondition(StaticFilters.FILTER_LANDS))); + new CreateTokenEffect(new TreasureToken()), condition, + "create a Treasure token if an opponent controls more lands than you" + )); ability.addEffect(new ConditionalOneShotEffect( - new GainLifeEffect(4), OpponentHasMoreLifeCondition.instance)); + new GainLifeEffect(4), OpponentHasMoreLifeCondition.instance, + "you gain 4 life if an opponent has more life than you" + )); ability.addEffect(new ConditionalOneShotEffect( - new CreateTokenEffect(new FishNoAbilityToken(), 2), new OpponentControlsMoreCondition(StaticFilters.FILTER_PERMANENT_CREATURES))); + new CreateTokenEffect(new FishNoAbilityToken(), 2), condition2, + "Create two 1/1 blue Fish creature tokens if an opponent controls more creatures than you" + )); ability.addEffect(new ConditionalOneShotEffect( - new DrawCardSourceControllerEffect(1), BezaOpponentHasMoreCardsInHandThanYouCondition.instance)); - this.addAbility(ability); + new DrawCardSourceControllerEffect(1), + BezaOpponentHasMoreCardsInHandThanYouCondition.instance, + "draw a card if an opponent has more cards in hand than you" + )); + this.addAbility(ability.addHint(hint).addHint(hint2).addHint(hint3).addHint(hint4)); } private BezaTheBoundingSpring(final BezaTheBoundingSpring card) { @@ -61,22 +82,23 @@ public final class BezaTheBoundingSpring extends CardImpl { //Based on MoreCardsInHandThanOpponentsCondition enum BezaOpponentHasMoreCardsInHandThanYouCondition implements Condition { - instance; @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - int cardsInHand = player.getHand().size(); - for (UUID playerId : game.getOpponents(source.getControllerId())) { - Player opponent = game.getPlayer(playerId); - if (opponent != null && opponent.getHand().size() > cardsInHand) { - return true; - } - } - } - return false; + int cardsInHand = Optional + .ofNullable(source) + .map(Controllable::getControllerId) + .map(game::getPlayer) + .map(Player::getHand) + .map(Set::size) + .orElse(0); + return game.getOpponents(source.getControllerId()) + .stream() + .map(game::getPlayer) + .map(Player::getHand) + .mapToInt(Set::size) + .anyMatch(x -> x > cardsInHand); } @Override diff --git a/Mage.Sets/src/mage/cards/b/BighornerRancher.java b/Mage.Sets/src/mage/cards/b/BighornerRancher.java index e5967f1599f..5e5f0ec2375 100644 --- a/Mage.Sets/src/mage/cards/b/BighornerRancher.java +++ b/Mage.Sets/src/mage/cards/b/BighornerRancher.java @@ -5,8 +5,7 @@ import mage.Mana; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; -import mage.abilities.dynamicvalue.common.GreatestToughnessAmongControlledCreaturesValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.keyword.VigilanceAbility; import mage.abilities.mana.DynamicManaAbility; @@ -35,16 +34,16 @@ public final class BighornerRancher extends CardImpl { // {T}: Add an amount of {G} equal to the greatest power among creatures you control. this.addAbility(new DynamicManaAbility( - Mana.GreenMana(1), GreatestPowerAmongControlledCreaturesValue.instance, new TapSourceCost(), + Mana.GreenMana(1), GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES, new TapSourceCost(), "Add an amount of {G} equal to the greatest power among creatures you control." - ).addHint(GreatestPowerAmongControlledCreaturesValue.getHint())); + ).addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint())); // Sacrifice Bighorner Rancher: You gain life equal to the greatest toughness among other creatures you control. this.addAbility(new SimpleActivatedAbility( - new GainLifeEffect(GreatestToughnessAmongControlledCreaturesValue.OTHER) + new GainLifeEffect(GreatestAmongPermanentsValue.TOUGHNESS_OTHER_CONTROLLED_CREATURES) .setText("You gain life equal to the greatest toughness among other creatures you control."), new SacrificeSourceCost() - ).addHint(GreatestToughnessAmongControlledCreaturesValue.OTHER.getHint())); + ).addHint(GreatestAmongPermanentsValue.TOUGHNESS_OTHER_CONTROLLED_CREATURES.getHint())); } private BighornerRancher(final BighornerRancher card) { diff --git a/Mage.Sets/src/mage/cards/b/BindingTheOldGods.java b/Mage.Sets/src/mage/cards/b/BindingTheOldGods.java index 2651b188742..966e3bdc186 100644 --- a/Mage.Sets/src/mage/cards/b/BindingTheOldGods.java +++ b/Mage.Sets/src/mage/cards/b/BindingTheOldGods.java @@ -1,35 +1,30 @@ package mage.cards.b; -import java.util.UUID; - import mage.abilities.common.SagaAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; import mage.abilities.keyword.DeathtouchAbility; -import mage.constants.*; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.filter.common.FilterBySubtypeCard; -import mage.filter.common.FilterNonlandPermanent; +import mage.target.TargetPermanent; import mage.target.common.TargetCardInLibrary; -import mage.target.common.TargetNonlandPermanent; + +import java.util.UUID; /** - * * @author weirddan455 */ public final class BindingTheOldGods extends CardImpl { - private static final FilterNonlandPermanent filter - = new FilterNonlandPermanent("nonland permanent an opponent controls"); - private static final FilterBySubtypeCard filter2 - = new FilterBySubtypeCard(SubType.FOREST); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } + private static final FilterCard filter = new FilterBySubtypeCard(SubType.FOREST); public BindingTheOldGods(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}{G}"); @@ -41,11 +36,11 @@ public final class BindingTheOldGods extends CardImpl { // I — Destroy target nonland permanent an opponent controls. sagaAbility.addChapterEffect( this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_I, - new DestroyTargetEffect(), new TargetNonlandPermanent(filter) + new DestroyTargetEffect(), new TargetPermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_NON_LAND) ); // II — Search your library for a Forest card, put it onto the battlefield tapped, then shuffle your library. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II, - new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter2), true) + new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter), true) ); // III — Creatures you control gain deathtouch until end of turn. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, diff --git a/Mage.Sets/src/mage/cards/b/BiowasteBlob.java b/Mage.Sets/src/mage/cards/b/BiowasteBlob.java index 27285952858..f6bd6943055 100644 --- a/Mage.Sets/src/mage/cards/b/BiowasteBlob.java +++ b/Mage.Sets/src/mage/cards/b/BiowasteBlob.java @@ -1,12 +1,11 @@ package mage.cards.b; import mage.MageInt; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.ControlACommanderCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.CreateTokenCopySourceEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -36,12 +35,8 @@ public final class BiowasteBlob extends CardImpl { ))); // At the beginning of your upkeep, if you control a commander, create a token that's a copy of Biowaste Blob. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility( - new CreateTokenCopySourceEffect(), false - ), ControlACommanderCondition.instance, "At the beginning of your upkeep, " + - "if you control a commander, create a token that's a copy of {this}." - )); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new CreateTokenCopySourceEffect()) + .withInterveningIf(ControlACommanderCondition.instance)); } private BiowasteBlob(final BiowasteBlob card) { diff --git a/Mage.Sets/src/mage/cards/b/BitterDownfall.java b/Mage.Sets/src/mage/cards/b/BitterDownfall.java index 5a0e2234a03..2ea9546449c 100644 --- a/Mage.Sets/src/mage/cards/b/BitterDownfall.java +++ b/Mage.Sets/src/mage/cards/b/BitterDownfall.java @@ -10,9 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.WasDealtDamageThisTurnPredicate; +import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -22,14 +20,7 @@ import java.util.UUID; */ public final class BitterDownfall extends CardImpl { - private static final FilterPermanent filter - = new FilterCreaturePermanent("a creature that was dealt damage this turn"); - - static { - filter.add(WasDealtDamageThisTurnPredicate.instance); - } - - private static final Condition condition = new SourceTargetsPermanentCondition(filter); + private static final Condition condition = new SourceTargetsPermanentCondition(StaticFilters.FILTER_CREATURE_DAMAGED_THIS_TURN); public BitterDownfall(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{B}"); diff --git a/Mage.Sets/src/mage/cards/b/BlackChocobo.java b/Mage.Sets/src/mage/cards/b/BlackChocobo.java new file mode 100644 index 00000000000..9b6dc77adfe --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BlackChocobo.java @@ -0,0 +1,54 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.common.LandfallAbility; +import mage.abilities.common.TransformIntoSourceTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BlackChocobo extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.BIRD, "Birds"); + + public BlackChocobo(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.subtype.add(SubType.BIRD); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + this.nightCard = true; + this.color.setGreen(true); + + // When this permanent transforms into Black Chocobo, search your library for a land card, put it onto the battlefield tapped, then shuffle. + this.addAbility(new TransformIntoSourceTriggeredAbility( + new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_LAND_A), true) + )); + + // Landfall -- Whenever a land you control enters, Birds you control get +1/+0 until end of turn. + this.addAbility(new LandfallAbility(new BoostControlledEffect( + 1, 0, Duration.EndOfTurn, filter, false + ))); + } + + private BlackChocobo(final BlackChocobo card) { + super(card); + } + + @Override + public BlackChocobo copy() { + return new BlackChocobo(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BlackWaltzNo3.java b/Mage.Sets/src/mage/cards/b/BlackWaltzNo3.java new file mode 100644 index 00000000000..7981293fe88 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BlackWaltzNo3.java @@ -0,0 +1,52 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BlackWaltzNo3 extends CardImpl { + + public BlackWaltzNo3(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // Whenever you cast a noncreature spell, Black Waltz No. 3 deals 2 damage to each opponent. + this.addAbility(new SpellCastControllerTriggeredAbility( + new DamagePlayersEffect(2, TargetController.OPPONENT), + StaticFilters.FILTER_SPELL_A_NON_CREATURE, false + )); + } + + private BlackWaltzNo3(final BlackWaltzNo3 card) { + super(card); + } + + @Override + public BlackWaltzNo3 copy() { + return new BlackWaltzNo3(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BlacksmithsTalent.java b/Mage.Sets/src/mage/cards/b/BlacksmithsTalent.java index 5dbd9b4a5d1..96cfe78ccbf 100644 --- a/Mage.Sets/src/mage/cards/b/BlacksmithsTalent.java +++ b/Mage.Sets/src/mage/cards/b/BlacksmithsTalent.java @@ -5,7 +5,7 @@ import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.MyTurnCondition; import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachTargetToTargetEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.effects.common.continuous.GainClassAbilitySourceEffect; @@ -16,19 +16,17 @@ import mage.abilities.keyword.HasteAbility; import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.common.FilterEquipmentPermanent; import mage.filter.predicate.permanent.EquippedPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.game.permanent.token.SwordToken; import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; -import mage.target.targetpointer.EachTargetPointer; -import java.util.List; import java.util.UUID; /** @@ -36,12 +34,10 @@ import java.util.UUID; */ public final class BlacksmithsTalent extends CardImpl { - private static final FilterPermanent filter = new FilterEquipmentPermanent("equipment you control"); - private static final FilterPermanent filter2 = new FilterCreaturePermanent(); + private static final FilterPermanent filter = new FilterCreaturePermanent(); static { - filter.add(TargetController.YOU.getControllerPredicate()); - filter2.add(EquippedPredicate.instance); + filter.add(EquippedPredicate.instance); } public BlacksmithsTalent(UUID ownerId, CardSetInfo setInfo) { @@ -59,10 +55,8 @@ public final class BlacksmithsTalent extends CardImpl { this.addAbility(new ClassLevelAbility(2, "{2}{R}")); // At the beginning of combat on your turn, attach target Equipment you control to up to one target creature you control. - Ability ability = new BeginningOfCombatTriggeredAbility( - new BlacksmithsTalentEffect() - ); - ability.addTarget(new TargetPermanent(filter)); + Ability ability = new BeginningOfCombatTriggeredAbility(new AttachTargetToTargetEffect()); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT)); ability.addTarget(new TargetControlledCreaturePermanent(0, 1)); this.addAbility(new SimpleStaticAbility(new GainClassAbilitySourceEffect(ability, 2))); @@ -71,10 +65,10 @@ public final class BlacksmithsTalent extends CardImpl { // During your turn, equipped creatures you control have double strike and haste. ability = new SimpleStaticAbility(new ConditionalContinuousEffect(new GainAbilityControlledEffect( - DoubleStrikeAbility.getInstance(), Duration.WhileOnBattlefield, filter2 + DoubleStrikeAbility.getInstance(), Duration.WhileOnBattlefield, filter ), MyTurnCondition.instance, "during your turn, equipped creatures you control have double strike")); ability.addEffect(new ConditionalContinuousEffect(new GainAbilityControlledEffect( - HasteAbility.getInstance(), Duration.WhileOnBattlefield, filter2 + HasteAbility.getInstance(), Duration.WhileOnBattlefield, filter ), MyTurnCondition.instance, "and haste")); this.addAbility(new SimpleStaticAbility(new GainClassAbilitySourceEffect(ability, 3))); } @@ -88,32 +82,3 @@ public final class BlacksmithsTalent extends CardImpl { return new BlacksmithsTalent(this); } } - -class BlacksmithsTalentEffect extends OneShotEffect { - - BlacksmithsTalentEffect() { - super(Outcome.Benefit); - this.setTargetPointer(new EachTargetPointer()); - staticText = "attach target Equipment you control to up to one target creature you control"; - } - - private BlacksmithsTalentEffect(final BlacksmithsTalentEffect effect) { - super(effect); - } - - @Override - public BlacksmithsTalentEffect copy() { - return new BlacksmithsTalentEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - List targets = this.getTargetPointer().getTargets(game, source); - if (targets.size() < 2) { - return false; - } - Permanent equipment = game.getPermanent(targets.get(0)); - Permanent creature = game.getPermanent(targets.get(1)); - return equipment != null && creature != null && creature.addAttachment(equipment.getId(), source, game); - } -} diff --git a/Mage.Sets/src/mage/cards/b/BladeOfSharedSouls.java b/Mage.Sets/src/mage/cards/b/BladeOfSharedSouls.java index 1331a8007bd..e4074b2f5a1 100644 --- a/Mage.Sets/src/mage/cards/b/BladeOfSharedSouls.java +++ b/Mage.Sets/src/mage/cards/b/BladeOfSharedSouls.java @@ -20,6 +20,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; +import mage.util.CardUtil; import java.util.UUID; @@ -67,14 +68,11 @@ enum BladeOfSharedSoulsPredicate implements ObjectSourcePlayerPredicate input, Game game) { - return input.getSource() - .getEffects() - .stream() - .map(effect -> effect.getValue("attachedPermanent")) - .filter(Permanent.class::isInstance) - .map(Permanent.class::cast) - .noneMatch(permanent -> input.getObject().getId().equals(permanent.getId()) - && input.getObject().getZoneChangeCounter(game) == permanent.getZoneChangeCounter(game)); + return !CardUtil + .getEffectValueFromAbility(input.getSource(), "attachedPermanent", Permanent.class) + .filter(permanent -> input.getObject().getId().equals(permanent.getId()) + && input.getObject().getZoneChangeCounter(game) == permanent.getZoneChangeCounter(game)) + .isPresent(); } } @@ -141,4 +139,4 @@ class BladeOfSharedSoulsCopyEffect extends CopyEffect { } return false; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/b/BladeTribeBerserkers.java b/Mage.Sets/src/mage/cards/b/BladeTribeBerserkers.java index 1eca1c8df02..d5575de58a8 100644 --- a/Mage.Sets/src/mage/cards/b/BladeTribeBerserkers.java +++ b/Mage.Sets/src/mage/cards/b/BladeTribeBerserkers.java @@ -1,10 +1,9 @@ package mage.cards.b; import mage.MageInt; -import mage.abilities.TriggeredAbility; +import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.MetalcraftCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.hint.common.MetalcraftHint; @@ -23,8 +22,6 @@ import java.util.UUID; */ public final class BladeTribeBerserkers extends CardImpl { - private static final String effectText = "When {this} enters, if you control three or more artifacts, {this} gets +3/+3 and gains haste until end of turn."; - public BladeTribeBerserkers(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); this.subtype.add(SubType.HUMAN, SubType.BERSERKER); @@ -33,12 +30,14 @@ public final class BladeTribeBerserkers extends CardImpl { this.toughness = new MageInt(3); //Metalcraft — When Blade-Tribe Berserkers enters the battlefield, if you control three or more artifacts, Blade-Tribe Berserkers gets +3/+3 and gains haste until end of turn. - TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new BoostSourceEffect(3, 3, Duration.EndOfTurn), false); - ability.addEffect(new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.EndOfTurn)); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, MetalcraftCondition.instance, effectText) - .setAbilityWord(AbilityWord.METALCRAFT) - .addHint(MetalcraftHint.instance) - ); + Ability ability = new EntersBattlefieldTriggeredAbility( + new BoostSourceEffect(3, 3, Duration.EndOfTurn) + .setText("{this} gets +3/+3"), false + ).withInterveningIf(MetalcraftCondition.instance); + ability.addEffect(new GainAbilitySourceEffect( + HasteAbility.getInstance(), Duration.EndOfTurn + ).setText("and gains haste until end of turn")); + this.addAbility(ability.setAbilityWord(AbilityWord.METALCRAFT).addHint(MetalcraftHint.instance)); } private BladeTribeBerserkers(final BladeTribeBerserkers card) { diff --git a/Mage.Sets/src/mage/cards/b/BladegraftAspirant.java b/Mage.Sets/src/mage/cards/b/BladegraftAspirant.java index 0bbc3ff48e5..2517e1981b6 100644 --- a/Mage.Sets/src/mage/cards/b/BladegraftAspirant.java +++ b/Mage.Sets/src/mage/cards/b/BladegraftAspirant.java @@ -96,7 +96,7 @@ class BladegraftAspirantCostReductionEffect extends CostModificationEffectImpl { Permanent permanent = game.getPermanentOrLKIBattlefield(abilityToModify.getSourceId()); - if (!(permanent != null && permanent.getSubtype(game).contains(SubType.EQUIPMENT) && permanent.isControlledBy(source.getControllerId()))) { + if (!(permanent != null && permanent.hasSubtype(SubType.EQUIPMENT, game) && permanent.isControlledBy(source.getControllerId()))) { return false; } diff --git a/Mage.Sets/src/mage/cards/b/BlademaneBaku.java b/Mage.Sets/src/mage/cards/b/BlademaneBaku.java index 3538121037b..f0c57416e82 100644 --- a/Mage.Sets/src/mage/cards/b/BlademaneBaku.java +++ b/Mage.Sets/src/mage/cards/b/BlademaneBaku.java @@ -37,7 +37,7 @@ public final class BlademaneBaku extends CardImpl { this.toughness = new MageInt(1); // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Blademane Baku. - this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, true)); // {1}, Remove X ki counters from Blademane Baku: For each counter removed, Blademane Baku gets +2/+0 until end of turn. Effect effect = new BoostSourceEffect(xValue, StaticValue.get(0), Duration.EndOfTurn); diff --git a/Mage.Sets/src/mage/cards/b/BlastZone.java b/Mage.Sets/src/mage/cards/b/BlastZone.java index ec7319abe93..1d579a535a3 100644 --- a/Mage.Sets/src/mage/cards/b/BlastZone.java +++ b/Mage.Sets/src/mage/cards/b/BlastZone.java @@ -8,21 +8,16 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.dynamicvalue.common.GetXValue; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyAllEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.mana.ColorlessManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.ComparisonType; -import mage.constants.Outcome; import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterNonlandPermanent; -import mage.filter.predicate.mageobject.ManaValuePredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.filter.predicate.mageobject.ManaValueEqualToCountersSourceCountPredicate; import java.util.UUID; @@ -31,6 +26,13 @@ import java.util.UUID; */ public final class BlastZone extends CardImpl { + private static final FilterPermanent filter = new FilterNonlandPermanent( + "each nonland permanent with mana value equal to the number of charge counters on {this}" + ); + static { + filter.add(new ManaValueEqualToCountersSourceCountPredicate(CounterType.CHARGE)); + } + public BlastZone(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); @@ -52,7 +54,7 @@ public final class BlastZone extends CardImpl { this.addAbility(ability); // {3}, {T}, Sacrifice Blast Zone: Destroy each nonland permanent with converted mana cost equal to the number of charge counters on Blast Zone. - ability = new SimpleActivatedAbility(new BlastZoneEffect(), new GenericManaCost(3)); + ability = new SimpleActivatedAbility(new DestroyAllEffect(filter), new GenericManaCost(3)); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); @@ -67,30 +69,3 @@ public final class BlastZone extends CardImpl { return new BlastZone(this); } } - -class BlastZoneEffect extends OneShotEffect { - - BlastZoneEffect() { - super(Outcome.Benefit); - staticText = "Destroy each nonland permanent with mana value " + - "equal to the number of charge counters on {this}"; - } - - private BlastZoneEffect(final BlastZoneEffect effect) { - super(effect); - } - - @Override - public BlastZoneEffect copy() { - return new BlastZoneEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - int xValue = permanent.getCounters(game).getCount(CounterType.CHARGE); - FilterPermanent filter = new FilterNonlandPermanent(); - filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, xValue)); - return new DestroyAllEffect(filter).apply(game, source); - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/b/BlatantThievery.java b/Mage.Sets/src/mage/cards/b/BlatantThievery.java index b83844fa642..aa976f84a2e 100644 --- a/Mage.Sets/src/mage/cards/b/BlatantThievery.java +++ b/Mage.Sets/src/mage/cards/b/BlatantThievery.java @@ -6,7 +6,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.target.TargetPermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; import java.util.UUID; @@ -24,7 +24,7 @@ public final class BlatantThievery extends CardImpl { .setTargetPointer(new EachTargetPointer()) .setText("for each opponent, gain control of target permanent that player controls")); this.getSpellAbility().addTarget(new TargetPermanent()); - this.getSpellAbility().setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + this.getSpellAbility().setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); } private BlatantThievery(final BlatantThievery card) { diff --git a/Mage.Sets/src/mage/cards/b/BlazingBomb.java b/Mage.Sets/src/mage/cards/b/BlazingBomb.java new file mode 100644 index 00000000000..65d58c177fe --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BlazingBomb.java @@ -0,0 +1,59 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.dynamicvalue.common.SourcePermanentPowerValue; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BlazingBomb extends CardImpl { + + public BlazingBomb(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}"); + + this.subtype.add(SubType.ELEMENTAL); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Whenever you cast a noncreature spell, if at least four mana was spent to cast it, put a +1/+1 counter on this creature. + this.addAbility(new SpellCastControllerTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), + StaticFilters.FILTER_NONCREATURE_SPELL_FOUR_MANA_SPENT, false + )); + + // Blow Up -- {T}, Sacrifice this creature: It deals damage equal to its power to target creature. Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility( + new DamageTargetEffect(SourcePermanentPowerValue.NOT_NEGATIVE) + .setText("it deals damage equal to its power to target creature"), + new TapSourceCost() + ); + ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability.withFlavorWord("Blow Up")); + } + + private BlazingBomb(final BlazingBomb card) { + super(card); + } + + @Override + public BlazingBomb copy() { + return new BlazingBomb(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BleakCovenVampires.java b/Mage.Sets/src/mage/cards/b/BleakCovenVampires.java index 1a372c9bc7f..3bd656b42cd 100644 --- a/Mage.Sets/src/mage/cards/b/BleakCovenVampires.java +++ b/Mage.Sets/src/mage/cards/b/BleakCovenVampires.java @@ -1,10 +1,9 @@ package mage.cards.b; import mage.MageInt; -import mage.abilities.TriggeredAbility; +import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.MetalcraftCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.hint.common.MetalcraftHint; @@ -13,7 +12,6 @@ import mage.cards.CardSetInfo; import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; -import mage.target.Target; import mage.target.TargetPlayer; import java.util.UUID; @@ -23,8 +21,6 @@ import java.util.UUID; */ public final class BleakCovenVampires extends CardImpl { - private static final String effectText = "When {this} enters, if you control three or more artifacts, target player loses 4 life and you gain 4 life."; - public BleakCovenVampires(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); this.subtype.add(SubType.VAMPIRE, SubType.WARRIOR); @@ -33,15 +29,11 @@ public final class BleakCovenVampires extends CardImpl { this.toughness = new MageInt(3); //Metalcraft — When Bleak Coven Vampires enters the battlefield, if you control three or more artifacts, target player loses 4 life and you gain 4 life. - TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new LoseLifeTargetEffect(4), false); - ability.addEffect(new GainLifeEffect(4)); - Target target = new TargetPlayer(); - ability.addTarget(target); - - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, MetalcraftCondition.instance, effectText) - .setAbilityWord(AbilityWord.METALCRAFT) - .addHint(MetalcraftHint.instance) - ); + Ability ability = new EntersBattlefieldTriggeredAbility(new LoseLifeTargetEffect(4)) + .withInterveningIf(MetalcraftCondition.instance); + ability.addEffect(new GainLifeEffect(4).concatBy("and")); + ability.addTarget(new TargetPlayer()); + this.addAbility(ability.setAbilityWord(AbilityWord.METALCRAFT).addHint(MetalcraftHint.instance)); } private BleakCovenVampires(final BleakCovenVampires card) { diff --git a/Mage.Sets/src/mage/cards/b/BlindObedience.java b/Mage.Sets/src/mage/cards/b/BlindObedience.java index 5f0a7323da0..a8c942dd0ad 100644 --- a/Mage.Sets/src/mage/cards/b/BlindObedience.java +++ b/Mage.Sets/src/mage/cards/b/BlindObedience.java @@ -24,7 +24,7 @@ public final class BlindObedience extends CardImpl { // Artifacts and creatures your opponents control enter the battlefield tapped. this.addAbility(new SimpleStaticAbility(new PermanentsEnterBattlefieldTappedEffect( StaticFilters.FILTER_OPPONENTS_PERMANENT_ARTIFACT_OR_CREATURE - ).setText("artifacts and creatures your opponents control enter the battlefield tapped"))); + ).setText("artifacts and creatures your opponents control enter tapped"))); } diff --git a/Mage.Sets/src/mage/cards/b/BlindZealot.java b/Mage.Sets/src/mage/cards/b/BlindZealot.java index 2d1cb8f963b..502ab8e6f1b 100644 --- a/Mage.Sets/src/mage/cards/b/BlindZealot.java +++ b/Mage.Sets/src/mage/cards/b/BlindZealot.java @@ -14,7 +14,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; import mage.target.TargetPermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -39,7 +39,7 @@ public final class BlindZealot extends CardImpl { OneShotEffect effect = new DoIfCostPaid(new DestroyTargetEffect(), new SacrificeSourceCost()); Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(effect, false, true); ability.addTarget(new TargetPermanent(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/Blitzball.java b/Mage.Sets/src/mage/cards/b/Blitzball.java new file mode 100644 index 00000000000..9221a957099 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/Blitzball.java @@ -0,0 +1,111 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.mana.AnyColorManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.DamagedPlayerEvent; +import mage.game.events.GameEvent; +import mage.watchers.Watcher; + +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Blitzball extends CardImpl { + + public Blitzball(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + // {T}: Add one mana of any color. + this.addAbility(new AnyColorManaAbility()); + + // GOOOOAAAALLL! -- {T}, Sacrifice this artifact: Draw two cards. Activate only if an opponent was dealt combat damage by a legendary creature this turn. + Ability ability = new ActivateIfConditionActivatedAbility( + new DrawCardSourceControllerEffect(2), + new TapSourceCost(), BlitzballCondition.instance + ); + ability.addCost(new SacrificeSourceCost()); + this.addAbility(ability.addHint(BlitzballCondition.getHint()).withFlavorWord("GOOOOAAAALLL!"), new BlitzballWatcher()); + } + + private Blitzball(final Blitzball card) { + super(card); + } + + @Override + public Blitzball copy() { + return new Blitzball(this); + } +} + +enum BlitzballCondition implements Condition { + instance; + private static final Hint hint = new ConditionHint(instance); + + public static Hint getHint() { + return hint; + } + + @Override + public boolean apply(Game game, Ability source) { + return BlitzballWatcher.checkPlayer(game, source); + } + + @Override + public String toString() { + return "an opponent was dealt combat damage by a legendary creature this turn"; + } +} + +class BlitzballWatcher extends Watcher { + + private final Set set = new HashSet<>(); + + BlitzballWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.DAMAGED_PLAYER + && ((DamagedPlayerEvent) event).isCombatDamage() + && Optional + .ofNullable(event) + .map(GameEvent::getSourceId) + .map(game::getPermanent) + .filter(permanent -> permanent.isLegendary(game)) + .filter(permanent -> permanent.isCreature(game)) + .isPresent()) { + set.addAll(game.getOpponents(event.getTargetId())); + } + } + + @Override + public void reset() { + super.reset(); + set.clear(); + } + + static boolean checkPlayer(Game game, Ability source) { + return game + .getState() + .getWatcher(BlitzballWatcher.class) + .set + .contains(source.getControllerId()); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BlitzballShot.java b/Mage.Sets/src/mage/cards/b/BlitzballShot.java new file mode 100644 index 00000000000..9809f1b7366 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BlitzballShot.java @@ -0,0 +1,35 @@ +package mage.cards.b; + +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BlitzballShot extends CardImpl { + + public BlitzballShot(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); + + // Target creature gets +3/+3 and gains trample until end of turn. + this.getSpellAbility().addEffect(new BoostTargetEffect(3, 3).setText("target creature gets +3/+3")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect(TrampleAbility.getInstance()).setText("and gains trample until end of turn")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private BlitzballShot(final BlitzballShot card) { + super(card); + } + + @Override + public BlitzballShot copy() { + return new BlitzballShot(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/BlitzballStadium.java b/Mage.Sets/src/mage/cards/b/BlitzballStadium.java new file mode 100644 index 00000000000..11b57549a94 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BlitzballStadium.java @@ -0,0 +1,90 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.combat.CantBeBlockedTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetadjustment.XTargetsCountAdjuster; + +import java.util.UUID; + +/** + * @author balazskristof + */ +public final class BlitzballStadium extends CardImpl { + + public BlitzballStadium(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{X}{U}"); + + // When this artifact enters, support X. + Ability supportAbility = new EntersBattlefieldTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()) + .setText("support X. (Put a +1/+1 counter on each of up to X target creatures.)") + ); + supportAbility.addTarget(new TargetCreaturePermanent(0, 1)); + supportAbility.setTargetAdjuster(new XTargetsCountAdjuster()); + this.addAbility(supportAbility); + + // Go for the Goal! -- {3}, {T}: Until end of turn, target creature gains "Whenever this creature deals combat damage to a player, draw a card for each kind of counter on it" and it can't be blocked this turn. + Ability ability = new SimpleActivatedAbility(new GainAbilityTargetEffect( + new DealsCombatDamageToAPlayerTriggeredAbility(new DrawCardSourceControllerEffect(BlitzballStadiumValue.instance)) + ).setText("Until end of turn, target creature gains \"Whenever this creature deals " + + "combat damage to a player, draw a card for each kind of counter on it\""), new GenericManaCost(3)); + ability.addCost(new TapSourceCost()); + ability.addEffect(new CantBeBlockedTargetEffect().setText("and it can't be blocked this turn")); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability.withFlavorWord("Go for the Goal!")); + } + + private BlitzballStadium(final BlitzballStadium card) { + super(card); + } + + @Override + public BlitzballStadium copy() { + return new BlitzballStadium(this); + } +} + +enum BlitzballStadiumValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + Permanent permanent = sourceAbility.getSourcePermanentOrLKI(game); + if (permanent == null) { + return 0; + } + return permanent.getCounters(game).size(); + } + + @Override + public BlitzballStadiumValue copy() { + return this; + } + + @Override + public String getMessage() { + return "kind of counter on it"; + } + + @Override + public String toString() { + return "1"; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BlizzardStrix.java b/Mage.Sets/src/mage/cards/b/BlizzardStrix.java index a8beebc7b75..b412fd2f57f 100644 --- a/Mage.Sets/src/mage/cards/b/BlizzardStrix.java +++ b/Mage.Sets/src/mage/cards/b/BlizzardStrix.java @@ -5,7 +5,6 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.ExileReturnBattlefieldNextEndStepTargetEffect; import mage.abilities.keyword.FlashAbility; import mage.abilities.keyword.FlyingAbility; @@ -26,7 +25,7 @@ import java.util.UUID; public final class BlizzardStrix extends CardImpl { private static final FilterPermanent filter = new FilterPermanent("another target permanent"); - private static final FilterPermanent filter2 = new FilterPermanent(); + private static final FilterPermanent filter2 = new FilterPermanent("you control another snow permanent"); static { filter.add(AnotherPredicate.instance); @@ -51,12 +50,9 @@ public final class BlizzardStrix extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Blizzard Strix enters the battlefield, if you control another snow permanent, exile target permanent other than Blizzard Strix. Return that card to the battlefield under its owner's control at the beginning of the next end step. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new ExileReturnBattlefieldNextEndStepTargetEffect()), condition, - "When {this} enters, if you control another snow permanent, " + - "exile target permanent other than {this}. Return that card to the battlefield " + - "under its owner's control at the beginning of the next end step." - ); + Ability ability = new EntersBattlefieldTriggeredAbility(new ExileReturnBattlefieldNextEndStepTargetEffect() + .setText("exile target permanent other than {this}. Return that card to the " + + "battlefield under its owner's control at the beginning of the next end step")); ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/BloodchiefAscension.java b/Mage.Sets/src/mage/cards/b/BloodchiefAscension.java index 660376d076c..a93045dbc35 100644 --- a/Mage.Sets/src/mage/cards/b/BloodchiefAscension.java +++ b/Mage.Sets/src/mage/cards/b/BloodchiefAscension.java @@ -1,16 +1,14 @@ - package mage.cards.b; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.PutCardIntoGraveFromAnywhereAllTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.OpponentLostLifeCondition; import mage.abilities.condition.common.SourceHasCounterCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -18,14 +16,17 @@ import mage.constants.ComparisonType; import mage.constants.SetTargetPointer; import mage.constants.TargetController; import mage.counters.CounterType; -import mage.filter.FilterCard; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class BloodchiefAscension extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.QUEST, 3); + public BloodchiefAscension(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{B}"); @@ -36,14 +37,12 @@ public final class BloodchiefAscension extends CardImpl { )); // Whenever a card is put into an opponent's graveyard from anywhere, if Bloodchief Ascension has three or more quest counters on it, you may have that player lose 2 life. If you do, you gain 2 life. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new PutCardIntoGraveFromAnywhereAllTriggeredAbility( - new LoseLifeTargetEffect(2), true, new FilterCard("a card"), TargetController.OPPONENT, SetTargetPointer.PLAYER), - new SourceHasCounterCondition(CounterType.QUEST, 3, Integer.MAX_VALUE), - "Whenever a card is put into an opponent's graveyard from anywhere, if {this} has three or more quest counters on it, you may have that player lose 2 life. If you do, you gain 2 life"); - ability.addEffect(new GainLifeEffect(2)); + Ability ability = new PutCardIntoGraveFromAnywhereAllTriggeredAbility( + new LoseLifeTargetEffect(2), true, StaticFilters.FILTER_CARD_A, + TargetController.OPPONENT, SetTargetPointer.PLAYER + ).withInterveningIf(condition); + ability.addEffect(new GainLifeEffect(2).concatBy("If you do,")); this.addAbility(ability); - } private BloodchiefAscension(final BloodchiefAscension card) { diff --git a/Mage.Sets/src/mage/cards/b/Bloodcurdler.java b/Mage.Sets/src/mage/cards/b/Bloodcurdler.java index bf7db105f9d..126a33b6777 100644 --- a/Mage.Sets/src/mage/cards/b/Bloodcurdler.java +++ b/Mage.Sets/src/mage/cards/b/Bloodcurdler.java @@ -41,7 +41,7 @@ public final class Bloodcurdler extends CardImpl { // Threshold - As long as seven or more cards are in your graveyard, Bloodcurdler gets +1/+1 and has "At the beginning of your end step, exile two cards from your graveyard." Ability thresholdAbility = new SimpleStaticAbility(new ConditionalContinuousEffect( new BoostSourceEffect(1, 1, Duration.WhileOnBattlefield), ThresholdCondition.instance, - "If seven or more cards are in your graveyard, {this} gets +1/+1" + "As long as seven or more cards are in your graveyard, {this} gets +1/+1" )); thresholdAbility.addEffect(new ConditionalContinuousEffect( new GainAbilitySourceEffect(new BeginningOfEndStepTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/b/BloodhallPriest.java b/Mage.Sets/src/mage/cards/b/BloodhallPriest.java index 9a7f4509167..343a50d980d 100644 --- a/Mage.Sets/src/mage/cards/b/BloodhallPriest.java +++ b/Mage.Sets/src/mage/cards/b/BloodhallPriest.java @@ -1,13 +1,10 @@ - package mage.cards.b; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; +import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility; import mage.abilities.condition.common.HellbentCondition; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.keyword.MadnessAbility; import mage.cards.CardImpl; @@ -16,8 +13,9 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.target.common.TargetAnyTarget; +import java.util.UUID; + /** - * * @author fireshoes */ public final class BloodhallPriest extends CardImpl { @@ -29,13 +27,10 @@ public final class BloodhallPriest extends CardImpl { this.toughness = new MageInt(4); // Whenever Bloodhall Priest enters the battlefield or attacks, if you have no cards in hand, Bloodhall Priest deals 2 damage to any target. - TriggeredAbility triggeredAbility = new EntersBattlefieldOrAttacksSourceTriggeredAbility(new DamageTargetEffect(2)); - triggeredAbility.addTarget(new TargetAnyTarget()); - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - triggeredAbility, - HellbentCondition.instance, - "Whenever {this} enters or attacks, if you have no cards in hand, {this} deals 2 damage to any target" - )); + Ability ability = new EntersBattlefieldOrAttacksSourceTriggeredAbility(new DamageTargetEffect(2)) + .withInterveningIf(HellbentCondition.instance); + ability.addTarget(new TargetAnyTarget()); + this.addAbility(ability); // Madness {1}{B}{R} this.addAbility(new MadnessAbility(new ManaCostsImpl<>("{1}{B}{R}"))); diff --git a/Mage.Sets/src/mage/cards/b/BloodlineShaman.java b/Mage.Sets/src/mage/cards/b/BloodlineShaman.java index 81c61082952..549aa1bb86d 100644 --- a/Mage.Sets/src/mage/cards/b/BloodlineShaman.java +++ b/Mage.Sets/src/mage/cards/b/BloodlineShaman.java @@ -89,7 +89,7 @@ class BloodlineShamanEffect extends OneShotEffect { if (card != null) { // If that card is a creature card of the chosen type, put it into your hand. - if (card.isCreature(game) && subType != null && card.getSubtype(game).contains(subType)) { + if (card.isCreature(game) && subType != null && card.hasSubtype(subType, game)) { controller.moveCards(card, Zone.HAND, source, game); // Otherwise, put it into your graveyard. } else { diff --git a/Mage.Sets/src/mage/cards/b/BloodtitheCollector.java b/Mage.Sets/src/mage/cards/b/BloodtitheCollector.java index 5845f5f9b7a..587d9757c0a 100644 --- a/Mage.Sets/src/mage/cards/b/BloodtitheCollector.java +++ b/Mage.Sets/src/mage/cards/b/BloodtitheCollector.java @@ -3,7 +3,6 @@ package mage.cards.b; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.OpponentsLostLifeCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.discard.DiscardEachPlayerEffect; import mage.abilities.hint.common.OpponentsLostLifeHint; import mage.abilities.keyword.FlyingAbility; @@ -32,11 +31,8 @@ public final class BloodtitheCollector extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When this creature enters, if an opponent lost life this turn, each opponent discards a card. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new DiscardEachPlayerEffect(TargetController.OPPONENT)), - OpponentsLostLifeCondition.instance, "When this creature enters, " + - "if an opponent lost life this turn, each opponent discards a card." - ).addHint(OpponentsLostLifeHint.instance)); + this.addAbility(new EntersBattlefieldTriggeredAbility(new DiscardEachPlayerEffect(TargetController.OPPONENT)) + .withInterveningIf(OpponentsLostLifeCondition.instance).addHint(OpponentsLostLifeHint.instance)); } private BloodtitheCollector(final BloodtitheCollector card) { diff --git a/Mage.Sets/src/mage/cards/b/BlotOut.java b/Mage.Sets/src/mage/cards/b/BlotOut.java index 65abb8e9870..cff566f0886 100644 --- a/Mage.Sets/src/mage/cards/b/BlotOut.java +++ b/Mage.Sets/src/mage/cards/b/BlotOut.java @@ -10,9 +10,10 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.common.FilterControlledCreatureOrPlaneswalkerPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.ObjectSourcePlayer; import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -67,11 +68,15 @@ class BlotOutEffect extends OneShotEffect { } } - private static final FilterPermanent filter = new FilterControlledCreatureOrPlaneswalkerPermanent( + private static final FilterPermanent filter = new FilterControlledPermanent( "creature or planeswalker you control with the greatest mana value" ); static { + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + CardType.PLANESWALKER.getPredicate() + )); filter.add(BlotOutPredicate.instance); } diff --git a/Mage.Sets/src/mage/cards/b/BlowflyInfestation.java b/Mage.Sets/src/mage/cards/b/BlowflyInfestation.java index b95a9a435ce..d0e0f7afcd7 100644 --- a/Mage.Sets/src/mage/cards/b/BlowflyInfestation.java +++ b/Mage.Sets/src/mage/cards/b/BlowflyInfestation.java @@ -1,45 +1,38 @@ package mage.cards.b; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; import mage.abilities.common.DiesCreatureTriggeredAbility; -import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.counters.CounterType; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * - * - * * @author jeffwadsworth - * - * - * */ public final class BlowflyInfestation extends CardImpl { - private static final String rule = "Whenever a creature dies, if it had a -1/-1 counter on it, put a -1/-1 counter on target creature."; + private static final FilterPermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(CounterType.M1M1.getPredicate()); + } public BlowflyInfestation(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); - //Whenever a creature dies, if it had a -1/-1 counter on it, put a -1/-1 counter on target creature. - Effect effect = new BlowflyInfestationEffect(); - TriggeredAbility triggeredAbility = new DiesCreatureTriggeredAbility(effect, false, false, true); - triggeredAbility.addTarget(new TargetCreaturePermanent()); - Condition condition = new BlowflyInfestationCondition(); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(triggeredAbility, condition, rule)); - + // Whenever a creature dies, if it had a -1/-1 counter on it, put a -1/-1 counter on target creature. + Ability ability = new DiesCreatureTriggeredAbility( + new AddCountersTargetEffect(CounterType.M1M1.createInstance()), false, filter + ).setTriggerPhrase("Whenever a creature dies, if it had a -1/-1 counter on it, "); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); } private BlowflyInfestation(final BlowflyInfestation card) { @@ -51,47 +44,3 @@ public final class BlowflyInfestation extends CardImpl { return new BlowflyInfestation(this); } } - -class BlowflyInfestationCondition implements Condition { - - private Permanent permanent; - - @Override - public boolean apply(Game game, Ability source) { - for (Effect effect : source.getEffects()) { - if (effect.getTargetPointer().getFirst(game, source) != null) { - permanent = effect.getTargetPointer().getFirstTargetPermanentOrLKI(game, source); - } - } - if (permanent != null) { - return permanent.getCounters(game).containsKey(CounterType.M1M1); - } - return false; - } -} - -class BlowflyInfestationEffect extends OneShotEffect { - - BlowflyInfestationEffect() { - super(Outcome.Detriment); - } - - private BlowflyInfestationEffect(final BlowflyInfestationEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent creature = game.getPermanent(source.getFirstTarget()); - if (creature != null) { - creature.addCounters(CounterType.M1M1.createInstance(), source.getControllerId(), source, game); - return true; - } - return false; - } - - @Override - public BlowflyInfestationEffect copy() { - return new BlowflyInfestationEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/b/BlueMagesCane.java b/Mage.Sets/src/mage/cards/b/BlueMagesCane.java index ebebb243889..dfe6702e266 100644 --- a/Mage.Sets/src/mage/cards/b/BlueMagesCane.java +++ b/Mage.Sets/src/mage/cards/b/BlueMagesCane.java @@ -1,10 +1,6 @@ package mage.cards.b; -import java.util.UUID; - -import mage.ApprovingObject; import mage.abilities.Ability; -import mage.abilities.SpellAbility; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -13,11 +9,11 @@ import mage.abilities.effects.common.continuous.AddCardSubtypeAttachedEffect; import mage.abilities.effects.common.continuous.BoostEquippedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EquipAbility; -import mage.cards.Card; -import mage.constants.*; import mage.abilities.keyword.JobSelectAbility; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.*; import mage.filter.common.FilterInstantOrSorceryCard; import mage.filter.predicate.card.DefendingPlayerOwnsCardPredicate; import mage.game.Game; @@ -25,6 +21,8 @@ import mage.players.Player; import mage.target.common.TargetCardInGraveyard; import mage.util.CardUtil; +import java.util.UUID; + /** * @author balazskristof */ @@ -40,7 +38,7 @@ public final class BlueMagesCane extends CardImpl { public BlueMagesCane(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{U}"); - + this.subtype.add(SubType.EQUIPMENT); // Job select @@ -54,7 +52,7 @@ public final class BlueMagesCane extends CardImpl { Ability attackAbility = new AttacksTriggeredAbility(new BlueMagesCaneEffect()); attackAbility.addTarget(new TargetCardInGraveyard(0, 1, filter)); ability.addEffect(new GainAbilityAttachedEffect(attackAbility, AttachmentType.EQUIPMENT) - .setText("and has \"Whenever this creature attacks, exile up to one target instant or sorcery card from defending player's graveyard. " + .setText(", and has \"Whenever this creature attacks, exile up to one target instant or sorcery card from defending player's graveyard. " + "If you do, copy it. You may cast the copy by paying {3} rather than paying its mana cost.\"") ); this.addAbility(ability); @@ -105,4 +103,4 @@ class BlueMagesCaneEffect extends OneShotEffect { CardUtil.castSingle(controller, source, game, copiedCard, new ManaCostsImpl<>("{3}")); return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/b/BogBadger.java b/Mage.Sets/src/mage/cards/b/BogBadger.java index 95556d1325b..a9e42cba42d 100644 --- a/Mage.Sets/src/mage/cards/b/BogBadger.java +++ b/Mage.Sets/src/mage/cards/b/BogBadger.java @@ -3,7 +3,6 @@ package mage.cards.b; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.KickedCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.KickerAbility; import mage.abilities.keyword.MenaceAbility; @@ -32,13 +31,9 @@ public final class BogBadger extends CardImpl { this.addAbility(new KickerAbility("{B}")); // When Bog Badger enters the battlefield, if it was kicked, creatures you control gain menace until end of turn. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new GainAbilityControlledEffect( - new MenaceAbility(false), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURES - )), KickedCondition.ONCE, "When {this} enters, " + - "if it was kicked, creatures you control gain menace until end of turn. " + - "(A creature with menace can't be blocked except by two or more creatures.)" - )); + this.addAbility(new EntersBattlefieldTriggeredAbility(new GainAbilityControlledEffect( + new MenaceAbility(false), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURES + )).withInterveningIf(KickedCondition.ONCE)); } private BogBadger(final BogBadger card) { diff --git a/Mage.Sets/src/mage/cards/b/Boltbender.java b/Mage.Sets/src/mage/cards/b/Boltbender.java index a88d773039f..68adb07b509 100644 --- a/Mage.Sets/src/mage/cards/b/Boltbender.java +++ b/Mage.Sets/src/mage/cards/b/Boltbender.java @@ -1,9 +1,9 @@ package mage.cards.b; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotNonTargetEffect; import mage.abilities.effects.common.ChooseNewTargetsTargetEffect; import mage.abilities.keyword.DisguiseAbility; import mage.cards.CardImpl; @@ -32,10 +32,9 @@ public final class Boltbender extends CardImpl { this.addAbility(new DisguiseAbility(this, new ManaCostsImpl<>("{1}{R}"))); // When Boltbender is turned face up, you may choose new targets for any number of other spells and/or abilities. - Ability ability = new TurnedFaceUpSourceTriggeredAbility(new ChooseNewTargetsTargetEffect() - .setText("you may choose new targets for any number of other spells and/or abilities")); - ability.addTarget(new TargetStackObject(0, Integer.MAX_VALUE, StaticFilters.FILTER_SPELL_OR_ABILITY)); - this.addAbility(ability); + this.addAbility(new TurnedFaceUpSourceTriggeredAbility(new OneShotNonTargetEffect( + new ChooseNewTargetsTargetEffect().setText("you may choose new targets for any number of other spells and/or abilities"), + new TargetStackObject(0, Integer.MAX_VALUE, StaticFilters.FILTER_SPELL_OR_ABILITY)))); } private Boltbender(final Boltbender card) { diff --git a/Mage.Sets/src/mage/cards/b/BondOfRevival.java b/Mage.Sets/src/mage/cards/b/BondOfRevival.java index e10a10f214c..03e73b20864 100644 --- a/Mage.Sets/src/mage/cards/b/BondOfRevival.java +++ b/Mage.Sets/src/mage/cards/b/BondOfRevival.java @@ -14,9 +14,11 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -69,13 +71,14 @@ class BondOfRevivalEffect extends OneShotEffect { if (player == null || card == null) { return false; } - ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.UntilYourNextTurn); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game) + 1)); if (player.moveCards(card, Zone.BATTLEFIELD, source, game)) { - game.addEffect(effect, source); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); + if (permanent != null) { + ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.UntilYourNextTurn); + effect.setTargetPointer(new FixedTarget(permanent.getId(), game)); + game.addEffect(effect, source); + } } return true; } } - - diff --git a/Mage.Sets/src/mage/cards/b/BoneMask.java b/Mage.Sets/src/mage/cards/b/BoneMask.java index 574a6c78eaf..41e19ca7aab 100644 --- a/Mage.Sets/src/mage/cards/b/BoneMask.java +++ b/Mage.Sets/src/mage/cards/b/BoneMask.java @@ -1,27 +1,26 @@ package mage.cards.b; -import java.util.Set; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.PreventionEffectData; -import mage.abilities.effects.PreventionEffectImpl; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; -import mage.target.TargetSource; +import mage.target.Target; + +import java.util.Set; +import java.util.UUID; /** - * * @author awjackson */ public final class BoneMask extends CardImpl { @@ -29,8 +28,14 @@ public final class BoneMask extends CardImpl { public BoneMask(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); - // {2}, {tap}: The next time a source of your choice would deal damage to you this turn, prevent that damage. Exile cards from the top of your library equal to the damage prevented this way. - Ability ability = new SimpleActivatedAbility(new BoneMaskEffect(), new GenericManaCost(2)); + // {2}, {T}: The next time a source of your choice would deal damage to you this turn, prevent that damage. Exile cards from the top of your library equal to the damage prevented this way. + Ability ability = new SimpleActivatedAbility( + new PreventNextDamageFromChosenSourceEffect( + Duration.EndOfTurn, true, + BoneMaskEffectPreventionApplier.instance + ), + new GenericManaCost(2) + ); ability.addCost(new TapSourceCost()); this.addAbility(ability); } @@ -45,54 +50,24 @@ public final class BoneMask extends CardImpl { } } -class BoneMaskEffect extends PreventionEffectImpl { +enum BoneMaskEffectPreventionApplier implements PreventNextDamageFromChosenSourceEffect.ApplierOnPrevention { + instance; - private final TargetSource target; - - public BoneMaskEffect() { - super(Duration.EndOfTurn); - this.staticText = "The next time a source of your choice would deal damage to you this turn, prevent that damage. " - + "Exile cards from the top of your library equal to the damage prevented this way."; - this.target = new TargetSource(); - } - - private BoneMaskEffect(final BoneMaskEffect effect) { - super(effect); - this.target = effect.target.copy(); - } - - @Override - public BoneMaskEffect copy() { - return new BoneMaskEffect(this); - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - PreventionEffectData preventionData = preventDamageAction(event, source, game); - this.used = true; - this.discard(); - if (preventionData.getPreventedDamage() > 0) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Set cards = controller.getLibrary().getTopCards(game, preventionData.getPreventedDamage()); - controller.moveCards(cards, Zone.EXILED, source, game); - } + public boolean apply(PreventionEffectData data, Target target, GameEvent event, Ability source, Game game) { + if (data == null || data.getPreventedDamage() <= 0) { + return false; } - return false; + int prevented = data.getPreventedDamage(); + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Set cards = controller.getLibrary().getTopCards(game, prevented); + controller.moveCards(cards, Zone.EXILED, source, game); + return true; } - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - return (!this.used - && super.applies(event, source, game) - && event.getTargetId().equals(source.getControllerId()) - && event.getSourceId().equals(target.getFirstTarget()) - ); + public String getText() { + return "Exile cards from the top of your library equal to the damage prevented this way"; } } diff --git a/Mage.Sets/src/mage/cards/b/BoonOfBoseiju.java b/Mage.Sets/src/mage/cards/b/BoonOfBoseiju.java index 00dade8105d..ae213feda49 100644 --- a/Mage.Sets/src/mage/cards/b/BoonOfBoseiju.java +++ b/Mage.Sets/src/mage/cards/b/BoonOfBoseiju.java @@ -1,19 +1,12 @@ package mage.cards.b; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.UntapTargetEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.StaticFilters; -import mage.game.Game; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -28,11 +21,13 @@ public final class BoonOfBoseiju extends CardImpl { // Target creature gets +X/+X until end of turn, where X is the greatest mana value among permanents you control. Untap that creature. this.getSpellAbility().addEffect(new BoostTargetEffect( - BoonOfBoseijuValue.instance, BoonOfBoseijuValue.instance, Duration.EndOfTurn + GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_PERMANENTS, + GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_PERMANENTS, + Duration.EndOfTurn )); + this.getSpellAbility().addHint(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_PERMANENTS.getHint()); this.getSpellAbility().addEffect(new UntapTargetEffect().setText("Untap it")); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - this.getSpellAbility().addHint(BoonOfBoseijuValue.getHint()); } private BoonOfBoseiju(final BoonOfBoseiju card) { @@ -43,36 +38,4 @@ public final class BoonOfBoseiju extends CardImpl { public BoonOfBoseiju copy() { return new BoonOfBoseiju(this); } -} - -enum BoonOfBoseijuValue implements DynamicValue { - instance; - private static final Hint hint = new ValueHint("The greatest mana value among permanents you control", instance); - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - return game.getBattlefield().getActivePermanents( - StaticFilters.FILTER_CONTROLLED_PERMANENT, - sourceAbility.getControllerId(), sourceAbility, game - ).stream().mapToInt(MageObject::getManaValue).max().orElse(0); - } - - @Override - public BoonOfBoseijuValue copy() { - return this; - } - - @Override - public String getMessage() { - return "the greatest mana value among permanents you control"; - } - - @Override - public String toString() { - return "X"; - } - - public static Hint getHint() { - return hint; - } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/b/BoonReflection.java b/Mage.Sets/src/mage/cards/b/BoonReflection.java index b377bebe77a..846442bdece 100644 --- a/Mage.Sets/src/mage/cards/b/BoonReflection.java +++ b/Mage.Sets/src/mage/cards/b/BoonReflection.java @@ -1,17 +1,10 @@ package mage.cards.b; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.replacement.GainDoubleLifeReplacementEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.util.CardUtil; import java.util.UUID; @@ -24,7 +17,7 @@ public final class BoonReflection extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{W}"); // If you would gain life, you gain twice that much life instead. - this.addAbility(new SimpleStaticAbility(new BoonReflectionEffect())); + this.addAbility(new SimpleStaticAbility(new GainDoubleLifeReplacementEffect())); } private BoonReflection(final BoonReflection card) { @@ -36,36 +29,3 @@ public final class BoonReflection extends CardImpl { return new BoonReflection(this); } } - -class BoonReflectionEffect extends ReplacementEffectImpl { - - BoonReflectionEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "If you would gain life, you gain twice that much life instead"; - } - - private BoonReflectionEffect(final BoonReflectionEffect effect) { - super(effect); - } - - @Override - public BoonReflectionEffect copy() { - return new BoonReflectionEffect(this); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - event.setAmount(CardUtil.overflowMultiply(event.getAmount(), 2)); - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.GAIN_LIFE; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - return event.getPlayerId().equals(source.getControllerId()) && (source.getControllerId() != null); - } -} diff --git a/Mage.Sets/src/mage/cards/b/BorealOutrider.java b/Mage.Sets/src/mage/cards/b/BorealOutrider.java index c66c0c8c7b0..94b9fd62674 100644 --- a/Mage.Sets/src/mage/cards/b/BorealOutrider.java +++ b/Mage.Sets/src/mage/cards/b/BorealOutrider.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.ReplacementEffectImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -35,14 +34,10 @@ public final class BorealOutrider extends CardImpl { this.toughness = new MageInt(2); // Whenever you cast a creature spell, if {S} of any of that spell's color was spent to cast it, that creature enters the battlefield with an additional +1/+1 counter on it. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new SpellCastControllerTriggeredAbility( - new BorealOutriderEffect(), StaticFilters.FILTER_SPELL_A_CREATURE, - false, SetTargetPointer.SPELL - ), BorealOutriderCondition.instance, "Whenever you cast a creature spell, " + - "if {S} of any of that spell's colors was spent to cast it, that creature " + - "enters the battlefield with an additional +1/+1 counter on it." - )); + this.addAbility(new SpellCastControllerTriggeredAbility( + new BorealOutriderEffect(), StaticFilters.FILTER_SPELL_A_CREATURE, + false, SetTargetPointer.SPELL + ).withInterveningIf(BorealOutriderCondition.instance)); } private BorealOutrider(final BorealOutrider card) { @@ -63,6 +58,11 @@ enum BorealOutriderCondition implements Condition { Spell spell = (Spell) source.getEffects().get(0).getValue("spellCast"); return spell != null && ManaPaidSourceWatcher.checkSnowColor(spell, game); } + + @Override + public String toString() { + return "{S} of any of that spell's color was spent to cast it"; + } } class BorealOutriderEffect extends ReplacementEffectImpl { diff --git a/Mage.Sets/src/mage/cards/b/BorosStrikeCaptain.java b/Mage.Sets/src/mage/cards/b/BorosStrikeCaptain.java new file mode 100644 index 00000000000..f753b05abf7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BorosStrikeCaptain.java @@ -0,0 +1,142 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.keyword.BattalionAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.util.CardUtil; +import mage.watchers.Watcher; + +import java.util.*; + +/** + * @author TheElk801 + */ +public final class BorosStrikeCaptain extends CardImpl { + + public BorosStrikeCaptain(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R/W}{R/W}"); + + this.subtype.add(SubType.MINOTAUR); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Battalion -- Whenever Boros Strike-Captain and at least two other creatures attack, exile the top card of your library. During any turn you attacked with three or more creatures, you may play that card. + this.addAbility(new BattalionAbility(new BorosStrikeCaptainEffect()) + .addHint(BorosStrikeCaptainCondition.getHint()), new BorosStrikeCaptainWatcher()); + } + + private BorosStrikeCaptain(final BorosStrikeCaptain card) { + super(card); + } + + @Override + public BorosStrikeCaptain copy() { + return new BorosStrikeCaptain(this); + } +} + +class BorosStrikeCaptainEffect extends OneShotEffect { + + BorosStrikeCaptainEffect() { + super(Outcome.Benefit); + staticText = "exile the top card of your library. During any turn you attacked " + + "with three or more creatures, you may play that card"; + } + + private BorosStrikeCaptainEffect(final BorosStrikeCaptainEffect effect) { + super(effect); + } + + @Override + public BorosStrikeCaptainEffect copy() { + return new BorosStrikeCaptainEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Card card = player.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + player.moveCards(card, Zone.EXILED, source, game); + CardUtil.makeCardPlayable( + game, source, card, false, Duration.Custom, false, + player.getId(), BorosStrikeCaptainCondition.instance + ); + return true; + } +} + +enum BorosStrikeCaptainCondition implements Condition { + instance; + private static final Hint hint = new ConditionHint(instance); + + public static Hint getHint() { + return hint; + } + + @Override + public boolean apply(Game game, Ability source) { + return BorosStrikeCaptainWatcher.checkPlayer(game, source); + } + + @Override + public String toString() { + return "you attacked with three or more creatures this turn"; + } +} + +class BorosStrikeCaptainWatcher extends Watcher { + + private final Map> map = new HashMap<>(); + + BorosStrikeCaptainWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.ATTACKER_DECLARED) { + return; + } + Optional.ofNullable(event) + .map(GameEvent::getTargetId) + .map(game::getPermanent) + .ifPresent(permanent -> map + .computeIfAbsent(permanent.getControllerId(), x -> new HashSet<>()) + .add(new MageObjectReference(permanent, game))); + + } + + @Override + public void reset() { + super.reset(); + map.clear(); + } + + static boolean checkPlayer(Game game, Ability source) { + return game + .getState() + .getWatcher(BorosStrikeCaptainWatcher.class) + .map + .getOrDefault(source.getControllerId(), Collections.emptySet()) + .size() >= 3; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BortukBonerattle.java b/Mage.Sets/src/mage/cards/b/BortukBonerattle.java index 1d7bb539f7b..53242931f0c 100644 --- a/Mage.Sets/src/mage/cards/b/BortukBonerattle.java +++ b/Mage.Sets/src/mage/cards/b/BortukBonerattle.java @@ -1,30 +1,29 @@ package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.CastFromEverywhereSourceCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.dynamicvalue.common.DomainValue; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.abilities.hint.common.DomainHint; import mage.cards.Card; -import mage.constants.AbilityWord; -import mage.constants.SubType; -import mage.constants.SuperType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.filter.StaticFilters; import mage.game.Game; import mage.target.common.TargetCardInYourGraveyard; +import java.util.UUID; + /** - * * @author weirddan455 */ public final class BortukBonerattle extends CardImpl { @@ -41,18 +40,12 @@ public final class BortukBonerattle extends CardImpl { // Domain — When Bortuk Bonerattle enters the battlefield, if you cast it, choose target creature card in your graveyard. // Return that card to the battlefield if its mana value is less than or equal to the number of basic land types among lands you control. // Otherwise, put it into your hand. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new ConditionalOneShotEffect( - new ReturnFromGraveyardToBattlefieldTargetEffect(), - new ReturnFromGraveyardToHandTargetEffect(), - BortukBonerattleCondition.instance, - null - )), - CastFromEverywhereSourceCondition.instance, - "When {this} enters, if you cast it, choose target creature card in your graveyard. " + - "Return that card to the battlefield if its mana value is less than or equal to the number of basic land types among lands you control. " + - "Otherwise, put it into your hand." - ); + Ability ability = new EntersBattlefieldTriggeredAbility(new ConditionalOneShotEffect( + new ReturnFromGraveyardToBattlefieldTargetEffect(), new ReturnFromGraveyardToHandTargetEffect(), + BortukBonerattleCondition.instance, "choose target creature card in your graveyard. " + + "Return that card to the battlefield if its mana value is less than or equal to " + + "the number of basic land types among lands you control. Otherwise, put it into your hand." + )).withInterveningIf(CastFromEverywhereSourceCondition.instance); ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); ability.addHint(DomainHint.instance); ability.setAbilityWord(AbilityWord.DOMAIN); diff --git a/Mage.Sets/src/mage/cards/b/BouncersBeatdown.java b/Mage.Sets/src/mage/cards/b/BouncersBeatdown.java index e0033956622..14d83654312 100644 --- a/Mage.Sets/src/mage/cards/b/BouncersBeatdown.java +++ b/Mage.Sets/src/mage/cards/b/BouncersBeatdown.java @@ -4,7 +4,7 @@ import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceTargetsPermanentCondition; -import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.ExileTargetIfDiesEffect; import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; @@ -41,10 +41,11 @@ public final class BouncersBeatdown extends CardImpl { ).setRuleAtTheTop(true)); // Bouncer's Beatdown deals X damage to target creature or planeswalker, where X is the greatest power among creatures you control. If that creature or planeswalker would die this turn, exile it instead. - this.getSpellAbility().addEffect(new DamageTargetEffect(GreatestPowerAmongControlledCreaturesValue.instance)); + this.getSpellAbility().addEffect(new DamageTargetEffect(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES) + .setText("{this} deals X damage to target creature or planeswalker, where X is the greatest power among creatures you control")); + this.getSpellAbility().addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint()); this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); this.getSpellAbility().addEffect(new ExileTargetIfDiesEffect("creature or planeswalker")); - this.getSpellAbility().addHint(GreatestPowerAmongControlledCreaturesValue.getHint()); } private BouncersBeatdown(final BouncersBeatdown card) { diff --git a/Mage.Sets/src/mage/cards/b/BoundaryLandsRanger.java b/Mage.Sets/src/mage/cards/b/BoundaryLandsRanger.java index db8efb5c5d7..ff8377a9320 100644 --- a/Mage.Sets/src/mage/cards/b/BoundaryLandsRanger.java +++ b/Mage.Sets/src/mage/cards/b/BoundaryLandsRanger.java @@ -1,13 +1,12 @@ package mage.cards.b; import mage.MageInt; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.abilities.condition.common.FerociousCondition; import mage.abilities.costs.common.DiscardCardCost; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.hint.common.FerociousHint; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -29,14 +28,9 @@ public final class BoundaryLandsRanger extends CardImpl { this.toughness = new MageInt(2); // At the beginning of combat on your turn, if you control a creature with power 4 or greater, you may discard a card. If you do, draw a card. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfCombatTriggeredAbility( - new DoIfCostPaid( - new DrawCardSourceControllerEffect(1), new DiscardCardCost() - ) - ), FerociousCondition.instance, "At the beginning of combat on your turn, if you control " + - "a creature with power 4 or greater, you may discard a card. If you do, draw a card." - ).addHint(FerociousHint.instance)); + this.addAbility(new BeginningOfCombatTriggeredAbility(new DoIfCostPaid( + new DrawCardSourceControllerEffect(1), new DiscardCardCost() + )).withInterveningIf(FerociousCondition.instance).addHint(FerociousHint.instance)); } private BoundaryLandsRanger(final BoundaryLandsRanger card) { diff --git a/Mage.Sets/src/mage/cards/b/BounteousKirin.java b/Mage.Sets/src/mage/cards/b/BounteousKirin.java index aa4cb58e370..eb6700bb3cb 100644 --- a/Mage.Sets/src/mage/cards/b/BounteousKirin.java +++ b/Mage.Sets/src/mage/cards/b/BounteousKirin.java @@ -33,7 +33,7 @@ public final class BounteousKirin extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever you cast a Spirit or Arcane spell, you may gain life equal to that spell's converted mana cost. this.addAbility(new SpellCastControllerTriggeredAbility( - new BounteousKirinEffect(), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, + new BounteousKirinEffect(), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, true, SetTargetPointer.SPELL )); } diff --git a/Mage.Sets/src/mage/cards/b/BountyOfSkemfar.java b/Mage.Sets/src/mage/cards/b/BountyOfSkemfar.java index 03729cb3666..f364d7ddfaa 100644 --- a/Mage.Sets/src/mage/cards/b/BountyOfSkemfar.java +++ b/Mage.Sets/src/mage/cards/b/BountyOfSkemfar.java @@ -48,8 +48,8 @@ class BountyOfSkemfarEffect extends OneShotEffect { BountyOfSkemfarEffect() { super(Outcome.Benefit); - staticText = "reveal the top six cards of your library. You may put a land card from among them " + - "onto the battlefield tapped and an Elf card from among them into your hand. " + + staticText = "reveal the top six cards of your library. You may put up to one land card from among them " + + "onto the battlefield tapped and up to one Elf card from among them into your hand. " + "Put the rest on the bottom of your library in a random order"; } diff --git a/Mage.Sets/src/mage/cards/b/BoxingRing.java b/Mage.Sets/src/mage/cards/b/BoxingRing.java index 84ffb7d9453..9c9fbb84a4a 100644 --- a/Mage.Sets/src/mage/cards/b/BoxingRing.java +++ b/Mage.Sets/src/mage/cards/b/BoxingRing.java @@ -14,7 +14,6 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.ObjectSourcePlayer; import mage.filter.predicate.ObjectSourcePlayerPredicate; @@ -23,10 +22,10 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.permanent.token.TreasureToken; import mage.target.TargetPermanent; +import mage.util.CardUtil; import mage.watchers.Watcher; import java.util.HashSet; -import java.util.Objects; import java.util.Set; import java.util.UUID; @@ -75,19 +74,11 @@ enum BoxingRingPredicate implements ObjectSourcePlayerPredicate { @Override public boolean apply(ObjectSourcePlayer input, Game game) { - return input - .getObject() - .getManaValue() - == input - .getSource() - .getEffects() - .stream() - .map(effect -> effect.getValue("permanentEnteringBattlefield")) - .map(Permanent.class::cast) - .filter(Objects::nonNull) + return CardUtil + .getEffectValueFromAbility(input.getSource(), "permanentEnteringBattlefield", Permanent.class) .map(MageObject::getManaValue) - .findFirst() - .orElse(-1); + .filter(x -> x == input.getObject().getManaValue()) + .isPresent(); } } diff --git a/Mage.Sets/src/mage/cards/b/BraidsArisenNightmare.java b/Mage.Sets/src/mage/cards/b/BraidsArisenNightmare.java index 54769817e1d..809a042df26 100644 --- a/Mage.Sets/src/mage/cards/b/BraidsArisenNightmare.java +++ b/Mage.Sets/src/mage/cards/b/BraidsArisenNightmare.java @@ -67,7 +67,7 @@ class BraidsArisenNightmareEffect extends OneShotEffect { public BraidsArisenNightmareEffect() { super(Outcome.Sacrifice); this.staticText = "you may sacrifice an artifact, creature, enchantment, land, or planeswalker. " + - "If you do, each opponent may sacrifice a permanent that shares a card type with it. " + + "If you do, each opponent may sacrifice a permanent of their choice that shares a card type with it. " + "For each opponent who doesn't, that player loses 2 life and you draw a card"; } diff --git a/Mage.Sets/src/mage/cards/b/BrainInAJar.java b/Mage.Sets/src/mage/cards/b/BrainInAJar.java index 60e702b582a..ead1917f3c6 100644 --- a/Mage.Sets/src/mage/cards/b/BrainInAJar.java +++ b/Mage.Sets/src/mage/cards/b/BrainInAJar.java @@ -8,6 +8,7 @@ import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.dynamicvalue.common.GetXValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.keyword.ScryEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -44,7 +45,7 @@ public final class BrainInAJar extends CardImpl { this.addAbility(ability); // {3}, {T}, Remove X charge counters from Brain in a Jar: Scry X. - ability = new SimpleActivatedAbility(new BrainInAJarScryEffect(), new GenericManaCost(3)); + ability = new SimpleActivatedAbility(new ScryEffect(GetXValue.instance), new GenericManaCost(3)); ability.addCost(new TapSourceCost()); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.CHARGE)); this.addAbility(ability); @@ -90,34 +91,4 @@ class BrainInAJarCastEffect extends OneShotEffect { filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, counters)); return CardUtil.castSpellWithAttributesForFree(controller, source, game, controller.getHand(), filter); } -} - -class BrainInAJarScryEffect extends OneShotEffect { - - BrainInAJarScryEffect() { - super(Outcome.Benefit); - this.staticText = "Scry X"; - } - - private BrainInAJarScryEffect(final BrainInAJarScryEffect effect) { - super(effect); - } - - @Override - public BrainInAJarScryEffect copy() { - return new BrainInAJarScryEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - int x = GetXValue.instance.calculate(game, source, this); - if (x > 0) { - return controller.scry(x, source, game); - } - return true; - } - return false; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/b/BraskasFinalAeon.java b/Mage.Sets/src/mage/cards/b/BraskasFinalAeon.java index 13c1c757d9a..a59cb3fc942 100644 --- a/Mage.Sets/src/mage/cards/b/BraskasFinalAeon.java +++ b/Mage.Sets/src/mage/cards/b/BraskasFinalAeon.java @@ -44,7 +44,7 @@ public final class BraskasFinalAeon extends CardImpl { ability.addEffect(new SacrificeOpponentsEffect(2, StaticFilters.FILTER_PERMANENT_CREATURES)); ability.withFlavorWord("Ultimate Jecht Shot"); }); - this.addAbility(sagaAbility); + this.addAbility(sagaAbility.withShowSacText(true)); // Menace this.addAbility(new MenaceAbility()); diff --git a/Mage.Sets/src/mage/cards/b/BrassSquire.java b/Mage.Sets/src/mage/cards/b/BrassSquire.java index 4418896aed4..8ad5dcedc0f 100644 --- a/Mage.Sets/src/mage/cards/b/BrassSquire.java +++ b/Mage.Sets/src/mage/cards/b/BrassSquire.java @@ -1,46 +1,35 @@ - package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachTargetToTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterControlledPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; -import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; /** - * * @author North */ public final class BrassSquire extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("Equipment you control"); - - static { - filter.add(SubType.EQUIPMENT.getPredicate()); - } - public BrassSquire(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); this.subtype.add(SubType.MYR); this.power = new MageInt(1); this.toughness = new MageInt(3); // {tap}: Attach target Equipment you control to target creature you control. - Ability ability = new SimpleActivatedAbility(new EquipEffect(), new TapSourceCost()); - ability.addTarget(new TargetControlledPermanent(filter)); + Ability ability = new SimpleActivatedAbility(new AttachTargetToTargetEffect(), new TapSourceCost()); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT)); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); } @@ -54,30 +43,3 @@ public final class BrassSquire extends CardImpl { return new BrassSquire(this); } } - -class EquipEffect extends OneShotEffect { - - EquipEffect() { - super(Outcome.BoostCreature); - staticText = "Attach target Equipment you control to target creature you control"; - } - - private EquipEffect(final EquipEffect effect) { - super(effect); - } - - @Override - public EquipEffect copy() { - return new EquipEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent equipment = game.getPermanent(source.getFirstTarget()); - Permanent creature = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if (creature != null && equipment != null) { - return creature.addAttachment(equipment.getId(), source, game); - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/b/BrasssTunnelGrinder.java b/Mage.Sets/src/mage/cards/b/BrasssTunnelGrinder.java index 8700a24950f..16459284827 100644 --- a/Mage.Sets/src/mage/cards/b/BrasssTunnelGrinder.java +++ b/Mage.Sets/src/mage/cards/b/BrasssTunnelGrinder.java @@ -1,8 +1,8 @@ package mage.cards.b; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.DescendedThisTurnCondition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.decorator.ConditionalOneShotEffect; @@ -12,6 +12,7 @@ import mage.abilities.effects.common.RemoveAllCountersSourceEffect; import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.keyword.TransformAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -30,6 +31,8 @@ import java.util.UUID; */ public final class BrasssTunnelGrinder extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.BORE, 3); + public BrasssTunnelGrinder(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{R}"); this.secondSideCardClazz = mage.cards.t.TecutlanTheSearingRift.class; @@ -45,14 +48,10 @@ public final class BrasssTunnelGrinder extends CardImpl { TargetController.YOU, new AddCountersSourceEffect(CounterType.BORE.createInstance()), false, DescendedThisTurnCondition.instance ); - - ConditionalOneShotEffect secondCheck = new ConditionalOneShotEffect( - new RemoveAllCountersSourceEffect(CounterType.BORE), - new SourceHasCounterCondition(CounterType.BORE, 3, Integer.MAX_VALUE), + ability.addEffect(new ConditionalOneShotEffect( + new RemoveAllCountersSourceEffect(CounterType.BORE), condition, "Then if there are three or more bore counters on it, remove those counters and transform it" - ); - secondCheck.addEffect(new TransformSourceEffect()); - ability.addEffect(secondCheck); + ).addEffect(new TransformSourceEffect())); ability.addHint(DescendedThisTurnCount.getHint()); this.addAbility(ability, new DescendedWatcher()); } diff --git a/Mage.Sets/src/mage/cards/b/Brawn.java b/Mage.Sets/src/mage/cards/b/Brawn.java index 10e7f902d69..4ca0a1e8a4a 100644 --- a/Mage.Sets/src/mage/cards/b/Brawn.java +++ b/Mage.Sets/src/mage/cards/b/Brawn.java @@ -24,7 +24,7 @@ import mage.filter.common.FilterCreaturePermanent; */ public final class Brawn extends CardImpl { - private static final String ruleText = "As long as Brawn is in your graveyard and you control a Forest, creatures you control have trample"; + private static final String ruleText = "As long as this card is in your graveyard and you control a Forest, creatures you control have trample"; private static final FilterControlledPermanent filter = new FilterControlledPermanent("Forest"); diff --git a/Mage.Sets/src/mage/cards/b/BrazenBlademaster.java b/Mage.Sets/src/mage/cards/b/BrazenBlademaster.java index 9aafe6f1931..f929f8551ea 100644 --- a/Mage.Sets/src/mage/cards/b/BrazenBlademaster.java +++ b/Mage.Sets/src/mage/cards/b/BrazenBlademaster.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -12,7 +11,7 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.constants.Duration; import mage.constants.SubType; -import mage.filter.StaticFilters; +import mage.filter.common.FilterArtifactPermanent; import java.util.UUID; @@ -22,22 +21,22 @@ import java.util.UUID; public final class BrazenBlademaster extends CardImpl { private static final Condition condition = new PermanentsOnTheBattlefieldCondition( - StaticFilters.FILTER_PERMANENT_ARTIFACT, ComparisonType.MORE_THAN, 1, true); + new FilterArtifactPermanent("you control two or more artifacts"), + ComparisonType.MORE_THAN, 1, true + ); public BrazenBlademaster(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); - + this.subtype.add(SubType.ORC); this.subtype.add(SubType.PIRATE); this.power = new MageInt(2); this.toughness = new MageInt(3); // Whenever Brazen Blademaster attacks while you control two or more artifacts, it gets +2/+1 until end of turn. - this.addAbility(new ConditionalTriggeredAbility( - new AttacksTriggeredAbility(new BoostSourceEffect(2, 1, Duration.EndOfTurn), false), - condition, - "Whenever {this} attacks while you control two or more artifacts, it gets +2/+1 until end of turn." - )); + this.addAbility(new AttacksTriggeredAbility( + new BoostSourceEffect(2, 1, Duration.EndOfTurn, "it") + ).withTriggerCondition(condition)); } private BrazenBlademaster(final BrazenBlademaster card) { diff --git a/Mage.Sets/src/mage/cards/b/BrazenCannonade.java b/Mage.Sets/src/mage/cards/b/BrazenCannonade.java index 1c37415cb3d..b0e990ee842 100644 --- a/Mage.Sets/src/mage/cards/b/BrazenCannonade.java +++ b/Mage.Sets/src/mage/cards/b/BrazenCannonade.java @@ -1,13 +1,11 @@ package mage.cards.b; -import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfPostcombatMainTriggeredAbility; import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.condition.common.RaidCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DamagePlayersEffect; import mage.abilities.effects.common.ExileTopXMayPlayUntilEffect; import mage.abilities.hint.common.RaidHint; +import mage.abilities.triggers.BeginningOfPostcombatMainTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.AbilityWord; @@ -42,16 +40,11 @@ public final class BrazenCannonade extends CardImpl { )); // Raid -- At the beginning of your postcombat main phase, if you attacked with a creature this turn, exile the top card of your library. Until end of combat on your next turn, you may play that card. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new BeginningOfPostcombatMainTriggeredAbility( - new ExileTopXMayPlayUntilEffect( - 1, Duration.UntilEndCombatOfYourNextTurn - ), false - ), RaidCondition.instance, "At the beginning of each of your postcombat main phases, " + - "if you attacked this turn, exile the top card of your library. " + - "Until end of combat on your next turn, you may play that card." - ); - this.addAbility(ability.setAbilityWord(AbilityWord.RAID).addHint(RaidHint.instance), new PlayerAttackedWatcher()); + this.addAbility(new BeginningOfPostcombatMainTriggeredAbility( + new ExileTopXMayPlayUntilEffect(1, Duration.UntilEndCombatOfYourNextTurn), false + ).withInterveningIf(RaidCondition.instance) + .setAbilityWord(AbilityWord.RAID) + .addHint(RaidHint.instance), new PlayerAttackedWatcher()); } private BrazenCannonade(final BrazenCannonade card) { diff --git a/Mage.Sets/src/mage/cards/b/BreachingLeviathan.java b/Mage.Sets/src/mage/cards/b/BreachingLeviathan.java index 531bbb2f92a..4885b678e02 100644 --- a/Mage.Sets/src/mage/cards/b/BreachingLeviathan.java +++ b/Mage.Sets/src/mage/cards/b/BreachingLeviathan.java @@ -1,15 +1,10 @@ - package mage.cards.b; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; @@ -26,25 +21,25 @@ import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTargets; import mage.watchers.common.CastFromHandWatcher; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class BreachingLeviathan extends CardImpl { public BreachingLeviathan(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{7}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{7}{U}{U}"); this.subtype.add(SubType.LEVIATHAN); this.power = new MageInt(9); this.toughness = new MageInt(9); // When Breaching Leviathan enters the battlefield, if you cast it from your hand, tap all nonblue creatures. Those creatures don't untap during their controllers' next untap steps. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new BreachingLeviathanEffect(), false), - CastFromHandSourcePermanentCondition.instance, - "When {this} enters, if you cast it from your hand, tap all nonblue creatures. Those creatures don't untap during their controllers' next untap steps."), - new CastFromHandWatcher()); + this.addAbility(new EntersBattlefieldTriggeredAbility(new BreachingLeviathanEffect(), false) + .withInterveningIf(CastFromHandSourcePermanentCondition.instance), new CastFromHandWatcher()); } private BreachingLeviathan(final BreachingLeviathan card) { diff --git a/Mage.Sets/src/mage/cards/b/BreakOut.java b/Mage.Sets/src/mage/cards/b/BreakOut.java index ac9b8c370db..fd54bde0a53 100644 --- a/Mage.Sets/src/mage/cards/b/BreakOut.java +++ b/Mage.Sets/src/mage/cards/b/BreakOut.java @@ -23,6 +23,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetCard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @@ -93,7 +94,7 @@ class BreakOutEffect extends OneShotEffect { if (pickedCard.getManaValue() <= 2 && controller.chooseUse(Outcome.PutCardInPlay, "Put it onto the battlefield?", source, game) && controller.moveCards(pickedCard, Zone.BATTLEFIELD, source, game)) { - Permanent permanent = game.getPermanent(pickedCard.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(pickedCard, game); if (permanent != null) { ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); effect.setTargetPointer(new FixedTarget(permanent, game)); diff --git a/Mage.Sets/src/mage/cards/b/BreakingEntering.java b/Mage.Sets/src/mage/cards/b/BreakingEntering.java index b3ff9d74011..cb6f44a56db 100644 --- a/Mage.Sets/src/mage/cards/b/BreakingEntering.java +++ b/Mage.Sets/src/mage/cards/b/BreakingEntering.java @@ -17,11 +17,13 @@ import mage.constants.SpellAbilityType; import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; import mage.target.TargetPlayer; import mage.target.common.TargetCardInGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; public final class BreakingEntering extends SplitCard { @@ -68,22 +70,22 @@ class EnteringReturnFromGraveyardToBattlefieldEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Target target = new TargetCardInGraveyard(StaticFilters.FILTER_CARD_CREATURE); - target.withNotTarget(true); - if (target.canChoose(source.getControllerId(), source, game) - && controller.chooseTarget(outcome, target, source, game)) { - Card card = game.getCard(target.getFirstTarget()); - if (card != null) { - if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { - ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); - effect.setTargetPointer(new FixedTarget(card.getId(), game)); - game.addEffect(effect, source); - } + if (controller == null) { + return false; + } + Target target = new TargetCardInGraveyard(StaticFilters.FILTER_CARD_CREATURE).withNotTarget(true); + if (target.canChoose(source.getControllerId(), source, game) + && controller.chooseTarget(outcome, target, source, game)) { + Card card = game.getCard(target.getFirstTarget()); + if (card != null && controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); + if (permanent != null) { + ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); + effect.setTargetPointer(new FixedTarget(permanent.getId(), game)); + game.addEffect(effect, source); } } - return true; } - return false; + return true; } } diff --git a/Mage.Sets/src/mage/cards/b/BreathOfTheSleepless.java b/Mage.Sets/src/mage/cards/b/BreathOfTheSleepless.java index 2784d29afdf..08650d32144 100644 --- a/Mage.Sets/src/mage/cards/b/BreathOfTheSleepless.java +++ b/Mage.Sets/src/mage/cards/b/BreathOfTheSleepless.java @@ -4,7 +4,6 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.condition.common.OpponentsTurnCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.TapTargetEffect; import mage.abilities.effects.common.continuous.CastAsThoughItHadFlashAllEffect; import mage.abilities.hint.common.OpponentsTurnHint; @@ -39,10 +38,9 @@ public final class BreathOfTheSleepless extends CardImpl { )); // Whenever you cast a creature spell during an opponent's turn, tap up to one target creature. - Ability ability = new ConditionalTriggeredAbility(new SpellCastControllerTriggeredAbility( + Ability ability = new SpellCastControllerTriggeredAbility( new TapTargetEffect(), StaticFilters.FILTER_SPELL_A_CREATURE, false - ), OpponentsTurnCondition.instance, "Whenever you cast a creature spell " + - "during an opponent's turn, tap up to one target creature."); + ).withTriggerCondition(OpponentsTurnCondition.instance); ability.addTarget(new TargetCreaturePermanent(0, 1)); this.addAbility(ability.addHint(OpponentsTurnHint.instance)); } diff --git a/Mage.Sets/src/mage/cards/b/BriarknitKami.java b/Mage.Sets/src/mage/cards/b/BriarknitKami.java index fedf9683a7a..961faa6a646 100644 --- a/Mage.Sets/src/mage/cards/b/BriarknitKami.java +++ b/Mage.Sets/src/mage/cards/b/BriarknitKami.java @@ -27,7 +27,7 @@ public final class BriarknitKami extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); // Whenever you cast a Spirit or Arcane spell, put a +1/+1 counter on target creature. - Ability ability = new SpellCastControllerTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false); + Ability ability = new SpellCastControllerTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, false); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/BridesGown.java b/Mage.Sets/src/mage/cards/b/BridesGown.java index f1ff3a5670e..f4c477ce638 100644 --- a/Mage.Sets/src/mage/cards/b/BridesGown.java +++ b/Mage.Sets/src/mage/cards/b/BridesGown.java @@ -17,12 +17,9 @@ import mage.constants.AttachmentType; import mage.constants.CardType; import mage.constants.SubType; import mage.filter.FilterPermanent; -import mage.filter.common.FilterEquipmentPermanent; -import mage.filter.predicate.ObjectSourcePlayer; -import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.filter.StaticFilters; import mage.filter.predicate.mageobject.NamePredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.filter.predicate.permanent.AttachedToPredicate; import java.util.UUID; @@ -31,11 +28,11 @@ import java.util.UUID; */ public final class BridesGown extends CardImpl { - private static final FilterPermanent filter = new FilterEquipmentPermanent(); + private static final FilterPermanent filter = new FilterPermanent(SubType.EQUIPMENT, ""); static { filter.add(new NamePredicate("Groom's Finery")); - filter.add(BridesGownPredicate.instance); + filter.add(new AttachedToPredicate(StaticFilters.FILTER_CONTROLLED_CREATURE)); } private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter, false); @@ -75,15 +72,3 @@ public final class BridesGown extends CardImpl { return new BridesGown(this); } } - -enum BridesGownPredicate implements ObjectSourcePlayerPredicate { - instance; - - @Override - public boolean apply(ObjectSourcePlayer input, Game game) { - Permanent permanent = game.getPermanent(input.getObject().getAttachedTo()); - return permanent != null - && permanent.isCreature(game) - && permanent.isControlledBy(input.getPlayerId()); - } -} diff --git a/Mage.Sets/src/mage/cards/b/BrineGiant.java b/Mage.Sets/src/mage/cards/b/BrineGiant.java index 074145eb82a..349c8bfcb60 100644 --- a/Mage.Sets/src/mage/cards/b/BrineGiant.java +++ b/Mage.Sets/src/mage/cards/b/BrineGiant.java @@ -1,18 +1,12 @@ package mage.cards.b; import mage.MageInt; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.common.AffinityEffect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.AffinityAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AffinityType; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterControlledEnchantmentPermanent; -import mage.filter.common.FilterControlledPermanent; import java.util.UUID; @@ -21,9 +15,6 @@ import java.util.UUID; */ public final class BrineGiant extends CardImpl { - static final FilterControlledPermanent filter = new FilterControlledEnchantmentPermanent("enchantments"); - private static final Hint hint = new ValueHint("Enchantments you control", new PermanentsOnBattlefieldCount(filter)); - public BrineGiant(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{U}"); @@ -31,8 +22,8 @@ public final class BrineGiant extends CardImpl { this.power = new MageInt(5); this.toughness = new MageInt(6); - // This spell costs {1} less to cast for each enchantment you control. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filter)).addHint(hint)); + // Affinity for enchantments + this.addAbility(new AffinityAbility(AffinityType.ENCHANTMENTS)); } private BrineGiant(final BrineGiant card) { diff --git a/Mage.Sets/src/mage/cards/b/BrinebornCutthroat.java b/Mage.Sets/src/mage/cards/b/BrinebornCutthroat.java index cf9775b8397..274e66f56ba 100644 --- a/Mage.Sets/src/mage/cards/b/BrinebornCutthroat.java +++ b/Mage.Sets/src/mage/cards/b/BrinebornCutthroat.java @@ -3,7 +3,6 @@ package mage.cards.b; import mage.MageInt; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.condition.common.OpponentsTurnCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.hint.common.OpponentsTurnHint; import mage.abilities.keyword.FlashAbility; @@ -32,12 +31,9 @@ public final class BrinebornCutthroat extends CardImpl { this.addAbility(FlashAbility.getInstance()); // Whenever you cast a spell during an opponent's turn, put a +1/+1 counter on Brineborn Cutthroat. - this.addAbility(new ConditionalTriggeredAbility( - new SpellCastControllerTriggeredAbility( - new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false - ), OpponentsTurnCondition.instance, "Whenever you cast a spell during an opponent's turn, " + - "put a +1/+1 counter on {this}." - ).addHint(OpponentsTurnHint.instance)); + this.addAbility(new SpellCastControllerTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false + ).withTriggerCondition(OpponentsTurnCondition.instance).addHint(OpponentsTurnHint.instance)); } private BrinebornCutthroat(final BrinebornCutthroat card) { diff --git a/Mage.Sets/src/mage/cards/b/BringerOfTheLastGift.java b/Mage.Sets/src/mage/cards/b/BringerOfTheLastGift.java index 2c68a8553a2..a0972727acc 100644 --- a/Mage.Sets/src/mage/cards/b/BringerOfTheLastGift.java +++ b/Mage.Sets/src/mage/cards/b/BringerOfTheLastGift.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.CastFromEverywhereSourceCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.Card; @@ -39,12 +38,8 @@ public final class BringerOfTheLastGift extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Bringer of the Last Gift enters the battlefield, if you cast it, each player sacrifices all other creatures they control. Then each player returns all creature cards from their graveyard that weren't put there this way to the battlefield. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new BringerOfTheLastGiftEffect()), - CastFromEverywhereSourceCondition.instance, - "When {this} enters, if you cast it, each player sacrifices all other creatures they control. " - + "Then each player returns all creature cards from their graveyard that weren't put there this way to the battlefield." - )); + this.addAbility(new EntersBattlefieldTriggeredAbility(new BringerOfTheLastGiftEffect()) + .withInterveningIf(CastFromEverywhereSourceCondition.instance)); } private BringerOfTheLastGift(final BringerOfTheLastGift card) { @@ -61,6 +56,9 @@ class BringerOfTheLastGiftEffect extends OneShotEffect { BringerOfTheLastGiftEffect() { super(Outcome.Benefit); + staticText = "each player sacrifices all other creatures they control. " + + "Then each player returns all creature cards from their graveyard " + + "that weren't put there this way to the battlefield"; } private BringerOfTheLastGiftEffect(final BringerOfTheLastGiftEffect effect) { diff --git a/Mage.Sets/src/mage/cards/b/BrinkOfMadness.java b/Mage.Sets/src/mage/cards/b/BrinkOfMadness.java index a0181157d5a..7e3653f13a8 100644 --- a/Mage.Sets/src/mage/cards/b/BrinkOfMadness.java +++ b/Mage.Sets/src/mage/cards/b/BrinkOfMadness.java @@ -1,14 +1,13 @@ package mage.cards.b; -import mage.abilities.TriggeredAbility; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.condition.common.CardsInHandCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.Ability; +import mage.abilities.condition.common.HellbentCondition; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.effects.common.discard.DiscardHandTargetEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; import mage.target.common.TargetOpponent; import java.util.UUID; @@ -22,12 +21,10 @@ public final class BrinkOfMadness extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}{B}"); // At the beginning of your upkeep, if you have no cards in hand, sacrifice Brink of Madness and target opponent discards their hand. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new SacrificeSourceEffect()); - ability.addEffect(new DiscardHandTargetEffect()); + Ability ability = new BeginningOfUpkeepTriggeredAbility(new SacrificeSourceEffect()).withInterveningIf(HellbentCondition.instance); + ability.addEffect(new DiscardHandTargetEffect().concatBy("and")); ability.addTarget(new TargetOpponent()); - CardsInHandCondition condition = new CardsInHandCondition(ComparisonType.EQUAL_TO, 0); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, condition, "At the beginning of your upkeep, if you have no cards in hand, sacrifice {this} and target opponent discards their hand.")); - + this.addAbility(ability); } private BrinkOfMadness(final BrinkOfMadness card) { diff --git a/Mage.Sets/src/mage/cards/b/BronzebeakForagers.java b/Mage.Sets/src/mage/cards/b/BronzebeakForagers.java index b6e9a72d1bf..1e561a65853 100644 --- a/Mage.Sets/src/mage/cards/b/BronzebeakForagers.java +++ b/Mage.Sets/src/mage/cards/b/BronzebeakForagers.java @@ -19,7 +19,7 @@ import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInExile; import mage.target.common.TargetNonlandPermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetadjustment.TargetAdjuster; import mage.target.targetpointer.EachTargetPointer; import mage.util.CardUtil; @@ -47,7 +47,7 @@ public final class BronzebeakForagers extends CardImpl { .setText("for each opponent, exile up to one target nonland permanent that player controls until {this} leaves the battlefield") ); etbAbility.addTarget(new TargetNonlandPermanent(0, 1)); - etbAbility.setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + etbAbility.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); this.addAbility(etbAbility); // {X}{W}: Put target card with mana value X exiled with Bronzebeak Foragers into its owner's graveyard. diff --git a/Mage.Sets/src/mage/cards/b/BronzehideLion.java b/Mage.Sets/src/mage/cards/b/BronzehideLion.java index eac53bc1526..66472601850 100644 --- a/Mage.Sets/src/mage/cards/b/BronzehideLion.java +++ b/Mage.Sets/src/mage/cards/b/BronzehideLion.java @@ -24,6 +24,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; +import mage.util.CardUtil; /** * @author LevelX2, TheElk801 @@ -94,7 +95,7 @@ class BronzehideLionReturnEffect extends OneShotEffect { } game.addEffect(new BronzehideLionContinuousEffect(game.getState().getZoneChangeCounter(source.getSourceId()) + 1), source); controller.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent aura = game.getPermanent(card.getId()); + Permanent aura = CardUtil.getPermanentFromCardPutToBattlefield(card, game); Permanent creature = game.getPermanent(target.getFirstTarget()); if (aura == null || creature == null) { return true; diff --git a/Mage.Sets/src/mage/cards/b/BrotherhoodSpy.java b/Mage.Sets/src/mage/cards/b/BrotherhoodSpy.java index e3a2af67a09..e5251a1d417 100644 --- a/Mage.Sets/src/mage/cards/b/BrotherhoodSpy.java +++ b/Mage.Sets/src/mage/cards/b/BrotherhoodSpy.java @@ -2,17 +2,19 @@ package mage.cards.b; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect; -import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.hint.ConditionHint; import mage.abilities.hint.Hint; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; @@ -23,14 +25,15 @@ import java.util.UUID; */ public final class BrotherhoodSpy extends CardImpl { - private static final FilterPermanent filter = new FilterControlledPermanent(SubType.ASSASSIN); + private static final FilterPermanent filter + = new FilterControlledPermanent(SubType.ASSASSIN, "you control a legendary Assassin"); static { filter.add(SuperType.LEGENDARY.getPredicate()); } private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); - private static final Hint hint = new ConditionHint(condition, "You control a legendary Assassin"); + private static final Hint hint = new ConditionHint(condition); public BrotherhoodSpy(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); @@ -41,13 +44,11 @@ public final class BrotherhoodSpy extends CardImpl { this.toughness = new MageInt(3); // At the beginning of combat on your turn, if you control a legendary Assassin, Brotherhood Spy gets +1/+0 until end of turn and can't be blocked this turn. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new BeginningOfCombatTriggeredAbility( - new BoostTargetEffect(1, 0) - ), condition, "At the beginning of combat on your turn, if you control a legendary Assassin, " + - "{this} gets +1/+0 until end of turn and can't be blocked this turn." - ); - ability.addEffect(new CantBeBlockedSourceEffect(Duration.EndOfTurn)); + Ability ability = new BeginningOfCombatTriggeredAbility( + new BoostSourceEffect(1, 0, Duration.EndOfTurn) + ).withInterveningIf(condition); + ability.addEffect(new CantBeBlockedSourceEffect(Duration.EndOfTurn) + .setText("and can't be blocked this turn")); this.addAbility(ability.addHint(hint)); } diff --git a/Mage.Sets/src/mage/cards/b/BrutalizerExarch.java b/Mage.Sets/src/mage/cards/b/BrutalizerExarch.java index 9f2cab7d024..ec16f001d68 100644 --- a/Mage.Sets/src/mage/cards/b/BrutalizerExarch.java +++ b/Mage.Sets/src/mage/cards/b/BrutalizerExarch.java @@ -84,7 +84,7 @@ class BrutalizerExarchEffect2 extends OneShotEffect { Permanent permanent = game.getPermanent(source.getFirstTarget()); Player controller = game.getPlayer(source.getControllerId()); if (permanent != null && controller != null) { - return controller.putCardsOnBottomOfLibrary(permanent, game, source, true); + return controller.putCardsOnBottomOfLibrary(permanent, game, source); } return false; } diff --git a/Mage.Sets/src/mage/cards/b/BucolicRanch.java b/Mage.Sets/src/mage/cards/b/BucolicRanch.java index 19a944bdbb5..5218b25e370 100644 --- a/Mage.Sets/src/mage/cards/b/BucolicRanch.java +++ b/Mage.Sets/src/mage/cards/b/BucolicRanch.java @@ -108,7 +108,7 @@ class BucolicRanchEffect extends OneShotEffect { if (Zone.LIBRARY.equals(game.getState().getZone(card.getId())) && player.chooseUse( outcome, "Put " + card.getName() + " on the bottom of your library?", source, game )) { - player.putCardsOnBottomOfLibrary(card, game, source, false); + player.putCardsOnBottomOfLibrary(card, game, source); } return true; } diff --git a/Mage.Sets/src/mage/cards/b/BudokaPupil.java b/Mage.Sets/src/mage/cards/b/BudokaPupil.java index 672c00d9b22..1abf18db01e 100644 --- a/Mage.Sets/src/mage/cards/b/BudokaPupil.java +++ b/Mage.Sets/src/mage/cards/b/BudokaPupil.java @@ -3,35 +3,35 @@ package mage.cards.b; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.OnEventTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.RemoveCountersSourceCost; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.FlipSourceEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.keyword.TrampleAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.counters.CounterType; import mage.filter.StaticFilters; -import mage.game.events.GameEvent; import mage.game.permanent.token.TokenImpl; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; /** - * * @author LevelX2 */ public final class BudokaPupil extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.KI, 2); + public BudokaPupil(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{G}"); this.subtype.add(SubType.HUMAN, SubType.MONK); this.power = new MageInt(2); @@ -40,13 +40,12 @@ public final class BudokaPupil extends CardImpl { this.flipCardName = "Ichiga, Who Topples Oaks"; // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Budoka Pupil. - this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, true)); // At the beginning of the end step, if there are two or more ki counters on Budoka Pupil, you may flip it. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new OnEventTriggeredAbility(GameEvent.EventType.END_TURN_STEP_PRE, "beginning of the end step", true, new FlipSourceEffect(new IchigaWhoTopplesOaks()), true), - new SourceHasCounterCondition(CounterType.KI, 2, Integer.MAX_VALUE), - "At the beginning of the end step, if there are two or more ki counters on {this}, you may flip it.")); + this.addAbility(new BeginningOfEndStepTriggeredAbility( + TargetController.NEXT, new FlipSourceEffect(new IchigaWhoTopplesOaks()).setText("flip it"), true, condition + )); } private BudokaPupil(final BudokaPupil card) { @@ -80,6 +79,7 @@ class IchigaWhoTopplesOaks extends TokenImpl { ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } + private IchigaWhoTopplesOaks(final IchigaWhoTopplesOaks token) { super(token); } diff --git a/Mage.Sets/src/mage/cards/b/BullRushBruiser.java b/Mage.Sets/src/mage/cards/b/BullRushBruiser.java index d69fb1ad63d..b4749fa28e9 100644 --- a/Mage.Sets/src/mage/cards/b/BullRushBruiser.java +++ b/Mage.Sets/src/mage/cards/b/BullRushBruiser.java @@ -1,33 +1,35 @@ - package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.keyword.FirstStrikeAbility; -import mage.constants.Duration; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; import mage.filter.common.FilterTeamPermanent; import mage.filter.predicate.mageobject.AnotherPredicate; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class BullRushBruiser extends CardImpl { - private static final FilterTeamPermanent filter = new FilterTeamPermanent(SubType.WARRIOR, "another Warrior"); + private static final FilterTeamPermanent filter + = new FilterTeamPermanent(SubType.WARRIOR, "your team controls another Warrior"); static { filter.add(AnotherPredicate.instance); } + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + public BullRushBruiser(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); @@ -37,12 +39,9 @@ public final class BullRushBruiser extends CardImpl { this.toughness = new MageInt(3); // Whenever Bull-Rush Bruiser attacks, if your team controls another Warrior, Bull-Rush Bruiser gains first strike until end of turn. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new AttacksTriggeredAbility(new GainAbilitySourceEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn), false), - new PermanentsOnTheBattlefieldCondition(filter), - "Whenever {this} attacks, if your team controls another Warrior, " - + "{this} gains first strike until end of turn." - )); + this.addAbility(new AttacksTriggeredAbility(new GainAbilitySourceEffect( + FirstStrikeAbility.getInstance(), Duration.EndOfTurn + )).withInterveningIf(condition)); } private BullRushBruiser(final BullRushBruiser card) { diff --git a/Mage.Sets/src/mage/cards/b/BurningEyeZubera.java b/Mage.Sets/src/mage/cards/b/BurningEyeZubera.java index bf660252995..be50407423d 100644 --- a/Mage.Sets/src/mage/cards/b/BurningEyeZubera.java +++ b/Mage.Sets/src/mage/cards/b/BurningEyeZubera.java @@ -1,38 +1,36 @@ - package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetAnyTarget; +import java.util.Optional; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class BurningEyeZubera extends CardImpl { public BurningEyeZubera(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); this.subtype.add(SubType.ZUBERA, SubType.SPIRIT); this.power = new MageInt(3); this.toughness = new MageInt(3); // When Burning-Eye Zubera dies, if 4 or more damage was dealt to it this turn, Burning-Eye Zubera deals 3 damage to any target. - Ability ability = new ConditionalInterveningIfTriggeredAbility(new DiesSourceTriggeredAbility(new DamageTargetEffect(3)),new SourceGotFourDamage(), - "When {this} dies, if 4 or more damage was dealt to it this turn, Burning-Eye Zubera deals 3 damage to any target"); + Ability ability = new DiesSourceTriggeredAbility(new DamageTargetEffect(3)) + .withInterveningIf(BurningEyeZuberaCondition.instance); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); } @@ -47,13 +45,19 @@ public final class BurningEyeZubera extends CardImpl { } } -class SourceGotFourDamage implements Condition { +enum BurningEyeZuberaCondition implements Condition { + instance; + @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent == null) { - permanent = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); + return Optional + .ofNullable(source.getSourcePermanentOrLKI(game)) + .map(Permanent::getDamage) + .orElse(0) >= 4; } - return permanent.getDamage() > 3; + + @Override + public String toString() { + return "4 or more damage was dealt to it this turn"; } } diff --git a/Mage.Sets/src/mage/cards/b/BurningSands.java b/Mage.Sets/src/mage/cards/b/BurningSands.java index a67dcebbebd..aa4aff061d5 100644 --- a/Mage.Sets/src/mage/cards/b/BurningSands.java +++ b/Mage.Sets/src/mage/cards/b/BurningSands.java @@ -1,17 +1,13 @@ package mage.cards.b; -import mage.abilities.Ability; import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.effects.common.SacrificeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.target.targetadjustment.TargetAdjuster; -import mage.target.targetpointer.FixedTarget; import java.util.UUID; @@ -24,11 +20,9 @@ public final class BurningSands extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{R}{R}"); // Whenever a creature dies, that creature's controller sacrifices a land. - Ability ability = new DiesCreatureTriggeredAbility(new SacrificeEffect( - StaticFilters.FILTER_LAND, 1, "that creature's controller" - ), false, false, true); - ability.setTargetAdjuster(BurningSandsAdjuster.instance); - this.addAbility(ability); + this.addAbility(new DiesCreatureTriggeredAbility( + new SacrificeEffect(StaticFilters.FILTER_LAND, 1, "that creature's controller"), + SetTargetPointer.PLAYER)); } private BurningSands(final BurningSands card) { @@ -40,16 +34,3 @@ public final class BurningSands extends CardImpl { return new BurningSands(this); } } - -enum BurningSandsAdjuster implements TargetAdjuster { - instance; - - @Override - public void adjustTargets(Ability ability, Game game) { - UUID creatureId = ability.getEffects().get(0).getTargetPointer().getFirst(game, ability); - Permanent creature = game.getPermanentOrLKIBattlefield(creatureId); - if (creature != null) { - ability.getEffects().get(0).setTargetPointer(new FixedTarget(creature.getControllerId())); - } - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/b/BurningSunCavalry.java b/Mage.Sets/src/mage/cards/b/BurningSunCavalry.java index 059fe58e43b..0b2d6ddf6af 100644 --- a/Mage.Sets/src/mage/cards/b/BurningSunCavalry.java +++ b/Mage.Sets/src/mage/cards/b/BurningSunCavalry.java @@ -4,8 +4,9 @@ import mage.MageInt; import mage.abilities.common.AttacksOrBlocksTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -21,22 +22,22 @@ import java.util.UUID; public final class BurningSunCavalry extends CardImpl { private static final Condition condition = new PermanentsOnTheBattlefieldCondition( - new FilterControlledPermanent(SubType.DINOSAUR)); + new FilterControlledPermanent(SubType.DINOSAUR, "you control a Dinosaur") + ); + private static final Hint hint = new ConditionHint(condition, "You control a Dinosaur"); public BurningSunCavalry(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); - + this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.KNIGHT); this.power = new MageInt(2); this.toughness = new MageInt(2); // Whenever Burning Sun Cavalry attacks or blocks while you control a Dinosaur, Burning Sun Cavalry gets +1/+1 until end of turn. - this.addAbility(new ConditionalTriggeredAbility( - new AttacksOrBlocksTriggeredAbility(new BoostSourceEffect(1, 1, Duration.EndOfTurn), false), - condition, - "Whenever {this} attacks or blocks while you control a Dinosaur, {this} gets +1/+1 until end of turn." - )); + this.addAbility(new AttacksOrBlocksTriggeredAbility( + new BoostSourceEffect(1, 1, Duration.EndOfTurn), false + ).withTriggerCondition(condition).addHint(hint)); } private BurningSunCavalry(final BurningSunCavalry card) { diff --git a/Mage.Sets/src/mage/cards/b/BurrentonForgeTender.java b/Mage.Sets/src/mage/cards/b/BurrentonForgeTender.java index 3723515c746..70c6d353548 100644 --- a/Mage.Sets/src/mage/cards/b/BurrentonForgeTender.java +++ b/Mage.Sets/src/mage/cards/b/BurrentonForgeTender.java @@ -1,35 +1,34 @@ package mage.cards.b; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; -import mage.abilities.effects.common.PreventDamageBySourceEffect; +import mage.abilities.effects.common.PreventDamageByChosenSourceEffect; import mage.abilities.keyword.ProtectionAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class BurrentonForgeTender extends CardImpl { - private static final FilterObject filterObject = new FilterObject("a red"); + private static final FilterSource filter = new FilterSource("a red source"); static { - filterObject.add(new ColorPredicate(ObjectColor.RED)); + filter.add(new ColorPredicate(ObjectColor.RED)); } public BurrentonForgeTender(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); this.subtype.add(SubType.KITHKIN, SubType.WIZARD); this.power = new MageInt(1); @@ -37,9 +36,9 @@ public final class BurrentonForgeTender extends CardImpl { // Protection from red this.addAbility(ProtectionAbility.from(ObjectColor.RED)); - + // Sacrifice Burrenton Forge-Tender: Prevent all damage a red source of your choice would deal this turn. - this.addAbility(new SimpleActivatedAbility( new PreventDamageBySourceEffect(filterObject), new SacrificeSourceCost())); + this.addAbility(new SimpleActivatedAbility(new PreventDamageByChosenSourceEffect(filter), new SacrificeSourceCost())); } diff --git a/Mage.Sets/src/mage/cards/b/BushiTenderfoot.java b/Mage.Sets/src/mage/cards/b/BushiTenderfoot.java index 180f201dbe0..fc8e1049961 100644 --- a/Mage.Sets/src/mage/cards/b/BushiTenderfoot.java +++ b/Mage.Sets/src/mage/cards/b/BushiTenderfoot.java @@ -11,8 +11,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.WasDealtDamageThisTurnPredicate; import mage.game.permanent.token.TokenImpl; import java.util.UUID; @@ -22,12 +20,6 @@ import java.util.UUID; */ public final class BushiTenderfoot extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature that was dealt damage this turn"); - - static { - filter.add(WasDealtDamageThisTurnPredicate.instance); - } - public BushiTenderfoot(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); this.subtype.add(SubType.HUMAN, SubType.SOLDIER); @@ -68,6 +60,7 @@ class KenzoTheHardhearted extends TokenImpl { this.addAbility(DoubleStrikeAbility.getInstance()); this.addAbility(new BushidoAbility(2)); } + private KenzoTheHardhearted(final KenzoTheHardhearted token) { super(token); } diff --git a/Mage.Sets/src/mage/cards/b/BushyBodyguard.java b/Mage.Sets/src/mage/cards/b/BushyBodyguard.java index aa6f98136c0..57daacd29e6 100644 --- a/Mage.Sets/src/mage/cards/b/BushyBodyguard.java +++ b/Mage.Sets/src/mage/cards/b/BushyBodyguard.java @@ -32,7 +32,7 @@ public final class BushyBodyguard extends CardImpl { // When this creature enters, you may forage. If you do, put two +1/+1 counters on it. this.addAbility(new EntersBattlefieldTriggeredAbility(new DoIfCostPaid( - new AddCountersSourceEffect(CounterType.P1P1.createInstance(2)), new ForageCost() + new AddCountersSourceEffect(CounterType.P1P1.createInstance(2)).setText("put two +1/+1 counters on it"), new ForageCost() ))); } diff --git a/Mage.Sets/src/mage/cards/b/BusterSword.java b/Mage.Sets/src/mage/cards/b/BusterSword.java index e8b929dea56..6dea433cb0b 100644 --- a/Mage.Sets/src/mage/cards/b/BusterSword.java +++ b/Mage.Sets/src/mage/cards/b/BusterSword.java @@ -1,10 +1,10 @@ package mage.cards.b; import mage.abilities.Ability; -import mage.abilities.common.DealsCombatDamageEquippedTriggeredAbility; +import mage.abilities.common.DealsDamageToAPlayerAttachedTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.DrawCardTargetEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.BoostEquippedEffect; import mage.abilities.keyword.EquipAbility; import mage.cards.CardImpl; @@ -36,7 +36,9 @@ public final class BusterSword extends CardImpl { this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(3, 2))); // Whenever equipped creature deals combat damage to a player, draw a card, then you may cast a spell from your hand with mana value less than or equal to that damage without paying its mana cost. - Ability ability = new DealsCombatDamageEquippedTriggeredAbility(new DrawCardTargetEffect(1)); + Ability ability = new DealsDamageToAPlayerAttachedTriggeredAbility( + new DrawCardSourceControllerEffect(1), "equipped", false + ); ability.addEffect(new BusterSwordEffect()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/b/BygoneMarvels.java b/Mage.Sets/src/mage/cards/b/BygoneMarvels.java index 9b2429fa71b..2367fc7e168 100644 --- a/Mage.Sets/src/mage/cards/b/BygoneMarvels.java +++ b/Mage.Sets/src/mage/cards/b/BygoneMarvels.java @@ -1,7 +1,6 @@ package mage.cards.b; import mage.abilities.condition.common.DescendCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.CastSourceTriggeredAbility; import mage.abilities.effects.common.CopySourceSpellEffect; import mage.abilities.effects.common.ExileSpellEffect; @@ -27,11 +26,11 @@ public final class BygoneMarvels extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{G}{G}"); // Descend 8 -- When you cast this spell, if there are eight or more permanent cards in your graveyard, copy this spell twice. You may choose new targets for the copies. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new CastSourceTriggeredAbility(new CopySourceSpellEffect(2)), - DescendCondition.EIGHT, "When you cast this spell, if there are eight or more " + - "permanent cards in your graveyard, copy this spell twice. You may choose new targets for the copies." - ).setAbilityWord(AbilityWord.DESCEND_8).addHint(DescendCondition.getHint())); + this.addAbility(new CastSourceTriggeredAbility(new CopySourceSpellEffect(2) + .setText("copy this spell twice. You may choose new targets for the copies")) + .withInterveningIf(DescendCondition.EIGHT) + .setAbilityWord(AbilityWord.DESCEND_8) + .addHint(DescendCondition.getHint())); // Return target permanent card from your graveyard to your hand. Exile Bygone Marvels. this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); diff --git a/Mage.Sets/src/mage/cards/c/CabalConditioning.java b/Mage.Sets/src/mage/cards/c/CabalConditioning.java index 0e678b17422..e96b59210b1 100644 --- a/Mage.Sets/src/mage/cards/c/CabalConditioning.java +++ b/Mage.Sets/src/mage/cards/c/CabalConditioning.java @@ -1,16 +1,16 @@ package mage.cards.c; -import java.util.UUID; -import mage.abilities.dynamicvalue.common.HighestManaValueCount; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.discard.DiscardTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author nigelzor */ public final class CabalConditioning extends CardImpl { @@ -18,10 +18,11 @@ public final class CabalConditioning extends CardImpl { public CabalConditioning(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{6}{B}"); - // Any number of target players each discard a number of cards equal to the highest converted mana cost among permanents you control. - this.getSpellAbility().addEffect(new DiscardTargetEffect(new HighestManaValueCount()) - .setText("Any number of target players each discard a number of cards equal to the highest mana value among permanents you control.") + // Any number of target players each discard a number of cards equal to the greatest converted mana cost among permanents you control. + this.getSpellAbility().addEffect(new DiscardTargetEffect(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_PERMANENTS) + .setText("Any number of target players each discard a number of cards equal to the greatest mana value among permanents you control.") ); + this.getSpellAbility().addHint(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_PERMANENTS.getHint()); this.getSpellAbility().addTarget(new TargetPlayer(0, Integer.MAX_VALUE, false)); } diff --git a/Mage.Sets/src/mage/cards/c/CabalPaladin.java b/Mage.Sets/src/mage/cards/c/CabalPaladin.java index 5f47303d4e7..ccb8d01a016 100644 --- a/Mage.Sets/src/mage/cards/c/CabalPaladin.java +++ b/Mage.Sets/src/mage/cards/c/CabalPaladin.java @@ -12,7 +12,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.TargetController; -import mage.filter.common.FilterHistoricSpell; +import mage.filter.StaticFilters; /** * @@ -32,7 +32,7 @@ public final class CabalPaladin extends CardImpl { this.addAbility(new SpellCastControllerTriggeredAbility( new DamagePlayersEffect(Outcome.Damage, StaticValue.get(2), TargetController.OPPONENT) .setText("{this} deals 2 damage to each opponent. (Artifacts, legendaries, and Sagas are historic.)"), - new FilterHistoricSpell(), false + StaticFilters.FILTER_SPELL_HISTORIC, false )); } diff --git a/Mage.Sets/src/mage/cards/c/CabarettiAscendancy.java b/Mage.Sets/src/mage/cards/c/CabarettiAscendancy.java index cb3cf2dde63..23e76bb3493 100644 --- a/Mage.Sets/src/mage/cards/c/CabarettiAscendancy.java +++ b/Mage.Sets/src/mage/cards/c/CabarettiAscendancy.java @@ -73,7 +73,7 @@ class CabarettiAscendencyEffect extends OneShotEffect { controller.revealCards(source, new CardsImpl(card), game); controller.moveCards(card, Zone.HAND, source, game); } else if (controller.chooseUse(Outcome.Neutral, "Put " + card.getIdName() + " on the bottom of your library?", source, game)) { - controller.putCardsOnBottomOfLibrary(card, game, source, false); + controller.putCardsOnBottomOfLibrary(card, game, source); } return true; } diff --git a/Mage.Sets/src/mage/cards/c/CacophonyUnleashed.java b/Mage.Sets/src/mage/cards/c/CacophonyUnleashed.java index 5c37b77015f..33dd02d30ac 100644 --- a/Mage.Sets/src/mage/cards/c/CacophonyUnleashed.java +++ b/Mage.Sets/src/mage/cards/c/CacophonyUnleashed.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.CastFromEverywhereSourceCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DestroyAllEffect; import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; import mage.abilities.keyword.DeathtouchAbility; @@ -37,24 +36,18 @@ public final class CacophonyUnleashed extends CardImpl { public CacophonyUnleashed(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{5}{B}{B}"); - // When Cacophony Unleashed enters the battlefield, if you cast it, destroy all nonenchantment creatures. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new DestroyAllEffect(filter)), - CastFromEverywhereSourceCondition.instance, - "When {this} enters, if you cast it, destroy all nonenchantment creatures." - )); + this.addAbility(new EntersBattlefieldTriggeredAbility(new DestroyAllEffect(filter)) + .withInterveningIf(CastFromEverywhereSourceCondition.instance)); // Whenever Cacophony Unleashed or another enchantment you control enters, until end of turn, Cacophony Unleashed becomes a legendary 6/6 Nightmare God creature with menace and deathtouch. It's still an enchantment. - this.addAbility( - new EntersBattlefieldThisOrAnotherTriggeredAbility( + this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility( new BecomesCreatureSourceEffect(new CacophonyUnleashedToken(), CardType.ENCHANTMENT, Duration.EndOfTurn) - .setText("until end of turn, Cacophony Unleashed becomes a legendary 6/6 Nightmare God " - + "creature with menace and deathtouch. It's still an enchantment."), + .setText("until end of turn, {this} becomes a legendary 6/6 Nightmare God " + + "creature with menace and deathtouch. It's still an enchantment."), StaticFilters.FILTER_PERMANENT_ENCHANTMENT, false, true - ) - ); + )); } private CacophonyUnleashed(final CacophonyUnleashed card) { diff --git a/Mage.Sets/src/mage/cards/c/Cactuar.java b/Mage.Sets/src/mage/cards/c/Cactuar.java new file mode 100644 index 00000000000..963ae7dd237 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/Cactuar.java @@ -0,0 +1,45 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.condition.common.SourceEnteredThisTurnCondition; +import mage.abilities.effects.common.ReturnToHandSourceEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Cactuar extends CardImpl { + + public Cactuar(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}"); + + this.subtype.add(SubType.PLANT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // At the beginning of your end step, if this creature didn't enter the battlefield this turn, return it to its owner's hand. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + new ReturnToHandSourceEffect(true) + .setText("return it to its owner's hand") + ).withInterveningIf(SourceEnteredThisTurnCondition.DIDNT)); + } + + private Cactuar(final Cactuar card) { + super(card); + } + + @Override + public Cactuar copy() { + return new Cactuar(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CaligoSkinWitch.java b/Mage.Sets/src/mage/cards/c/CaligoSkinWitch.java index 79205ecbc1a..d8735f5903c 100644 --- a/Mage.Sets/src/mage/cards/c/CaligoSkinWitch.java +++ b/Mage.Sets/src/mage/cards/c/CaligoSkinWitch.java @@ -1,11 +1,9 @@ package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.KickedCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.discard.DiscardEachPlayerEffect; import mage.abilities.keyword.KickerAbility; @@ -15,8 +13,9 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.TargetController; +import java.util.UUID; + /** - * * @author JRHerlehy */ public final class CaligoSkinWitch extends CardImpl { @@ -32,15 +31,9 @@ public final class CaligoSkinWitch extends CardImpl { this.addAbility(new KickerAbility("{3}{B}")); // When Caligo Skin-Witch enters the battlefield, if it was kicked, each opponent discards two cards. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new DiscardEachPlayerEffect( - StaticValue.get(2), - false, - TargetController.OPPONENT - )), - KickedCondition.ONCE, - "When {this} enters, if it was kicked, each opponent discards two cards." - )); + this.addAbility(new EntersBattlefieldTriggeredAbility(new DiscardEachPlayerEffect( + StaticValue.get(2), false, TargetController.OPPONENT + )).withInterveningIf(KickedCondition.ONCE)); } private CaligoSkinWitch(final CaligoSkinWitch card) { diff --git a/Mage.Sets/src/mage/cards/c/CallOfTheFullMoon.java b/Mage.Sets/src/mage/cards/c/CallOfTheFullMoon.java index fa705881e8a..c675832399f 100644 --- a/Mage.Sets/src/mage/cards/c/CallOfTheFullMoon.java +++ b/Mage.Sets/src/mage/cards/c/CallOfTheFullMoon.java @@ -1,54 +1,49 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.TwoOrMoreSpellsWereCastLastTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.TrampleAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; -/** - * - * @author LoneFox +import java.util.UUID; +/** + * @author LoneFox */ public final class CallOfTheFullMoon extends CardImpl { public CallOfTheFullMoon(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{R}"); this.subtype.add(SubType.AURA); // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.Benefit)); - Ability ability = new EnchantAbility(auraTarget); - this.addAbility(ability); + this.addAbility(new EnchantAbility(auraTarget)); + // Enchanted creature gets +3/+2 and has trample. - ability = new SimpleStaticAbility(new BoostEnchantedEffect(3, 2, Duration.WhileOnBattlefield)); - Effect effect = new GainAbilityAttachedEffect(TrampleAbility.getInstance(), AttachmentType.AURA); - effect.setText("and has trample."); - ability.addEffect(effect); + Ability ability = new SimpleStaticAbility(new BoostEnchantedEffect(3, 2)); + ability.addEffect(new GainAbilityAttachedEffect( + TrampleAbility.getInstance(), AttachmentType.AURA + ).setText("and has trample")); this.addAbility(ability); // At the beginning of each upkeep, if a player cast two or more spells last turn, sacrifice Call of the Full Moon. - TriggeredAbility ability2 = new BeginningOfUpkeepTriggeredAbility(TargetController.ANY, new SacrificeSourceEffect(), false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability2, TwoOrMoreSpellsWereCastLastTurnCondition.instance, - "At the beginning of each upkeep, if a player cast two or more spells last turn, sacrifice {this}.")); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + TargetController.ANY, new SacrificeSourceEffect(), false + ).withInterveningIf(TwoOrMoreSpellsWereCastLastTurnCondition.instance)); } private CallOfTheFullMoon(final CallOfTheFullMoon card) { diff --git a/Mage.Sets/src/mage/cards/c/CallTheMountainChocobo.java b/Mage.Sets/src/mage/cards/c/CallTheMountainChocobo.java new file mode 100644 index 00000000000..e2827027849 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CallTheMountainChocobo.java @@ -0,0 +1,47 @@ +package mage.cards.c; + +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.abilities.keyword.FlashbackAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.game.permanent.token.ChocoboToken; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CallTheMountainChocobo extends CardImpl { + + private static final FilterCard filter = new FilterCard("Mountain card"); + + static { + filter.add(SubType.MOUNTAIN.getPredicate()); + } + + public CallTheMountainChocobo(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}"); + + // Search your library for a Mountain card, reveal it, put it into your hand, then shuffle. Create a 2/2 green Bird creature token with "Whenever a land you control enters, this token gets +1/+0 until end of turn." + this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true)); + this.getSpellAbility().addEffect(new CreateTokenEffect(new ChocoboToken())); + + // Flashback {5}{R} + this.addAbility(new FlashbackAbility(this, new ManaCostsImpl<>("{5}{R}"))); + } + + private CallTheMountainChocobo(final CallTheMountainChocobo card) { + super(card); + } + + @Override + public CallTheMountainChocobo copy() { + return new CallTheMountainChocobo(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CallToTheGrave.java b/Mage.Sets/src/mage/cards/c/CallToTheGrave.java index 4ccc7548b6b..42dbde627e9 100644 --- a/Mage.Sets/src/mage/cards/c/CallToTheGrave.java +++ b/Mage.Sets/src/mage/cards/c/CallToTheGrave.java @@ -1,14 +1,11 @@ - package mage.cards.c; -import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.common.OnEventTriggeredAbility; -import mage.abilities.condition.common.CreatureCountCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.effects.common.SacrificeEffect; import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -16,33 +13,36 @@ import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.game.events.GameEvent; import java.util.UUID; /** - * * @author nantuko */ public final class CallToTheGrave extends CardImpl { - private static final String ruleText = "At the beginning of the end step, if no creatures are on the battlefield, sacrifice {this}."; private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("non-Zombie creature"); static { filter.add(Predicates.not(SubType.ZOMBIE.getPredicate())); } + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + new FilterCreaturePermanent("no creatures are on the battlefield"), false + ); + public CallToTheGrave(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{4}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{B}"); // At the beginning of each player's upkeep, that player sacrifices a non-Zombie creature. - Ability ability = new BeginningOfUpkeepTriggeredAbility(TargetController.EACH_PLAYER, new SacrificeEffect(filter, 1, "that player"), false); - this.addAbility(ability); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + TargetController.EACH_PLAYER, new SacrificeEffect(filter, 1, "that player"), false + )); // At the beginning of the end step, if no creatures are on the battlefield, sacrifice Call to the Grave. - TriggeredAbility triggered = new OnEventTriggeredAbility(GameEvent.EventType.END_TURN_STEP_PRE, "beginning of the end step", true, new SacrificeSourceEffect()); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(triggered, new CreatureCountCondition(0, TargetController.ANY), ruleText)); + this.addAbility(new BeginningOfEndStepTriggeredAbility( + TargetController.NEXT, new SacrificeSourceEffect(), false + ).withInterveningIf(condition)); } private CallToTheGrave(final CallToTheGrave card) { diff --git a/Mage.Sets/src/mage/cards/c/CallousOppressor.java b/Mage.Sets/src/mage/cards/c/CallousOppressor.java index 08905a71b70..4b762b2cca9 100644 --- a/Mage.Sets/src/mage/cards/c/CallousOppressor.java +++ b/Mage.Sets/src/mage/cards/c/CallousOppressor.java @@ -48,7 +48,7 @@ public final class CallousOppressor extends CardImpl { ConditionalContinuousEffect effect = new ConditionalContinuousEffect( new GainControlTargetEffect(Duration.OneUse), SourceTappedCondition.TAPPED, - "Gain control of target creature for as long as {this} remains tapped"); + "Gain control of target creature that isn't of the chosen type for as long as {this} remains tapped"); Ability ability = new SimpleActivatedAbility(effect, new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent(new CallousOppressorFilter())); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/c/CallowJushi.java b/Mage.Sets/src/mage/cards/c/CallowJushi.java index 87636c0c55e..2abe3c35a17 100644 --- a/Mage.Sets/src/mage/cards/c/CallowJushi.java +++ b/Mage.Sets/src/mage/cards/c/CallowJushi.java @@ -1,37 +1,37 @@ - package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.OnEventTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.abilities.effects.common.FlipSourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.Zone; +import mage.constants.TargetController; import mage.counters.CounterType; import mage.filter.StaticFilters; -import mage.game.events.GameEvent; import mage.game.permanent.token.TokenImpl; import mage.target.TargetSpell; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class CallowJushi extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.KI, 2); + public CallowJushi(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{U}"); this.subtype.add(SubType.HUMAN); @@ -43,13 +43,12 @@ public final class CallowJushi extends CardImpl { this.flipCardName = "Jaraku the Interloper"; // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Callow Jushi. - this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, true)); // At the beginning of the end step, if there are two or more ki counters on Callow Jushi, you may flip it. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new OnEventTriggeredAbility(GameEvent.EventType.END_TURN_STEP_PRE, "beginning of the end step", true, new FlipSourceEffect(new JarakuTheInterloper()), true), - new SourceHasCounterCondition(CounterType.KI, 2, Integer.MAX_VALUE), - "At the beginning of the end step, if there are two or more ki counters on {this}, you may flip it.")); + this.addAbility(new BeginningOfEndStepTriggeredAbility( + TargetController.NEXT, new FlipSourceEffect(new JarakuTheInterloper()).setText("flip it"), true, condition + )); } private CallowJushi(final CallowJushi card) { @@ -80,6 +79,7 @@ class JarakuTheInterloper extends TokenImpl { ability.addTarget(new TargetSpell()); this.addAbility(ability); } + private JarakuTheInterloper(final JarakuTheInterloper token) { super(token); } diff --git a/Mage.Sets/src/mage/cards/c/CandlegroveWitch.java b/Mage.Sets/src/mage/cards/c/CandlegroveWitch.java index e7fef672fa0..4215ec692c8 100644 --- a/Mage.Sets/src/mage/cards/c/CandlegroveWitch.java +++ b/Mage.Sets/src/mage/cards/c/CandlegroveWitch.java @@ -1,15 +1,17 @@ package mage.cards.c; import mage.MageInt; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.abilities.condition.common.CovenCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.hint.common.CovenHint; import mage.abilities.keyword.FlyingAbility; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.AbilityWord; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; import java.util.UUID; @@ -27,14 +29,9 @@ public final class CandlegroveWitch extends CardImpl { this.toughness = new MageInt(2); // Coven — At the beginning of combat on your turn, if you control three or more creatures with different powers, Candlegrove Witch gains flying until end of turn. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfCombatTriggeredAbility( - new GainAbilitySourceEffect( - FlyingAbility.getInstance(), Duration.EndOfTurn - ) - ), CovenCondition.instance, "At the beginning of combat on your turn, if you control three " + - "or more creatures with different powers, {this} gains flying until end of turn." - ).addHint(CovenHint.instance).setAbilityWord(AbilityWord.COVEN)); + this.addAbility(new BeginningOfCombatTriggeredAbility( + new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.EndOfTurn) + ).withInterveningIf(CovenCondition.instance).addHint(CovenHint.instance).setAbilityWord(AbilityWord.COVEN)); } private CandlegroveWitch(final CandlegroveWitch card) { diff --git a/Mage.Sets/src/mage/cards/c/CandlelitCavalry.java b/Mage.Sets/src/mage/cards/c/CandlelitCavalry.java index bbcbaa73149..afffa3173cd 100644 --- a/Mage.Sets/src/mage/cards/c/CandlelitCavalry.java +++ b/Mage.Sets/src/mage/cards/c/CandlelitCavalry.java @@ -1,15 +1,17 @@ package mage.cards.c; import mage.MageInt; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.abilities.condition.common.CovenCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.hint.common.CovenHint; import mage.abilities.keyword.TrampleAbility; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.AbilityWord; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; import java.util.UUID; @@ -27,14 +29,9 @@ public final class CandlelitCavalry extends CardImpl { this.toughness = new MageInt(5); // Coven — At the beginning of combat on your turn, if you control three or more creatures with different powers, Candlelit Cavalry gains trample until end of turn. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfCombatTriggeredAbility( - new GainAbilitySourceEffect( - TrampleAbility.getInstance(), Duration.EndOfTurn - ) - ), CovenCondition.instance, "At the beginning of combat on your turn, if you control " + - "three or more creatures with different powers, {this} gains trample until end of turn." - ).addHint(CovenHint.instance).setAbilityWord(AbilityWord.COVEN)); + this.addAbility(new BeginningOfCombatTriggeredAbility(new GainAbilitySourceEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn + )).withInterveningIf(CovenCondition.instance).addHint(CovenHint.instance).setAbilityWord(AbilityWord.COVEN)); } private CandlelitCavalry(final CandlelitCavalry card) { diff --git a/Mage.Sets/src/mage/cards/c/CantStayAway.java b/Mage.Sets/src/mage/cards/c/CantStayAway.java index e337d900dcb..6abce314899 100644 --- a/Mage.Sets/src/mage/cards/c/CantStayAway.java +++ b/Mage.Sets/src/mage/cards/c/CantStayAway.java @@ -23,6 +23,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @@ -81,7 +82,7 @@ class CantStayAwayEffect extends OneShotEffect { return false; } controller.moveCards(targetCard, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(targetCard.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(targetCard, game); if (permanent != null) { ContinuousEffect effect = new GainAbilityTargetEffect(new SimpleStaticAbility(new CantStayAwayReplacementEffect()), Duration.Custom); effect.setTargetPointer(new FixedTarget(permanent, game)); diff --git a/Mage.Sets/src/mage/cards/c/CaptainAmericaFirstAvenger.java b/Mage.Sets/src/mage/cards/c/CaptainAmericaFirstAvenger.java index 807a333291b..22382d5c7c5 100644 --- a/Mage.Sets/src/mage/cards/c/CaptainAmericaFirstAvenger.java +++ b/Mage.Sets/src/mage/cards/c/CaptainAmericaFirstAvenger.java @@ -1,28 +1,26 @@ package mage.cards.c; -import java.util.UUID; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.costs.CostImpl; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.Cost; +import mage.abilities.costs.CostImpl; import mage.abilities.costs.EarlyTargetCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageMultiEffect; -import mage.constants.*; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledPermanent; -import mage.filter.common.FilterEquipmentPermanent; -import mage.filter.predicate.ObjectSourcePlayer; -import mage.filter.predicate.ObjectSourcePlayerPredicate; -import mage.filter.predicate.permanent.AttachedToPredicate; +import mage.filter.StaticFilters; +import mage.filter.predicate.permanent.AttachedToSourcePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -30,21 +28,16 @@ import mage.target.Target; import mage.target.TargetPermanent; import mage.target.common.TargetAnyTargetAmount; +import java.util.UUID; + /** - * * @author Grath */ public final class CaptainAmericaFirstAvenger extends CardImpl { - private static final FilterPermanent filter = new FilterEquipmentPermanent("Equipment you control"); - - static { - filter.add(TargetController.YOU.getControllerPredicate()); - } - public CaptainAmericaFirstAvenger(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{W}{U}"); - + this.supertype.add(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SOLDIER); @@ -65,7 +58,7 @@ public final class CaptainAmericaFirstAvenger extends CardImpl { ability = new BeginningOfCombatTriggeredAbility( new CaptainAmericaFirstAvengerCatchEffect() ); - ability.addTarget(new TargetPermanent(0, 1, filter)); + ability.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT)); this.addAbility(ability.withFlavorWord("... Catch")); } @@ -79,25 +72,6 @@ public final class CaptainAmericaFirstAvenger extends CardImpl { } } -enum CaptainAmericaPredicate implements ObjectSourcePlayerPredicate { - instance; - - // Functional negation of AnotherPredicate. - @Override - public boolean apply(ObjectSourcePlayer input, Game game) { - if (!input.getObject().getId().equals(input.getSourceId())) { - return false; - } - int zcc = input.getSource().getSourceObjectZoneChangeCounter(); - return zcc == input.getObject().getZoneChangeCounter(game); - } - - @Override - public String toString() { - return "{this}"; - } -} - enum CaptainAmericaFirstAvengerValue implements DynamicValue { instance; @@ -133,12 +107,10 @@ enum CaptainAmericaFirstAvengerValue implements DynamicValue { class CaptainAmericaFirstAvengerUnattachCost extends CostImpl implements EarlyTargetCost { - private static final FilterPermanent filter = new FilterEquipmentPermanent("equipment attached to this creature"); - private static final FilterPermanent subfilter = new FilterControlledPermanent("{this}"); + private static final FilterPermanent filter = new FilterPermanent(SubType.EQUIPMENT, "equipment attached to this creature"); static { - subfilter.add(CaptainAmericaPredicate.instance); - filter.add(new AttachedToPredicate(subfilter)); + filter.add(AttachedToSourcePredicate.instance); } CaptainAmericaFirstAvengerUnattachCost() { diff --git a/Mage.Sets/src/mage/cards/c/Carbonize.java b/Mage.Sets/src/mage/cards/c/Carbonize.java index 482897002ca..3a4516960f8 100644 --- a/Mage.Sets/src/mage/cards/c/Carbonize.java +++ b/Mage.Sets/src/mage/cards/c/Carbonize.java @@ -1,17 +1,24 @@ - package mage.cards.c; -import java.util.UUID; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; -import mage.abilities.effects.common.ExileTargetIfDiesEffect; +import mage.abilities.effects.common.replacement.DiesReplacementEffect; import mage.abilities.effects.common.ruleModifying.CantRegenerateTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; import mage.target.common.TargetAnyTarget; +import mage.target.targetpointer.FixedTarget; import mage.watchers.common.DamagedByWatcher; +import java.util.UUID; + /** * * @author markedagain @@ -21,12 +28,10 @@ public final class Carbonize extends CardImpl { public Carbonize(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); - // Carbonize deals 3 damage to any target. That creature can't be regenerated this turn. If the creature would die this turn, exile it instead. + // Carbonize deals 3 damage to any target. If it's a creature, it can't be regenerated this turn, and if it would die this turn, exile it instead. this.getSpellAbility().addEffect(new DamageTargetEffect(3)); - this.getSpellAbility().addEffect(new CantRegenerateTargetEffect(Duration.EndOfTurn, "That creature")); - this.getSpellAbility().addEffect(new ExileTargetIfDiesEffect().setText("If the creature would die this turn, exile it instead")); + this.getSpellAbility().addEffect(new CarbonizeEffect()); this.getSpellAbility().addTarget(new TargetAnyTarget()); - this.getSpellAbility().addWatcher(new DamagedByWatcher(false)); } @@ -39,3 +44,32 @@ public final class Carbonize extends CardImpl { return new Carbonize(this); } } + +class CarbonizeEffect extends OneShotEffect { + + CarbonizeEffect() { + super(Outcome.Benefit); + staticText = "If it's a creature, it can't be regenerated this turn, and if it would die this turn, exile it instead"; + } + + private CarbonizeEffect(final CarbonizeEffect effect) { + super(effect); + } + + @Override + public CarbonizeEffect copy() { + return new CarbonizeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (permanent == null || !permanent.isCreature(game)) { + return false; + } + game.addEffect(new CantRegenerateTargetEffect(Duration.EndOfTurn, "") + .setTargetPointer(new FixedTarget(permanent, game)), source); + game.addEffect(new DiesReplacementEffect(new MageObjectReference(permanent, game), Duration.EndOfTurn), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CargoShip.java b/Mage.Sets/src/mage/cards/c/CargoShip.java new file mode 100644 index 00000000000..3fc9f5f4417 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CargoShip.java @@ -0,0 +1,86 @@ +package mage.cards.c; + +import mage.ConditionalMana; +import mage.MageInt; +import mage.MageObject; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.abilities.keyword.CrewAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.abilities.mana.ConditionalColorlessManaAbility; +import mage.abilities.mana.builder.ConditionalManaBuilder; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.Game; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CargoShip extends CardImpl { + + public CargoShip(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{U}"); + + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // {T}: Add {C}. Spend this mana only to cast an artifact spell or activate an ability of an artifact source. + this.addAbility(new ConditionalColorlessManaAbility(1, new CargoShipManaBuilder())); + + // Crew 1 + this.addAbility(new CrewAbility(1)); + } + + private CargoShip(final CargoShip card) { + super(card); + } + + @Override + public CargoShip copy() { + return new CargoShip(this); + } +} + +class CargoShipManaBuilder extends ConditionalManaBuilder { + + @Override + public ConditionalMana build(Object... options) { + return new CargoShipConditionalMana(this.mana); + } + + @Override + public String getRule() { + return "Spend this mana only to cast an artifact spell or activate an ability of an artifact source"; + } +} + +class CargoShipConditionalMana extends ConditionalMana { + + CargoShipConditionalMana(Mana mana) { + super(mana); + addCondition(CargoShipCondition.instance); + } +} + +enum CargoShipCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + MageObject object = game.getObject(source); + return object != null && object.isArtifact(game) && !source.isActivated(); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CarryAway.java b/Mage.Sets/src/mage/cards/c/CarryAway.java index 8cdfda62ae9..bf653a4f4c0 100644 --- a/Mage.Sets/src/mage/cards/c/CarryAway.java +++ b/Mage.Sets/src/mage/cards/c/CarryAway.java @@ -1,27 +1,25 @@ - package mage.cards.c; -import java.util.UUID; -import mage.target.common.TargetEquipmentPermanent; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.ControlEnchantedEffect; -import mage.constants.Outcome; -import mage.target.TargetPermanent; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class CarryAway extends CardImpl { @@ -32,15 +30,14 @@ public final class CarryAway extends CardImpl { this.subtype.add(SubType.AURA); // Enchant Equipment - TargetPermanent auraTarget = new TargetEquipmentPermanent(); + TargetPermanent auraTarget = new TargetPermanent(StaticFilters.FILTER_PERMANENT_EQUIPMENT); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.Benefit)); - Ability ability = new EnchantAbility(auraTarget); - this.addAbility(ability); + this.addAbility(new EnchantAbility(auraTarget)); // When Carry Away enters the battlefield, unattach enchanted Equipment. - ability = new EntersBattlefieldTriggeredAbility(new CarryAwayEffect()); - this.addAbility(ability); + this.addAbility(new EntersBattlefieldTriggeredAbility(new CarryAwayEffect())); + // You control enchanted Equipment. this.addAbility(new SimpleStaticAbility(new ControlEnchantedEffect("Equipment"))); } diff --git a/Mage.Sets/src/mage/cards/c/CascadeSeer.java b/Mage.Sets/src/mage/cards/c/CascadeSeer.java index c77dc5f960f..21096b446e9 100644 --- a/Mage.Sets/src/mage/cards/c/CascadeSeer.java +++ b/Mage.Sets/src/mage/cards/c/CascadeSeer.java @@ -1,18 +1,14 @@ package mage.cards.c; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.dynamicvalue.common.PartyCount; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.keyword.ScryEffect; import mage.abilities.hint.common.PartyCountHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.game.Game; -import mage.players.Player; import java.util.UUID; @@ -20,7 +16,6 @@ import java.util.UUID; * @author TheElk801 */ public final class CascadeSeer extends CardImpl { - public CascadeSeer(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); @@ -30,9 +25,14 @@ public final class CascadeSeer extends CardImpl { this.toughness = new MageInt(3); // When Cascade Seer enters the battlefield, scry X, where X is the number of creatures in your party. - this.addAbility(new EntersBattlefieldTriggeredAbility(new CascadeSeerEffect()).addHint(PartyCountHint.instance)); + this.addAbility( + new EntersBattlefieldTriggeredAbility( + new ScryEffect(PartyCount.instance) + .setText("scry X, where X is the number of creatures in your party") + ).addHint(PartyCountHint.instance)); } + private CascadeSeer(final CascadeSeer card) { super(card); } @@ -41,31 +41,4 @@ public final class CascadeSeer extends CardImpl { public CascadeSeer copy() { return new CascadeSeer(this); } -} - -class CascadeSeerEffect extends OneShotEffect { - - CascadeSeerEffect() { - super(Outcome.Benefit); - staticText = "scry X, where X is the number of creatures in your party. " + PartyCount.getReminder(); - } - - private CascadeSeerEffect(final CascadeSeerEffect effect) { - super(effect); - } - - @Override - public CascadeSeerEffect copy() { - return new CascadeSeerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player == null) { - return false; - } - int partyCount = PartyCount.instance.calculate(game, source, this); - return partyCount > 0 && player.scry(partyCount, source, game); - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/c/CaseOfTheCrimsonPulse.java b/Mage.Sets/src/mage/cards/c/CaseOfTheCrimsonPulse.java index b41bf1a0b57..d00c4f784e2 100644 --- a/Mage.Sets/src/mage/cards/c/CaseOfTheCrimsonPulse.java +++ b/Mage.Sets/src/mage/cards/c/CaseOfTheCrimsonPulse.java @@ -1,25 +1,24 @@ package mage.cards.c; -import java.util.UUID; - import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.CaseAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.HellbentCondition; import mage.abilities.condition.common.SolvedSourceCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.discard.DiscardControllerEffect; import mage.abilities.effects.common.discard.DiscardHandControllerEffect; import mage.abilities.hint.common.CaseSolvedHint; -import mage.constants.SubType; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** * Case of the Crimson Pulse {2}{R} * Enchantment - Case @@ -41,9 +40,8 @@ public final class CaseOfTheCrimsonPulse extends CardImpl { initialAbility.addEffect(new DrawCardSourceControllerEffect(2).setText(", then draw two cards.")); // To solve -- You have no cards in hand. // Solved -- At the beginning of your upkeep, discard your hand, then draw two cards. - Ability solvedAbility = new ConditionalTriggeredAbility(new BeginningOfUpkeepTriggeredAbility( - new DiscardHandControllerEffect()), - SolvedSourceCondition.SOLVED, null); + Ability solvedAbility = new BeginningOfUpkeepTriggeredAbility(new DiscardHandControllerEffect()) + .withTriggerCondition(SolvedSourceCondition.SOLVED); solvedAbility.addEffect(new DrawCardSourceControllerEffect(2).concatBy(", then")); this.addAbility(new CaseAbility(initialAbility, HellbentCondition.instance, solvedAbility) diff --git a/Mage.Sets/src/mage/cards/c/CaseOfTheGorgonsKiss.java b/Mage.Sets/src/mage/cards/c/CaseOfTheGorgonsKiss.java index 90c0c633d49..1ce3a754862 100644 --- a/Mage.Sets/src/mage/cards/c/CaseOfTheGorgonsKiss.java +++ b/Mage.Sets/src/mage/cards/c/CaseOfTheGorgonsKiss.java @@ -1,7 +1,5 @@ package mage.cards.c; -import java.util.UUID; - import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -16,19 +14,19 @@ import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; import mage.abilities.hint.common.CaseSolvedHint; import mage.abilities.keyword.DeathtouchAbility; import mage.abilities.keyword.LifelinkAbility; -import mage.constants.Duration; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.WasDealtDamageThisTurnPredicate; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.token.TokenImpl; import mage.target.TargetPermanent; import mage.watchers.common.CardsPutIntoGraveyardWatcher; +import java.util.UUID; + /** * Case of the Gorgon's Kiss {B} * Enchantment - Case @@ -40,13 +38,6 @@ import mage.watchers.common.CardsPutIntoGraveyardWatcher; */ public final class CaseOfTheGorgonsKiss extends CardImpl { - private static final FilterPermanent filter - = new FilterCreaturePermanent("creature that was dealt damage this turn"); - - static { - filter.add(WasDealtDamageThisTurnPredicate.instance); - } - public CaseOfTheGorgonsKiss(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{B}"); @@ -55,7 +46,7 @@ public final class CaseOfTheGorgonsKiss extends CardImpl { // When this Case enters the battlefield, destroy up to one target creature that was dealt damage this turn. Ability initialAbility = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect()) .setTriggerPhrase("When this Case enters, "); - initialAbility.addTarget(new TargetPermanent(0, 1, filter)); + initialAbility.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CREATURE_DAMAGED_THIS_TURN)); // To solve -- Three or more creature cards were put into graveyards from anywhere this turn. Condition toSolveCondition = new CaseOfTheGorgonsKissCondition(); // Solved -- This Case is a 4/4 Gorgon creature with deathtouch and lifelink in addition to its other types. @@ -66,7 +57,7 @@ public final class CaseOfTheGorgonsKiss extends CardImpl { .setText("This Case is a 4/4 Gorgon creature with deathtouch and lifelink in addition to its other types.")); this.addAbility(new CaseAbility(initialAbility, toSolveCondition, solvedAbility) - .addHint(new CaseOfTheGorgonsKissHint(toSolveCondition)), + .addHint(new CaseOfTheGorgonsKissHint(toSolveCondition)), new CardsPutIntoGraveyardWatcher()); } @@ -115,7 +106,7 @@ class CaseOfTheGorgonsKissHint extends CaseSolvedHint { @Override public String getConditionText(Game game, Ability ability) { - int creatures = (int)game.getState() + int creatures = (int) game.getState() .getWatcher(CardsPutIntoGraveyardWatcher.class) .getCardsPutIntoGraveyardFromAnywhere(game) .stream() diff --git a/Mage.Sets/src/mage/cards/c/CaseOfTheRansackedLab.java b/Mage.Sets/src/mage/cards/c/CaseOfTheRansackedLab.java index 9e6a71c503d..74b58330afe 100644 --- a/Mage.Sets/src/mage/cards/c/CaseOfTheRansackedLab.java +++ b/Mage.Sets/src/mage/cards/c/CaseOfTheRansackedLab.java @@ -1,23 +1,18 @@ package mage.cards.c; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - import mage.abilities.Ability; import mage.abilities.common.CaseAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.SolvedSourceCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; import mage.abilities.hint.common.CaseSolvedHint; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; import mage.constants.WatcherScope; import mage.filter.FilterCard; import mage.filter.StaticFilters; @@ -27,8 +22,11 @@ import mage.game.events.GameEvent; import mage.game.stack.Spell; import mage.watchers.Watcher; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + /** - * * @author DominionSpy */ public final class CaseOfTheRansackedLab extends CardImpl { @@ -51,10 +49,10 @@ public final class CaseOfTheRansackedLab extends CardImpl { Ability initialAbility = new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 1)); // To solve -- You've cast four or more instant and sorcery spells this turn. // Solved -- Whenever you cast an instant or sorcery spell, draw a card. - Ability solvedAbility = new ConditionalTriggeredAbility( - new SpellCastControllerTriggeredAbility(new DrawCardSourceControllerEffect(1), - StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, false), - SolvedSourceCondition.SOLVED, ""); + Ability solvedAbility = new SpellCastControllerTriggeredAbility( + new DrawCardSourceControllerEffect(1), + StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, false + ).withTriggerCondition(SolvedSourceCondition.SOLVED); this.addAbility(new CaseAbility(initialAbility, CaseOfTheRansackedLabCondition.instance, solvedAbility) .addHint(new CaseOfTheRansackedLabHint(CaseOfTheRansackedLabCondition.instance)), diff --git a/Mage.Sets/src/mage/cards/c/CaseOfTheShatteredPact.java b/Mage.Sets/src/mage/cards/c/CaseOfTheShatteredPact.java index 007aa4b1a92..2aaf706104a 100644 --- a/Mage.Sets/src/mage/cards/c/CaseOfTheShatteredPact.java +++ b/Mage.Sets/src/mage/cards/c/CaseOfTheShatteredPact.java @@ -1,33 +1,30 @@ package mage.cards.c; -import java.util.UUID; - import mage.ObjectColor; import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.abilities.common.CaseAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.SolvedSourceCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.abilities.hint.common.CaseSolvedHint; import mage.abilities.keyword.DoubleStrikeAbility; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.VigilanceAbility; -import mage.constants.SubType; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; import mage.filter.StaticFilters; import mage.game.Game; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetControlledCreaturePermanent; +import java.util.UUID; + /** - * * @author DominionSpy */ public final class CaseOfTheShatteredPact extends CardImpl { @@ -42,16 +39,14 @@ public final class CaseOfTheShatteredPact extends CardImpl { new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true)); // To solve -- There are five colors among permanents you control. // Solved -- At the beginning of combat on your turn, target creature you control gains flying, double strike, and vigilance until end of turn. - TriggeredAbility triggeredAbility = new BeginningOfCombatTriggeredAbility(new GainAbilityTargetEffect(FlyingAbility.getInstance()) + Ability solvedAbility = new BeginningOfCombatTriggeredAbility(new GainAbilityTargetEffect(FlyingAbility.getInstance()) .setText("target creature you control gains flying") - ); - triggeredAbility.addEffect(new GainAbilityTargetEffect(DoubleStrikeAbility.getInstance()) + ).withTriggerCondition(SolvedSourceCondition.SOLVED); + solvedAbility.addEffect(new GainAbilityTargetEffect(DoubleStrikeAbility.getInstance()) .setText(", double strike,")); - triggeredAbility.addEffect(new GainAbilityTargetEffect(VigilanceAbility.getInstance()) + solvedAbility.addEffect(new GainAbilityTargetEffect(VigilanceAbility.getInstance()) .setText("and vigilance until end of turn.")); - triggeredAbility.addTarget(new TargetControlledCreaturePermanent()); - Ability solvedAbility = new ConditionalTriggeredAbility( - triggeredAbility, SolvedSourceCondition.SOLVED, ""); + solvedAbility.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(new CaseAbility(initialAbility, CaseOfTheShatteredPactCondition.instance, solvedAbility) .addHint(new CaseOfTheShatteredPactHint(CaseOfTheShatteredPactCondition.instance))); diff --git a/Mage.Sets/src/mage/cards/c/CaseOfTheShiftingVisage.java b/Mage.Sets/src/mage/cards/c/CaseOfTheShiftingVisage.java index 1ea4d5622ae..95ccef878a7 100644 --- a/Mage.Sets/src/mage/cards/c/CaseOfTheShiftingVisage.java +++ b/Mage.Sets/src/mage/cards/c/CaseOfTheShiftingVisage.java @@ -1,19 +1,21 @@ package mage.cards.c; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.CaseAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.CardsInControllerGraveyardCondition; import mage.abilities.condition.common.SolvedSourceCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.CopyTargetStackObjectEffect; import mage.abilities.effects.keyword.SurveilEffect; import mage.abilities.hint.common.CaseSolvedHint; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SetTargetPointer; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.filter.StaticFilters; import mage.filter.common.FilterCreatureSpell; import mage.filter.predicate.Predicates; @@ -42,9 +44,11 @@ public final class CaseOfTheShiftingVisage extends CardImpl { // To solve — There are fifteen or more cards in your graveyard. Condition toSolveCondition = new CardsInControllerGraveyardCondition(15); // Solved — Whenever you cast a nonlegendary creature spell, copy that spell. - Ability solvedAbility = new ConditionalTriggeredAbility(new SpellCastControllerTriggeredAbility( - new CopyTargetStackObjectEffect(true).setText("copy that spell. (The copy becomes a token.)"), filter, false, SetTargetPointer.SPELL - ), SolvedSourceCondition.SOLVED, null); + Ability solvedAbility = new SpellCastControllerTriggeredAbility( + new CopyTargetStackObjectEffect(true) + .setText("copy that spell. (The copy becomes a token.)"), + filter, false, SetTargetPointer.SPELL + ).withTriggerCondition(SolvedSourceCondition.SOLVED); this.addAbility(new CaseAbility(initialAbility, toSolveCondition, solvedAbility) .addHint(new CaseOfTheShiftingVisageHint(toSolveCondition))); diff --git a/Mage.Sets/src/mage/cards/c/CaseOfTheTrampledGarden.java b/Mage.Sets/src/mage/cards/c/CaseOfTheTrampledGarden.java index 127b95a330e..164ce217f29 100644 --- a/Mage.Sets/src/mage/cards/c/CaseOfTheTrampledGarden.java +++ b/Mage.Sets/src/mage/cards/c/CaseOfTheTrampledGarden.java @@ -1,7 +1,5 @@ package mage.cards.c; -import java.util.UUID; - import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; @@ -10,16 +8,15 @@ import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.FormidableCondition; import mage.abilities.condition.common.SolvedSourceCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.effects.common.counter.DistributeCountersEffect; import mage.abilities.hint.common.CaseSolvedHint; import mage.abilities.keyword.TrampleAbility; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; @@ -28,8 +25,9 @@ import mage.game.permanent.Permanent; import mage.target.common.TargetAttackingCreature; import mage.target.common.TargetCreaturePermanentAmount; +import java.util.UUID; + /** - * * @author DominionSpy */ public final class CaseOfTheTrampledGarden extends CardImpl { @@ -44,10 +42,9 @@ public final class CaseOfTheTrampledGarden extends CardImpl { initialAbility.addTarget(new TargetCreaturePermanentAmount(2, StaticFilters.FILTER_CONTROLLED_CREATURES)); // To solve -- Creatures you control have total power 8 or greater. // Solved -- Whenever you attack, put a +1/+1 counter on target attacking creature. It gains trample until end of turn. - Ability solvedAbility = new ConditionalTriggeredAbility( - new AttacksWithCreaturesTriggeredAbility( - new AddCountersTargetEffect(CounterType.P1P1.createInstance()), 1), - SolvedSourceCondition.SOLVED, ""); + Ability solvedAbility = new AttacksWithCreaturesTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), 1 + ).withTriggerCondition(SolvedSourceCondition.SOLVED); solvedAbility.addEffect(new GainAbilityTargetEffect(TrampleAbility.getInstance()) .setText("it gains trample until end of turn")); solvedAbility.addTarget(new TargetAttackingCreature()); diff --git a/Mage.Sets/src/mage/cards/c/CatalystStone.java b/Mage.Sets/src/mage/cards/c/CatalystStone.java index 142a0892b57..11dbd13d808 100644 --- a/Mage.Sets/src/mage/cards/c/CatalystStone.java +++ b/Mage.Sets/src/mage/cards/c/CatalystStone.java @@ -53,7 +53,7 @@ class CatalystStoneCostReductionEffect extends CostModificationEffectImpl { CatalystStoneCostReductionEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST); - this.staticText = "Flashback costs you pay cost up to {2} less"; + this.staticText = "Flashback costs you pay cost {2} less"; } protected CatalystStoneCostReductionEffect(final CatalystStoneCostReductionEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/CatapultFodder.java b/Mage.Sets/src/mage/cards/c/CatapultFodder.java index 2853b20b656..97882939d50 100644 --- a/Mage.Sets/src/mage/cards/c/CatapultFodder.java +++ b/Mage.Sets/src/mage/cards/c/CatapultFodder.java @@ -1,26 +1,43 @@ package mage.cards.c; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; import mage.abilities.keyword.TransformAbility; -import mage.constants.SubType; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.ToughnessGreaterThanPowerPredicate; + +import java.util.UUID; /** - * * @author weirddan455 */ public final class CatapultFodder extends CardImpl { + private static final FilterPermanent filter = new FilterControlledCreaturePermanent( + "you control three or more creatures that each have toughness greater than their power" + ); + + static { + filter.add(ToughnessGreaterThanPowerPredicate.instance); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 2); + private static final Hint hint = new ValueHint( + "Creatures you control with toughness greater than their power", new PermanentsOnBattlefieldCount(filter) + ); + public CatapultFodder(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); @@ -31,11 +48,7 @@ public final class CatapultFodder extends CardImpl { // At the beginning of combat on your turn, if you control three or more creatures that each have toughness greater than their power, transform Catapult Fodder. this.addAbility(new TransformAbility()); - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfCombatTriggeredAbility(new TransformSourceEffect()), - CatapultFodderCondition.instance, - "At the beginning of combat on your turn, if you control three or more creatures that each have toughness greater than their power, transform {this}" - )); + this.addAbility(new BeginningOfCombatTriggeredAbility(new TransformSourceEffect()).withInterveningIf(condition)); } private CatapultFodder(final CatapultFodder card) { @@ -47,26 +60,3 @@ public final class CatapultFodder extends CardImpl { return new CatapultFodder(this); } } - -enum CatapultFodderCondition implements Condition { - instance; - - @Override - public boolean apply(Game game, Ability source) { - int creatures = 0; - for (Permanent permanent : game.getBattlefield().getAllActivePermanents(source.getControllerId())) { - if (permanent.isCreature(game) && permanent.getToughness().getValue() > permanent.getPower().getValue()) { - creatures++; - if (creatures >= 3) { - return true; - } - } - } - return false; - } - - @Override - public String toString() { - return "you control three or more creatures that each have toughness greater than their power"; - } -} diff --git a/Mage.Sets/src/mage/cards/c/CauldronDance.java b/Mage.Sets/src/mage/cards/c/CauldronDance.java index 7f14a074b8d..7329ac8e4ff 100644 --- a/Mage.Sets/src/mage/cards/c/CauldronDance.java +++ b/Mage.Sets/src/mage/cards/c/CauldronDance.java @@ -22,6 +22,7 @@ import mage.players.Player; import mage.target.common.TargetCardInHand; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @author nomage @@ -76,7 +77,7 @@ class CauldronDanceReturnFromGraveyardToBattlefieldTargetEffect extends OneShotE Card card = game.getCard(targetId); if (card != null) { controller.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent creature = game.getPermanent(card.getId()); + Permanent creature = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (creature != null) { ContinuousEffect hasteEffect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); hasteEffect.setTargetPointer(new FixedTarget(creature, game)); @@ -123,7 +124,7 @@ class CauldronDancePutCreatureFromHandOntoBattlefieldEffect extends OneShotEffec Card card = game.getCard(target.getFirstTarget()); if (card != null) { if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); effect.setTargetPointer(new FixedTarget(permanent, game)); diff --git a/Mage.Sets/src/mage/cards/c/CausticWasps.java b/Mage.Sets/src/mage/cards/c/CausticWasps.java index b7b6fb7f341..cf2c5bf75e7 100644 --- a/Mage.Sets/src/mage/cards/c/CausticWasps.java +++ b/Mage.Sets/src/mage/cards/c/CausticWasps.java @@ -12,7 +12,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterArtifactPermanent; import mage.target.TargetPermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -37,7 +37,7 @@ public final class CausticWasps extends CardImpl { // Whenever Caustic Wasps deals combat damage to a player, you may destroy target artifact that player controls. Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new DestroyTargetEffect(), true, true); ability.addTarget(new TargetPermanent(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/CelestialKirin.java b/Mage.Sets/src/mage/cards/c/CelestialKirin.java index a385158f633..40b576b47ab 100644 --- a/Mage.Sets/src/mage/cards/c/CelestialKirin.java +++ b/Mage.Sets/src/mage/cards/c/CelestialKirin.java @@ -36,7 +36,7 @@ public final class CelestialKirin extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever you cast a Spirit or Arcane spell, destroy all permanents with that spell's converted mana cost. this.addAbility(new SpellCastControllerTriggeredAbility( - new CelestialKirinEffect(), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, + new CelestialKirinEffect(), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, false, SetTargetPointer.SPELL )); } diff --git a/Mage.Sets/src/mage/cards/c/ChainOfSilence.java b/Mage.Sets/src/mage/cards/c/ChainOfSilence.java index 3e53c21fa54..cb2943c91f8 100644 --- a/Mage.Sets/src/mage/cards/c/ChainOfSilence.java +++ b/Mage.Sets/src/mage/cards/c/ChainOfSilence.java @@ -49,7 +49,7 @@ class ChainOfSilenceEffect extends OneShotEffect { ChainOfSilenceEffect() { super(Outcome.PreventDamage); - this.staticText = "Prevent all damage target creature would deal this turn. That creature's controller may sacrifice a land. If the player does, they may copy this spell and may choose a new target for that copy"; + this.staticText = "Prevent all damage target creature would deal this turn. That creature's controller may sacrifice a land of their choice. If the player does, they may copy this spell and may choose a new target for that copy"; } private ChainOfSilenceEffect(final ChainOfSilenceEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/ChainOfVapor.java b/Mage.Sets/src/mage/cards/c/ChainOfVapor.java index 1c49e722148..6df3f3ebee8 100644 --- a/Mage.Sets/src/mage/cards/c/ChainOfVapor.java +++ b/Mage.Sets/src/mage/cards/c/ChainOfVapor.java @@ -48,7 +48,7 @@ class ChainOfVaporEffect extends OneShotEffect { ChainOfVaporEffect() { super(Outcome.ReturnToHand); - this.staticText = "Return target nonland permanent to its owner's hand. Then that permanent's controller may sacrifice a land. If the player does, they may copy this spell and may choose a new target for that copy"; + this.staticText = "Return target nonland permanent to its owner's hand. Then that permanent's controller may sacrifice a land of their choice. If the player does, they may copy this spell and may choose a new target for that copy"; } private ChainOfVaporEffect(final ChainOfVaporEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/ChainerDementiaMaster.java b/Mage.Sets/src/mage/cards/c/ChainerDementiaMaster.java index 3d40820b2cd..de9c184a6ae 100644 --- a/Mage.Sets/src/mage/cards/c/ChainerDementiaMaster.java +++ b/Mage.Sets/src/mage/cards/c/ChainerDementiaMaster.java @@ -36,7 +36,7 @@ import mage.target.targetpointer.FixedTarget; */ public final class ChainerDementiaMaster extends CardImpl { - private static final FilterCreaturePermanent filterCreature = new FilterCreaturePermanent("Nightmare creatures"); + private static final FilterCreaturePermanent filterCreature = new FilterCreaturePermanent("All Nightmares"); private static final FilterPermanent filterPermanent = new FilterPermanent("Nightmares"); static { filterCreature.add(SubType.NIGHTMARE.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/c/Chainsaw.java b/Mage.Sets/src/mage/cards/c/Chainsaw.java index 8477a05584a..9dbc3fa349a 100644 --- a/Mage.Sets/src/mage/cards/c/Chainsaw.java +++ b/Mage.Sets/src/mage/cards/c/Chainsaw.java @@ -43,7 +43,8 @@ public final class Chainsaw extends CardImpl { StaticFilters.FILTER_PERMANENT_CREATURES, false)); // Equipped creature gets +X/+0, where X is the number of rev counters on Chainsaw. - this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(xValue, StaticValue.get(0)))); + this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(xValue, StaticValue.get(0)) + .setText("equipped creature gets +X/+0, where X is the number of rev counters on {this}"))); // Equip {3} this.addAbility(new EquipAbility(3, false)); diff --git a/Mage.Sets/src/mage/cards/c/ChakramRetriever.java b/Mage.Sets/src/mage/cards/c/ChakramRetriever.java index 90ac5b72afc..5c2c150c333 100644 --- a/Mage.Sets/src/mage/cards/c/ChakramRetriever.java +++ b/Mage.Sets/src/mage/cards/c/ChakramRetriever.java @@ -4,9 +4,7 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.condition.common.MyTurnCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.UntapTargetEffect; -import mage.abilities.hint.common.MyTurnHint; import mage.abilities.keyword.PartnerWithAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -33,11 +31,8 @@ public final class ChakramRetriever extends CardImpl { this.addAbility(new PartnerWithAbility("Chakram Slinger")); // Whenever you cast a spell during your turn, untap target creature. - Ability ability = new ConditionalTriggeredAbility( - new SpellCastControllerTriggeredAbility(new UntapTargetEffect(), false), - MyTurnCondition.instance, - "Whenever you cast a spell during your turn, untap target creature." - ).addHint(MyTurnHint.instance); + Ability ability = new SpellCastControllerTriggeredAbility(new UntapTargetEffect(), false) + .withTriggerCondition(MyTurnCondition.instance); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/ChaliceOfLife.java b/Mage.Sets/src/mage/cards/c/ChaliceOfLife.java index 8f436e29e57..f809074e503 100644 --- a/Mage.Sets/src/mage/cards/c/ChaliceOfLife.java +++ b/Mage.Sets/src/mage/cards/c/ChaliceOfLife.java @@ -1,36 +1,37 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.common.MoreThanStartingLifeTotalCondition; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; + +import java.util.UUID; /** - * * @author intimidatingant */ public final class ChaliceOfLife extends CardImpl { public ChaliceOfLife(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); this.secondSideCardClazz = mage.cards.c.ChaliceOfDeath.class; this.addAbility(new TransformAbility()); - // {tap}: You gain 1 life. Then if you have at least 10 life more than your starting life total, transform Chalice of Life. - this.addAbility(new SimpleActivatedAbility(new ChaliceOfLifeEffect(), new TapSourceCost())); + Ability ability = new SimpleActivatedAbility(new GainLifeEffect(1), new TapSourceCost()); + ability.addEffect(new ConditionalOneShotEffect( + new TransformSourceEffect(), MoreThanStartingLifeTotalCondition.TEN, + "Then if you have at least 10 life more than your starting life total, transform {this}" + )); + this.addAbility(ability); } private ChaliceOfLife(final ChaliceOfLife card) { @@ -42,39 +43,3 @@ public final class ChaliceOfLife extends CardImpl { return new ChaliceOfLife(this); } } - -class ChaliceOfLifeEffect extends OneShotEffect { - - ChaliceOfLifeEffect() { - super(Outcome.GainLife); - staticText = "You gain 1 life. Then if you have at least 10 life more than your starting life total, transform Chalice of Life"; - } - - private ChaliceOfLifeEffect(final ChaliceOfLifeEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - Player player = game.getPlayer(source.getControllerId()); - if(player != null) { - //gain 1 life - player.gainLife(1, game, source); - - // if you have at least 10 life more than your starting life total, transform Chalice of Life. - if (player.getLife() >= game.getStartingLife() + 10) { - permanent.transform(source, game); - } - } - } - return false; - } - - @Override - public ChaliceOfLifeEffect copy() { - return new ChaliceOfLifeEffect(this); - } - -} diff --git a/Mage.Sets/src/mage/cards/c/ChampionsOfMinasTirith.java b/Mage.Sets/src/mage/cards/c/ChampionsOfMinasTirith.java index ef1d9bd4855..126cac226e5 100644 --- a/Mage.Sets/src/mage/cards/c/ChampionsOfMinasTirith.java +++ b/Mage.Sets/src/mage/cards/c/ChampionsOfMinasTirith.java @@ -2,11 +2,9 @@ package mage.cards.c; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.MonarchIsSourceControllerCondition; import mage.abilities.costs.Cost; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.dynamicvalue.common.CardsInTargetPlayerHandCount; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; @@ -14,6 +12,7 @@ import mage.abilities.effects.common.BecomesMonarchSourceEffect; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.combat.TargetPlayerCantAttackYouEffect; import mage.abilities.hint.common.MonarchHint; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -40,16 +39,9 @@ public final class ChampionsOfMinasTirith extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new BecomesMonarchSourceEffect()).addHint(MonarchHint.instance)); // At the beginning of combat on each opponent's turn, if you're the monarch, that opponent may pay {X}, where X is the number of cards in their hand. If they don't, they can't attack you this combat. - this.addAbility(new ConditionalTriggeredAbility( - new BeginningOfCombatTriggeredAbility( - Zone.BATTLEFIELD, - TargetController.OPPONENT, new ChampionsOfMinasTirithEffect(), - false - ), - MonarchIsSourceControllerCondition.instance, - "At the beginning of combat on each opponent's turn, if you're the monarch, that opponent may pay {X}, " - + "where X is the number of cards in their hand. If they don't, they can't attack you this combat." - ).addHint(MonarchHint.instance)); + this.addAbility(new BeginningOfCombatTriggeredAbility( + TargetController.OPPONENT, new ChampionsOfMinasTirithEffect(), false + ).withInterveningIf(MonarchIsSourceControllerCondition.instance).addHint(MonarchHint.instance)); } private ChampionsOfMinasTirith(final ChampionsOfMinasTirith card) { @@ -66,6 +58,7 @@ class ChampionsOfMinasTirithEffect extends OneShotEffect { ChampionsOfMinasTirithEffect() { super(Outcome.Benefit); + staticText = "that opponent may pay {X}, where X is the number of cards in their hand. If they don't, they can't attack you this combat"; } private ChampionsOfMinasTirithEffect(final ChampionsOfMinasTirithEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/CharitableLevy.java b/Mage.Sets/src/mage/cards/c/CharitableLevy.java index b814b907ea6..bbc6d4a8e7a 100644 --- a/Mage.Sets/src/mage/cards/c/CharitableLevy.java +++ b/Mage.Sets/src/mage/cards/c/CharitableLevy.java @@ -37,7 +37,7 @@ public final class CharitableLevy extends CardImpl { filter.add(Predicates.not(CardType.CREATURE.getPredicate())); } - private static final Condition condition = new SourceHasCounterCondition(CounterType.COLLECTION, 3, Integer.MAX_VALUE); + private static final Condition condition = new SourceHasCounterCondition(CounterType.COLLECTION, 3); private static final FilterCard filterPlains = new FilterBySubtypeCard(SubType.PLAINS); diff --git a/Mage.Sets/src/mage/cards/c/ChasmSkulker.java b/Mage.Sets/src/mage/cards/c/ChasmSkulker.java index a17ce113f04..45e1edc39dd 100644 --- a/Mage.Sets/src/mage/cards/c/ChasmSkulker.java +++ b/Mage.Sets/src/mage/cards/c/ChasmSkulker.java @@ -1,32 +1,28 @@ - package mage.cards.c; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.DrawCardControllerTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.Zone; import mage.counters.CounterType; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.game.permanent.token.SquidToken; -import mage.players.Player; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class ChasmSkulker extends CardImpl { + private static final DynamicValue xValue = new CountersSourceCount(CounterType.P1P1); + public ChasmSkulker(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); this.subtype.add(SubType.SQUID); @@ -36,10 +32,14 @@ public final class ChasmSkulker extends CardImpl { this.toughness = new MageInt(1); // Whenever you draw a card, put a +1/+1 counter on Chasm Skulker. - this.addAbility(new DrawCardControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false)); + this.addAbility(new DrawCardControllerTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false + )); // When Chasm Skulker dies, create X 1/1 blue Squid creature tokens with islandwalk, where X is the number of +1/+1 counters on Chasm Skulker. - this.addAbility(new DiesSourceTriggeredAbility(new ChasmSkulkerEffect(), false)); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new SquidToken(), xValue) + .setText("create X 1/1 blue Squid creature tokens with islandwalk, " + + "where X is the number of +1/+1 counters on {this}"), false)); } private ChasmSkulker(final ChasmSkulker card) { @@ -51,36 +51,3 @@ public final class ChasmSkulker extends CardImpl { return new ChasmSkulker(this); } } - -class ChasmSkulkerEffect extends OneShotEffect { - - ChasmSkulkerEffect() { - super(Outcome.Benefit); - this.staticText = "create X 1/1 blue Squid creature tokens with islandwalk, where X is the number of +1/+1 counters on Chasm Skulker"; - } - - private ChasmSkulkerEffect(final ChasmSkulkerEffect effect) { - super(effect); - } - - @Override - public ChasmSkulkerEffect copy() { - return new ChasmSkulkerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Permanent permanent = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (permanent != null) { - int counters = permanent.getCounters(game).getCount(CounterType.P1P1); - if (counters > 0) { - return new CreateTokenEffect(new SquidToken(), counters).apply(game, source); - } - return true; - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/c/ChevillBaneOfMonsters.java b/Mage.Sets/src/mage/cards/c/ChevillBaneOfMonsters.java index 106c45379c1..3469c669238 100644 --- a/Mage.Sets/src/mage/cards/c/ChevillBaneOfMonsters.java +++ b/Mage.Sets/src/mage/cards/c/ChevillBaneOfMonsters.java @@ -2,15 +2,14 @@ package mage.cards.c; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -27,7 +26,7 @@ import java.util.UUID; public final class ChevillBaneOfMonsters extends CardImpl { private static final FilterPermanent filter - = new FilterPermanent(); + = new FilterPermanent("your opponents control no permanents with bounty counters on them"); private static final FilterPermanent filter2 = new FilterCreatureOrPlaneswalkerPermanent("creature or planeswalker an opponent controls"); private static final FilterPermanent filter3 @@ -59,13 +58,9 @@ public final class ChevillBaneOfMonsters extends CardImpl { this.addAbility(DeathtouchAbility.getInstance()); // At the beginning of your upkeep, if your opponents control no permanents with bounty counters on them, put a bounty counter on target creature or planeswalker an opponent controls. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility( - new AddCountersTargetEffect(CounterType.BOUNTY.createInstance()), false - ), condition, "At the beginning of your upkeep, " + - "if your opponents control no permanents with bounty counters on them, " + - "put a bounty counter on target creature or planeswalker an opponent controls." - ); + Ability ability = new BeginningOfUpkeepTriggeredAbility( + new AddCountersTargetEffect(CounterType.BOUNTY.createInstance()) + ).withInterveningIf(condition); ability.addTarget(new TargetPermanent(filter2)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/c/ChildhoodHorror.java b/Mage.Sets/src/mage/cards/c/ChildhoodHorror.java index 8962e7a938a..0522f3a2a4e 100644 --- a/Mage.Sets/src/mage/cards/c/ChildhoodHorror.java +++ b/Mage.Sets/src/mage/cards/c/ChildhoodHorror.java @@ -36,7 +36,7 @@ public final class ChildhoodHorror extends CardImpl { // Threshold - As long as seven or more cards are in your graveyard, Childhood Horror gets +2/+2 and can't block. Ability thresholdAbility = new SimpleStaticAbility(new ConditionalContinuousEffect( new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), - ThresholdCondition.instance, "If seven or more cards are in your graveyard, {this} gets +2/+2" + ThresholdCondition.instance, "As long as seven or more cards are in your graveyard, {this} gets +2/+2" )); thresholdAbility.addEffect(new ConditionalRestrictionEffect( new CantBlockSourceEffect(Duration.WhileOnBattlefield), ThresholdCondition.instance diff --git a/Mage.Sets/src/mage/cards/c/ChimilTheInnerSun.java b/Mage.Sets/src/mage/cards/c/ChimilTheInnerSun.java index 38fc82643f5..b4655a087c4 100644 --- a/Mage.Sets/src/mage/cards/c/ChimilTheInnerSun.java +++ b/Mage.Sets/src/mage/cards/c/ChimilTheInnerSun.java @@ -27,7 +27,7 @@ public final class ChimilTheInnerSun extends CardImpl { // Spells you control can't be countered. this.addAbility(new SimpleStaticAbility( - new CantBeCounteredControlledEffect(filter, null, Duration.WhileOnBattlefield) + new CantBeCounteredControlledEffect(filter, Duration.WhileOnBattlefield) )); // At the beginning of your end step, discover 5. diff --git a/Mage.Sets/src/mage/cards/c/ChoArrimAlchemist.java b/Mage.Sets/src/mage/cards/c/ChoArrimAlchemist.java index 664608f5dba..fed16f93fde 100644 --- a/Mage.Sets/src/mage/cards/c/ChoArrimAlchemist.java +++ b/Mage.Sets/src/mage/cards/c/ChoArrimAlchemist.java @@ -1,42 +1,41 @@ package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.DiscardCardCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.PreventionEffectData; -import mage.abilities.effects.PreventionEffectImpl; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.players.Player; -import mage.target.TargetSource; +import mage.constants.SubType; + +import java.util.UUID; /** - * * @author anonymous */ public final class ChoArrimAlchemist extends CardImpl { public ChoArrimAlchemist(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SPELLSHAPER); this.power = new MageInt(1); this.toughness = new MageInt(1); - // {1}{W}{W}, {tap}, Discard a card: The next time a source of your choice would deal damage to you this turn, prevent that damage. You gain life equal to the damage prevented this way. - Ability ability = new SimpleActivatedAbility(new ChoArrimAlchemistEffect(), new ManaCostsImpl<>("{1}{W}{W}")); + // {1}{W}{W}, {T}, Discard a card: The next time a source of your choice would deal damage to you this turn, prevent that damage. You gain life equal to the damage prevented this way. + Ability ability = new SimpleActivatedAbility( + new PreventNextDamageFromChosenSourceEffect( + Duration.EndOfTurn, true, + PreventNextDamageFromChosenSourceEffect.ON_PREVENT_YOU_GAIN_THAT_MUCH_LIFE + ), + new ManaCostsImpl<>("{1}{W}{W}") + ); ability.addCost(new TapSourceCost()); ability.addCost(new DiscardCardCost()); this.addAbility(ability); @@ -50,55 +49,4 @@ public final class ChoArrimAlchemist extends CardImpl { public ChoArrimAlchemist copy() { return new ChoArrimAlchemist(this); } -} - -class ChoArrimAlchemistEffect extends PreventionEffectImpl { - - private final TargetSource target; - - public ChoArrimAlchemistEffect() { - super(Duration.EndOfTurn, Integer.MAX_VALUE, false, false); - this.staticText = "The next time a source of your choice would deal damage to you this turn, prevent that damage. You gain life equal to the damage prevented this way."; - this.target = new TargetSource(); - } - - private ChoArrimAlchemistEffect(final ChoArrimAlchemistEffect effect) { - super(effect); - this.target = effect.target.copy(); - } - - @Override - public ChoArrimAlchemistEffect copy() { - return new ChoArrimAlchemistEffect(this); - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - PreventionEffectData preventionData = preventDamageAction(event, source, game); - this.used = true; - this.discard(); // only one use - if (preventionData.getPreventedDamage() > 0) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - player.gainLife(preventionData.getPreventedDamage(), game, source); - } - } - return true; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (!this.used && super.applies(event, source, game)) { - if (event.getTargetId().equals(source.getControllerId()) && event.getSourceId().equals(target.getFirstTarget())) { - return true; - } - } - return false; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/c/ChocoComet.java b/Mage.Sets/src/mage/cards/c/ChocoComet.java new file mode 100644 index 00000000000..1df17c2a89f --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChocoComet.java @@ -0,0 +1,38 @@ +package mage.cards.c; + +import mage.abilities.dynamicvalue.common.GetXValue; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.permanent.token.ChocoboToken; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ChocoComet extends CardImpl { + + public ChocoComet(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{R}{R}"); + + // Choco-Comet deals X damage to any target. + this.getSpellAbility().addEffect(new DamageTargetEffect(GetXValue.instance)); + this.getSpellAbility().addTarget(new TargetAnyTarget()); + + // Create a 2/2 green Bird creature token with "Whenever a land you control enters, this token gets +1/+0 until end of turn." + this.getSpellAbility().addEffect(new CreateTokenEffect(new ChocoboToken()).concatBy("
    ")); + } + + private ChocoComet(final ChocoComet card) { + super(card); + } + + @Override + public ChocoComet copy() { + return new ChocoComet(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ChocoSeekerOfParadise.java b/Mage.Sets/src/mage/cards/c/ChocoSeekerOfParadise.java index 1f57195cc1d..0b2c22e85c8 100644 --- a/Mage.Sets/src/mage/cards/c/ChocoSeekerOfParadise.java +++ b/Mage.Sets/src/mage/cards/c/ChocoSeekerOfParadise.java @@ -38,7 +38,9 @@ public final class ChocoSeekerOfParadise extends CardImpl { this.toughness = new MageInt(5); // Whenever one or more Birds you control attack, look at that many cards from the top of your library. You may put one of them into your hand. Then put any number of land cards from among them onto the battlefield tapped and the rest into your graveyard. - this.addAbility(new AttacksWithCreaturesTriggeredAbility(new ChocoSeekerOfParadiseEffect(), 1, filter)); + this.addAbility(new AttacksWithCreaturesTriggeredAbility( + new ChocoSeekerOfParadiseEffect(), 1, filter + ).setTriggerPhrase("Whenever one or more Birds you control attack, ")); // Landfall -- Whenever a land you control enters, Choco gets +1/+0 until end of turn. this.addAbility(new LandfallAbility(new BoostSourceEffect(1, 0, Duration.EndOfTurn))); diff --git a/Mage.Sets/src/mage/cards/c/ChocoboKick.java b/Mage.Sets/src/mage/cards/c/ChocoboKick.java new file mode 100644 index 00000000000..cc20fbfc9f8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChocoboKick.java @@ -0,0 +1,50 @@ +package mage.cards.c; + +import mage.abilities.condition.common.KickedCondition; +import mage.abilities.costs.common.ReturnToHandChosenControlledPermanentCost; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.DamageWithPowerFromOneToAnotherTargetEffect; +import mage.abilities.keyword.KickerAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetControlledPermanent; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ChocoboKick extends CardImpl { + + public ChocoboKick(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}"); + + // Kicker--Return a land you control to its owner's hand. + this.addAbility(new KickerAbility(new ReturnToHandChosenControlledPermanentCost( + new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_A_LAND) + ))); + + // Target creature you control deals damage equal to its power to target creature an opponent controls. If this spell was kicked, the creature you control deals twice that much damage instead. + this.getSpellAbility().addEffect(new ConditionalOneShotEffect( + new DamageWithPowerFromOneToAnotherTargetEffect("", 2), + new DamageWithPowerFromOneToAnotherTargetEffect(), KickedCondition.ONCE, "target creature " + + "you control deals damage equal to its power to target creature an opponent controls. " + + "If this spell was kicked, the creature you control deals twice that much damage instead" + )); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + this.getSpellAbility().addTarget(new TargetOpponentsCreaturePermanent()); + } + + private ChocoboKick(final ChocoboKick card) { + super(card); + } + + @Override + public ChocoboKick copy() { + return new ChocoboKick(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ChocoboRacetrack.java b/Mage.Sets/src/mage/cards/c/ChocoboRacetrack.java new file mode 100644 index 00000000000..e3cbbfd20a8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChocoboRacetrack.java @@ -0,0 +1,32 @@ +package mage.cards.c; + +import mage.abilities.common.LandfallAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.permanent.token.ChocoboToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ChocoboRacetrack extends CardImpl { + + public ChocoboRacetrack(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}{G}{G}"); + + // Landfall -- Whenever a land you control enters, create a 2/2 green Bird creature token with "Whenever a land you control enters, this token gets +1/+0 until end of turn." + this.addAbility(new LandfallAbility(new CreateTokenEffect(new ChocoboToken()))); + } + + private ChocoboRacetrack(final ChocoboRacetrack card) { + super(card); + } + + @Override + public ChocoboRacetrack copy() { + return new ChocoboRacetrack(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ChokingSands.java b/Mage.Sets/src/mage/cards/c/ChokingSands.java index df0be09ebe4..0f99f49fced 100644 --- a/Mage.Sets/src/mage/cards/c/ChokingSands.java +++ b/Mage.Sets/src/mage/cards/c/ChokingSands.java @@ -9,12 +9,13 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; +import mage.filter.FilterPermanent; import mage.filter.common.FilterLandPermanent; import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.common.TargetLandPermanent; +import mage.target.TargetPermanent; import java.util.UUID; @@ -23,7 +24,7 @@ import java.util.UUID; */ public final class ChokingSands extends CardImpl { - private static final FilterLandPermanent filter = new FilterLandPermanent("non-Swamp land"); + private static final FilterPermanent filter = new FilterLandPermanent("non-Swamp land"); static { filter.add(Predicates.not(SubType.SWAMP.getPredicate())); @@ -34,7 +35,7 @@ public final class ChokingSands extends CardImpl { // Destroy target non-Swamp land. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetLandPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); // If that land was nonbasic, Choking Sands deals 2 damage to the land's controller. this.getSpellAbility().addEffect(new ChokingSandsEffect()); @@ -54,7 +55,7 @@ class ChokingSandsEffect extends OneShotEffect { ChokingSandsEffect() { super(Outcome.Damage); - this.staticText = "If that land was nonbasic, Choking Sands deals 2 damage to the land's controller"; + this.staticText = "If that land was nonbasic, {this} deals 2 damage to the land's controller"; } private ChokingSandsEffect(final ChokingSandsEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/ChromeReplicator.java b/Mage.Sets/src/mage/cards/c/ChromeReplicator.java index 20cd7226010..807308840d1 100644 --- a/Mage.Sets/src/mage/cards/c/ChromeReplicator.java +++ b/Mage.Sets/src/mage/cards/c/ChromeReplicator.java @@ -5,7 +5,6 @@ import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -37,12 +36,7 @@ public final class ChromeReplicator extends CardImpl { this.toughness = new MageInt(4); // When Chrome Replicator enters the battlefield, if you control two or more nonland, nontoken permanents with the same name as one another, create a 4/4 colorless Construct artifact creature token. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new Construct4Token())), - ChromeReplicatorCondition.instance, "When {this} enters, " + - "if you control two or more nonland, nontoken permanents with the same name as one another, " + - "create a 4/4 colorless Construct artifact creature token." - )); + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new Construct4Token())).withInterveningIf(ChromeReplicatorCondition.instance)); } private ChromeReplicator(final ChromeReplicator card) { @@ -78,4 +72,9 @@ enum ChromeReplicatorCondition implements Condition { .filter(s -> !s.isEmpty()) .anyMatch(s -> nameMap.compute(s, CardUtil::setOrIncrementValue) >= 2); } + + @Override + public String toString() { + return "you control two or more nonland, nontoken permanents with the same name as one another"; + } } diff --git a/Mage.Sets/src/mage/cards/c/Chronozoa.java b/Mage.Sets/src/mage/cards/c/Chronozoa.java index 97f3248396b..37e6fc3511e 100644 --- a/Mage.Sets/src/mage/cards/c/Chronozoa.java +++ b/Mage.Sets/src/mage/cards/c/Chronozoa.java @@ -1,12 +1,9 @@ - package mage.cards.c; import mage.MageInt; import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.condition.common.LastTimeCounterRemovedCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.CreateTokenCopySourceEffect; -import mage.abilities.effects.Effect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.VanishingAbility; import mage.cards.CardImpl; @@ -34,11 +31,10 @@ public final class Chronozoa extends CardImpl { this.addAbility(new VanishingAbility(3)); // When Chronozoa dies, if it had no time counters on it, create two tokens that are copies of it. - Effect effect = new CreateTokenCopySourceEffect(2); - effect.setText("create two tokens that are copies of it"); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(new DiesSourceTriggeredAbility(effect, false), - LastTimeCounterRemovedCondition.instance, - "When {this} dies, if it had no time counters on it, create two tokens that are copies of it.")); + this.addAbility(new DiesSourceTriggeredAbility( + new CreateTokenCopySourceEffect(2) + .setText("create two tokens that are copies of it"), false + ).withInterveningIf(LastTimeCounterRemovedCondition.instance)); } private Chronozoa(final Chronozoa card) { diff --git a/Mage.Sets/src/mage/cards/c/CircleOfProtectionArtifacts.java b/Mage.Sets/src/mage/cards/c/CircleOfProtectionArtifacts.java index 9a73859f84f..31a98658b80 100644 --- a/Mage.Sets/src/mage/cards/c/CircleOfProtectionArtifacts.java +++ b/Mage.Sets/src/mage/cards/c/CircleOfProtectionArtifacts.java @@ -1,35 +1,34 @@ package mage.cards.c; -import java.util.UUID; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; -import mage.filter.FilterObject; +import mage.filter.FilterSource; + +import java.util.UUID; /** - * * @author Plopman */ public final class CircleOfProtectionArtifacts extends CardImpl { - private static final FilterObject filter = new FilterObject("artifact source"); + private static final FilterSource filter = new FilterSource("artifact source"); static { filter.add(CardType.ARTIFACT.getPredicate()); } - + public CircleOfProtectionArtifacts(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); // {2}: The next time an artifact source of your choice would deal damage to you this turn, prevent that damage. - Effect effect = new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn, filter); + Effect effect = new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, true, filter); this.addAbility(new SimpleActivatedAbility(effect, new ManaCostsImpl<>("{2}"))); } diff --git a/Mage.Sets/src/mage/cards/c/CircleOfProtectionBlack.java b/Mage.Sets/src/mage/cards/c/CircleOfProtectionBlack.java index a42b75122cf..617a182a435 100644 --- a/Mage.Sets/src/mage/cards/c/CircleOfProtectionBlack.java +++ b/Mage.Sets/src/mage/cards/c/CircleOfProtectionBlack.java @@ -1,38 +1,37 @@ package mage.cards.c; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author Plopman */ public final class CircleOfProtectionBlack extends CardImpl { - private static final FilterObject filter = new FilterObject("black source"); + private static final FilterSource filter = new FilterSource("black source"); static { filter.add(new ColorPredicate(ObjectColor.BLACK)); } - + public CircleOfProtectionBlack(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); // {1}: The next time a black source of your choice would deal damage to you this turn, prevent that damage. - Effect effect = new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn, filter); + Effect effect = new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, true, filter); this.addAbility(new SimpleActivatedAbility(effect, new ManaCostsImpl<>("{1}"))); } diff --git a/Mage.Sets/src/mage/cards/c/CircleOfProtectionBlue.java b/Mage.Sets/src/mage/cards/c/CircleOfProtectionBlue.java index 7a52cc659bd..185317f8683 100644 --- a/Mage.Sets/src/mage/cards/c/CircleOfProtectionBlue.java +++ b/Mage.Sets/src/mage/cards/c/CircleOfProtectionBlue.java @@ -1,38 +1,37 @@ package mage.cards.c; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author Plopman */ public final class CircleOfProtectionBlue extends CardImpl { - private static final FilterObject filter = new FilterObject("blue source"); + private static final FilterSource filter = new FilterSource("blue source"); static { filter.add(new ColorPredicate(ObjectColor.BLUE)); } public CircleOfProtectionBlue(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); // {1}: The next time a blue source of your choice would deal damage to you this turn, prevent that damage. - Effect effect = new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn, filter); + Effect effect = new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, true, filter); this.addAbility(new SimpleActivatedAbility(effect, new ManaCostsImpl<>("{1}"))); } diff --git a/Mage.Sets/src/mage/cards/c/CircleOfProtectionGreen.java b/Mage.Sets/src/mage/cards/c/CircleOfProtectionGreen.java index ab565226201..7585afa504d 100644 --- a/Mage.Sets/src/mage/cards/c/CircleOfProtectionGreen.java +++ b/Mage.Sets/src/mage/cards/c/CircleOfProtectionGreen.java @@ -1,38 +1,37 @@ package mage.cards.c; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author Plopman */ public final class CircleOfProtectionGreen extends CardImpl { - private static final FilterObject filter = new FilterObject("green source"); + private static final FilterSource filter = new FilterSource("green source"); static { filter.add(new ColorPredicate(ObjectColor.GREEN)); } public CircleOfProtectionGreen(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); // {1}: The next time a green source of your choice would deal damage to you this turn, prevent that damage. - Effect effect = new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn, filter); + Effect effect = new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, true, filter); this.addAbility(new SimpleActivatedAbility(effect, new ManaCostsImpl<>("{1}"))); } diff --git a/Mage.Sets/src/mage/cards/c/CircleOfProtectionRed.java b/Mage.Sets/src/mage/cards/c/CircleOfProtectionRed.java index 7ed4b49518e..84cc319d464 100644 --- a/Mage.Sets/src/mage/cards/c/CircleOfProtectionRed.java +++ b/Mage.Sets/src/mage/cards/c/CircleOfProtectionRed.java @@ -1,38 +1,37 @@ package mage.cards.c; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author Plopman */ public final class CircleOfProtectionRed extends CardImpl { - private static final FilterObject filter = new FilterObject("red source"); + private static final FilterSource filter = new FilterSource("red source"); static { filter.add(new ColorPredicate(ObjectColor.RED)); } public CircleOfProtectionRed(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); // {1}: The next time a red source of your choice would deal damage to you this turn, prevent that damage. - Effect effect = new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn, filter); + Effect effect = new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, true, filter); this.addAbility(new SimpleActivatedAbility(effect, new ManaCostsImpl<>("{1}"))); } diff --git a/Mage.Sets/src/mage/cards/c/CircleOfProtectionShadow.java b/Mage.Sets/src/mage/cards/c/CircleOfProtectionShadow.java index 7eaa168dc64..eea2a46f828 100644 --- a/Mage.Sets/src/mage/cards/c/CircleOfProtectionShadow.java +++ b/Mage.Sets/src/mage/cards/c/CircleOfProtectionShadow.java @@ -1,37 +1,37 @@ package mage.cards.c; -import java.util.UUID; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.abilities.keyword.ShadowAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; +import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.AbilityPredicate; +import java.util.UUID; + /** - * * @author LoneFox */ public final class CircleOfProtectionShadow extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature of your choice with shadow"); + private static final FilterPermanent filter = new FilterCreaturePermanent("a creature of your choice with shadow"); static { filter.add(new AbilityPredicate(ShadowAbility.class)); } public CircleOfProtectionShadow(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); // {1}: The next time a creature of your choice with shadow would deal damage to you this turn, prevent that damage. - Effect effect = new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn, filter); + Effect effect = new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, true, filter); effect.setText("The next time a creature of your choice with shadow would deal damage to you this turn, prevent that damage"); this.addAbility(new SimpleActivatedAbility(effect, new ManaCostsImpl<>("{1}"))); } diff --git a/Mage.Sets/src/mage/cards/c/CircleOfProtectionWhite.java b/Mage.Sets/src/mage/cards/c/CircleOfProtectionWhite.java index 39c6d8ae8bb..18226add18b 100644 --- a/Mage.Sets/src/mage/cards/c/CircleOfProtectionWhite.java +++ b/Mage.Sets/src/mage/cards/c/CircleOfProtectionWhite.java @@ -1,38 +1,37 @@ package mage.cards.c; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author Plopman */ public final class CircleOfProtectionWhite extends CardImpl { - private static final FilterObject filter = new FilterObject("white source"); + private static final FilterSource filter = new FilterSource("white source"); static { filter.add(new ColorPredicate(ObjectColor.WHITE)); } public CircleOfProtectionWhite(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); // {1}: The next time a white source of your choice would deal damage to you this turn, prevent that damage. - Effect effect = new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn, filter); + Effect effect = new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, true, filter); this.addAbility(new SimpleActivatedAbility(effect, new ManaCostsImpl<>("{1}"))); } diff --git a/Mage.Sets/src/mage/cards/c/CircleOfSolace.java b/Mage.Sets/src/mage/cards/c/CircleOfSolace.java index 02513f84e0c..ff520c81ead 100644 --- a/Mage.Sets/src/mage/cards/c/CircleOfSolace.java +++ b/Mage.Sets/src/mage/cards/c/CircleOfSolace.java @@ -60,7 +60,7 @@ class CircleOfSolaceEffect extends PreventionEffectImpl { Permanent perm = game.getPermanent(event.getSourceId()); if (perm != null) { SubType subType = ChooseCreatureTypeEffect.getChosenCreatureType(source.getSourceId(), game); - return perm.getCardType().contains(CardType.CREATURE) && perm.getSubtype().contains(subType); + return perm.isCreature() && perm.hasSubtype(subType, game); } } } diff --git a/Mage.Sets/src/mage/cards/c/CitanulWoodreaders.java b/Mage.Sets/src/mage/cards/c/CitanulWoodreaders.java index 3b3193f57b7..d9b27fbea9f 100644 --- a/Mage.Sets/src/mage/cards/c/CitanulWoodreaders.java +++ b/Mage.Sets/src/mage/cards/c/CitanulWoodreaders.java @@ -1,11 +1,8 @@ - package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.KickedCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.KickerAbility; import mage.cards.CardImpl; @@ -13,8 +10,9 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class CitanulWoodreaders extends CardImpl { @@ -31,11 +29,8 @@ public final class CitanulWoodreaders extends CardImpl { this.addAbility(new KickerAbility("{2}{G}")); // When Citanul Woodreaders enters the battlefield, if it was kicked, draw two cards. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(2)), - KickedCondition.ONCE, - "When {this} enters, if it was kicked, draw two cards." - )); + this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(2)) + .withInterveningIf(KickedCondition.ONCE)); } private CitanulWoodreaders(final CitanulWoodreaders card) { diff --git a/Mage.Sets/src/mage/cards/c/ClaimJumper.java b/Mage.Sets/src/mage/cards/c/ClaimJumper.java index ffa895f0f65..568a2c47f67 100644 --- a/Mage.Sets/src/mage/cards/c/ClaimJumper.java +++ b/Mage.Sets/src/mage/cards/c/ClaimJumper.java @@ -3,8 +3,8 @@ package mage.cards.c; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.OpponentControlsMoreCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; import mage.abilities.keyword.VigilanceAbility; @@ -29,6 +29,8 @@ public final class ClaimJumper extends CardImpl { filter.add(SubType.PLAINS.getPredicate()); } + private static final Condition condition = new OpponentControlsMoreCondition(StaticFilters.FILTER_LANDS); + public ClaimJumper(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); @@ -41,27 +43,13 @@ public final class ClaimJumper extends CardImpl { this.addAbility(VigilanceAbility.getInstance()); // When Claim Jumper enters the battlefield, if an opponent controls more lands than you, you may search your library for a Plains card and put it onto the battlefield tapped. Then if an opponent controls more lands than you, repeat this process once. If you search your library this way, shuffle. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility( - new SearchLibraryPutInPlayEffect( - new TargetCardInLibrary(0, 1, filter), true - ), - true - ), - new OpponentControlsMoreCondition(StaticFilters.FILTER_LANDS), - "When {this} enters, if an opponent controls more lands than you, " - + "you may search your library for a Plains card and put it onto the battlefield tapped. " - + "Then if an opponent controls more lands than you, repeat this process once. " - + "If you search your library this way, shuffle." - ); - ability.addEffect( - new ConditionalOneShotEffect( - new SearchLibraryPutInPlayEffect( - new TargetCardInLibrary(0, 1, filter), true, false, true - ), - new OpponentControlsMoreCondition(StaticFilters.FILTER_LANDS) - ) - ); + Ability ability = new EntersBattlefieldTriggeredAbility( + new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter), true), true + ).withInterveningIf(condition); + ability.addEffect(new ConditionalOneShotEffect( + new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter), true, false, true), + condition, "Then if an opponent controls more lands than you, repeat this process once. If you search your library this way, shuffle" + )); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/ClashOfTheEikons.java b/Mage.Sets/src/mage/cards/c/ClashOfTheEikons.java new file mode 100644 index 00000000000..7cb828e86e4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ClashOfTheEikons.java @@ -0,0 +1,56 @@ +package mage.cards.c; + +import mage.abilities.Mode; +import mage.abilities.effects.common.FightTargetsEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.effects.common.counter.RemoveCounterTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ClashOfTheEikons extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.SAGA); + + public ClashOfTheEikons(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{G}"); + + // Choose one or more -- + this.getSpellAbility().getModes().setMinModes(1); + this.getSpellAbility().getModes().setMaxModes(3); + + // * Target creature you control fights target creature an opponent controls. + this.getSpellAbility().addEffect(new FightTargetsEffect()); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + this.getSpellAbility().addTarget(new TargetOpponentsCreaturePermanent()); + + // * Remove a lore counter from target Saga you control. + this.getSpellAbility().addMode(new Mode(new RemoveCounterTargetEffect(CounterType.LORE.createInstance())) + .addTarget(new TargetPermanent(filter))); + + // * Put a lore counter on target Saga you control. + this.getSpellAbility().addMode(new Mode(new AddCountersTargetEffect(CounterType.LORE.createInstance())) + .addTarget(new TargetPermanent(filter))); + } + + private ClashOfTheEikons(final ClashOfTheEikons card) { + super(card); + } + + @Override + public ClashOfTheEikons copy() { + return new ClashOfTheEikons(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CleavingSkyrider.java b/Mage.Sets/src/mage/cards/c/CleavingSkyrider.java index 9dd232ba792..7641522e7d1 100644 --- a/Mage.Sets/src/mage/cards/c/CleavingSkyrider.java +++ b/Mage.Sets/src/mage/cards/c/CleavingSkyrider.java @@ -1,28 +1,30 @@ package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.KickedCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.AttackingCreatureCount; import mage.abilities.effects.common.DamageTargetEffect; -import mage.constants.SubType; import mage.abilities.keyword.FlashAbility; -import mage.abilities.keyword.KickerAbility; import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.KickerAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; import mage.target.common.TargetAnyTarget; +import java.util.UUID; + /** - * * @author weirddan455 */ public final class CleavingSkyrider extends CardImpl { + private static final DynamicValue xValue = new AttackingCreatureCount("the number of attacking creatures"); + public CleavingSkyrider(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); @@ -41,11 +43,9 @@ public final class CleavingSkyrider extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Cleaving Skyrider enters the battlefield, if it was kicked, it deals X damage to any target, where X is the number of attacking creatures. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new DamageTargetEffect(new AttackingCreatureCount())), - KickedCondition.ONCE, - "When {this} enters, if it was kicked, it deals X damage to any target, where X is the number of attacking creatures." - ); + Ability ability = new EntersBattlefieldTriggeredAbility( + new DamageTargetEffect(xValue, "it") + ).withInterveningIf(KickedCondition.ONCE); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/ClementTheWorrywort.java b/Mage.Sets/src/mage/cards/c/ClementTheWorrywort.java index b65236ddf23..558cfab74e0 100644 --- a/Mage.Sets/src/mage/cards/c/ClementTheWorrywort.java +++ b/Mage.Sets/src/mage/cards/c/ClementTheWorrywort.java @@ -15,30 +15,38 @@ import mage.abilities.mana.builder.ConditionalManaBuilder; import mage.abilities.mana.conditional.CreatureCastManaCondition; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.filter.Filter; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; +import mage.util.CardUtil; import java.util.UUID; /** - * * @author earchip94 */ public final class ClementTheWorrywort extends CardImpl { + private static final FilterPermanent filter = new FilterControlledCreaturePermanent("creature you control with lesser mana value"); private static final FilterPermanent frogFilter = new FilterPermanent(SubType.FROG, "Frogs"); + static { + filter.add(ClementTheWorrywortPredicate.instance); + } + public ClementTheWorrywort(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{U}"); - + this.supertype.add(SuperType.LEGENDARY); this.subtype.add(SubType.FROG); this.subtype.add(SubType.DRUID); @@ -49,12 +57,16 @@ public final class ClementTheWorrywort extends CardImpl { this.addAbility(VigilanceAbility.getInstance()); // Whenever Clement, the Worrywort or another creature you control enters, return up to one target creature you control with lesser mana value to its owner's hand. - this.addAbility(new ClementTheWorrywortTriggeredAbility()); + Ability ability = new EntersBattlefieldThisOrAnotherTriggeredAbility( + new ReturnToHandTargetEffect(), StaticFilters.FILTER_CONTROLLED_CREATURE, false, false + ); + ability.addTarget(new TargetPermanent(0, 1, filter)); + this.addAbility(ability); // Frogs you control have "{T}: Add {G} or {U}. Spend this mana only to cast a creature spell." Ability gMana = new ConditionalColoredManaAbility(new TapSourceCost(), Mana.GreenMana(1), new ClementTheWorrywortManaBuilder()); Ability bMana = new ConditionalColoredManaAbility(new TapSourceCost(), Mana.BlueMana(1), new ClementTheWorrywortManaBuilder()); - Ability ability = new SimpleStaticAbility( + ability = new SimpleStaticAbility( new GainAbilityControlledEffect(gMana, Duration.WhileOnBattlefield, frogFilter, false) .setText("Frogs you control have \"{T}: Add {G} or {U}.") ); @@ -75,39 +87,17 @@ public final class ClementTheWorrywort extends CardImpl { } } -class ClementTheWorrywortTriggeredAbility extends EntersBattlefieldThisOrAnotherTriggeredAbility { - - ClementTheWorrywortTriggeredAbility() { - super(new ReturnToHandTargetEffect().setText("return up to one target creature you control with lesser mana value to its owner's hand"), - StaticFilters.FILTER_PERMANENT_CREATURE, false, true); - } - - ClementTheWorrywortTriggeredAbility(final ClementTheWorrywortTriggeredAbility ability) { - super(ability); - } +enum ClementTheWorrywortPredicate implements ObjectSourcePlayerPredicate { + instance; @Override - public ClementTheWorrywortTriggeredAbility copy() { - return new ClementTheWorrywortTriggeredAbility(this); + public boolean apply(ObjectSourcePlayer input, Game game) { + return CardUtil.getEffectValueFromAbility( + input.getSource(), "permanentEnteringBattlefield", Permanent.class + ) + .filter(permanent -> input.getObject().getManaValue() < permanent.getManaValue()) + .isPresent(); } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (super.checkTrigger(event, game)) { - Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent == null) { - return false; - } - int mv = permanent.getManaValue(); - FilterControlledCreaturePermanent filter = - new FilterControlledCreaturePermanent("creature you control with mana value " + (mv - 1) + " or less"); - filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, mv)); - this.addTarget(new TargetPermanent(0,1, filter)); - return true; - } - return false; - } - } class ClementTheWorrywortConditionalMana extends ConditionalMana { diff --git a/Mage.Sets/src/mage/cards/c/ClericClass.java b/Mage.Sets/src/mage/cards/c/ClericClass.java index 2f6fabe7124..23e9bebf612 100644 --- a/Mage.Sets/src/mage/cards/c/ClericClass.java +++ b/Mage.Sets/src/mage/cards/c/ClericClass.java @@ -24,6 +24,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetControlledCreaturePermanent; +import mage.util.CardUtil; import java.util.UUID; @@ -98,7 +99,7 @@ class ClericClassReturnEffect extends OneShotEffect { } player.moveCards(card, Zone.BATTLEFIELD, source, game); game.processAction(); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); int toughness = permanent != null ? permanent.getToughness().getValue() : card.getToughness().getValue(); player.gainLife(toughness, game, source); return true; diff --git a/Mage.Sets/src/mage/cards/c/CliveIfritsDominant.java b/Mage.Sets/src/mage/cards/c/CliveIfritsDominant.java index 4555ed7dee1..8a18243df9c 100644 --- a/Mage.Sets/src/mage/cards/c/CliveIfritsDominant.java +++ b/Mage.Sets/src/mage/cards/c/CliveIfritsDominant.java @@ -38,7 +38,8 @@ public final class CliveIfritsDominant extends CardImpl { // When Clive enters, you may discard your hand, then draw cards equal to your devotion to red. Ability ability = new EntersBattlefieldTriggeredAbility(new DiscardHandControllerEffect(), true); - ability.addEffect(new DrawCardSourceControllerEffect(DevotionCount.R).concatBy(", then")); + ability.addEffect(new DrawCardSourceControllerEffect(DevotionCount.R) + .setText(", then draw cards equal to your devotion to red")); this.addAbility(ability.addHint(DevotionCount.R.getHint())); // {4}{R}{R}, {T}: Exile Clive, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery. diff --git a/Mage.Sets/src/mage/cards/c/ClivesHideaway.java b/Mage.Sets/src/mage/cards/c/ClivesHideaway.java new file mode 100644 index 00000000000..136a1b212ab --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ClivesHideaway.java @@ -0,0 +1,66 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.HideawayPlayEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.HideawayAbility; +import mage.abilities.mana.ColorlessManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ClivesHideaway extends CardImpl { + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + StaticFilters.FILTER_CONTROLLED_CREATURE_LEGENDARY, ComparisonType.MORE_THAN, 3 + ); + private static final Hint hint = new ValueHint( + "Legendary creatures you control", + new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_CREATURE_LEGENDARY) + ); + + public ClivesHideaway(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.subtype.add(SubType.TOWN); + + // Hideaway 4 + this.addAbility(new HideawayAbility(this, 4)); + + // {T}: Add {C}. + this.addAbility(new ColorlessManaAbility()); + + // {2}, {T}: You may play the exiled card without paying its mana cost if you control four or more legendary creatures. + Ability ability = new SimpleActivatedAbility(new ConditionalOneShotEffect( + new HideawayPlayEffect(), condition, "you may play the exiled card " + + "without paying its mana cost if you control four or more legendary creatures" + ), new GenericManaCost(2)); + ability.addCost(new TapSourceCost()); + this.addAbility(ability.addHint(hint)); + } + + private ClivesHideaway(final ClivesHideaway card) { + super(card); + } + + @Override + public ClivesHideaway copy() { + return new ClivesHideaway(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ClockworkAvian.java b/Mage.Sets/src/mage/cards/c/ClockworkAvian.java index 77f3d5787aa..fbe109ce863 100644 --- a/Mage.Sets/src/mage/cards/c/ClockworkAvian.java +++ b/Mage.Sets/src/mage/cards/c/ClockworkAvian.java @@ -9,7 +9,6 @@ import mage.abilities.condition.common.IsStepCondition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalActivatedAbility; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; @@ -47,12 +46,9 @@ public final class ClockworkAvian extends CardImpl { )); // At end of combat, if Clockwork Avian attacked or blocked this combat, remove a +1/+0 counter from it. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EndOfCombatTriggeredAbility( - new RemoveCounterSourceEffect(CounterType.P1P0.createInstance()), false - ), AttackedOrBlockedThisCombatSourceCondition.instance, "At end of combat, " + - "if {this} attacked or blocked this combat, remove a +1/+0 counter from it." - ), new AttackedOrBlockedThisCombatWatcher()); + this.addAbility(new EndOfCombatTriggeredAbility( + new RemoveCounterSourceEffect(CounterType.P1P0.createInstance()).setText("remove a +1/+0 counter from it"), false + ).withInterveningIf(AttackedOrBlockedThisCombatSourceCondition.instance), new AttackedOrBlockedThisCombatWatcher()); // {X}, {tap}: Put up to X +1/+0 counters on Clockwork Avian. This ability can't cause the total number of +1/+0 counters on Clockwork Avian to be greater than four. Activate this ability only during your upkeep. Ability ability = new ConditionalActivatedAbility( diff --git a/Mage.Sets/src/mage/cards/c/ClockworkBeast.java b/Mage.Sets/src/mage/cards/c/ClockworkBeast.java index 499f38d6457..85647d084f9 100644 --- a/Mage.Sets/src/mage/cards/c/ClockworkBeast.java +++ b/Mage.Sets/src/mage/cards/c/ClockworkBeast.java @@ -10,7 +10,6 @@ import mage.abilities.condition.common.IsStepCondition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalActivatedAbility; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; @@ -44,12 +43,9 @@ public final class ClockworkBeast extends CardImpl { )); // At end of combat, if Clockwork Beast attacked or blocked this combat, remove a +1/+0 counter from it. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EndOfCombatTriggeredAbility( - new RemoveCounterSourceEffect(CounterType.P1P0.createInstance()), false - ), AttackedOrBlockedThisCombatSourceCondition.instance, "At end of combat, " + - "if {this} attacked or blocked this combat, remove a +1/+0 counter from it." - ), new AttackedOrBlockedThisCombatWatcher()); + this.addAbility(new EndOfCombatTriggeredAbility( + new RemoveCounterSourceEffect(CounterType.P1P0.createInstance()).setText("remove a +1/+0 counter from it"), false + ).withInterveningIf(AttackedOrBlockedThisCombatSourceCondition.instance), new AttackedOrBlockedThisCombatWatcher()); // {X}, {tap}: Put up to X +1/+0 counters on Clockwork Beast. This ability can't cause the total number of +1/+0 counters on Clockwork Beast to be greater than seven. Activate this ability only during your upkeep. Ability ability = new ConditionalActivatedAbility( diff --git a/Mage.Sets/src/mage/cards/c/ClockworkServant.java b/Mage.Sets/src/mage/cards/c/ClockworkServant.java index 4893641718d..6ee4007302a 100644 --- a/Mage.Sets/src/mage/cards/c/ClockworkServant.java +++ b/Mage.Sets/src/mage/cards/c/ClockworkServant.java @@ -3,10 +3,10 @@ package mage.cards.c; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.AdamantCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; @@ -25,11 +25,8 @@ public final class ClockworkServant extends CardImpl { this.toughness = new MageInt(3); // Adamant - When Clockwork Servant enters the battlefield, if at least three mana of the same color was spent to cast it, draw a card. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1)), - AdamantCondition.ANY, "
    Adamant — When {this} enters, " + - "if at least three mana of the same color was spent to cast it, draw a card." - )); + this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1)) + .withInterveningIf(AdamantCondition.ANY).setAbilityWord(AbilityWord.ADAMANT)); } private ClockworkServant(final ClockworkServant card) { diff --git a/Mage.Sets/src/mage/cards/c/ClockworkSteed.java b/Mage.Sets/src/mage/cards/c/ClockworkSteed.java index d658e619f68..4aa2284535a 100644 --- a/Mage.Sets/src/mage/cards/c/ClockworkSteed.java +++ b/Mage.Sets/src/mage/cards/c/ClockworkSteed.java @@ -10,7 +10,6 @@ import mage.abilities.condition.common.IsStepCondition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalActivatedAbility; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesSourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; @@ -52,12 +51,9 @@ public final class ClockworkSteed extends CardImpl { this.addAbility(new SimpleEvasionAbility(new CantBeBlockedByCreaturesSourceEffect(filter, Duration.WhileOnBattlefield))); // At end of combat, if Clockwork Steed attacked or blocked this combat, remove a +1/+0 counter from it. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EndOfCombatTriggeredAbility( - new RemoveCounterSourceEffect(CounterType.P1P0.createInstance()), false - ), AttackedOrBlockedThisCombatSourceCondition.instance, "At end of combat, " + - "if {this} attacked or blocked this combat, remove a +1/+0 counter from it." - ), new AttackedOrBlockedThisCombatWatcher()); + this.addAbility(new EndOfCombatTriggeredAbility( + new RemoveCounterSourceEffect(CounterType.P1P0.createInstance()).setText("remove a +1/+0 counter from it"), false + ).withInterveningIf(AttackedOrBlockedThisCombatSourceCondition.instance), new AttackedOrBlockedThisCombatWatcher()); // {X}, {tap}: Put up to X +1/+0 counters on Clockwork Steed. This ability can't cause the total number of +1/+0 counters on Clockwork Steed to be greater than four. Activate this ability only during your upkeep. Ability ability = new ConditionalActivatedAbility( diff --git a/Mage.Sets/src/mage/cards/c/ClockworkSwarm.java b/Mage.Sets/src/mage/cards/c/ClockworkSwarm.java index 002b971d144..311315bc130 100644 --- a/Mage.Sets/src/mage/cards/c/ClockworkSwarm.java +++ b/Mage.Sets/src/mage/cards/c/ClockworkSwarm.java @@ -10,7 +10,6 @@ import mage.abilities.condition.common.IsStepCondition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalActivatedAbility; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesSourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; @@ -56,12 +55,9 @@ public final class ClockworkSwarm extends CardImpl { this.addAbility(new SimpleEvasionAbility(new CantBeBlockedByCreaturesSourceEffect(filter, Duration.WhileOnBattlefield))); // At end of combat, if Clockwork Swarm attacked or blocked this combat, remove a +1/+0 counter from it. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EndOfCombatTriggeredAbility( - new RemoveCounterSourceEffect(CounterType.P1P0.createInstance()), false - ), AttackedOrBlockedThisCombatSourceCondition.instance, "At end of combat, " + - "if {this} attacked or blocked this combat, remove a +1/+0 counter from it." - ), new AttackedOrBlockedThisCombatWatcher()); + this.addAbility(new EndOfCombatTriggeredAbility( + new RemoveCounterSourceEffect(CounterType.P1P0.createInstance()).setText("remove a +1/+0 counter from it"), false + ).withInterveningIf(AttackedOrBlockedThisCombatSourceCondition.instance), new AttackedOrBlockedThisCombatWatcher()); // {X}, {tap}: Put up to X +1/+0 counters on Clockwork Swarm. This ability can't cause the total number of +1/+0 counters on Clockwork Swarm to be greater than four. Activate this ability only during your upkeep. Ability ability = new ConditionalActivatedAbility( diff --git a/Mage.Sets/src/mage/cards/c/ClosingStatement.java b/Mage.Sets/src/mage/cards/c/ClosingStatement.java index 83c29a92f1d..12444cbfcb2 100644 --- a/Mage.Sets/src/mage/cards/c/ClosingStatement.java +++ b/Mage.Sets/src/mage/cards/c/ClosingStatement.java @@ -44,11 +44,11 @@ public final class ClosingStatement extends CardImpl { this.addAbility(ability); // Destroy target creature or planeswalker you don't control. Put a +1/+1 counter on up to one target creature you control. - this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker(1, 1, filter, false)); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker(1, 1, filter, false) + .setTargetTag(1)); this.getSpellAbility().addEffect(new DestroyTargetEffect()); - Target target = new TargetControlledCreaturePermanent(0, 1); - target.setTargetTag(2); - this.getSpellAbility().addTarget(target); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent(0, 1) + .setTargetTag(2)); this.getSpellAbility().addEffect(new ClosingStatementEffect()); } @@ -94,4 +94,4 @@ class ClosingStatementEffect extends OneShotEffect { } return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/c/CloudExSOLDIER.java b/Mage.Sets/src/mage/cards/c/CloudExSOLDIER.java index 722d1801066..54a598cb1d6 100644 --- a/Mage.Sets/src/mage/cards/c/CloudExSOLDIER.java +++ b/Mage.Sets/src/mage/cards/c/CloudExSOLDIER.java @@ -1,25 +1,6 @@ package mage.cards.c; -import java.util.UUID; import mage.MageInt; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.constants.TargetController; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledArtifactPermanent; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.common.FilterEquipmentPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.PowerPredicate; -import mage.filter.predicate.permanent.AttackingPredicate; -import mage.filter.predicate.permanent.EnchantedPredicate; -import mage.filter.predicate.permanent.EquippedPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.permanent.token.InsectToken; -import mage.game.permanent.token.TreasureToken; -import mage.target.TargetPermanent; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -32,36 +13,40 @@ import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.ComparisonType; -import mage.constants.Outcome; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.PowerPredicate; +import mage.filter.predicate.permanent.AttackingPredicate; +import mage.filter.predicate.permanent.EquippedPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.TreasureToken; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author balazskristof */ public final class CloudExSOLDIER extends CardImpl { - private static final FilterControlledArtifactPermanent filter = new FilterControlledArtifactPermanent("Equipment you control"); - private static final FilterControlledCreaturePermanent filter2 = new FilterControlledCreaturePermanent("equipped attacking creature you control"); - private static final FilterCreaturePermanent filter3 = new FilterCreaturePermanent(); + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("equipped attacking creature you control"); + private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent(); static { - filter.add(SubType.EQUIPMENT.getPredicate()); - filter2.add(Predicates.and( - AttackingPredicate.instance, - EquippedPredicate.instance + filter.add(Predicates.and( + AttackingPredicate.instance, + EquippedPredicate.instance )); - filter3.add(new PowerPredicate(ComparisonType.MORE_THAN, 6)); - } - - static { - filter.add(TargetController.YOU.getControllerPredicate()); + filter2.add(new PowerPredicate(ComparisonType.MORE_THAN, 6)); } public CloudExSOLDIER(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{G}{W}"); - + this.supertype.add(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SOLDIER); @@ -74,15 +59,15 @@ public final class CloudExSOLDIER extends CardImpl { // When Cloud enters, attach up to one target Equipment you control to it. Ability ability = new EntersBattlefieldTriggeredAbility(new CloudExSOLDIEREntersEffect()); - ability.addTarget(new TargetPermanent(0, 1, filter)); + ability.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT)); this.addAbility(ability); // Whenever Cloud attacks, draw a card for each equipped attacking creature you control. Then if Cloud has power 7 or greater, create two Treasure tokens. - Ability ability2 = new AttacksTriggeredAbility(new DrawCardSourceControllerEffect(new PermanentsOnBattlefieldCount(filter2))); + Ability ability2 = new AttacksTriggeredAbility(new DrawCardSourceControllerEffect(new PermanentsOnBattlefieldCount(filter))); ability2.addEffect(new ConditionalOneShotEffect( - new CreateTokenEffect(new TreasureToken(), 2), - new SourceMatchesFilterCondition(filter3), - "Then if {this} has power 7 or greater, create two Treasure tokens." + new CreateTokenEffect(new TreasureToken(), 2), + new SourceMatchesFilterCondition(filter2), + "Then if {this} has power 7 or greater, create two Treasure tokens." )); this.addAbility(ability2); } @@ -102,7 +87,7 @@ class CloudExSOLDIEREntersEffect extends OneShotEffect { CloudExSOLDIEREntersEffect() { super(Outcome.Benefit); - staticText = "attach up to one target Equipment you control to {this}"; + staticText = "attach up to one target Equipment you control to it"; } private CloudExSOLDIEREntersEffect(final CloudExSOLDIEREntersEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/CloudOfDarkness.java b/Mage.Sets/src/mage/cards/c/CloudOfDarkness.java new file mode 100644 index 00000000000..ad7f38f1e14 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CloudOfDarkness.java @@ -0,0 +1,56 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.common.DescendCondition; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CloudOfDarkness extends CardImpl { + + private static final DynamicValue xValue = + new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_PERMANENT, -1); + + public CloudOfDarkness(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{G}{G}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.AVATAR); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Particle Beam -- When Cloud of Darkness enters, target creature an opponent controls gets -X/-X until end of turn, where X is the number of permanent cards in your graveyard. + Ability ability = new EntersBattlefieldTriggeredAbility(new BoostTargetEffect(xValue, xValue) + .setText("target creature an opponent controls gets -X/-X until end of turn, " + + "where X is the number of permanent cards in your graveyard")); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + this.addAbility(ability.withFlavorWord("Particle Beam").addHint(DescendCondition.getHint())); + } + + private CloudOfDarkness(final CloudOfDarkness card) { + super(card); + } + + @Override + public CloudOfDarkness copy() { + return new CloudOfDarkness(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CloudhoofKirin.java b/Mage.Sets/src/mage/cards/c/CloudhoofKirin.java index 2bb33d307e4..696872e5154 100644 --- a/Mage.Sets/src/mage/cards/c/CloudhoofKirin.java +++ b/Mage.Sets/src/mage/cards/c/CloudhoofKirin.java @@ -35,7 +35,7 @@ public final class CloudhoofKirin extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // Whenever you cast a Spirit or Arcane spell, you may have target player put the top X cards of their library into their graveyard, where X is that spell's converted mana cost. - Ability ability = new SpellCastControllerTriggeredAbility(Zone.BATTLEFIELD, new CloudhoofKirinEffect(), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true, SetTargetPointer.SPELL); + Ability ability = new SpellCastControllerTriggeredAbility(Zone.BATTLEFIELD, new CloudhoofKirinEffect(), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, true, SetTargetPointer.SPELL); ability.addTarget(new TargetPlayer()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/CoalStoker.java b/Mage.Sets/src/mage/cards/c/CoalStoker.java index c8aadcb84fb..fdf9c173e7b 100644 --- a/Mage.Sets/src/mage/cards/c/CoalStoker.java +++ b/Mage.Sets/src/mage/cards/c/CoalStoker.java @@ -1,12 +1,9 @@ - package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.Mana; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.mana.BasicManaEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -14,24 +11,22 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.watchers.common.CastFromHandWatcher; +import java.util.UUID; + /** - * * @author ilcartographer */ public final class CoalStoker extends CardImpl { public CoalStoker(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); this.subtype.add(SubType.ELEMENTAL); this.power = new MageInt(3); this.toughness = new MageInt(3); // When Coal Stoker enters the battlefield, if you cast it from your hand, add {R}{R}{R}. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new BasicManaEffect(Mana.RedMana(3)), false), - CastFromHandSourcePermanentCondition.instance, - "When {this} enters, if you cast it from your hand, add {R}{R}{R}."), - new CastFromHandWatcher()); + this.addAbility(new EntersBattlefieldTriggeredAbility(new BasicManaEffect(Mana.RedMana(3))) + .withInterveningIf(CastFromHandSourcePermanentCondition.instance), new CastFromHandWatcher()); } private CoalStoker(final CoalStoker card) { diff --git a/Mage.Sets/src/mage/cards/c/CoalstokeGearhulk.java b/Mage.Sets/src/mage/cards/c/CoalstokeGearhulk.java index 8e04f69764a..4089c20f5b7 100644 --- a/Mage.Sets/src/mage/cards/c/CoalstokeGearhulk.java +++ b/Mage.Sets/src/mage/cards/c/CoalstokeGearhulk.java @@ -6,16 +6,12 @@ import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldWithCounterTargetEffect; -import mage.abilities.effects.common.SacrificeTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.HasteAbility; -import mage.cards.Card; import mage.constants.ComparisonType; -import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.SubType; import mage.abilities.keyword.MenaceAbility; @@ -24,16 +20,11 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.TargetController; -import mage.constants.Zone; import mage.counters.CounterType; -import mage.filter.Filter; -import mage.filter.StaticFilters; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.card.ManaValueLessThanControlledLandCountPredicate; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetCardInGraveyard; import mage.target.targetpointer.FixedTarget; @@ -113,4 +104,4 @@ class CoalstokeGearhulkEffect extends OneShotEffect { game.addDelayedTriggeredAbility(delayedAbility, source); return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/c/CoatiScavenger.java b/Mage.Sets/src/mage/cards/c/CoatiScavenger.java index 3f1c243b9d1..caf38252d54 100644 --- a/Mage.Sets/src/mage/cards/c/CoatiScavenger.java +++ b/Mage.Sets/src/mage/cards/c/CoatiScavenger.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.DescendCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -29,11 +28,8 @@ public final class CoatiScavenger extends CardImpl { this.toughness = new MageInt(2); // Descend 4 -- When Coati Scavenger enters the battlefield, if there are four or more permanent cards in your graveyard, return target permanent card from your graveyard to your hand. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect()), - DescendCondition.FOUR, "When {this} enters, if there are four or more " + - "permanent cards in your graveyard, return target permanent card from your graveyard to your hand." - ); + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect()) + .withInterveningIf(DescendCondition.FOUR); ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_PERMANENT)); this.addAbility(ability.setAbilityWord(AbilityWord.DESCEND_4).addHint(DescendCondition.getHint())); } diff --git a/Mage.Sets/src/mage/cards/c/Cocoon.java b/Mage.Sets/src/mage/cards/c/Cocoon.java index 9f75b0963b4..4a8d9da05ac 100644 --- a/Mage.Sets/src/mage/cards/c/Cocoon.java +++ b/Mage.Sets/src/mage/cards/c/Cocoon.java @@ -46,12 +46,12 @@ public final class Cocoon extends CardImpl { // When Cocoon enters the battlefield, tap enchanted creature and put three pupa counters on Cocoon. Ability ability = new EntersBattlefieldTriggeredAbility(new TapEnchantedEffect()); - ability.addEffect(new AddCountersSourceEffect(CounterType.PUPA.createInstance(3))); + ability.addEffect(new AddCountersSourceEffect(CounterType.PUPA.createInstance(3)).concatBy("and")); this.addAbility(ability); // Enchanted creature doesn’t untap during your untap step if Cocoon has a pupa counter on it. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousRuleModifyingEffect(new DontUntapInControllersUntapStepEnchantedEffect(), - new SourceHasCounterCondition(CounterType.PUPA)).setText("Enchanted creature doesn't untap during its controller's untap step if Cocoon has a pupa counter on it"))); + new SourceHasCounterCondition(CounterType.PUPA)).setText("Enchanted creature doesn't untap during your untap step if {this} has a pupa counter on it"))); // At the beginning of your upkeep, remove a pupa counter from Cocoon. If you can’t, sacrifice it, put a +1/+1 counter on enchanted creature, and that creature gains flying. this.addAbility(new BeginningOfUpkeepTriggeredAbility(new CocoonEffect())); diff --git a/Mage.Sets/src/mage/cards/c/CodespellCleric.java b/Mage.Sets/src/mage/cards/c/CodespellCleric.java index 8718d4b0ee6..0f92de10eef 100644 --- a/Mage.Sets/src/mage/cards/c/CodespellCleric.java +++ b/Mage.Sets/src/mage/cards/c/CodespellCleric.java @@ -5,7 +5,6 @@ import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; @@ -39,11 +38,8 @@ public final class CodespellCleric extends CardImpl { this.addAbility(VigilanceAbility.getInstance()); // When Codespell Cleric enters the battlefield, if it was the second spell you cast this turn, put a +1/+1 counter on target creature. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())), - CodespellClericCondition.instance, "When {this} enters, " + - "if it was the second spell you cast this turn, put a +1/+1 counter on target creature." - ); + Ability ability = new EntersBattlefieldTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())) + .withInterveningIf(CodespellClericCondition.instance); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability, new CodespellClericWatcher()); } @@ -66,6 +62,11 @@ enum CodespellClericCondition implements Condition { CodespellClericWatcher watcher = game.getState().getWatcher(CodespellClericWatcher.class); return watcher != null && watcher.checkSpell(source, game); } + + @Override + public String toString() { + return "it was the second spell you cast this turn"; + } } class CodespellClericWatcher extends Watcher { diff --git a/Mage.Sets/src/mage/cards/c/CodsworthHandyHelper.java b/Mage.Sets/src/mage/cards/c/CodsworthHandyHelper.java index e49386eff30..3ff76f8e4b1 100644 --- a/Mage.Sets/src/mage/cards/c/CodsworthHandyHelper.java +++ b/Mage.Sets/src/mage/cards/c/CodsworthHandyHelper.java @@ -10,7 +10,7 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachTargetToTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.WardAbility; import mage.abilities.mana.ConditionalColoredManaAbility; @@ -18,17 +18,17 @@ import mage.abilities.mana.builder.ConditionalManaBuilder; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CommanderPredicate; import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; -import mage.util.CardUtil; import java.util.UUID; @@ -65,7 +65,7 @@ public final class CodsworthHandyHelper extends CardImpl { )); // {T}: Attach target Aura or Equipment you control to target creature you control. Activate only as a sorcery. - Ability ability = new ActivateAsSorceryActivatedAbility(new CodsworthHandyHelperEffect(), new TapSourceCost()); + Ability ability = new ActivateAsSorceryActivatedAbility(new AttachTargetToTargetEffect(), new TapSourceCost()); ability.addTarget(new TargetPermanent(filter2)); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); @@ -118,51 +118,9 @@ class CodsworthHandyHelperManaCondition implements Condition { if (source instanceof SpellAbility) { Card card = game.getCard(source.getSourceId()); return card != null && ( - card.getSubtype(game).contains(SubType.AURA) || card.getSubtype(game).contains(SubType.EQUIPMENT) + card.hasSubtype(SubType.AURA, game) || card.hasSubtype(SubType.EQUIPMENT, game) ); } return false; } } -class CodsworthHandyHelperEffect extends OneShotEffect { - - CodsworthHandyHelperEffect() { - super(Outcome.Benefit); - staticText = "Attach target Aura or Equipment you control to target creature you control"; - } - - private CodsworthHandyHelperEffect(final CodsworthHandyHelperEffect effect) { - super(effect); - } - - @Override - public CodsworthHandyHelperEffect copy() { - return new CodsworthHandyHelperEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent attachment = game.getPermanent(source.getTargets().get(0).getFirstTarget()); - Permanent creature = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if (controller == null || attachment == null || creature == null) { - return false; - } - - if (creature.cantBeAttachedBy(attachment, source, game, true)) { - game.informPlayers(attachment.getLogName() + " was not attached to " + creature.getLogName() - + " because it's not a legal target" + CardUtil.getSourceLogName(game, source)); - return false; - } - Permanent oldCreature = game.getPermanent(attachment.getAttachedTo()); - if (oldCreature != null) { - oldCreature.removeAttachment(attachment.getId(), source, game); - } - creature.addAttachment(attachment.getId(), source, game); - game.informPlayers(attachment.getLogName() + " was " - + (oldCreature != null ? "unattached from " + oldCreature.getLogName() + " and " : "") - + "attached to " + creature.getLogName() - ); - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/c/CoilingRebirth.java b/Mage.Sets/src/mage/cards/c/CoilingRebirth.java index 5b7fec0fb6b..889baa323f9 100644 --- a/Mage.Sets/src/mage/cards/c/CoilingRebirth.java +++ b/Mage.Sets/src/mage/cards/c/CoilingRebirth.java @@ -18,6 +18,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -74,7 +75,7 @@ class CoilingRebirthEffect extends OneShotEffect { controller.moveCards(card, Zone.BATTLEFIELD, source, game); if (GiftWasPromisedCondition.TRUE.apply(game, source)) { game.processAction(); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null && !permanent.isLegendary(game)) { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect( null, null, false, 1, false, false, diff --git a/Mage.Sets/src/mage/cards/c/CoinOfFate.java b/Mage.Sets/src/mage/cards/c/CoinOfFate.java new file mode 100644 index 00000000000..7e0c284f5ea --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CoinOfFate.java @@ -0,0 +1,110 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.ExileFromGraveCost; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.BecomesMonarchSourceEffect; +import mage.abilities.effects.keyword.SurveilEffect; +import mage.abilities.hint.common.MonarchHint; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.common.TargetOpponent; + +import java.util.Set; +import java.util.UUID; + +/** + * @author balazskristof + */ +public final class CoinOfFate extends CardImpl { + + public CoinOfFate(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{W}"); + + // When this artifact enters, surveil 1. + this.addAbility(new EntersBattlefieldTriggeredAbility(new SurveilEffect(1))); + + // {3}{W},{T}, Exile two creature cards from your graveyard, Sacrifice this artifact: An opponent chooses one of the exiled cards. You put that card on the bottom of your library and return the other to the battlefield tapped. You become the monarch. + Ability ability = new SimpleActivatedAbility(new CoinOfFateEffect(), new ManaCostsImpl<>("{3}{W}")).addHint(MonarchHint.instance); + ability.addCost(new TapSourceCost()); + ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(2, StaticFilters.FILTER_CARD_CREATURES), true)); + ability.addCost(new SacrificeSourceCost()); + ability.addEffect(new BecomesMonarchSourceEffect()); + this.addAbility(ability); + } + + private CoinOfFate(final CoinOfFate card) { + super(card); + } + + @Override + public CoinOfFate copy() { + return new CoinOfFate(this); + } +} + +class CoinOfFateEffect extends OneShotEffect { + + public CoinOfFateEffect() { + super(Outcome.Benefit); + staticText = "An opponent chooses one of the exiled cards. " + + "You put that card on the bottom of your library and return the other to the battlefield tapped"; + } + + private CoinOfFateEffect(final CoinOfFateEffect effect) { + super(effect); + } + + @Override + public CoinOfFateEffect copy() { + return new CoinOfFateEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Cards cards = new CardsImpl(getTargetPointer().getTargets(game, source)); + if (cards.isEmpty()) { + return false; + } + Player opponent; + Set opponents = game.getOpponents(controller.getId()); + if (opponents.size() == 1) { + opponent = game.getPlayer(opponents.iterator().next()); + } else { + TargetOpponent targetOpponent = new TargetOpponent(true); + controller.chooseTarget(Outcome.Detriment, targetOpponent, source, game); + opponent = game.getPlayer(targetOpponent.getFirstTarget()); + } + if (opponent == null) { + return false; + } + TargetCard targetCard = new TargetCard(Zone.EXILED, StaticFilters.FILTER_CARD_CREATURE); + targetCard.withChooseHint("card to put on the bottom of opponent's library, the other is put onto the battlefield tapped"); + opponent.chooseTarget(Outcome.Benefit, cards, targetCard, source, game); + Card cardToLibrary = game.getCard(targetCard.getFirstTarget()); + if (cardToLibrary != null) { + controller.moveCardToLibraryWithInfo(cardToLibrary, source, game, Zone.EXILED, false, true); + cards.remove(cardToLibrary); + } + if (!cards.isEmpty()) { + controller.moveCards(game.getCard(cards.iterator().next()), Zone.BATTLEFIELD, source, game, true, false, true, null); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/c/ColiseumBehemoth.java b/Mage.Sets/src/mage/cards/c/ColiseumBehemoth.java new file mode 100644 index 00000000000..b0468682ddc --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ColiseumBehemoth.java @@ -0,0 +1,52 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ColiseumBehemoth extends CardImpl { + + public ColiseumBehemoth(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{G}{G}"); + + this.subtype.add(SubType.BEAST); + this.power = new MageInt(7); + this.toughness = new MageInt(7); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // When this creature enters, choose one-- + // * Destroy target artifact or enchantment. + Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect()); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); + + // * Draw a card. + ability.addMode(new Mode(new DrawCardSourceControllerEffect(1))); + this.addAbility(ability); + } + + private ColiseumBehemoth(final ColiseumBehemoth card) { + super(card); + } + + @Override + public ColiseumBehemoth copy() { + return new ColiseumBehemoth(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CollectiveBrutality.java b/Mage.Sets/src/mage/cards/c/CollectiveBrutality.java index 17a02642c6e..e6f6261ae95 100644 --- a/Mage.Sets/src/mage/cards/c/CollectiveBrutality.java +++ b/Mage.Sets/src/mage/cards/c/CollectiveBrutality.java @@ -1,7 +1,6 @@ package mage.cards.c; import mage.abilities.Mode; -import mage.abilities.costs.Cost; import mage.abilities.costs.common.DiscardCardCost; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; @@ -35,9 +34,7 @@ public final class CollectiveBrutality extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}"); // Escalate - Discard a card. - Cost cost = new DiscardCardCost(); - cost.setText("— Discard a card"); - this.addAbility(new EscalateAbility(cost)); + this.addAbility(new EscalateAbility(new DiscardCardCost())); // Choose one or more — this.getSpellAbility().getModes().setMinModes(1); diff --git a/Mage.Sets/src/mage/cards/c/CollectiveEffort.java b/Mage.Sets/src/mage/cards/c/CollectiveEffort.java index a56194bcda7..ccff1b45ca4 100644 --- a/Mage.Sets/src/mage/cards/c/CollectiveEffort.java +++ b/Mage.Sets/src/mage/cards/c/CollectiveEffort.java @@ -2,7 +2,6 @@ package mage.cards.c; import mage.abilities.Ability; import mage.abilities.Mode; -import mage.abilities.costs.Cost; import mage.abilities.costs.common.TapTargetCost; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; @@ -16,11 +15,8 @@ import mage.constants.Outcome; import mage.counters.CounterType; import mage.filter.FilterPlayer; import mage.filter.StaticFilters; -import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.common.FilterEnchantmentPermanent; import mage.filter.predicate.mageobject.PowerPredicate; -import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -36,13 +32,10 @@ import java.util.UUID; */ public final class CollectiveEffort extends CardImpl { - private static final FilterControlledCreaturePermanent filterUntapped = new FilterControlledCreaturePermanent("untapped creature you control"); private static final FilterCreaturePermanent filterDestroyCreature = new FilterCreaturePermanent("creature with power 4 or greater"); - private static final FilterEnchantmentPermanent filterDestroyEnchantment = new FilterEnchantmentPermanent("enchantment to destroy"); private static final FilterPlayer filterPlayer = new FilterPlayer("player whose creatures get +1/+1 counters"); static { - filterUntapped.add(TappedPredicate.UNTAPPED); filterDestroyCreature.add(new PowerPredicate(ComparisonType.MORE_THAN, 3)); } @@ -50,9 +43,7 @@ public final class CollectiveEffort extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{W}{W}"); // Escalate — Tap an untapped creature you control. - Cost cost = new TapTargetCost(new TargetControlledCreaturePermanent(filterUntapped)); - cost.setText("— Tap an untapped creature you control"); - this.addAbility(new EscalateAbility(cost)); + this.addAbility(new EscalateAbility(new TapTargetCost(new TargetControlledCreaturePermanent(StaticFilters.FILTER_CONTROLLED_UNTAPPED_CREATURE)))); // Choose one or more — this.getSpellAbility().getModes().setMinModes(1); @@ -66,7 +57,7 @@ public final class CollectiveEffort extends CardImpl { Effect effect = new DestroyTargetEffect(); effect.setText("Destroy target enchantment"); Mode mode = new Mode(effect); - mode.addTarget(new TargetEnchantmentPermanent(filterDestroyEnchantment).withChooseHint("destroy")); + mode.addTarget(new TargetEnchantmentPermanent().withChooseHint("destroy")); this.getSpellAbility().addMode(mode); // Put a +1/+1 counter on each creature target player controls. diff --git a/Mage.Sets/src/mage/cards/c/CollisionCourse.java b/Mage.Sets/src/mage/cards/c/CollisionCourse.java index a638e55c259..d33a6607735 100644 --- a/Mage.Sets/src/mage/cards/c/CollisionCourse.java +++ b/Mage.Sets/src/mage/cards/c/CollisionCourse.java @@ -42,7 +42,9 @@ public final class CollisionCourse extends CardImpl { // Choose one -- // * Collision Course deals X damage to target creature, where X is the number of permanents you control that are creatures and/or Vehicles. - this.getSpellAbility().addEffect(new DamageTargetEffect(xValue)); + this.getSpellAbility().addEffect(new DamageTargetEffect(xValue) + .setText("{this} deals X damage to target creature, where X is " + + "the number of permanents you control that are creatures and/or Vehicles")); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addHint(hint); diff --git a/Mage.Sets/src/mage/cards/c/ColossalMajesty.java b/Mage.Sets/src/mage/cards/c/ColossalMajesty.java index 3b9395291dd..61c2aa0ee8b 100644 --- a/Mage.Sets/src/mage/cards/c/ColossalMajesty.java +++ b/Mage.Sets/src/mage/cards/c/ColossalMajesty.java @@ -1,10 +1,9 @@ package mage.cards.c; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.condition.common.FerociousCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.hint.common.FerociousHint; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -20,15 +19,8 @@ public final class ColossalMajesty extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); // At the beginning of your upkeep, if you control a creature with power 4 or greater, draw a card. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility( - new DrawCardSourceControllerEffect(1), false - ), - FerociousCondition.instance, - "At the beginning of your upkeep, " - + "if you control a creature with power 4 or greater, " - + "draw a card." - ).addHint(FerociousHint.instance)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new DrawCardSourceControllerEffect(1)) + .withInterveningIf(FerociousCondition.instance).addHint(FerociousHint.instance)); } private ColossalMajesty(final ColossalMajesty card) { diff --git a/Mage.Sets/src/mage/cards/c/CombatTutorial.java b/Mage.Sets/src/mage/cards/c/CombatTutorial.java new file mode 100644 index 00000000000..94786f5d017 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CombatTutorial.java @@ -0,0 +1,40 @@ +package mage.cards.c; + +import mage.abilities.effects.common.DrawCardTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.target.TargetPlayer; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.SecondTargetPointer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CombatTutorial extends CardImpl { + + public CombatTutorial(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}"); + + // Target player draws two cards. Put a +1/+1 counter on up to one target creature you control. + this.getSpellAbility().addEffect(new DrawCardTargetEffect(2)); + this.getSpellAbility().addTarget(new TargetPlayer()); + this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance()) + .setText("put a +1/+1 counter on up to one target creature you control") + .setTargetPointer(new SecondTargetPointer())); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 1)); + } + + private CombatTutorial(final CombatTutorial card) { + super(card); + } + + @Override + public CombatTutorial copy() { + return new CombatTutorial(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CombineGuildmage.java b/Mage.Sets/src/mage/cards/c/CombineGuildmage.java index 9353935c98a..33acf4c366c 100644 --- a/Mage.Sets/src/mage/cards/c/CombineGuildmage.java +++ b/Mage.Sets/src/mage/cards/c/CombineGuildmage.java @@ -5,21 +5,20 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.EntersWithCountersControlledEffect; +import mage.abilities.effects.common.counter.MoveCounterTargetsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; import java.util.UUID; @@ -28,10 +27,12 @@ import java.util.UUID; */ public final class CombineGuildmage extends CardImpl { - private static final FilterPermanent filter1 - = new FilterControlledCreaturePermanent("creature you control (to remove a counter from)"); - private static final FilterPermanent filter2 - = new FilterControlledCreaturePermanent("creature you control (to move a counter to)"); + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("another target creature you control"); + + static { + filter.add(new AnotherTargetPredicate(2)); + } public CombineGuildmage(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{U}"); @@ -50,12 +51,10 @@ public final class CombineGuildmage extends CardImpl { this.addAbility(ability); // {1}{U}, {T}: Move a +1/+1 counter from target creature you control onto another target creature you control. - ability = new SimpleActivatedAbility( - new CombineGuildmageCounterEffect(), new ManaCostsImpl<>("{1}{U}") - ); + ability = new SimpleActivatedAbility(new MoveCounterTargetsEffect(CounterType.P1P1), new ManaCostsImpl<>("{1}{U}")); ability.addCost(new TapSourceCost()); - ability.addTarget(new TargetPermanent(filter1)); - ability.addTarget(new TargetPermanent(filter2)); + ability.addTarget(new TargetControlledCreaturePermanent().withChooseHint("to remove a counter from").setTargetTag(1)); + ability.addTarget(new TargetPermanent(filter).withChooseHint("to move a counter to").setTargetTag(2)); this.addAbility(ability); } @@ -68,34 +67,3 @@ public final class CombineGuildmage extends CardImpl { return new CombineGuildmage(this); } } - -class CombineGuildmageCounterEffect extends OneShotEffect { - - CombineGuildmageCounterEffect() { - super(Outcome.Benefit); - staticText = "Move a +1/+1 counter from target creature you control onto another target creature you control."; - } - - private CombineGuildmageCounterEffect(final CombineGuildmageCounterEffect effect) { - super(effect); - } - - @Override - public CombineGuildmageCounterEffect copy() { - return new CombineGuildmageCounterEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent fromPermanent = game.getPermanent(source.getFirstTarget()); - Permanent toPermanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if (fromPermanent == null || toPermanent == null) { - return false; - } - if (fromPermanent.getCounters(game).getCount(CounterType.P1P1) > 0) { - fromPermanent.removeCounters(CounterType.P1P1.createInstance(), source, game); - toPermanent.addCounters(CounterType.P1P1.createInstance(), source.getControllerId(), source, game); - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/c/ComboAttack.java b/Mage.Sets/src/mage/cards/c/ComboAttack.java index b426489e07b..2b17b496161 100644 --- a/Mage.Sets/src/mage/cards/c/ComboAttack.java +++ b/Mage.Sets/src/mage/cards/c/ComboAttack.java @@ -55,20 +55,21 @@ class ComboAttackEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - if (source.getTargets().size() < 2 || source.getTargets().get(0).getTargets().size() < 2) { + if (source.getTargets().size() < 2) { return false; } - Permanent permanent1 = game.getPermanent(source.getTargets().get(0).getTargets().get(0)); - Permanent permanent2 = game.getPermanent(source.getTargets().get(0).getTargets().get(1)); Permanent permanent3 = game.getPermanent(source.getTargets().get(1).getFirstTarget()); if (permanent3 == null) { return false; } - if (permanent1 != null) { - permanent3.damage(permanent1.getPower().getValue(), permanent1.getId(), source, game, false, true); - } - if (permanent2 != null) { - permanent3.damage(permanent2.getPower().getValue(), permanent2.getId(), source, game, false, true); + // You can’t cast Combo Attack without targeting two creatures your team controls. + // If one of those creatures is an illegal target as Combo Attack resolves, + // the other will still deal damage equal to its power. (2018-06-08) + for (UUID id : source.getTargets().get(0).getTargets()) { + Permanent permanent = game.getPermanent(id); + if (permanent != null) { + permanent3.damage(permanent.getPower().getValue(), permanent.getId(), source, game); + } } return true; } diff --git a/Mage.Sets/src/mage/cards/c/ComeBackWrong.java b/Mage.Sets/src/mage/cards/c/ComeBackWrong.java index 6a055cfcfaf..78da560cc9c 100644 --- a/Mage.Sets/src/mage/cards/c/ComeBackWrong.java +++ b/Mage.Sets/src/mage/cards/c/ComeBackWrong.java @@ -18,6 +18,7 @@ import mage.target.targetpointer.FixedTarget; import java.util.UUID; import mage.game.permanent.PermanentToken; +import mage.util.CardUtil; /** * @author TheElk801 @@ -81,7 +82,7 @@ class ComeBackWrongEffect extends OneShotEffect { return true; } player.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent creature = game.getPermanent(card.getId()); + Permanent creature = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (creature != null) { game.addDelayedTriggeredAbility(new AtTheBeginOfPlayersNextEndStepDelayedTriggeredAbility( new SacrificeTargetEffect("sacrifice it") diff --git a/Mage.Sets/src/mage/cards/c/CommanderLiaraPortyr.java b/Mage.Sets/src/mage/cards/c/CommanderLiaraPortyr.java new file mode 100644 index 00000000000..ee037b68bc7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CommanderLiaraPortyr.java @@ -0,0 +1,120 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.cards.*; +import mage.constants.*; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.util.CardUtil; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CommanderLiaraPortyr extends CardImpl { + + public CommanderLiaraPortyr(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(5); + this.toughness = new MageInt(3); + + // Whenever you attack, spells you cast from exile this turn cost {X} less to cast, where X is the number of players being attacked. Exile the top X cards of your library. Until end of turn, you may cast spells from among those exiled cards. + Ability ability = new AttacksWithCreaturesTriggeredAbility(new CommanderLiaraPortyrCostEffect(), 1); + ability.addEffect(new CommanderLiaraPortyrExileEffect()); + this.addAbility(ability); + } + + private CommanderLiaraPortyr(final CommanderLiaraPortyr card) { + super(card); + } + + @Override + public CommanderLiaraPortyr copy() { + return new CommanderLiaraPortyr(this); + } +} + +class CommanderLiaraPortyrCostEffect extends CostModificationEffectImpl { + + CommanderLiaraPortyrCostEffect() { + super(Duration.EndOfTurn, Outcome.Benefit, CostModificationType.REDUCE_COST); + staticText = "spells you cast from exile this turn cost {X} less to cast, " + + "where X is the number of players being attacked"; + } + + private CommanderLiaraPortyrCostEffect(final CommanderLiaraPortyrCostEffect effect) { + super(effect); + } + + @Override + public CommanderLiaraPortyrCostEffect copy() { + return new CommanderLiaraPortyrCostEffect(this); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + Optional.ofNullable((Integer) getValue(AttacksWithCreaturesTriggeredAbility.VALUEKEY_NUMBER_DEFENDING_PLAYERS)) + .ifPresent(i -> CardUtil.reduceCost(abilityToModify, 1)); + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + return Optional + .ofNullable(abilityToModify) + .map(Ability::getSourceId) + .map(game::getSpell) + .map(Spell::getFromZone) + .filter(Zone.EXILED::match) + .isPresent(); + } +} + +class CommanderLiaraPortyrExileEffect extends OneShotEffect { + + CommanderLiaraPortyrExileEffect() { + super(Outcome.Benefit); + staticText = "Exile the top X cards of your library. Until end of turn, " + + "you may cast spells from among those exiled cards"; + } + + private CommanderLiaraPortyrExileEffect(final CommanderLiaraPortyrExileEffect effect) { + super(effect); + } + + @Override + public CommanderLiaraPortyrExileEffect copy() { + return new CommanderLiaraPortyrExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + int count = Optional.ofNullable((Integer) getValue( + AttacksWithCreaturesTriggeredAbility.VALUEKEY_NUMBER_DEFENDING_PLAYERS + )).orElse(0); + if (player == null || count < 1) { + return true; + } + Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, count)); + if (cards.isEmpty()) { + return false; + } + player.moveCards(cards, Zone.EXILED, source, game); + for (Card card : cards.getCards(game)) { + CardUtil.makeCardPlayable(game, source, card, true, Duration.EndOfTurn, false); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CommandoRaid.java b/Mage.Sets/src/mage/cards/c/CommandoRaid.java index be0b73f7b6c..c0d604217ac 100644 --- a/Mage.Sets/src/mage/cards/c/CommandoRaid.java +++ b/Mage.Sets/src/mage/cards/c/CommandoRaid.java @@ -12,7 +12,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -29,7 +29,7 @@ public final class CommandoRaid extends CardImpl { effect.setText("have it deal damage equal to its power to target creature that player controls."); Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(effect, true, true); ability.addTarget(new TargetCreaturePermanent()); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.getSpellAbility().addEffect(new GainAbilityTargetEffect( ability, Duration.EndOfTurn, diff --git a/Mage.Sets/src/mage/cards/c/CommunalBrewing.java b/Mage.Sets/src/mage/cards/c/CommunalBrewing.java index 1ea3d970642..a2e0ec724c7 100644 --- a/Mage.Sets/src/mage/cards/c/CommunalBrewing.java +++ b/Mage.Sets/src/mage/cards/c/CommunalBrewing.java @@ -12,7 +12,7 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.SetTargetPointer; import mage.counters.CounterType; -import mage.filter.common.FilterCreatureSpell; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; @@ -28,8 +28,6 @@ import java.util.UUID; */ public final class CommunalBrewing extends CardImpl { - private static final FilterCreatureSpell filter = new FilterCreatureSpell("a creature spell"); - public CommunalBrewing(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); @@ -41,7 +39,11 @@ public final class CommunalBrewing extends CardImpl { // Whenever you cast a creature spell, that creature enters with X additional +1/+1 // counters on it, where X is the number of ingredient counters on Communal Brewing. - this.addAbility(new SpellCastControllerTriggeredAbility(new CommunalBrewingCountersEffect(), filter, false, SetTargetPointer.SPELL)); + this.addAbility(new SpellCastControllerTriggeredAbility( + new CommunalBrewingCountersEffect(), + StaticFilters.FILTER_SPELL_A_CREATURE, + false, SetTargetPointer.SPELL + )); } private CommunalBrewing(final CommunalBrewing card) { @@ -93,7 +95,7 @@ class CommunalBrewingCountersEffect extends ReplacementEffectImpl { CommunalBrewingCountersEffect() { super(Duration.EndOfTurn, Outcome.BoostCreature); - this.staticText = "that creature enters with X additional +1/+1 counters on it, where X is is the number of ingredient counters on {this}"; + this.staticText = "that creature enters with X additional +1/+1 counters on it, where X is the number of ingredient counters on {this}"; } private CommunalBrewingCountersEffect(final CommunalBrewingCountersEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/ComplexAutomaton.java b/Mage.Sets/src/mage/cards/c/ComplexAutomaton.java index 39fb2aa589f..62db8ffaa9f 100644 --- a/Mage.Sets/src/mage/cards/c/ComplexAutomaton.java +++ b/Mage.Sets/src/mage/cards/c/ComplexAutomaton.java @@ -1,20 +1,16 @@ - package mage.cards.c; import mage.MageInt; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.ReturnToHandSourceEffect; -import mage.abilities.hint.ValueHint; +import mage.abilities.hint.common.PermanentsYouControlHint; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.ComparisonType; import mage.constants.SubType; -import mage.filter.StaticFilters; import mage.filter.common.FilterControlledPermanent; import java.util.UUID; @@ -25,7 +21,7 @@ import java.util.UUID; public final class ComplexAutomaton extends CardImpl { private static final Condition condition = new PermanentsOnTheBattlefieldCondition( - StaticFilters.FILTER_CONTROLLED_PERMANENT, ComparisonType.MORE_THAN, 6 + new FilterControlledPermanent("you control seven or more permanents"), ComparisonType.MORE_THAN, 6 ); public ComplexAutomaton(UUID ownerId, CardSetInfo setInfo) { @@ -36,12 +32,8 @@ public final class ComplexAutomaton extends CardImpl { this.toughness = new MageInt(4); // At the beginning of your upkeep, if you control seven or more permanents, return Complex Automaton to its owner's hand. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility( - new ReturnToHandSourceEffect(true), false - ), condition, "At the beginning of your upkeep, " + - "if you control seven or more permanents, return {this} to its owner's hand." - ).addHint(new ValueHint("Permanents you control", new PermanentsOnBattlefieldCount(new FilterControlledPermanent())))); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new ReturnToHandSourceEffect(true)) + .withInterveningIf(condition).addHint(PermanentsYouControlHint.instance)); } private ComplexAutomaton(final ComplexAutomaton card) { diff --git a/Mage.Sets/src/mage/cards/c/ConformerShuriken.java b/Mage.Sets/src/mage/cards/c/ConformerShuriken.java new file mode 100644 index 00000000000..7a551915287 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ConformerShuriken.java @@ -0,0 +1,93 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.DefendingPlayerControlsSourceAttackingPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ConformerShuriken extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("creature defending player controls"); + + static { + filter.add(DefendingPlayerControlsSourceAttackingPredicate.instance); + } + + public ConformerShuriken(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.EQUIPMENT); + + // Equipped creature has "Whenever this creature attacks, tap target creature defending player controls. If that creature has greater power than this creature, put a number of +1/+1 counters on this creature equal to the difference." + Ability ability = new AttacksTriggeredAbility(new TapTargetEffect()); + ability.addEffect(new ConformerShurikenEffect()); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect(ability, AttachmentType.EQUIPMENT))); + + // Equip {2} + this.addAbility(new EquipAbility(2)); + } + + private ConformerShuriken(final ConformerShuriken card) { + super(card); + } + + @Override + public ConformerShuriken copy() { + return new ConformerShuriken(this); + } +} + +class ConformerShurikenEffect extends OneShotEffect { + + ConformerShurikenEffect() { + super(Outcome.Benefit); + staticText = "If that creature has greater power than this creature, " + + "put a number of +1/+1 counters on this creature equal to the difference"; + } + + private ConformerShurikenEffect(final ConformerShurikenEffect effect) { + super(effect); + } + + @Override + public ConformerShurikenEffect copy() { + return new ConformerShurikenEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + return permanent != null + && Optional + .ofNullable(getTargetPointer().getFirst(game, source)) + .map(game::getPermanent) + .map(MageObject::getPower) + .map(MageInt::getValue) + .map(x -> permanent.getPower().getValue() - x) + .filter(x -> x > 0 && permanent.addCounters(CounterType.P1P1.createInstance(x), source, game)) + .isPresent(); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ConfoundingConundrum.java b/Mage.Sets/src/mage/cards/c/ConfoundingConundrum.java index dea670eae21..eeb9dbb09a8 100644 --- a/Mage.Sets/src/mage/cards/c/ConfoundingConundrum.java +++ b/Mage.Sets/src/mage/cards/c/ConfoundingConundrum.java @@ -4,8 +4,6 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; @@ -31,7 +29,7 @@ import java.util.UUID; */ public final class ConfoundingConundrum extends CardImpl { - private static final FilterPermanent filter = new FilterLandPermanent(); + private static final FilterPermanent filter = new FilterLandPermanent("a land an opponent controls"); static { filter.add(TargetController.OPPONENT.getControllerPredicate()); @@ -44,13 +42,10 @@ public final class ConfoundingConundrum extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1))); // Whenever a land enters the battlefield under an opponent's control, if that player had another land enter the battlefield under their control this turn, they return a land they control to its owner's hand. - this.addAbility(new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldAllTriggeredAbility( - Zone.BATTLEFIELD, new ConfoundingConundrumEffect(), filter, - false, SetTargetPointer.PLAYER - ), ConfoundingConundrumCondition.instance, "Whenever a land enters the battlefield under " + - "an opponent's control, if that player had another land enter the battlefield " + - "under their control this turn, they return a land they control to its owner's hand." - ), new ConfoundingConundrumWatcher()); + this.addAbility(new EntersBattlefieldAllTriggeredAbility( + Zone.BATTLEFIELD, new ConfoundingConundrumEffect(), + filter, false, SetTargetPointer.PLAYER + ).withInterveningIf(ConfoundingConundrumCondition.instance), new ConfoundingConundrumWatcher()); } private ConfoundingConundrum(final ConfoundingConundrum card) { @@ -69,17 +64,16 @@ enum ConfoundingConundrumCondition implements Condition { @Override public boolean apply(Game game, Ability source) { ConfoundingConundrumWatcher watcher = game.getState().getWatcher(ConfoundingConundrumWatcher.class); - if (watcher == null) { - return false; - } - Player player = null; - for (Effect effect : source.getEffects()) { - if (player != null) { - break; - } - player = game.getPlayer(effect.getTargetPointer().getFirst(game, source)); - } - return player != null && watcher.checkPlayer(player.getId()); + return watcher != null + && CardUtil + .getEffectValueFromAbility(source, "permanentEnteringControllerId", UUID.class) + .filter(watcher::checkPlayer) + .isPresent(); + } + + @Override + public String toString() { + return "that player had another land enter the battlefield under their control this turn"; } } @@ -87,6 +81,7 @@ class ConfoundingConundrumEffect extends OneShotEffect { ConfoundingConundrumEffect() { super(Outcome.Benefit); + staticText = "they return a land they control to its owner's hand"; } private ConfoundingConundrumEffect(final ConfoundingConundrumEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/ConfrontThePast.java b/Mage.Sets/src/mage/cards/c/ConfrontThePast.java index edb4bff4de0..4a6a9165b70 100644 --- a/Mage.Sets/src/mage/cards/c/ConfrontThePast.java +++ b/Mage.Sets/src/mage/cards/c/ConfrontThePast.java @@ -8,25 +8,25 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.counters.CounterType; +import mage.filter.FilterPermanent; import mage.filter.common.FilterPermanentCard; import mage.filter.common.FilterPlaneswalkerPermanent; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; import mage.target.common.TargetCardInYourGraveyard; -import mage.target.common.TargetPlaneswalkerPermanent; import mage.target.targetadjustment.TargetAdjuster; import mage.util.CardUtil; import java.util.UUID; /** - * * @author htrajan */ public final class ConfrontThePast extends CardImpl { - public static final FilterPlaneswalkerPermanent filter = new FilterPlaneswalkerPermanent(); + public static final FilterPermanent filter = new FilterPlaneswalkerPermanent(); static { filter.add(TargetController.OPPONENT.getControllerPredicate()); @@ -34,18 +34,18 @@ public final class ConfrontThePast extends CardImpl { public ConfrontThePast(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{B}"); - + this.subtype.add(SubType.LESSON); // Choose one — // • Return target planeswalker card with mana value X or less from your graveyard to the battlefield. this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect() - .setText("return target planeswalker card with mana value X or less from your graveyard to the battlefield")); + .setText("return target planeswalker card with mana value X or less from your graveyard to the battlefield")); this.getSpellAbility().setTargetAdjuster(ConfrontThePastAdjuster.instance); // • Remove twice X loyalty counters from target planeswalker an opponent controls. Mode mode = new Mode(new ConfrontThePastLoyaltyEffect()); - mode.addTarget(new TargetPlaneswalkerPermanent(filter)); + mode.addTarget(new TargetPermanent(filter)); this.getSpellAbility().addMode(mode); } @@ -65,7 +65,7 @@ enum ConfrontThePastAdjuster implements TargetAdjuster { @Override public void adjustTargets(Ability ability, Game game) { if (ability.getEffects().size() == 1 - && ability.getEffects().get(0) instanceof ReturnFromGraveyardToBattlefieldTargetEffect) { + && ability.getEffects().get(0) instanceof ReturnFromGraveyardToBattlefieldTargetEffect) { int xValue = CardUtil.getSourceCostsTag(game, ability, "X", 0); ability.getTargets().clear(); FilterPermanentCard filter = new FilterPermanentCard("planeswalker card with mana value X or less"); @@ -99,4 +99,4 @@ class ConfrontThePastLoyaltyEffect extends OneShotEffect { target.removeCounters(CounterType.LOYALTY.createInstance(xValue * 2), source, game); return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/c/ConsulateSurveillance.java b/Mage.Sets/src/mage/cards/c/ConsulateSurveillance.java index c578435ea36..04fc763a31f 100644 --- a/Mage.Sets/src/mage/cards/c/ConsulateSurveillance.java +++ b/Mage.Sets/src/mage/cards/c/ConsulateSurveillance.java @@ -1,7 +1,6 @@ package mage.cards.c; -import java.util.UUID; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.PayEnergyCost; @@ -11,24 +10,23 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; -import mage.filter.FilterObject; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class ConsulateSurveillance extends CardImpl { public ConsulateSurveillance(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}"); // When Consulate Surveillance enters the battlefield, you get {E}{E}{E}{E}. this.addAbility(new EntersBattlefieldTriggeredAbility(new GetEnergyCountersControllerEffect(4))); // Pay {E}{E}: Prevent all damage that would be dealt to you this turn by a source of your choice. this.addAbility(new SimpleActivatedAbility( - new PreventAllDamageFromChosenSourceToYouEffect(Duration.EndOfTurn, new FilterObject("source"), false), + new PreventAllDamageFromChosenSourceToYouEffect(Duration.EndOfTurn), new PayEnergyCost(2))); } diff --git a/Mage.Sets/src/mage/cards/c/ConsulsLieutenant.java b/Mage.Sets/src/mage/cards/c/ConsulsLieutenant.java index 4a1c5be70f7..d204a246592 100644 --- a/Mage.Sets/src/mage/cards/c/ConsulsLieutenant.java +++ b/Mage.Sets/src/mage/cards/c/ConsulsLieutenant.java @@ -1,11 +1,9 @@ package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.condition.common.RenownedSourceCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.keyword.FirstStrikeAbility; import mage.abilities.keyword.RenownAbility; @@ -14,23 +12,17 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterAttackingCreature; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class ConsulsLieutenant extends CardImpl { - private static final FilterAttackingCreature filter = new FilterAttackingCreature("attacking creatures you control"); - - static { - filter.add(TargetController.YOU.getControllerPredicate()); - } - public ConsulsLieutenant(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SOLDIER); this.power = new MageInt(2); @@ -38,13 +30,14 @@ public final class ConsulsLieutenant extends CardImpl { // First strike this.addAbility(FirstStrikeAbility.getInstance()); + // Renown 1 this.addAbility(new RenownAbility(1)); + // Whenever Consul's Lieutenant attacks, if it's renowned, other attacking creatures you control get +1/+1 until end of turn. - this.addAbility(new ConditionalInterveningIfTriggeredAbility(new AttacksTriggeredAbility( - new BoostControlledEffect(1, 1, Duration.EndOfTurn, filter, true), false), - RenownedSourceCondition.instance, - "Whenever Consul's Lieutenant attacks, if it's renowned, other attacking creatures you control get +1/+1 until end of turn.")); + this.addAbility(new AttacksTriggeredAbility(new BoostControlledEffect( + 1, 1, Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, true + )).withInterveningIf(RenownedSourceCondition.instance)); } private ConsulsLieutenant(final ConsulsLieutenant card) { diff --git a/Mage.Sets/src/mage/cards/c/ContaminantGrafter.java b/Mage.Sets/src/mage/cards/c/ContaminantGrafter.java index fa54288ffef..11bac1ff6ad 100644 --- a/Mage.Sets/src/mage/cards/c/ContaminantGrafter.java +++ b/Mage.Sets/src/mage/cards/c/ContaminantGrafter.java @@ -5,7 +5,6 @@ import mage.abilities.Ability; import mage.abilities.BatchTriggeredAbility; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.condition.common.CorruptedCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.PutCardFromHandOntoBattlefieldEffect; import mage.abilities.effects.common.counter.ProliferateEffect; @@ -49,15 +48,10 @@ public final class ContaminantGrafter extends CardImpl { // Corrupted — At the beginning of your end step, if an opponent has three or more poison // counters, draw a card, then you may put a land card from your hand onto the battlefield. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new BeginningOfEndStepTriggeredAbility(new DrawCardSourceControllerEffect(1)), - CorruptedCondition.instance, "At the beginning of your end step, if an opponent has three or more poison " + - "counters, draw a card, then you may put a land card from your hand onto the battlefield" - ); - ability.addEffect(new PutCardFromHandOntoBattlefieldEffect(StaticFilters.FILTER_CARD_LAND_A).concatBy("then")); - ability.setAbilityWord(AbilityWord.CORRUPTED); - ability.addHint(CorruptedCondition.getHint()); - this.addAbility(ability); + Ability ability = new BeginningOfEndStepTriggeredAbility(new DrawCardSourceControllerEffect(1)) + .withInterveningIf(CorruptedCondition.instance); + ability.addEffect(new PutCardFromHandOntoBattlefieldEffect(StaticFilters.FILTER_CARD_LAND_A).concatBy(", then")); + this.addAbility(ability.setAbilityWord(AbilityWord.CORRUPTED).addHint(CorruptedCondition.getHint())); } private ContaminantGrafter(final ContaminantGrafter card) { diff --git a/Mage.Sets/src/mage/cards/c/ConvalescentCare.java b/Mage.Sets/src/mage/cards/c/ConvalescentCare.java index 6df37fb1d05..462c541f382 100644 --- a/Mage.Sets/src/mage/cards/c/ConvalescentCare.java +++ b/Mage.Sets/src/mage/cards/c/ConvalescentCare.java @@ -1,11 +1,10 @@ package mage.cards.c; -import mage.abilities.TriggeredAbility; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.Ability; import mage.abilities.condition.common.FatefulHourCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -13,19 +12,17 @@ import mage.constants.CardType; import java.util.UUID; /** - * * @author fireshoes */ public final class ConvalescentCare extends CardImpl { public ConvalescentCare(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}{W}"); // At the beginning of your upkeep, if you have 5 or less life, you gain 3 life and draw a card. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new GainLifeEffect(3)); - ability.addEffect(new DrawCardSourceControllerEffect(1)); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, FatefulHourCondition.instance, - "At the beginning of your upkeep, if you have 5 or less life, you gain 3 life and draw a card.")); + Ability ability = new BeginningOfUpkeepTriggeredAbility(new GainLifeEffect(3)).withInterveningIf(FatefulHourCondition.instance); + ability.addEffect(new DrawCardSourceControllerEffect(1).concatBy("and")); + this.addAbility(ability); } private ConvalescentCare(final ConvalescentCare card) { @@ -36,6 +33,6 @@ public final class ConvalescentCare extends CardImpl { public ConvalescentCare copy() { return new ConvalescentCare(this); } - + } diff --git a/Mage.Sets/src/mage/cards/c/CoralSword.java b/Mage.Sets/src/mage/cards/c/CoralSword.java new file mode 100644 index 00000000000..19a035c97f3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CoralSword.java @@ -0,0 +1,52 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAttachToTarget; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.EquipAbility; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CoralSword extends CardImpl { + + public CoralSword(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{R}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // When this Equipment enters, attach it to target creature you control. That creature gains first strike until end of turn. + Ability ability = new EntersBattlefieldAttachToTarget(); + ability.addEffect(new GainAbilityTargetEffect(FirstStrikeAbility.getInstance()) + .setText("That creature gains first strike until end of turn")); + this.addAbility(ability); + + // Equipped creature gets +1/+0. + this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(1, 0))); + + // Equip {1} + this.addAbility(new EquipAbility(1)); + } + + private CoralSword(final CoralSword card) { + super(card); + } + + @Override + public CoralSword copy() { + return new CoralSword(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CorneredByBlackMages.java b/Mage.Sets/src/mage/cards/c/CorneredByBlackMages.java new file mode 100644 index 00000000000..6856fb6eb88 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CorneredByBlackMages.java @@ -0,0 +1,40 @@ +package mage.cards.c; + +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.SacrificeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.game.permanent.token.BlackWizardToken; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CorneredByBlackMages extends CardImpl { + + public CorneredByBlackMages(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}{B}"); + + // Target opponent sacrifices a creature of their choice. + this.getSpellAbility().addEffect(new SacrificeEffect( + StaticFilters.FILTER_PERMANENT_CREATURE, 1, "target opponent" + )); + this.getSpellAbility().addTarget(new TargetOpponent()); + + // Create a 0/1 black Wizard creature token with "Whenever you cast a noncreature spell, this token deals 1 damage to each opponent." + this.getSpellAbility().addEffect(new CreateTokenEffect(new BlackWizardToken()).concatBy("
    ")); + } + + private CorneredByBlackMages(final CorneredByBlackMages card) { + super(card); + } + + @Override + public CorneredByBlackMages copy() { + return new CorneredByBlackMages(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CorpseDance.java b/Mage.Sets/src/mage/cards/c/CorpseDance.java index 6a62a0c8fe5..f0e007e8b71 100644 --- a/Mage.Sets/src/mage/cards/c/CorpseDance.java +++ b/Mage.Sets/src/mage/cards/c/CorpseDance.java @@ -1,7 +1,6 @@ package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; @@ -22,15 +21,17 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class CorpseDance extends CardImpl { public CorpseDance(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}"); // Buyback {2} this.addAbility(new BuybackAbility("{2}")); @@ -68,32 +69,30 @@ class CorpseDanceEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Card lastCreatureCard = null; - for (Card card : controller.getGraveyard().getCards(game)) { - if (card.isCreature(game)) { - lastCreatureCard = card; - } - } - if (lastCreatureCard != null) { - if (controller.moveCards(lastCreatureCard, Zone.BATTLEFIELD, source, game)) { - Permanent creature = game.getPermanent(lastCreatureCard.getId()); - if (creature != null) { - FixedTarget blueprintTarget = new FixedTarget(creature, game); - // Gains Haste - ContinuousEffect hasteEffect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); - hasteEffect.setTargetPointer(blueprintTarget.copy()); - game.addEffect(hasteEffect, source); - // Exile it at end of turn - ExileTargetEffect exileEffect = new ExileTargetEffect(null, "", Zone.BATTLEFIELD); - exileEffect.setTargetPointer(blueprintTarget.copy()); - DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); - game.addDelayedTriggeredAbility(delayedAbility, source); - } - } - } - return true; + if (controller == null) { + return false; } - return false; + Card lastCreatureCard = null; + for (Card card : controller.getGraveyard().getCards(game)) { + if (card.isCreature(game)) { + lastCreatureCard = card; + } + } + if (lastCreatureCard != null && controller.moveCards(lastCreatureCard, Zone.BATTLEFIELD, source, game)) { + Permanent creature = CardUtil.getPermanentFromCardPutToBattlefield(lastCreatureCard, game); + if (creature != null) { + FixedTarget blueprintTarget = new FixedTarget(creature, game); + // Gains Haste + ContinuousEffect hasteEffect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); + hasteEffect.setTargetPointer(blueprintTarget.copy()); + game.addEffect(hasteEffect, source); + // Exile it at end of turn + ExileTargetEffect exileEffect = new ExileTargetEffect(null, "", Zone.BATTLEFIELD); + exileEffect.setTargetPointer(blueprintTarget.copy()); + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect); + game.addDelayedTriggeredAbility(delayedAbility, source); + } + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/c/CouncilOfEchoes.java b/Mage.Sets/src/mage/cards/c/CouncilOfEchoes.java index 38d74ce68ad..e80987d6522 100644 --- a/Mage.Sets/src/mage/cards/c/CouncilOfEchoes.java +++ b/Mage.Sets/src/mage/cards/c/CouncilOfEchoes.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.DescendCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -42,12 +41,7 @@ public final class CouncilOfEchoes extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Descend 4 -- When Council of Echoes enters the battlefield, if there are four or more permanent cards in your graveyard, return up to one target nonland permanent other than Council of Echoes to its owner's hand. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect(), false), - DescendCondition.FOUR, "When {this} enters, " - + "if there are four or more permanent cards in your graveyard, " - + "return up to one target nonland permanent other than {this} to its owner's hand" - ); + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect(), false).withInterveningIf(DescendCondition.FOUR); ability.addTarget(new TargetPermanent(0, 1, filter)); this.addAbility(ability.addHint(DescendCondition.getHint()).setAbilityWord(AbilityWord.DESCEND_4)); } diff --git a/Mage.Sets/src/mage/cards/c/CouncilsDeliberation.java b/Mage.Sets/src/mage/cards/c/CouncilsDeliberation.java index 4f2ffba28ef..49f43c0d995 100644 --- a/Mage.Sets/src/mage/cards/c/CouncilsDeliberation.java +++ b/Mage.Sets/src/mage/cards/c/CouncilsDeliberation.java @@ -4,7 +4,6 @@ import mage.abilities.common.ScryTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.costs.common.ExileSourceFromGraveCost; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; @@ -22,7 +21,7 @@ import java.util.UUID; public final class CouncilsDeliberation extends CardImpl { private static final Condition condition - = new PermanentsOnTheBattlefieldCondition(new FilterControlledPermanent(SubType.ISLAND)); + = new PermanentsOnTheBattlefieldCondition(new FilterControlledPermanent(SubType.ISLAND, "you control an Island")); public CouncilsDeliberation(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); @@ -31,15 +30,11 @@ public final class CouncilsDeliberation extends CardImpl { this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); // Whenever you scry, if you control an Island, you may exile Council's Deliberation from your graveyard. If you do, draw a card. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new ScryTriggeredAbility( - Zone.GRAVEYARD, - new DoIfCostPaid( - new DrawCardSourceControllerEffect(1), new ExileSourceFromGraveCost() - ), false - ), condition, "Whenever you scry, if you control an Island, " + - "you may exile {this} from your graveyard. If you do, draw a card." - )); + this.addAbility(new ScryTriggeredAbility( + Zone.GRAVEYARD, + new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new ExileSourceFromGraveCost()), + false + ).withInterveningIf(condition)); } private CouncilsDeliberation(final CouncilsDeliberation card) { diff --git a/Mage.Sets/src/mage/cards/c/CourageousGoblin.java b/Mage.Sets/src/mage/cards/c/CourageousGoblin.java index 57737238dab..70f8f03d665 100644 --- a/Mage.Sets/src/mage/cards/c/CourageousGoblin.java +++ b/Mage.Sets/src/mage/cards/c/CourageousGoblin.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.condition.common.FerociousCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.keyword.MenaceAbility; @@ -17,25 +16,24 @@ import mage.constants.SubType; import java.util.UUID; /** - * * @author ciaccona007 */ public final class CourageousGoblin extends CardImpl { public CourageousGoblin(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); - + this.subtype.add(SubType.GOBLIN); this.power = new MageInt(2); this.toughness = new MageInt(2); // Whenever this creature attacks while you control a creature with power 4 or greater, this creature gets +1/+0 and gains menace until end of turn. - Ability ability = new ConditionalTriggeredAbility( - new AttacksTriggeredAbility(new BoostSourceEffect(1, 0, Duration.EndOfTurn), false), - FerociousCondition.instance, - "Whenever this creature attacks while you control a creature with power 4 or greater, this creature gets +1/+0 and gains menace until end of turn" - ); - ability.addEffect(new GainAbilitySourceEffect(new MenaceAbility(), Duration.EndOfTurn)); + Ability ability = new AttacksTriggeredAbility( + new BoostSourceEffect(1, 0, Duration.EndOfTurn).setText("{this} gets +1/+0"), false + ).withTriggerCondition(FerociousCondition.instance); + ability.addEffect(new GainAbilitySourceEffect( + new MenaceAbility(false), Duration.EndOfTurn + ).setText("and gains menace until end of turn")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/CourageousResolve.java b/Mage.Sets/src/mage/cards/c/CourageousResolve.java index 361f8523223..b3719ad8181 100644 --- a/Mage.Sets/src/mage/cards/c/CourageousResolve.java +++ b/Mage.Sets/src/mage/cards/c/CourageousResolve.java @@ -1,4 +1,3 @@ - package mage.cards.c; import mage.MageObject; @@ -15,9 +14,7 @@ import mage.abilities.keyword.ProtectionAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; +import mage.constants.*; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; @@ -46,9 +43,9 @@ public final class CourageousResolve extends CardImpl { this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).setText("Draw a card. (It can't be blocked, targeted, dealt damage, enchanted, or equipped by anything controlled by those players.)")); - //Fateful hour — If you have 5 or less life, you can't lose life this turn, you can't lose the game this turn, + // Fateful hour — If you have 5 or less life, you can't lose life this turn, you can't lose the game this turn, // and your opponents can't win the game this turn. - this.getSpellAbility().addEffect(new ConditionalContinuousEffect(new CantLoseLifeEffect(), FatefulHourCondition.instance, "
    Fateful hour — If you have 5 or less life, you can't lose life this turn")); + this.getSpellAbility().addEffect(new ConditionalContinuousEffect(new CourageousResolveCantLoseLifeEffect(), FatefulHourCondition.instance, "
    Fateful hour — If you have 5 or less life, you can't lose life this turn")); this.getSpellAbility().addEffect(new ConditionalContinuousRuleModifyingEffect(new CourageousResolveWinLoseEffect(), FatefulHourCondition.instance)); @@ -132,19 +129,19 @@ class CourageousResolveProtectionAbility extends ProtectionAbility { } } -class CantLoseLifeEffect extends ContinuousEffectImpl { +class CourageousResolveCantLoseLifeEffect extends ContinuousEffectImpl { - public CantLoseLifeEffect() { - super(Duration.EndOfTurn, Outcome.Benefit); + CourageousResolveCantLoseLifeEffect() { + super(Duration.EndOfTurn, Layer.PlayerEffects, SubLayer.NA, Outcome.Benefit); } - protected CantLoseLifeEffect(final CantLoseLifeEffect effect) { + protected CourageousResolveCantLoseLifeEffect(final CourageousResolveCantLoseLifeEffect effect) { super(effect); } @Override - public CantLoseLifeEffect copy() { - return new CantLoseLifeEffect(this); + public CourageousResolveCantLoseLifeEffect copy() { + return new CourageousResolveCantLoseLifeEffect(this); } @Override @@ -154,7 +151,7 @@ class CantLoseLifeEffect extends ContinuousEffectImpl { player.setCanLoseLife(false); return true; } - return true; + return false; } } diff --git a/Mage.Sets/src/mage/cards/c/CourierBat.java b/Mage.Sets/src/mage/cards/c/CourierBat.java index 185c1319596..b4c358748bb 100644 --- a/Mage.Sets/src/mage/cards/c/CourierBat.java +++ b/Mage.Sets/src/mage/cards/c/CourierBat.java @@ -1,28 +1,30 @@ package mage.cards.c; -import java.util.UUID; - import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.YouGainedLifeCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.dynamicvalue.common.ControllerGainedLifeCount; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; -import mage.constants.ComparisonType; -import mage.constants.SubType; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; import mage.filter.StaticFilters; import mage.target.common.TargetCardInYourGraveyard; import mage.watchers.common.PlayerGainedLifeWatcher; +import java.util.UUID; + /** * @author weirddan455 */ public final class CourierBat extends CardImpl { + private static final Condition condition = new YouGainedLifeCondition(); + public CourierBat(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); @@ -34,13 +36,9 @@ public final class CourierBat extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Courier Bat enters the battlefield, if you gained life this turn, return up to one target creature card from your graveyard to your hand. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect()), - new YouGainedLifeCondition(), - "When {this} enters, if you gained life this turn, return up to one target creature card from your graveyard to your hand." - ); + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect()).withInterveningIf(condition); ability.addTarget(new TargetCardInYourGraveyard(0, 1, StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); - this.addAbility(ability, new PlayerGainedLifeWatcher()); + this.addAbility(ability.addHint(ControllerGainedLifeCount.getHint()), new PlayerGainedLifeWatcher()); } private CourierBat(final CourierBat card) { diff --git a/Mage.Sets/src/mage/cards/c/CovertCutpurse.java b/Mage.Sets/src/mage/cards/c/CovertCutpurse.java index 25f3911e341..c9372e1d538 100644 --- a/Mage.Sets/src/mage/cards/c/CovertCutpurse.java +++ b/Mage.Sets/src/mage/cards/c/CovertCutpurse.java @@ -3,7 +3,6 @@ package mage.cards.c; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.DisturbAbility; import mage.cards.CardImpl; @@ -27,7 +26,7 @@ public final class CovertCutpurse extends CardImpl { = new FilterCreaturePermanent("creature you don't control that was dealt damage this turn"); static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); + filter.add(TargetController.NOT_YOU.getControllerPredicate()); filter.add(WasDealtDamageThisTurnPredicate.instance); } diff --git a/Mage.Sets/src/mage/cards/c/CovetousElegy.java b/Mage.Sets/src/mage/cards/c/CovetousElegy.java new file mode 100644 index 00000000000..9d088f7a114 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CovetousElegy.java @@ -0,0 +1,92 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.TreasureToken; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CovetousElegy extends CardImpl { + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE); + private static final Hint hint = new ValueHint("Creatures your opponents control", xValue); + + public CovetousElegy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{W}{B}"); + + // Each player chooses up to two creatures they control, then sacrifices the rest. Then you create a tapped Treasure token for each creature your opponents control. + this.getSpellAbility().addEffect(new CovetousElegyEffect()); + this.getSpellAbility().addEffect(new CreateTokenEffect(new TreasureToken(), xValue).concatBy("Then")); + this.getSpellAbility().addHint(hint); + } + + private CovetousElegy(final CovetousElegy card) { + super(card); + } + + @Override + public CovetousElegy copy() { + return new CovetousElegy(this); + } +} + +class CovetousElegyEffect extends OneShotEffect { + + CovetousElegyEffect() { + super(Outcome.Benefit); + staticText = "each player chooses up to two creatures they control, then sacrifices the rest"; + } + + private CovetousElegyEffect(final CovetousElegyEffect effect) { + super(effect); + } + + @Override + public CovetousElegyEffect copy() { + return new CovetousElegyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Set creatures = new HashSet<>(); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + TargetPermanent target = new TargetControlledCreaturePermanent(0, 2); + target.withNotTarget(true); + target.withChooseHint("the rest will be sacrificed"); + player.choose(outcome, target, source, game); + creatures.addAll(target.getTargets()); + } + for (Permanent permanent : game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), source, game + )) { + if (!creatures.contains(permanent.getId())) { + permanent.sacrifice(source, game); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CracklingSpellslinger.java b/Mage.Sets/src/mage/cards/c/CracklingSpellslinger.java index 03acec9a9fa..bbd694b8ce0 100644 --- a/Mage.Sets/src/mage/cards/c/CracklingSpellslinger.java +++ b/Mage.Sets/src/mage/cards/c/CracklingSpellslinger.java @@ -3,7 +3,6 @@ package mage.cards.c; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.CastFromEverywhereSourceCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.continuous.NextSpellCastHasAbilityEffect; import mage.abilities.keyword.FlashAbility; import mage.abilities.keyword.StormAbility; @@ -35,12 +34,9 @@ public final class CracklingSpellslinger extends CardImpl { this.addAbility(FlashAbility.getInstance()); // When Crackling Spellslinger enters the battlefield, if you cast it, the next instant or sorcery spell you cast this turn has storm. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new NextSpellCastHasAbilityEffect(new StormAbility(), filter)), - CastFromEverywhereSourceCondition.instance, - "When {this} enters, if you cast it, " - + "the next instant or sorcery spell you cast this turn has storm" - )); + this.addAbility(new EntersBattlefieldTriggeredAbility( + new NextSpellCastHasAbilityEffect(new StormAbility(), filter) + ).withInterveningIf(CastFromEverywhereSourceCondition.instance)); } private CracklingSpellslinger(final CracklingSpellslinger card) { diff --git a/Mage.Sets/src/mage/cards/c/CraterhoofBehemoth.java b/Mage.Sets/src/mage/cards/c/CraterhoofBehemoth.java index 286e0873fd6..61f68c52bed 100644 --- a/Mage.Sets/src/mage/cards/c/CraterhoofBehemoth.java +++ b/Mage.Sets/src/mage/cards/c/CraterhoofBehemoth.java @@ -14,8 +14,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.StaticFilters; import java.util.UUID; @@ -24,12 +23,6 @@ import java.util.UUID; */ public final class CraterhoofBehemoth extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - - static { - filter.add(TargetController.YOU.getControllerPredicate()); - } - public CraterhoofBehemoth(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{G}{G}{G}"); this.subtype.add(SubType.BEAST); @@ -41,11 +34,11 @@ public final class CraterhoofBehemoth extends CardImpl { // When Craterhoof Behemoth enters the battlefield, creatures you control gain trample and get +X/+X until end of turn, where X is the number of creatures you control. Ability ability = new EntersBattlefieldTriggeredAbility(new GainAbilityControlledEffect( - TrampleAbility.getInstance(), Duration.EndOfTurn, filter + TrampleAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES ).setText("creatures you control gain trample")); ability.addEffect(new BoostControlledEffect( CreaturesYouControlCount.instance, CreaturesYouControlCount.instance, - Duration.EndOfTurn, filter, false + Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES, false ).setText("and get +X/+X until end of turn, where X is the number of creatures you control")); ability.addHint(CreaturesYouControlHint.instance); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/c/CrestedSunmare.java b/Mage.Sets/src/mage/cards/c/CrestedSunmare.java index 69e0f814e7c..8d171ec6652 100644 --- a/Mage.Sets/src/mage/cards/c/CrestedSunmare.java +++ b/Mage.Sets/src/mage/cards/c/CrestedSunmare.java @@ -1,17 +1,20 @@ package mage.cards.c; import mage.MageInt; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.YouGainedLifeCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.dynamicvalue.common.ControllerGainedLifeCount; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.abilities.keyword.IndestructibleAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.TargetController; import mage.filter.common.FilterControlledPermanent; import mage.game.permanent.token.CrestedSunmareToken; import mage.watchers.common.PlayerGainedLifeWatcher; @@ -29,6 +32,8 @@ public final class CrestedSunmare extends CardImpl { filter.add(SubType.HORSE.getPredicate()); } + private static final Condition condition = new YouGainedLifeCondition(); + public CrestedSunmare(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}"); @@ -42,14 +47,9 @@ public final class CrestedSunmare extends CardImpl { ))); // At the beginning of each end step, if you gained life this turn, create a 5/5 white Horse creature token. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfEndStepTriggeredAbility( - TargetController.ANY, new CreateTokenEffect(new CrestedSunmareToken()), - false - ), new YouGainedLifeCondition(), - "At the beginning of each end step, if you gained life this turn, " + - "create a 5/5 white Horse creature token." - ).addHint(ControllerGainedLifeCount.getHint()), new PlayerGainedLifeWatcher()); + this.addAbility(new BeginningOfEndStepTriggeredAbility( + TargetController.ANY, new CreateTokenEffect(new CrestedSunmareToken()), false + ).withInterveningIf(condition).addHint(ControllerGainedLifeCount.getHint()), new PlayerGainedLifeWatcher()); } private CrestedSunmare(final CrestedSunmare card) { diff --git a/Mage.Sets/src/mage/cards/c/CrestingMosasaurus.java b/Mage.Sets/src/mage/cards/c/CrestingMosasaurus.java index a23c3221b7a..1d771b4e084 100644 --- a/Mage.Sets/src/mage/cards/c/CrestingMosasaurus.java +++ b/Mage.Sets/src/mage/cards/c/CrestingMosasaurus.java @@ -1,22 +1,21 @@ package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.CastFromEverywhereSourceCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.ReturnToHandFromBattlefieldAllEffect; import mage.abilities.keyword.EmergeAbility; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; +import java.util.UUID; + /** - * * @author jimga150 */ public final class CrestingMosasaurus extends CardImpl { @@ -29,7 +28,7 @@ public final class CrestingMosasaurus extends CardImpl { public CrestingMosasaurus(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{U}{U}"); - + this.subtype.add(SubType.DINOSAUR); this.power = new MageInt(4); this.toughness = new MageInt(8); @@ -38,10 +37,9 @@ public final class CrestingMosasaurus extends CardImpl { this.addAbility(new EmergeAbility(this, "{6}{U}")); // When Cresting Mosasaurus enters the battlefield, if you cast it, return each non-Dinosaur creature to its owner's hand. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new ReturnToHandFromBattlefieldAllEffect(filter), false), - CastFromEverywhereSourceCondition.instance, - "When {this} enters, if you cast it, return each non-Dinosaur creature to its owner's hand.")); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ReturnToHandFromBattlefieldAllEffect(filter) + .setText("return each non-Dinosaur creature to its owner's hand"), false) + .withInterveningIf(CastFromEverywhereSourceCondition.instance)); } private CrestingMosasaurus(final CrestingMosasaurus card) { diff --git a/Mage.Sets/src/mage/cards/c/CrewCaptain.java b/Mage.Sets/src/mage/cards/c/CrewCaptain.java index a97cc967127..36070e043d2 100644 --- a/Mage.Sets/src/mage/cards/c/CrewCaptain.java +++ b/Mage.Sets/src/mage/cards/c/CrewCaptain.java @@ -32,7 +32,7 @@ public final class CrewCaptain extends CardImpl { // Crew Captain has indestructible as long as it entered the battlefield this turn. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( - new GainAbilitySourceEffect(IndestructibleAbility.getInstance()), SourceEnteredThisTurnCondition.instance, + new GainAbilitySourceEffect(IndestructibleAbility.getInstance()), SourceEnteredThisTurnCondition.DID, "{this} has indestructible as long as it entered the battlefield this turn" ))); } diff --git a/Mage.Sets/src/mage/cards/c/CrossroadsVillage.java b/Mage.Sets/src/mage/cards/c/CrossroadsVillage.java new file mode 100644 index 00000000000..db862d94031 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CrossroadsVillage.java @@ -0,0 +1,39 @@ +package mage.cards.c; + +import mage.abilities.common.EntersBattlefieldTappedAsItEntersChooseColorAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.mana.AddManaChosenColorEffect; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CrossroadsVillage extends CardImpl { + + public CrossroadsVillage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.subtype.add(SubType.TOWN); + + // This land enters tapped. As it enters, choose a color. + this.addAbility(new EntersBattlefieldTappedAsItEntersChooseColorAbility()); + + // {T}: Add one mana of the chosen color. + this.addAbility(new SimpleManaAbility(new AddManaChosenColorEffect(), new TapSourceCost())); + } + + private CrossroadsVillage(final CrossroadsVillage card) { + super(card); + } + + @Override + public CrossroadsVillage copy() { + return new CrossroadsVillage(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CrownOfGondor.java b/Mage.Sets/src/mage/cards/c/CrownOfGondor.java index d9c7c0cb3f9..01867a0d052 100644 --- a/Mage.Sets/src/mage/cards/c/CrownOfGondor.java +++ b/Mage.Sets/src/mage/cards/c/CrownOfGondor.java @@ -6,7 +6,6 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.MonarchIsNotSetCondition; import mage.abilities.condition.common.MonarchIsSourceControllerCondition; import mage.abilities.costs.CostAdjuster; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.BecomesMonarchSourceEffect; @@ -18,7 +17,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; import mage.util.CardUtil; @@ -42,14 +40,10 @@ public final class CrownOfGondor extends CardImpl { this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(xValue, xValue))); // When a legendary creature you control enters, if there is no monarch, you become the monarch. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldControlledTriggeredAbility( - new BecomesMonarchSourceEffect(), - StaticFilters.FILTER_CREATURE_LEGENDARY - ), - MonarchIsNotSetCondition.instance, - "When a legendary creature you control enters, if there is no monarch, you become the monarch." - ).addHint(MonarchHint.instance)); + this.addAbility(new EntersBattlefieldControlledTriggeredAbility( + new BecomesMonarchSourceEffect(), StaticFilters.FILTER_CREATURE_LEGENDARY + ).setTriggerPhrase("When a legendary creature you control enters, ") + .withInterveningIf(MonarchIsNotSetCondition.instance).addHint(MonarchHint.instance)); // Equip {4}. This ability costs {3} less to activate if you're the monarch. EquipAbility equip = new EquipAbility(4, false); diff --git a/Mage.Sets/src/mage/cards/c/CrownOfTheAges.java b/Mage.Sets/src/mage/cards/c/CrownOfTheAges.java index cf6c9951c26..10fc28e84ce 100644 --- a/Mage.Sets/src/mage/cards/c/CrownOfTheAges.java +++ b/Mage.Sets/src/mage/cards/c/CrownOfTheAges.java @@ -1,6 +1,5 @@ package mage.cards.c; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; @@ -11,7 +10,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; +import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterEnchantmentPermanent; import mage.filter.predicate.Predicates; @@ -23,10 +22,11 @@ import mage.players.Player; import mage.target.Target; import mage.target.TargetPermanent; +import java.util.Optional; import java.util.UUID; /** - * @author spjspj + * @author TheElk801 */ public final class CrownOfTheAges extends CardImpl { @@ -61,7 +61,7 @@ class CrownOfTheAgesEffect extends OneShotEffect { CrownOfTheAgesEffect() { super(Outcome.BoostCreature); - this.staticText = "Attach target Aura attached to a creature to another creature"; + this.staticText = "attach target Aura attached to a creature to another creature"; } private CrownOfTheAgesEffect(final CrownOfTheAgesEffect effect) { @@ -75,47 +75,27 @@ class CrownOfTheAgesEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent aura = game.getPermanent(source.getFirstTarget()); + Permanent aura = game.getPermanent(getTargetPointer().getFirst(game, source)); if (aura == null) { return false; } - Permanent fromPermanent = game.getPermanent(aura.getAttachedTo()); - Player controller = game.getPlayer(source.getControllerId()); - if (fromPermanent == null || controller == null) { + FilterPermanent filter = new FilterCreaturePermanent("another creature"); + filter.add(Predicates.not(new PermanentIdPredicate(aura.getAttachedTo()))); + if (!game.getBattlefield().contains(filter, source, game, 1)) { return false; } - boolean passed = true; - FilterCreaturePermanent filterChoice = new FilterCreaturePermanent("another creature"); - filterChoice.add(Predicates.not(new PermanentIdPredicate(fromPermanent.getId()))); - - Target chosenCreatureToAttachAura = new TargetPermanent(filterChoice); - chosenCreatureToAttachAura.withNotTarget(true); - - if (chosenCreatureToAttachAura.canChoose(source.getControllerId(), source, game) - && controller.choose(Outcome.Neutral, chosenCreatureToAttachAura, source, game)) { - Permanent creatureToAttachAura = game.getPermanent(chosenCreatureToAttachAura.getFirstTarget()); - if (creatureToAttachAura != null) { - if (passed) { - // Check the target filter - Target target = aura.getSpellAbility().getTargets().get(0); - if (target instanceof TargetPermanent) { - if (!target.getFilter().match(creatureToAttachAura, game)) { - passed = false; - } - } - // Check for protection - MageObject auraObject = game.getObject(aura.getId()); - if (auraObject != null && creatureToAttachAura.cantBeAttachedBy(auraObject, source, game, true)) { - passed = false; - } - } - if (passed) { - fromPermanent.removeAttachment(aura.getId(), source, game); - creatureToAttachAura.addAttachment(aura.getId(), source, game); - return true; - } - } + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; } - return true; + Target target = new TargetPermanent(filter); + target.withNotTarget(true); + controller.choose(Outcome.Neutral, target, source, game); + return Optional + .ofNullable(target) + .map(Target::getFirstTarget) + .map(game::getPermanent) + .map(permanent -> permanent.addAttachment(aura.getId(), source, game)) + .orElse(false); } } diff --git a/Mage.Sets/src/mage/cards/c/Crush.java b/Mage.Sets/src/mage/cards/c/Crush.java index 1db0441dcf5..49691ca1b6e 100644 --- a/Mage.Sets/src/mage/cards/c/Crush.java +++ b/Mage.Sets/src/mage/cards/c/Crush.java @@ -1,17 +1,17 @@ package mage.cards.c; -import java.util.UUID; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.common.FilterArtifactPermanent; import mage.filter.predicate.Predicates; -import mage.target.common.TargetArtifactPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author North */ public final class Crush extends CardImpl { @@ -23,10 +23,10 @@ public final class Crush extends CardImpl { } public Crush(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{R}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}"); - this.getSpellAbility().addTarget(new TargetArtifactPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); this.getSpellAbility().addEffect(new DestroyTargetEffect()); } diff --git a/Mage.Sets/src/mage/cards/c/CrushingPain.java b/Mage.Sets/src/mage/cards/c/CrushingPain.java index 7fbf6f552b8..857c7b78b63 100644 --- a/Mage.Sets/src/mage/cards/c/CrushingPain.java +++ b/Mage.Sets/src/mage/cards/c/CrushingPain.java @@ -1,34 +1,27 @@ package mage.cards.c; -import java.util.UUID; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.WasDealtDamageThisTurnPredicate; -import mage.target.common.TargetCreaturePermanent; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author LevelX */ public final class CrushingPain extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature that was dealt damage this turn"); - - static { - filter.add(WasDealtDamageThisTurnPredicate.instance); - } - - public CrushingPain (UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{R}"); + public CrushingPain(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); this.subtype.add(SubType.ARCANE); // Crushing Pain deals 6 damage to target creature that was dealt damage this turn. this.getSpellAbility().addEffect(new DamageTargetEffect(6)); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_DAMAGED_THIS_TURN)); } private CrushingPain(final CrushingPain card) { @@ -39,7 +32,4 @@ public final class CrushingPain extends CardImpl { public CrushingPain copy() { return new CrushingPain(this); } - } - - diff --git a/Mage.Sets/src/mage/cards/c/Cryoclasm.java b/Mage.Sets/src/mage/cards/c/Cryoclasm.java index 716f13b64ea..bc89558f29d 100644 --- a/Mage.Sets/src/mage/cards/c/Cryoclasm.java +++ b/Mage.Sets/src/mage/cards/c/Cryoclasm.java @@ -1,4 +1,3 @@ - package mage.cards.c; import mage.abilities.effects.common.DamageTargetControllerEffect; @@ -7,31 +6,30 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.common.FilterLandPermanent; +import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; -import mage.target.common.TargetLandPermanent; +import mage.target.TargetPermanent; import java.util.UUID; /** - * * @author dustinconrad */ public final class Cryoclasm extends CardImpl { - private static final FilterLandPermanent filter = new FilterLandPermanent("Plains or Island"); + private static final FilterPermanent filter = new FilterPermanent("Plains or Island"); static { filter.add(Predicates.or(SubType.PLAINS.getPredicate(), SubType.ISLAND.getPredicate())); } public Cryoclasm(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}"); // Destroy target Plains or Island. Cryoclasm deals 3 damage to that land's controller. this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addEffect(new DamageTargetControllerEffect(3, "land")); - this.getSpellAbility().addTarget(new TargetLandPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); } diff --git a/Mage.Sets/src/mage/cards/c/CryptFeaster.java b/Mage.Sets/src/mage/cards/c/CryptFeaster.java index df3a1fe5d6e..52f0ca7bf21 100644 --- a/Mage.Sets/src/mage/cards/c/CryptFeaster.java +++ b/Mage.Sets/src/mage/cards/c/CryptFeaster.java @@ -3,7 +3,6 @@ package mage.cards.c; import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.condition.common.ThresholdCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.MenaceAbility; import mage.cards.CardImpl; @@ -31,11 +30,8 @@ public final class CryptFeaster extends CardImpl { this.addAbility(new MenaceAbility()); // Threshold -- Whenever this creature attacks, if there are seven or more cards in your graveyard, this creature gets +2/+0 until end of turn. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new AttacksTriggeredAbility(new BoostSourceEffect(2, 0, Duration.EndOfTurn)), - ThresholdCondition.instance, "Whenever this creature attacks, if there are seven " + - "or more cards in your graveyard, this creature gets +2/+0 until end of turn." - ).setAbilityWord(AbilityWord.THRESHOLD)); + this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect(2, 0, Duration.EndOfTurn)) + .withInterveningIf(ThresholdCondition.instance).setAbilityWord(AbilityWord.THRESHOLD)); } private CryptFeaster(final CryptFeaster card) { diff --git a/Mage.Sets/src/mage/cards/c/CryptidInspector.java b/Mage.Sets/src/mage/cards/c/CryptidInspector.java index 2481532b2ff..f7caa7f7bb8 100644 --- a/Mage.Sets/src/mage/cards/c/CryptidInspector.java +++ b/Mage.Sets/src/mage/cards/c/CryptidInspector.java @@ -1,7 +1,5 @@ package mage.cards.c; -import java.util.UUID; - import mage.MageInt; import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.common.TurnedFaceUpAllTriggeredAbility; @@ -18,12 +16,14 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.card.FaceDownPredicate; +import java.util.UUID; + /** * @author jackd149 */ public final class CryptidInspector extends CardImpl { private static final FilterPermanent filter1 = new FilterPermanent("a face-down permanent"); - private static final FilterPermanent filter2 = new FilterControlledPermanent("Cryptid Inspector or another permanent you control"); + private static final FilterPermanent filter2 = new FilterControlledPermanent(); static { filter1.add(FaceDownPredicate.instance); @@ -40,17 +40,17 @@ public final class CryptidInspector extends CardImpl { // Whenever a face-down permanent you control enters and whenever Cryptid Inspector or another permanent you control is turned face up, // put a +1/+1 counter on Cryptid Inspector. this.addAbility(new OrTriggeredAbility( - Zone.BATTLEFIELD, - new AddCountersSourceEffect(CounterType.P1P1.createInstance()), - false, - "Whenever a face-down permanent you control enters and " - + "whenever Cryptid Inspector or another permanent you control is turned face up, ", - new EntersBattlefieldControlledTriggeredAbility(null, filter1), - new TurnedFaceUpAllTriggeredAbility(null, filter2) + Zone.BATTLEFIELD, + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), + false, + "Whenever a face-down permanent you control enters and " + + "whenever {this} or another permanent you control is turned face up, ", + new EntersBattlefieldControlledTriggeredAbility(null, filter1), + new TurnedFaceUpAllTriggeredAbility(null, filter2) )); } - private CryptidInspector(final CryptidInspector card){ + private CryptidInspector(final CryptidInspector card) { super(card); } @@ -58,4 +58,4 @@ public final class CryptidInspector extends CardImpl { public CryptidInspector copy() { return new CryptidInspector(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/c/CryptolithFragment.java b/Mage.Sets/src/mage/cards/c/CryptolithFragment.java index 5203e1feac5..ec264beae69 100644 --- a/Mage.Sets/src/mage/cards/c/CryptolithFragment.java +++ b/Mage.Sets/src/mage/cards/c/CryptolithFragment.java @@ -1,14 +1,14 @@ package mage.cards.c; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.LifeCompareCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.LoseLifeAllPlayersEffect; import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.keyword.TransformAbility; import mage.abilities.mana.AnyColorManaAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -22,6 +22,8 @@ import java.util.UUID; */ public final class CryptolithFragment extends CardImpl { + private static final Condition condition = new LifeCompareCondition(TargetController.EACH_PLAYER, ComparisonType.OR_LESS, 10); + public CryptolithFragment(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); @@ -37,10 +39,7 @@ public final class CryptolithFragment extends CardImpl { // At the beginning of your upkeep, if each player has 10 or less life, transform Cryptolith Fragment. this.addAbility(new TransformAbility()); - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect()), - new LifeCompareCondition(TargetController.EACH_PLAYER, ComparisonType.OR_LESS, 10), - "At the beginning of your upkeep, if each player has 10 or less life, transform {this}.")); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect()).withInterveningIf(condition)); } private CryptolithFragment(final CryptolithFragment card) { diff --git a/Mage.Sets/src/mage/cards/c/CrystalFragments.java b/Mage.Sets/src/mage/cards/c/CrystalFragments.java new file mode 100644 index 00000000000..145a755ba55 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CrystalFragments.java @@ -0,0 +1,50 @@ +package mage.cards.c; + +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.ExileAndReturnSourceEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.PutCards; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CrystalFragments extends CardImpl { + + public CrystalFragments(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{W}"); + + this.subtype.add(SubType.EQUIPMENT); + this.secondSideCardClazz = mage.cards.s.SummonAlexander.class; + + // Equipped creature gets +1/+1. + this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(1, 1))); + + // {5}{W}{W}: Exile this Equipment, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery. + this.addAbility(new TransformAbility()); + this.addAbility(new ActivateAsSorceryActivatedAbility( + new ExileAndReturnSourceEffect(PutCards.BATTLEFIELD_TRANSFORMED), new ManaCostsImpl<>("{5}{W}{W}") + )); + + // Equip {1} + this.addAbility(new EquipAbility(1)); + } + + private CrystalFragments(final CrystalFragments card) { + super(card); + } + + @Override + public CrystalFragments copy() { + return new CrystalFragments(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CrystalSkullIsuSpyglass.java b/Mage.Sets/src/mage/cards/c/CrystalSkullIsuSpyglass.java index 211e4f7b20e..e2805739d01 100644 --- a/Mage.Sets/src/mage/cards/c/CrystalSkullIsuSpyglass.java +++ b/Mage.Sets/src/mage/cards/c/CrystalSkullIsuSpyglass.java @@ -9,7 +9,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SuperType; import mage.filter.FilterCard; -import mage.filter.common.FilterHistoricCard; +import mage.filter.predicate.mageobject.HistoricPredicate; import java.util.UUID; @@ -18,7 +18,10 @@ import java.util.UUID; */ public final class CrystalSkullIsuSpyglass extends CardImpl { - private static final FilterCard filter = new FilterHistoricCard("play historic lands and cast historic spells"); + private static final FilterCard filter = new FilterCard("play historic lands and cast historic spells"); + static { + filter.add(HistoricPredicate.instance); + } public CrystalSkullIsuSpyglass(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{U}{U}"); diff --git a/Mage.Sets/src/mage/cards/c/CrystallizedSerah.java b/Mage.Sets/src/mage/cards/c/CrystallizedSerah.java index aa2e88a4097..a10acd8f683 100644 --- a/Mage.Sets/src/mage/cards/c/CrystallizedSerah.java +++ b/Mage.Sets/src/mage/cards/c/CrystallizedSerah.java @@ -30,7 +30,7 @@ public final class CrystallizedSerah extends CardImpl { // Legendary creatures you control get +2/+2. this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( - 2, 2, Duration.WhileControlled, StaticFilters.FILTER_CREATURES_LEGENDARY + 2, 2, Duration.WhileOnBattlefield, StaticFilters.FILTER_CREATURES_LEGENDARY ))); } diff --git a/Mage.Sets/src/mage/cards/c/CultivatorColossus.java b/Mage.Sets/src/mage/cards/c/CultivatorColossus.java index 6a1104d4d7a..7898eff956c 100644 --- a/Mage.Sets/src/mage/cards/c/CultivatorColossus.java +++ b/Mage.Sets/src/mage/cards/c/CultivatorColossus.java @@ -14,9 +14,11 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.StaticFilters; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetCardInHand; +import mage.util.CardUtil; import java.util.UUID; @@ -78,7 +80,7 @@ class CultivatorColossusEffect extends OneShotEffect { if (player == null) { return false; } - while (player.getHand().count(StaticFilters.FILTER_CARD_LAND, game) > 0) { + while (player.canRespond() && player.getHand().count(StaticFilters.FILTER_CARD_LAND, game) > 0) { TargetCard target = new TargetCardInHand( 0, 1, StaticFilters.FILTER_CARD_LAND ); @@ -91,10 +93,10 @@ class CultivatorColossusEffect extends OneShotEffect { card, Zone.BATTLEFIELD, source, game, true, false, false, null ); - if (game.getPermanent(card.getId()) == null) { - break; + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); + if (permanent != null) { + player.drawCards(1, source, game); } - player.drawCards(1, source, game); } return true; } diff --git a/Mage.Sets/src/mage/cards/c/CunningBandit.java b/Mage.Sets/src/mage/cards/c/CunningBandit.java index 8c9d58f9f4b..47586c5af03 100644 --- a/Mage.Sets/src/mage/cards/c/CunningBandit.java +++ b/Mage.Sets/src/mage/cards/c/CunningBandit.java @@ -1,37 +1,33 @@ - package mage.cards.c; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.OnEventTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.RemoveCountersSourceCost; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.FlipSourceEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.SuperType; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.CounterType; import mage.filter.StaticFilters; -import mage.game.events.GameEvent; import mage.game.permanent.token.TokenImpl; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class CunningBandit extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.KI, 2); + public CunningBandit(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{R}"); this.subtype.add(SubType.HUMAN); @@ -43,13 +39,12 @@ public final class CunningBandit extends CardImpl { this.flipCardName = "Azamuki, Treachery Incarnate"; // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Cunning Bandit. - this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, true)); // At the beginning of the end step, if there are two or more ki counters on Cunning Bandit, you may flip it. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new OnEventTriggeredAbility(GameEvent.EventType.END_TURN_STEP_PRE, "beginning of the end step", true, new FlipSourceEffect(new AzamukiTreacheryIncarnate()), true), - new SourceHasCounterCondition(CounterType.KI, 2, Integer.MAX_VALUE), - "At the beginning of the end step, if there are two or more ki counters on {this}, you may flip it.")); + this.addAbility(new BeginningOfEndStepTriggeredAbility( + TargetController.NEXT, new FlipSourceEffect(new AzamukiTreacheryIncarnate()).setText("flip it"), true, condition + )); } private CunningBandit(final CunningBandit card) { @@ -80,6 +75,7 @@ class AzamukiTreacheryIncarnate extends TokenImpl { ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } + private AzamukiTreacheryIncarnate(final AzamukiTreacheryIncarnate token) { super(token); } diff --git a/Mage.Sets/src/mage/cards/c/CunningGeysermage.java b/Mage.Sets/src/mage/cards/c/CunningGeysermage.java index d3591788dfb..f787e9e7f9b 100644 --- a/Mage.Sets/src/mage/cards/c/CunningGeysermage.java +++ b/Mage.Sets/src/mage/cards/c/CunningGeysermage.java @@ -4,16 +4,13 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.KickedCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.keyword.KickerAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import java.util.UUID; @@ -23,12 +20,6 @@ import java.util.UUID; */ public final class CunningGeysermage extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent("another creature"); - - static { - filter.add(AnotherPredicate.instance); - } - public CunningGeysermage(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); @@ -41,12 +32,9 @@ public final class CunningGeysermage extends CardImpl { this.addAbility(new KickerAbility("{2}{U}")); // When Cunning Geysermage enters the battlefield, if it was kicked, return up to one other target creature to its owner's hand. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect()), - KickedCondition.ONCE, "When {this} enters, " + - "if it was kicked, return up to one other target creature to its owner's hand." - ); - ability.addTarget(new TargetPermanent(0, 1, filter, false)); + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect() + .setText("return up to one other target creature to its owner's hand")).withInterveningIf(KickedCondition.ONCE); + ability.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_ANOTHER_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/c/CuriousHomunculus.java b/Mage.Sets/src/mage/cards/c/CuriousHomunculus.java index 7e04a100d66..b78941496aa 100644 --- a/Mage.Sets/src/mage/cards/c/CuriousHomunculus.java +++ b/Mage.Sets/src/mage/cards/c/CuriousHomunculus.java @@ -1,31 +1,34 @@ - package mage.cards.c; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.condition.Condition; +import mage.abilities.condition.common.CardsInControllerGraveyardCondition; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; import mage.abilities.keyword.TransformAbility; import mage.abilities.mana.ConditionalColorlessManaAbility; import mage.abilities.mana.builder.common.InstantOrSorcerySpellManaBuilder; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import mage.filter.StaticFilters; import mage.filter.common.FilterInstantOrSorceryCard; -import mage.game.Game; -import mage.players.Player; + +import java.util.UUID; /** * @author fireshoes */ public final class CuriousHomunculus extends CardImpl { + private static final Condition condition = new CardsInControllerGraveyardCondition(3, new FilterInstantOrSorceryCard("instant and/or sorcery cards")); + private static final Hint hint = new ValueHint("Instant and sorcery cards in your graveyard", new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY)); + public CuriousHomunculus(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.HOMUNCULUS); @@ -39,10 +42,7 @@ public final class CuriousHomunculus extends CardImpl { // At the beginning of your upkeep, if there are three or more instant and/or sorcery cards in your graveyard, transform Curious Homunculus. this.addAbility(new TransformAbility()); - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect()), - new InstantOrSorceryCardsInControllerGraveyardCondition(3), - "At the beginning of your upkeep, if there are three or more instant and/or sorcery cards in your graveyard, transform {this}")); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect()).withInterveningIf(condition).addHint(hint)); } private CuriousHomunculus(final CuriousHomunculus card) { @@ -54,21 +54,3 @@ public final class CuriousHomunculus extends CardImpl { return new CuriousHomunculus(this); } } - -class InstantOrSorceryCardsInControllerGraveyardCondition implements Condition { - - private int value; - - public InstantOrSorceryCardsInControllerGraveyardCondition(int value) { - this.value = value; - } - - @Override - public boolean apply(Game game, Ability source) { - Player p = game.getPlayer(source.getControllerId()); - if (p != null && p.getGraveyard().count(new FilterInstantOrSorceryCard(), game) >= value) { - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/c/CuriousObsession.java b/Mage.Sets/src/mage/cards/c/CuriousObsession.java index 87ecb417fc4..beff959e018 100644 --- a/Mage.Sets/src/mage/cards/c/CuriousObsession.java +++ b/Mage.Sets/src/mage/cards/c/CuriousObsession.java @@ -1,14 +1,11 @@ package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.InvertCondition; import mage.abilities.condition.common.RaidCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.SacrificeSourceEffect; @@ -16,23 +13,26 @@ import mage.abilities.effects.common.continuous.BoostEnchantedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.hint.common.RaidHint; import mage.abilities.keyword.EnchantAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.AttachmentType; import mage.constants.CardType; -import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.SubType; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; import mage.watchers.common.PlayerAttackedWatcher; +import java.util.UUID; + /** - * * @author awjackson */ public final class CuriousObsession extends CardImpl { + private static final Condition condition = new InvertCondition(RaidCondition.instance, "you didn't attack with a creature this turn"); + public CuriousObsession(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{U}"); @@ -42,25 +42,20 @@ public final class CuriousObsession extends CardImpl { TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget); - this.addAbility(ability); + this.addAbility(new EnchantAbility(auraTarget)); // Enchanted creature gets +1/+1 and has "Whenever this creature deals combat damage to a player, you may draw a card. - ability = new SimpleStaticAbility(new BoostEnchantedEffect(1, 1, Duration.WhileOnBattlefield)); - Ability gainedAbility = new DealsCombatDamageToAPlayerTriggeredAbility(new DrawCardSourceControllerEffect(1), true); - Effect effect = new GainAbilityAttachedEffect(gainedAbility, AttachmentType.AURA); - effect.setText("and has \"Whenever this creature deals combat damage to a player, you may draw a card.\""); - ability.addEffect(effect); + Ability ability = new SimpleStaticAbility(new BoostEnchantedEffect(1, 1)); + ability.addEffect(new GainAbilityAttachedEffect( + new DealsCombatDamageToAPlayerTriggeredAbility( + new DrawCardSourceControllerEffect(1), true + ), AttachmentType.AURA + ).setText("and has \"Whenever this creature deals combat damage to a player, you may draw a card.\"")); this.addAbility(ability); // At the beginning of your end step, if you didn't attack with a creature this turn sacrifice Curious Obsession. - ability = new ConditionalInterveningIfTriggeredAbility( - new BeginningOfEndStepTriggeredAbility(new SacrificeSourceEffect()), - new InvertCondition(RaidCondition.instance), - "At the beginning of your end step, if you didn't attack with a creature this turn, sacrifice {this}." - ); - ability.addHint(RaidHint.instance); - this.addAbility(ability, new PlayerAttackedWatcher()); + this.addAbility(new BeginningOfEndStepTriggeredAbility(new SacrificeSourceEffect()) + .withInterveningIf(condition).addHint(RaidHint.instance), new PlayerAttackedWatcher()); } private CuriousObsession(final CuriousObsession card) { diff --git a/Mage.Sets/src/mage/cards/c/CycloneSummoner.java b/Mage.Sets/src/mage/cards/c/CycloneSummoner.java index a04b95d6d78..e1875bcc528 100644 --- a/Mage.Sets/src/mage/cards/c/CycloneSummoner.java +++ b/Mage.Sets/src/mage/cards/c/CycloneSummoner.java @@ -3,7 +3,6 @@ package mage.cards.c; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.ReturnToHandFromBattlefieldAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -37,13 +36,10 @@ public final class CycloneSummoner extends CardImpl { this.toughness = new MageInt(7); // When Cyclone Summoner enters the battlefield, if you cast it from your hand, return all permanents to their owners' hands except for Giants, Wizards, and lands. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility( - new ReturnToHandFromBattlefieldAllEffect(filter), false - ), CastFromHandSourcePermanentCondition.instance, "When {this} enters, " + - "if you cast it from your hand, return all permanents to their owners' hands " + - "except for Giants, Wizards, and lands." - ), new CastFromHandWatcher()); + this.addAbility(new EntersBattlefieldTriggeredAbility( + new ReturnToHandFromBattlefieldAllEffect(filter) + .setText("return all permanents to their owners' hands except for Giants, Wizards, and lands") + ).withInterveningIf(CastFromHandSourcePermanentCondition.instance), new CastFromHandWatcher()); } private CycloneSummoner(final CycloneSummoner card) { diff --git a/Mage.Sets/src/mage/cards/c/CyclonicRift.java b/Mage.Sets/src/mage/cards/c/CyclonicRift.java index 8777b1ce291..728c94209e3 100644 --- a/Mage.Sets/src/mage/cards/c/CyclonicRift.java +++ b/Mage.Sets/src/mage/cards/c/CyclonicRift.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; import mage.abilities.effects.common.ReturnToHandFromBattlefieldAllEffect; @@ -12,10 +10,11 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.TargetController; import mage.filter.common.FilterNonlandPermanent; -import mage.target.common.TargetNonlandPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class CyclonicRift extends CardImpl { @@ -30,7 +29,7 @@ public final class CyclonicRift extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); // Return target nonland permanent you don't control to its owner's hand. - this.getSpellAbility().addTarget(new TargetNonlandPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); // Overload {6}{U} (You may cast this spell for its overload cost. If you do, change its text by replacing all instances of "target" with "each.") diff --git a/Mage.Sets/src/mage/cards/c/CyclopeanTomb.java b/Mage.Sets/src/mage/cards/c/CyclopeanTomb.java index d010d30c720..16a99bafa6f 100644 --- a/Mage.Sets/src/mage/cards/c/CyclopeanTomb.java +++ b/Mage.Sets/src/mage/cards/c/CyclopeanTomb.java @@ -18,6 +18,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.counters.CounterType; +import mage.filter.FilterPermanent; import mage.filter.common.FilterLandPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.PermanentIdPredicate; @@ -25,7 +26,7 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.common.TargetLandPermanent; +import mage.target.TargetPermanent; import mage.target.targetpointer.FixedTarget; import mage.watchers.Watcher; @@ -36,7 +37,7 @@ import java.util.*; */ public final class CyclopeanTomb extends CardImpl { - private static final FilterLandPermanent filter = new FilterLandPermanent(); + private static final FilterPermanent filter = new FilterLandPermanent(); static { filter.add(Predicates.not(SubType.SWAMP.getPredicate())); @@ -48,7 +49,7 @@ public final class CyclopeanTomb extends CardImpl { // {2}, {tap}: Put a mire counter on target non-Swamp land. That land is a Swamp for as long as it has a mire counter on it. Activate this ability only during your upkeep. Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new AddCountersTargetEffect(CounterType.MIRE.createInstance()), new GenericManaCost(2), new IsStepCondition(PhaseStep.UPKEEP), "{2}, {T}: Put a mire counter on target non-Swamp land. That land is a Swamp for as long as it has a mire counter on it. Activate only during your upkeep."); ability.addCost(new TapSourceCost()); - ability.addTarget(new TargetLandPermanent(filter)); + ability.addTarget(new TargetPermanent(filter)); ability.addEffect(new BecomeSwampEffect()); this.addAbility(ability, new CyclopeanTombCounterWatcher()); @@ -160,7 +161,7 @@ class CyclopeanTombEffect extends OneShotEffect { } } filter.add(Predicates.or(idPref)); - TargetLandPermanent target = new TargetLandPermanent(1, 1, filter, true); + TargetPermanent target = new TargetPermanent(1, 1, filter, true); /*Player must choose a land each upkeep. Using the message are above the player hand where frequent interactions * take place is the most logical way to prompt for this scenario. A new constructor added to provide a not optional * option for any cards like this where the player must choose a target in such the way this card requires. diff --git a/Mage.Sets/src/mage/cards/c/Cytoshape.java b/Mage.Sets/src/mage/cards/c/Cytoshape.java index a5795cb596b..bbbed0cfc4d 100644 --- a/Mage.Sets/src/mage/cards/c/Cytoshape.java +++ b/Mage.Sets/src/mage/cards/c/Cytoshape.java @@ -1,7 +1,6 @@ package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; @@ -18,32 +17,23 @@ import mage.target.Target; import mage.target.common.TargetCreaturePermanent; import mage.util.functions.EmptyCopyApplier; +import java.util.UUID; + /** * * @author jeffwadsworth */ public final class Cytoshape extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonlegendary creature"); - - static { - filter.add(Predicates.not(SuperType.LEGENDARY.getPredicate())); - } public Cytoshape(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}{U}"); // Choose a nonlegendary creature on the battlefield. Target creature becomes a copy of that creature until end of turn. this.getSpellAbility().addEffect(new CytoshapeEffect()); - Target target = new TargetCreaturePermanent(1, 1, filter, true); - target.setTargetTag(1); - this.getSpellAbility().addTarget(target); - - FilterCreaturePermanent filter2 = new FilterCreaturePermanent("target creature that will become a copy of that chosen creature"); - target = new TargetCreaturePermanent(filter2); - target.setTargetTag(2); - this.getSpellAbility().addTarget(target); + FilterCreaturePermanent filter = new FilterCreaturePermanent("target creature that will become a copy"); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); } private Cytoshape(final Cytoshape card) { @@ -58,6 +48,11 @@ public final class Cytoshape extends CardImpl { class CytoshapeEffect extends OneShotEffect { + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonlegendary creature"); + + static { + filter.add(Predicates.not(SuperType.LEGENDARY.getPredicate())); + } CytoshapeEffect() { super(Outcome.Copy); this.staticText = "Choose a nonlegendary creature on the battlefield. Target creature becomes a copy of that creature until end of turn."; @@ -74,9 +69,11 @@ class CytoshapeEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability ability) { - Permanent copyFrom = game.getPermanent(getTargetPointer().getFirst(game, ability)); + Target target = new TargetCreaturePermanent(1, 1, filter, true); + target.choose(Outcome.Copy, ability.getControllerId(), ability, game); + Permanent copyFrom = game.getPermanent(target.getFirstTarget()); if (copyFrom != null) { - Permanent copyTo = game.getPermanentOrLKIBattlefield(ability.getTargets().get(1).getFirstTarget()); + Permanent copyTo = game.getPermanentOrLKIBattlefield(ability.getTargets().get(0).getFirstTarget()); if (copyTo != null) { game.copyPermanent(Duration.EndOfTurn, copyFrom, copyTo.getId(), ability, new EmptyCopyApplier()); } diff --git a/Mage.Sets/src/mage/cards/d/DaghatarTheAdamant.java b/Mage.Sets/src/mage/cards/d/DaghatarTheAdamant.java index 2d2d495ac2b..826be14a536 100644 --- a/Mage.Sets/src/mage/cards/d/DaghatarTheAdamant.java +++ b/Mage.Sets/src/mage/cards/d/DaghatarTheAdamant.java @@ -1,38 +1,41 @@ package mage.cards.d; -import java.util.UUID; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.common.counter.MoveCounterTargetsEffect; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Outcome; import mage.constants.SuperType; -import mage.constants.Zone; import mage.counters.CounterType; +import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.filter.predicate.other.AnotherTargetPredicate; +import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class DaghatarTheAdamant extends CardImpl { + private static final FilterPermanent filter = new FilterCreaturePermanent("a second target creature"); + + static { + filter.add(new AnotherTargetPredicate(2)); + } + public DaghatarTheAdamant(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); this.supertype.add(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WARRIOR); @@ -41,17 +44,18 @@ public final class DaghatarTheAdamant extends CardImpl { // Vigilance this.addAbility(VigilanceAbility.getInstance()); + // Daghatar the Adamant enters the battlefield with four +1/+1 counters on it. this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(4)), "with four +1/+1 counters on it")); // {1}{B/G}{B/G}: Move a +1/+1 counter from target creature onto a second target creature. - Ability ability = new SimpleActivatedAbility(new MoveCounterFromTargetToTargetEffect(),new ManaCostsImpl<>("{1}{B/G}{B/G}")); - ability.addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature the +1/+1 counter is moved from"))); - ability.addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature the +1/+1 counter is moved to"))); + Ability ability = new SimpleActivatedAbility( + new MoveCounterTargetsEffect(CounterType.P1P1), new ManaCostsImpl<>("{1}{B/G}{B/G}") + ); + ability.addTarget(new TargetCreaturePermanent().withChooseHint("to remove a counter from").setTargetTag(1)); + ability.addTarget(new TargetPermanent(filter).withChooseHint("to move a counter to").setTargetTag(2)); this.addAbility(ability); - - } private DaghatarTheAdamant(final DaghatarTheAdamant card) { @@ -63,39 +67,3 @@ public final class DaghatarTheAdamant extends CardImpl { return new DaghatarTheAdamant(this); } } - -class MoveCounterFromTargetToTargetEffect extends OneShotEffect { - - MoveCounterFromTargetToTargetEffect() { - super(Outcome.Detriment); - this.staticText = "Move a +1/+1 counter from target creature onto a second target creature"; - } - - private MoveCounterFromTargetToTargetEffect(final MoveCounterFromTargetToTargetEffect effect) { - super(effect); - } - - @Override - public MoveCounterFromTargetToTargetEffect copy() { - return new MoveCounterFromTargetToTargetEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source); - if (sourceObject != null && controller != null) { - Permanent fromPermanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (fromPermanent != null && fromPermanent.getCounters(game).getCount(CounterType.P1P1) > 0) { - Permanent toPermanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if (toPermanent != null) { - fromPermanent.removeCounters(CounterType.P1P1.createInstance(), source, game); - toPermanent.addCounters(CounterType.P1P1.createInstance(), source.getControllerId(), source, game); - game.informPlayers(sourceObject.getLogName() + ": Moved a +1/+1 counter from " + fromPermanent.getLogName() +" to " + toPermanent.getLogName()); - } - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/d/DanithaNewBenaliasLight.java b/Mage.Sets/src/mage/cards/d/DanithaNewBenaliasLight.java index b458bd99339..08806f3f1d5 100644 --- a/Mage.Sets/src/mage/cards/d/DanithaNewBenaliasLight.java +++ b/Mage.Sets/src/mage/cards/d/DanithaNewBenaliasLight.java @@ -1,7 +1,7 @@ package mage.cards.d; import mage.MageInt; -import mage.abilities.common.CastFromGraveyardOnceEachTurnAbility; +import mage.abilities.common.CastFromGraveyardOnceDuringEachOfYourTurnAbility; import mage.abilities.keyword.LifelinkAbility; import mage.abilities.keyword.TrampleAbility; import mage.abilities.keyword.VigilanceAbility; @@ -21,6 +21,7 @@ import java.util.UUID; public final class DanithaNewBenaliasLight extends CardImpl { private static final FilterCard filter = new FilterCard("an Aura or Equipment spell"); + static { filter.add(Predicates.or( SubType.AURA.getPredicate(), SubType.EQUIPMENT.getPredicate() @@ -46,7 +47,7 @@ public final class DanithaNewBenaliasLight extends CardImpl { this.addAbility(LifelinkAbility.getInstance()); // Once during each of your turns, you may cast an Aura or Equipment spell from your graveyard. - this.addAbility(new CastFromGraveyardOnceEachTurnAbility(filter)); + this.addAbility(new CastFromGraveyardOnceDuringEachOfYourTurnAbility(filter)); } private DanithaNewBenaliasLight(final DanithaNewBenaliasLight card) { diff --git a/Mage.Sets/src/mage/cards/d/DannyPink.java b/Mage.Sets/src/mage/cards/d/DannyPink.java new file mode 100644 index 00000000000..2c01cfe2a55 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DannyPink.java @@ -0,0 +1,110 @@ +package mage.cards.d; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.TriggeredAbility; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.keyword.MentorAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.Counters; +import mage.counters.Counter; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentToken; +import mage.watchers.common.CountersAddedFirstTimeWatcher; + +/** + * + * @author padfoothelix + */ +public final class DannyPink extends CardImpl { + + public DannyPink(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.subtype.add(SubType.ADVISOR); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Mentor + this.addAbility(new MentorAbility()); + + // Creatures you control have "Whenever one or more counters are put on this creature for the first time each turn, draw a card." + this.addAbility(new SimpleStaticAbility( + new GainAbilityControlledEffect( + new DannyPinkTriggeredAbility(), + Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENT_CREATURES + ))); + + } + + private DannyPink(final DannyPink card) { + super(card); + } + + @Override + public DannyPink copy() { + return new DannyPink(this); + } +} + +class DannyPinkTriggeredAbility extends TriggeredAbilityImpl { + + DannyPinkTriggeredAbility() { + super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), false); + this.setTriggerPhrase("Whenever one or more counters are put on this creature for the first time each turn, "); + this.addWatcher(new CountersAddedFirstTimeWatcher()); + } + + private DannyPinkTriggeredAbility(final DannyPinkTriggeredAbility ability) { + super(ability); + } + + // Non-token creatures entering with counters do not see the COUNTERS_ADDED event, + // so we check for etb event too. + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.COUNTERS_ADDED + || (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!this.getSourceId().equals(event.getTargetId())) { + return false; + } + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent == null) { + return false; + } + // a non-token creature entering with counters does not see the COUNTERS_ADDED event. + // therefore, we return true in either of two cases : + // 1. a non-token creature enters with counters on it (no need to check the watcher) + // 2. a COUNTERS_ADDED event occurs and the watcher is valid + if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD) { + Counters counters = permanent.getCounters(game); + return !counters.values().stream().mapToInt(Counter::getCount).noneMatch(x -> x > 0) + && !(permanent instanceof PermanentToken); + } + if (event.getType() == GameEvent.EventType.COUNTERS_ADDED) { + return CountersAddedFirstTimeWatcher.checkEvent(event, permanent, game, 0); + } + return false; + } + + @Override + public DannyPinkTriggeredAbility copy() { + return new DannyPinkTriggeredAbility(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DaredevilDragster.java b/Mage.Sets/src/mage/cards/d/DaredevilDragster.java index 742cabce635..b5d5b6f1965 100644 --- a/Mage.Sets/src/mage/cards/d/DaredevilDragster.java +++ b/Mage.Sets/src/mage/cards/d/DaredevilDragster.java @@ -1,32 +1,33 @@ - package mage.cards.d; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EndOfCombatTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.AttackedOrBlockedThisCombatSourceCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.condition.common.SourceHasCounterCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.keyword.CrewAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; +import mage.constants.ComparisonType; import mage.constants.SubType; import mage.counters.CounterType; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.watchers.common.AttackedOrBlockedThisCombatWatcher; import java.util.UUID; /** - * * @author spjspj */ public final class DaredevilDragster extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.VELOCITY, ComparisonType.OR_GREATER, 2); + public DaredevilDragster(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); @@ -35,11 +36,15 @@ public final class DaredevilDragster extends CardImpl { this.toughness = new MageInt(4); // At end of combat, if Daredevil Dragster attacked or blocked this combat, put a velocity counter on it. Then if it has two or more velocity counters on it, sacrifice it and draw two cards. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EndOfCombatTriggeredAbility(new DaredevilDragsterEffect(), false), - AttackedOrBlockedThisCombatSourceCondition.instance, - "At end of combat, if {this} attacked or blocked this combat, put a velocity counter on it. Then if it has two or more velocity counters on it, sacrifice it and draw two cards."), - new AttackedOrBlockedThisCombatWatcher()); + Ability ability = new EndOfCombatTriggeredAbility( + new AddCountersSourceEffect(CounterType.VELOCITY.createInstance()) + .setText("put a velocity counter on it"), false + ).withInterveningIf(AttackedOrBlockedThisCombatSourceCondition.instance); + ability.addEffect(new ConditionalOneShotEffect( + new SacrificeSourceEffect(), condition, "Then if it has two " + + "or more velocity counters on it, sacrifice it and draw two cards" + ).addEffect(new DrawCardSourceControllerEffect(2))); + this.addAbility(ability, new AttackedOrBlockedThisCombatWatcher()); // Crew 2 this.addAbility(new CrewAbility(2)); @@ -54,35 +59,3 @@ public final class DaredevilDragster extends CardImpl { return new DaredevilDragster(this); } } - -class DaredevilDragsterEffect extends OneShotEffect { - - DaredevilDragsterEffect() { - super(Outcome.Damage); - this.staticText = "put a velocity counter on it. Then if it has two or more velocity counters on it, sacrifice it and draw two cards"; - } - - private DaredevilDragsterEffect(final DaredevilDragsterEffect effect) { - super(effect); - } - - @Override - public DaredevilDragsterEffect copy() { - return new DaredevilDragsterEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null && permanent != null) { - permanent.addCounters(CounterType.VELOCITY.createInstance(), source.getControllerId(), source, game); - if (permanent.getCounters(game).getCount(CounterType.VELOCITY) >= 2) { - permanent.sacrifice(source, game); - controller.drawCards(2, source, game); - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/d/DarettiRocketeerEngineer.java b/Mage.Sets/src/mage/cards/d/DarettiRocketeerEngineer.java index 3828e0ef204..ebc07004f7f 100644 --- a/Mage.Sets/src/mage/cards/d/DarettiRocketeerEngineer.java +++ b/Mage.Sets/src/mage/cards/d/DarettiRocketeerEngineer.java @@ -1,13 +1,11 @@ package mage.cards.d; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.SacrificeTargetCost; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.abilities.effects.common.continuous.SetBasePowerSourceEffect; @@ -16,8 +14,8 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; +import mage.constants.Zone; import mage.filter.StaticFilters; -import mage.game.Game; import mage.target.common.TargetCardInYourGraveyard; import java.util.UUID; @@ -37,8 +35,11 @@ public final class DarettiRocketeerEngineer extends CardImpl { this.toughness = new MageInt(5); // Daretti's power is equal to the greatest mana value among artifacts you control. - this.addAbility(new SimpleStaticAbility(new SetBasePowerSourceEffect(DarettiRocketeerEngineerValue.instance) - .setText("{this}'s power is equal to the greatest mana value among artifacts you control"))); + this.addAbility(new SimpleStaticAbility( + Zone.ALL, + new SetBasePowerSourceEffect(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_ARTIFACTS) + .setText("{this}'s power is equal to the greatest mana value among artifacts you control") + )); // Whenever Daretti enters or attacks, choose target artifact card in your graveyard. You may sacrifice an artifact. If you do, return the chosen card to the battlefield. Ability ability = new EntersBattlefieldOrAttacksSourceTriggeredAbility(new DoIfCostPaid( @@ -57,37 +58,4 @@ public final class DarettiRocketeerEngineer extends CardImpl { public DarettiRocketeerEngineer copy() { return new DarettiRocketeerEngineer(this); } -} - -enum DarettiRocketeerEngineerValue implements DynamicValue { - instance; - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - return game - .getBattlefield() - .getActivePermanents( - StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT, - sourceAbility.getControllerId(), sourceAbility, game - ) - .stream() - .mapToInt(MageObject::getManaValue) - .max() - .orElse(0); - } - - @Override - public DarettiRocketeerEngineerValue copy() { - return this; - } - - @Override - public String getMessage() { - return ""; - } - - @Override - public String toString() { - return "1"; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/d/DaringWaverider.java b/Mage.Sets/src/mage/cards/d/DaringWaverider.java index a253a60a4b5..09b1b6aa903 100644 --- a/Mage.Sets/src/mage/cards/d/DaringWaverider.java +++ b/Mage.Sets/src/mage/cards/d/DaringWaverider.java @@ -21,7 +21,7 @@ import java.util.UUID; * @author notgreat */ public final class DaringWaverider extends CardImpl { - private static final FilterCard filter = new FilterInstantOrSorceryCard("instant or sorcery card with mana value 4 or less"); + private static final FilterCard filter = new FilterInstantOrSorceryCard("instant or sorcery card with mana value 4 or less from your graveyard"); static { filter.add(new ManaValuePredicate(ComparisonType.OR_LESS, 4)); diff --git a/Mage.Sets/src/mage/cards/d/DarkApprenticeship.java b/Mage.Sets/src/mage/cards/d/DarkApprenticeship.java index 92fa2bdaf75..0cd1317e3c0 100644 --- a/Mage.Sets/src/mage/cards/d/DarkApprenticeship.java +++ b/Mage.Sets/src/mage/cards/d/DarkApprenticeship.java @@ -1,20 +1,19 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.abilities.condition.common.HateCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.target.TargetPlayer; import mage.watchers.common.LifeLossOtherFromCombatWatcher; +import java.util.UUID; + /** - * * @author Styxo */ public final class DarkApprenticeship extends CardImpl { @@ -23,12 +22,10 @@ public final class DarkApprenticeship extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{R}"); // Hate — At the beggining of your end step, if an opponent lost life from source other than combat damage this turn, Dark Apprenticeship deals 2 damage to target player. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new BeginningOfEndStepTriggeredAbility(new DamageTargetEffect(2)), - HateCondition.instance, - "Hate — At the beggining of your end step, if an opponent lost life from source other than combat damage this turn, Dark Apprenticeship deals 2 damage to target player."); + Ability ability = new BeginningOfEndStepTriggeredAbility(new DamageTargetEffect(2)) + .withInterveningIf(HateCondition.instance); ability.addTarget(new TargetPlayer()); - this.addAbility(ability, new LifeLossOtherFromCombatWatcher()); + this.addAbility(ability.setAbilityWord(AbilityWord.HATE), new LifeLossOtherFromCombatWatcher()); } private DarkApprenticeship(final DarkApprenticeship card) { diff --git a/Mage.Sets/src/mage/cards/d/DarkKnightsGreatsword.java b/Mage.Sets/src/mage/cards/d/DarkKnightsGreatsword.java new file mode 100644 index 00000000000..23648330bfb --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DarkKnightsGreatsword.java @@ -0,0 +1,53 @@ +package mage.cards.d; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.effects.common.continuous.AddCardSubtypeAttachedEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.abilities.keyword.JobSelectAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DarkKnightsGreatsword extends CardImpl { + + public DarkKnightsGreatsword(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{B}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Job select + this.addAbility(new JobSelectAbility()); + + // Equipped creature gets +3/+0 and is a Knight in addition to its other types. + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(3, 0)); + ability.addEffect(new AddCardSubtypeAttachedEffect( + SubType.KNIGHT, AttachmentType.EQUIPMENT + ).setText("and is a Knight in addition to its other types")); + this.addAbility(ability); + + // Chaosbringer -- Equip--Pay 3 life. Activate only once each turn. + EquipAbility equipAbility = new EquipAbility(Outcome.BoostCreature, new PayLifeCost(3)); + equipAbility.setMaxActivationsPerTurn(1); + this.addAbility(equipAbility.withFlavorWord("Chaosbringer")); + } + + private DarkKnightsGreatsword(final DarkKnightsGreatsword card) { + super(card); + } + + @Override + public DarkKnightsGreatsword copy() { + return new DarkKnightsGreatsword(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DarkSphere.java b/Mage.Sets/src/mage/cards/d/DarkSphere.java index 64fb3666bb5..e084e635a1a 100644 --- a/Mage.Sets/src/mage/cards/d/DarkSphere.java +++ b/Mage.Sets/src/mage/cards/d/DarkSphere.java @@ -1,35 +1,32 @@ package mage.cards.d; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.game.Game; import mage.game.events.DamageEvent; import mage.game.events.GameEvent; import mage.players.Player; -import mage.target.TargetSource; + +import java.util.UUID; /** - * * @author ThomasLerner */ public final class DarkSphere extends CardImpl { public DarkSphere(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{0}"); - + // {tap}, Sacrifice Dark Sphere: The next time a source of your choice would deal damage to you this turn, prevent half that damage, rounded down. Ability ability = new SimpleActivatedAbility(new DarkSpherePreventionEffect(), new TapSourceCost()); @@ -48,19 +45,17 @@ public final class DarkSphere extends CardImpl { } -class DarkSpherePreventionEffect extends ReplacementEffectImpl { +class DarkSpherePreventionEffect extends PreventNextDamageFromChosenSourceEffect { + + private static final FilterSource filter = new FilterSource("source"); - private final TargetSource targetSource; - public DarkSpherePreventionEffect() { - super(Duration.OneUse, Outcome.RedirectDamage); + super(Duration.EndOfTurn, true, filter); this.staticText = "The next time a source of your choice would deal damage to you this turn, prevent half that damage, rounded down"; - this.targetSource = new TargetSource(new FilterObject("source of your choice")); } - + private DarkSpherePreventionEffect(final DarkSpherePreventionEffect effect) { super(effect); - this.targetSource = effect.targetSource.copy(); } @Override @@ -68,40 +63,24 @@ class DarkSpherePreventionEffect extends ReplacementEffectImpl { return new DarkSpherePreventionEffect(this); } - @Override - public void init(Ability source, Game game) { - super.init(source, game); - this.targetSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); - } - @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = game.getObject(source); DamageEvent damageEvent = (DamageEvent) event; - if (controller != null) { - controller.damage((int) Math.ceil(damageEvent.getAmount() / 2.0), damageEvent.getSourceId(), source, game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), damageEvent.getAppliedEffects()); - StringBuilder sb = new StringBuilder(sourceObject != null ? sourceObject.getLogName() : ""); - sb.append(": ").append(damageEvent.getAmount() / 2).append(" damage prevented"); - sb.append(" from ").append(controller.getLogName()); - game.informPlayers(sb.toString()); - discard(); // only one use - return true; + int damage = damageEvent.getAmount(); + if (controller == null || damage <= 0) { + return false; } - return false; + controller.damage( + (int) Math.ceil(damage / 2.0), damageEvent.getSourceId(), source, game, + damageEvent.isCombatDamage(), damageEvent.isPreventable(), damageEvent.getAppliedEffects() + ); + StringBuilder sb = new StringBuilder(sourceObject != null ? sourceObject.getLogName() : ""); + sb.append(": ").append(damage / 2).append(" damage prevented"); + sb.append(" from ").append(controller.getLogName()); + game.informPlayers(sb.toString()); + discard(); // only one use + return true; } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_PLAYER; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getSourceId().equals(targetSource.getFirstTarget()) && event.getTargetId().equals(source.getControllerId())) { - return true; - } - return false; - } - } diff --git a/Mage.Sets/src/mage/cards/d/DarthMaul.java b/Mage.Sets/src/mage/cards/d/DarthMaul.java index 0a733776e9a..ea146b426bf 100644 --- a/Mage.Sets/src/mage/cards/d/DarthMaul.java +++ b/Mage.Sets/src/mage/cards/d/DarthMaul.java @@ -1,26 +1,21 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.condition.common.HateCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.combat.CantBeBlockedByTargetSourceEffect; import mage.abilities.keyword.DoubleStrikeAbility; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.SuperType; +import mage.constants.*; import mage.target.common.TargetCreaturePermanent; import mage.watchers.common.LifeLossOtherFromCombatWatcher; +import java.util.UUID; + /** - * * @author Styxo */ public final class DarthMaul extends CardImpl { @@ -40,12 +35,10 @@ public final class DarthMaul extends CardImpl { this.addAbility(HasteAbility.getInstance()); // Hate — Whenever Darth Maul attacks, if an opponent loses life from a source other than combat damage this turn, target creature can't block this turn. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new AttacksTriggeredAbility(new CantBeBlockedByTargetSourceEffect(Duration.EndOfTurn), false), - HateCondition.instance, - "Hate — Whenever Darth Maul attacks, if an opponent loses life from a source other than combat damage this turn, target creature can't block this turn."); + Ability ability = new AttacksTriggeredAbility(new CantBeBlockedByTargetSourceEffect(Duration.EndOfTurn)) + .withInterveningIf(HateCondition.instance); ability.addTarget(new TargetCreaturePermanent()); - this.addAbility(ability, new LifeLossOtherFromCombatWatcher()); + this.addAbility(ability.setAbilityWord(AbilityWord.HATE), new LifeLossOtherFromCombatWatcher()); } private DarthMaul(final DarthMaul card) { diff --git a/Mage.Sets/src/mage/cards/d/DaruCavalier.java b/Mage.Sets/src/mage/cards/d/DaruCavalier.java index 9e01ab2555e..d3ad1b9cb9f 100644 --- a/Mage.Sets/src/mage/cards/d/DaruCavalier.java +++ b/Mage.Sets/src/mage/cards/d/DaruCavalier.java @@ -38,7 +38,7 @@ public final class DaruCavalier extends CardImpl { this.addAbility(FirstStrikeAbility.getInstance()); // When Daru Cavalier enters the battlefield, you may search your library for a card named Daru Cavalier, reveal it, and put it into your hand. If you do, shuffle your library. - TargetCardInLibrary target = new TargetCardInLibrary(0, 1, filter); + TargetCardInLibrary target = new TargetCardInLibrary(filter); this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandEffect(target, true), true)); } diff --git a/Mage.Sets/src/mage/cards/d/DavrielRogueShadowmage.java b/Mage.Sets/src/mage/cards/d/DavrielRogueShadowmage.java index d73c39d5fdf..6d5c900626c 100644 --- a/Mage.Sets/src/mage/cards/d/DavrielRogueShadowmage.java +++ b/Mage.Sets/src/mage/cards/d/DavrielRogueShadowmage.java @@ -2,16 +2,14 @@ package mage.cards.d; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.condition.common.CardsInHandCondition; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.discard.DiscardTargetEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.game.Game; -import mage.players.Player; import mage.target.TargetPlayer; import java.util.UUID; @@ -21,6 +19,8 @@ import java.util.UUID; */ public final class DavrielRogueShadowmage extends CardImpl { + private static final Condition condition = new CardsInHandCondition(ComparisonType.OR_LESS, 1, TargetController.ACTIVE); + public DavrielRogueShadowmage(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{B}"); @@ -29,13 +29,12 @@ public final class DavrielRogueShadowmage extends CardImpl { this.setStartingLoyalty(3); // At the beginning of each opponent's upkeep, if that player has one or fewer cards in hand, Davriel, Rogue Shadowmage deals 2 damage to them. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility( - Zone.BATTLEFIELD, TargetController.OPPONENT, new DamageTargetEffect(2), - false - ), DavrielRogueShadowmageCondition.instance, "At the beginning of each opponent's upkeep, " + - "if that player has one or fewer cards in hand, {this} deals 2 damage to them." - )); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + TargetController.OPPONENT, + new DamageTargetEffect( + 2, true, "them", "{this}" + ), false + ).withInterveningIf(condition)); // -1: Target player discards a card. Ability ability = new LoyaltyAbility(new DiscardTargetEffect(1), -1); @@ -52,13 +51,3 @@ public final class DavrielRogueShadowmage extends CardImpl { return new DavrielRogueShadowmage(this); } } - -enum DavrielRogueShadowmageCondition implements Condition { - instance; - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(game.getActivePlayerId()); - return player != null && player.getHand().size() < 2; - } -} diff --git a/Mage.Sets/src/mage/cards/d/DawnOfTheDead.java b/Mage.Sets/src/mage/cards/d/DawnOfTheDead.java index f4afb6f8b80..9eac5551ec9 100644 --- a/Mage.Sets/src/mage/cards/d/DawnOfTheDead.java +++ b/Mage.Sets/src/mage/cards/d/DawnOfTheDead.java @@ -22,6 +22,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @@ -74,7 +75,7 @@ class DawnOfTheDeadEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null && card != null) { if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { - Permanent creature = game.getPermanent(card.getId()); + Permanent creature = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (creature != null) { // gains haste ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); diff --git a/Mage.Sets/src/mage/cards/d/DawnhartWardens.java b/Mage.Sets/src/mage/cards/d/DawnhartWardens.java index 64db6ce7043..162fd925da1 100644 --- a/Mage.Sets/src/mage/cards/d/DawnhartWardens.java +++ b/Mage.Sets/src/mage/cards/d/DawnhartWardens.java @@ -1,15 +1,17 @@ package mage.cards.d; import mage.MageInt; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.abilities.condition.common.CovenCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.hint.common.CovenHint; import mage.abilities.keyword.VigilanceAbility; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.AbilityWord; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; import java.util.UUID; @@ -30,13 +32,8 @@ public final class DawnhartWardens extends CardImpl { this.addAbility(VigilanceAbility.getInstance()); // Coven — At the beginning of combat on your turn, if you control three or more creatures with different powers, creatures you control get +1/+0 until end of turn. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfCombatTriggeredAbility( - new BoostControlledEffect(1, 0, Duration.EndOfTurn) - ), CovenCondition.instance, "At the beginning of combat on your turn, " + - "if you control three or more creatures with different powers, " + - "creatures you control get +1/+0 until end of turn." - ).addHint(CovenHint.instance).setAbilityWord(AbilityWord.COVEN)); + this.addAbility(new BeginningOfCombatTriggeredAbility(new BoostControlledEffect(1, 0, Duration.EndOfTurn)) + .withInterveningIf(CovenCondition.instance).addHint(CovenHint.instance).setAbilityWord(AbilityWord.COVEN)); } private DawnhartWardens(final DawnhartWardens card) { diff --git a/Mage.Sets/src/mage/cards/d/DawningPurist.java b/Mage.Sets/src/mage/cards/d/DawningPurist.java index b41e8c8ecf2..25fa10758d7 100644 --- a/Mage.Sets/src/mage/cards/d/DawningPurist.java +++ b/Mage.Sets/src/mage/cards/d/DawningPurist.java @@ -13,7 +13,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterEnchantmentPermanent; import mage.target.TargetPermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -36,7 +36,7 @@ public final class DawningPurist extends CardImpl { // Whenever Dawning Purist deals combat damage to a player, you may destroy target enchantment that player controls. Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new DestroyTargetEffect(), true, true); ability.addTarget(new TargetPermanent(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); // Morph {1}{W} diff --git a/Mage.Sets/src/mage/cards/d/DayOfTheMoon.java b/Mage.Sets/src/mage/cards/d/DayOfTheMoon.java index 249b14f173e..a5d100572d2 100644 --- a/Mage.Sets/src/mage/cards/d/DayOfTheMoon.java +++ b/Mage.Sets/src/mage/cards/d/DayOfTheMoon.java @@ -79,7 +79,7 @@ class DayOfTheMoonEffect extends OneShotEffect { String cardName = cardChoice.getChoice(); List names = getOrSetValue(game, source); names.add(cardName); - names.removeIf(Objects::nonNull); + names.removeIf(Objects::isNull); if (names.isEmpty()) { return true; } diff --git a/Mage.Sets/src/mage/cards/d/DeadeyeBrawler.java b/Mage.Sets/src/mage/cards/d/DeadeyeBrawler.java index 2918f6274b6..c71dc1e5727 100644 --- a/Mage.Sets/src/mage/cards/d/DeadeyeBrawler.java +++ b/Mage.Sets/src/mage/cards/d/DeadeyeBrawler.java @@ -3,7 +3,6 @@ package mage.cards.d; import mage.MageInt; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.condition.common.CitysBlessingCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.hint.common.CitysBlessingHint; import mage.abilities.keyword.AscendAbility; @@ -35,11 +34,8 @@ public final class DeadeyeBrawler extends CardImpl { this.addAbility(new AscendAbility()); // Whenever Deadeye Brawler deals combat damage to a player, if you have the city's blessing, draw a card. - this.addAbility(new ConditionalInterveningIfTriggeredAbility(new DealsCombatDamageToAPlayerTriggeredAbility( - new DrawCardSourceControllerEffect(1), false, false), CitysBlessingCondition.instance, - "Whenever {this} deals combat damage to a player, if you have the city's blessing, draw a card.") - .addHint(CitysBlessingHint.instance)); - + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new DrawCardSourceControllerEffect(1)) + .withInterveningIf(CitysBlessingCondition.instance).addHint(CitysBlessingHint.instance)); } private DeadeyeBrawler(final DeadeyeBrawler card) { diff --git a/Mage.Sets/src/mage/cards/d/DeadeyeRigHauler.java b/Mage.Sets/src/mage/cards/d/DeadeyeRigHauler.java index e8636b36be1..e791f67dbd8 100644 --- a/Mage.Sets/src/mage/cards/d/DeadeyeRigHauler.java +++ b/Mage.Sets/src/mage/cards/d/DeadeyeRigHauler.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.RaidCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.hint.common.RaidHint; import mage.cards.CardImpl; @@ -31,8 +30,7 @@ public final class DeadeyeRigHauler extends CardImpl { this.toughness = new MageInt(2); // Raid— When Deadeye Rig-Hauler enters the battlefield, if you attacked this turn, you may return target creature to its owner's hand. - Ability ability = new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect(), true), RaidCondition.instance, - "When {this} enters, if you attacked this turn, you may return target creature to its owner's hand."); + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect(), true).withInterveningIf(RaidCondition.instance); ability.addTarget(new TargetCreaturePermanent()); ability.setAbilityWord(AbilityWord.RAID); ability.addHint(RaidHint.instance); diff --git a/Mage.Sets/src/mage/cards/d/DeadlyGrub.java b/Mage.Sets/src/mage/cards/d/DeadlyGrub.java index 80e03776e96..7f1134f0253 100644 --- a/Mage.Sets/src/mage/cards/d/DeadlyGrub.java +++ b/Mage.Sets/src/mage/cards/d/DeadlyGrub.java @@ -3,7 +3,6 @@ package mage.cards.d; import mage.MageInt; import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.condition.common.LastTimeCounterRemovedCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.VanishingAbility; import mage.cards.CardImpl; @@ -29,8 +28,7 @@ public final class DeadlyGrub extends CardImpl { this.addAbility(new VanishingAbility(3)); // When Deadly Grub dies, if it had no time counters on it, create a 6/1 green Insect creature token with shroud. - this.addAbility(new ConditionalInterveningIfTriggeredAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new DeadlyGrubInsectToken(), 1)), - LastTimeCounterRemovedCondition.instance, "When {this} dies, if it had no time counters on it, create a 6/1 green Insect creature token with shroud.")); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new DeadlyGrubInsectToken(), 1)).withInterveningIf(LastTimeCounterRemovedCondition.instance)); } private DeadlyGrub(final DeadlyGrub card) { diff --git a/Mage.Sets/src/mage/cards/d/DeathCloud.java b/Mage.Sets/src/mage/cards/d/DeathCloud.java index 048f2bf268c..6944a0bca79 100644 --- a/Mage.Sets/src/mage/cards/d/DeathCloud.java +++ b/Mage.Sets/src/mage/cards/d/DeathCloud.java @@ -31,10 +31,10 @@ public final class DeathCloud extends CardImpl { effect.setText(", discards X cards"); this.getSpellAbility().addEffect(effect); effect = new SacrificeAllEffect(xValue, new FilterControlledCreaturePermanent("creatures")); - effect.setText(", sacrifices X creatures"); + effect.setText(", sacrifices X creatures of their choice"); this.getSpellAbility().addEffect(effect); effect = new SacrificeAllEffect(xValue, new FilterControlledLandPermanent("lands")); - effect.setText(", then sacrifices X lands"); + effect.setText(", then sacrifices X lands of their choice"); this.getSpellAbility().addEffect(effect); } diff --git a/Mage.Sets/src/mage/cards/d/DeathSpark.java b/Mage.Sets/src/mage/cards/d/DeathSpark.java index 7cdca5d2a58..6975eb8aca6 100644 --- a/Mage.Sets/src/mage/cards/d/DeathSpark.java +++ b/Mage.Sets/src/mage/cards/d/DeathSpark.java @@ -1,15 +1,12 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.ReturnSourceFromGraveyardToHandEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -20,8 +17,9 @@ import mage.game.Game; import mage.players.Player; import mage.target.common.TargetAnyTarget; +import java.util.UUID; + /** - * * @author emerald000 */ public final class DeathSpark extends CardImpl { @@ -29,19 +27,15 @@ public final class DeathSpark extends CardImpl { public DeathSpark(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}"); - // Death Spark deals 1 damage to any target. this.getSpellAbility().addEffect(new DamageTargetEffect(1)); this.getSpellAbility().addTarget(new TargetAnyTarget()); // At the beginning of your upkeep, if Death Spark is in your graveyard with a creature card directly above it, you may pay {1}. If you do, return Death Spark to your hand. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility( - Zone.GRAVEYARD, - TargetController.YOU, new DoIfCostPaid(new ReturnSourceFromGraveyardToHandEffect(), new GenericManaCost(1)), - false), - DeathSparkCondition.instance, - "At the beginning of your upkeep, if {this} is in your graveyard with a creature card directly above it, you may pay {1}. If you do, return {this} to your hand.")); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + Zone.GRAVEYARD, TargetController.YOU, + new DoIfCostPaid(new ReturnSourceFromGraveyardToHandEffect(), new GenericManaCost(1)), false + ).withInterveningIf(DeathSparkCondition.instance)); } private DeathSpark(final DeathSpark card) { @@ -55,21 +49,21 @@ public final class DeathSpark extends CardImpl { } enum DeathSparkCondition implements Condition { - instance; @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - boolean nextCard = false; - for (Card card : controller.getGraveyard().getCards(game)) { - if (nextCard) { - return card.isCreature(game); - } - if (card.getId().equals(source.getSourceId())) { - nextCard = true; - } + if (controller == null) { + return false; + } + boolean nextCard = false; + for (Card card : controller.getGraveyard().getCards(game)) { + if (nextCard) { + return card.isCreature(game); + } + if (card.getId().equals(source.getSourceId())) { + nextCard = true; } } return false; diff --git a/Mage.Sets/src/mage/cards/d/DeathbringerRegent.java b/Mage.Sets/src/mage/cards/d/DeathbringerRegent.java index 4e84fd7c6ae..66086b530ac 100644 --- a/Mage.Sets/src/mage/cards/d/DeathbringerRegent.java +++ b/Mage.Sets/src/mage/cards/d/DeathbringerRegent.java @@ -1,27 +1,25 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.CompoundCondition; import mage.abilities.condition.Condition; import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.effects.common.DestroyAllEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.SubType; -import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.AnotherPredicate; -import mage.game.Game; import mage.watchers.common.CastFromHandWatcher; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class DeathbringerRegent extends CardImpl { @@ -32,6 +30,12 @@ public final class DeathbringerRegent extends CardImpl { filter.add(AnotherPredicate.instance); } + private static final Condition condition = new CompoundCondition( + "you cast it from your hand and there are five or more other creatures on the battlefield", + CastFromHandSourcePermanentCondition.instance, + new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 4) + ); + public DeathbringerRegent(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{B}{B}"); this.subtype.add(SubType.DRAGON); @@ -42,11 +46,8 @@ public final class DeathbringerRegent extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Deathbringer Regent enters the battlefield, if you cast it from your hand and there are five or more other creatures on the battlefield, destroy all other creatures. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new DestroyAllEffect(filter), false), - new DeathbringerRegentCondition(), - "When {this} enters, if you cast it from your hand and there are five or more other creatures on the battlefield, destroy all other creatures."), - new CastFromHandWatcher()); + this.addAbility(new EntersBattlefieldTriggeredAbility(new DestroyAllEffect(filter)) + .withInterveningIf(condition), new CastFromHandWatcher()); } private DeathbringerRegent(final DeathbringerRegent card) { @@ -58,12 +59,3 @@ public final class DeathbringerRegent extends CardImpl { return new DeathbringerRegent(this); } } - -class DeathbringerRegentCondition implements Condition { - - @Override - public boolean apply(Game game, Ability source) { - return CastFromHandSourcePermanentCondition.instance.apply(game, source) - && game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), source, game).size() >= 6; - } -} diff --git a/Mage.Sets/src/mage/cards/d/DeathknellBerserker.java b/Mage.Sets/src/mage/cards/d/DeathknellBerserker.java index 5c40296135c..11a3d6a4119 100644 --- a/Mage.Sets/src/mage/cards/d/DeathknellBerserker.java +++ b/Mage.Sets/src/mage/cards/d/DeathknellBerserker.java @@ -1,22 +1,23 @@ package mage.cards.d; -import java.util.UUID; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.ZombieBerserkerToken; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author weirddan455 */ public final class DeathknellBerserker extends CardImpl { @@ -30,11 +31,8 @@ public final class DeathknellBerserker extends CardImpl { this.toughness = new MageInt(2); // When Deathknell Berserker dies, if its power was 3 or greater, create a 2/2 black Zombie Berserker creature token. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new DiesSourceTriggeredAbility(new CreateTokenEffect(new ZombieBerserkerToken())), - DeathknellBerserkerCondtion.instance, - "When {this} dies, if its power was 3 or greater, create a 2/2 black Zombie Berserker creature token." - )); + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new ZombieBerserkerToken())) + .withInterveningIf(DeathknellBerserkerCondtion.instance)); } private DeathknellBerserker(final DeathknellBerserker card) { @@ -53,7 +51,16 @@ enum DeathknellBerserkerCondtion implements Condition { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = (Permanent) source.getEffects().get(0).getValue("permanentLeftBattlefield"); - return permanent != null && permanent.getPower().getValue() >= 3; + return CardUtil + .getEffectValueFromAbility(source, "permanentLeftBattlefield", Permanent.class) + .map(MageObject::getPower) + .map(MageInt::getValue) + .filter(x -> x >= 3) + .isPresent(); + } + + @Override + public String toString() { + return "its power was 3 or greater"; } } diff --git a/Mage.Sets/src/mage/cards/d/DeathmistRaptor.java b/Mage.Sets/src/mage/cards/d/DeathmistRaptor.java index 3726d9e2150..3b3e02f246c 100644 --- a/Mage.Sets/src/mage/cards/d/DeathmistRaptor.java +++ b/Mage.Sets/src/mage/cards/d/DeathmistRaptor.java @@ -58,7 +58,7 @@ class DeathmistRaptorEffect extends OneShotEffect { DeathmistRaptorEffect() { super(Outcome.Benefit); - this.staticText = "you may return {this} from your graveyard to the battlefield face up or face down"; + this.staticText = "you may return this card from your graveyard to the battlefield face up or face down"; } private DeathmistRaptorEffect(final DeathmistRaptorEffect effect) { diff --git a/Mage.Sets/src/mage/cards/d/Deathrender.java b/Mage.Sets/src/mage/cards/d/Deathrender.java index 503576da976..d9bb48dc2ba 100644 --- a/Mage.Sets/src/mage/cards/d/Deathrender.java +++ b/Mage.Sets/src/mage/cards/d/Deathrender.java @@ -1,6 +1,5 @@ package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.DiesAttachedTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; @@ -12,17 +11,19 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInHand; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author Blinke */ public final class Deathrender extends CardImpl { @@ -33,8 +34,10 @@ public final class Deathrender extends CardImpl { // Equipped creature gets +2/+2. this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(2, 2))); + // Whenever equipped creature dies, you may put a creature card from your hand onto the battlefield and attach Deathrender to it. this.addAbility(new DiesAttachedTriggeredAbility(new DeathrenderEffect(), "equipped creature")); + // Equip {2} this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(2), false)); } @@ -68,19 +71,20 @@ class DeathrenderEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (controller != null && sourcePermanent != null) { - TargetCardInHand target = new TargetCardInHand(0, 1, StaticFilters.FILTER_CARD_CREATURE); - if (controller.choose(Outcome.PutCardInPlay, target, source, game)) { - Card creatureInHand = game.getCard(target.getFirstTarget()); - if (creatureInHand != null) { - if (controller.moveCards(creatureInHand, Zone.BATTLEFIELD, source, game)) { - game.getPermanent(creatureInHand.getId()).addAttachment(sourcePermanent.getId(), source, game); - } + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); + if (controller == null || sourcePermanent == null) { + return false; + } + TargetCardInHand target = new TargetCardInHand(0, 1, StaticFilters.FILTER_CARD_CREATURE); + if (controller.choose(Outcome.PutCardInPlay, target, source, game)) { + Card creatureInHand = game.getCard(target.getFirstTarget()); + if (creatureInHand != null && controller.moveCards(creatureInHand, Zone.BATTLEFIELD, source, game)) { + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(creatureInHand, game); + if (permanent != null) { + permanent.addAttachment(sourcePermanent.getId(), source, game); } } - return true; } - return false; + return true; } } diff --git a/Mage.Sets/src/mage/cards/d/DeathsOasis.java b/Mage.Sets/src/mage/cards/d/DeathsOasis.java index c156ca86a39..d55628c7463 100644 --- a/Mage.Sets/src/mage/cards/d/DeathsOasis.java +++ b/Mage.Sets/src/mage/cards/d/DeathsOasis.java @@ -5,8 +5,7 @@ import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.MillCardsControllerEffect; @@ -23,7 +22,6 @@ import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; -import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetCardInYourGraveyard; @@ -41,12 +39,13 @@ public final class DeathsOasis extends CardImpl { // Whenever a nontoken creature you control dies, put the top two cards of your library into your graveyard. Then return a creature card with lesser converted mana cost than the creature that died from the graveyard to your hand. this.addAbility(new DeathsOasisTriggeredAbility()); - // {1}, Sacrifice Death's Oasis: You gain life equal to the greatest converted mana cost among creatures you control. + // {1}, Sacrifice Death's Oasis: You gain life equal to the greatest mana value among creatures you control. Ability ability = new SimpleActivatedAbility( - new GainLifeEffect(DeathsOasisValue.instance) + new GainLifeEffect(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_CREATURES) .setText("you gain life equal to the highest mana value among creatures you control"), new GenericManaCost(1) ); + ability.addHint(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_CREATURES.getHint()); ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); } @@ -132,30 +131,4 @@ class DeathsOasisEffect extends OneShotEffect { } return player.moveCards(game.getCard(target.getFirstTarget()), Zone.HAND, source, game); } -} - -enum DeathsOasisValue implements DynamicValue { - instance; - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - return game - .getBattlefield() - .getAllActivePermanents(sourceAbility.getControllerId()) - .stream() - .filter(permanent -> permanent.isCreature(game)) - .mapToInt(Permanent::getManaValue) - .max() - .orElse(0); - } - - @Override - public DynamicValue copy() { - return this; - } - - @Override - public String getMessage() { - return "1"; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/d/DecoyGambit.java b/Mage.Sets/src/mage/cards/d/DecoyGambit.java index ed151069b49..00e5e21c49d 100644 --- a/Mage.Sets/src/mage/cards/d/DecoyGambit.java +++ b/Mage.Sets/src/mage/cards/d/DecoyGambit.java @@ -14,7 +14,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import java.util.Collection; import java.util.List; @@ -34,7 +34,7 @@ public final class DecoyGambit extends CardImpl { // then return that creature to its owner's hand unless its controller has you draw a card. this.getSpellAbility().addEffect(new DecoyGambitEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(0,1)); - this.getSpellAbility().setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + this.getSpellAbility().setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); } private DecoyGambit(final DecoyGambit card) { diff --git a/Mage.Sets/src/mage/cards/d/DecreeOfSilence.java b/Mage.Sets/src/mage/cards/d/DecreeOfSilence.java index f0a6185f0ba..87e4a4926a5 100644 --- a/Mage.Sets/src/mage/cards/d/DecreeOfSilence.java +++ b/Mage.Sets/src/mage/cards/d/DecreeOfSilence.java @@ -1,14 +1,13 @@ package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.CycleTriggeredAbility; import mage.abilities.common.SpellCastOpponentTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalOneShotEffect; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; @@ -19,33 +18,36 @@ import mage.constants.CardType; import mage.constants.SetTargetPointer; import mage.constants.Zone; import mage.counters.CounterType; -import mage.filter.FilterSpell; import mage.filter.StaticFilters; import mage.target.TargetSpell; +import java.util.UUID; + /** - * * @author LoneFox */ public final class DecreeOfSilence extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.DEPLETION, 3); + public DecreeOfSilence(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{6}{U}{U}"); // Whenever an opponent casts a spell, counter that spell and put a depletion counter on Decree of Silence. If there are three or more depletion counters on Decree of Silence, sacrifice it. - Effect effect = new CounterTargetEffect(); - effect.setText("counter that spell"); - Ability ability = new SpellCastOpponentTriggeredAbility(Zone.BATTLEFIELD, effect, StaticFilters.FILTER_SPELL_A, - false, SetTargetPointer.SPELL); - effect = new AddCountersSourceEffect(CounterType.DEPLETION.createInstance()); - effect.setText("and put a depletion counter on {this}."); - ability.addEffect(effect); - ability.addEffect(new ConditionalOneShotEffect(new SacrificeSourceEffect(), - new SourceHasCounterCondition(CounterType.DEPLETION, 3, Integer.MAX_VALUE), - " If there are three or more depletion counters on {this}, sacrifice it")); + Ability ability = new SpellCastOpponentTriggeredAbility( + Zone.BATTLEFIELD, new CounterTargetEffect().setText("counter that spell"), + StaticFilters.FILTER_SPELL_A, false, SetTargetPointer.SPELL + ); + ability.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance()).concatBy("and")); + ability.addEffect(new ConditionalOneShotEffect( + new SacrificeSourceEffect(), condition, "If there are " + + "three or more depletion counters on {this}, sacrifice it" + )); this.addAbility(ability); + // Cycling {4}{U}{U} this.addAbility(new CyclingAbility(new ManaCostsImpl<>("{4}{U}{U}"))); + // When you cycle Decree of Silence, you may counter target spell. ability = new CycleTriggeredAbility(new CounterTargetEffect(), true); ability.addTarget(new TargetSpell()); diff --git a/Mage.Sets/src/mage/cards/d/DeemInferior.java b/Mage.Sets/src/mage/cards/d/DeemInferior.java index fe76fa21372..2745bc88338 100644 --- a/Mage.Sets/src/mage/cards/d/DeemInferior.java +++ b/Mage.Sets/src/mage/cards/d/DeemInferior.java @@ -80,6 +80,6 @@ class DeemInferiorEffect extends OneShotEffect { )) { return player.putCardOnTopXOfLibrary(permanent, game, source, 2, true); } - return player.putCardsOnBottomOfLibrary(permanent, game, source, false); + return player.putCardsOnBottomOfLibrary(permanent, game, source); } } diff --git a/Mage.Sets/src/mage/cards/d/DefenseOfTheHeart.java b/Mage.Sets/src/mage/cards/d/DefenseOfTheHeart.java index 7c5aa889e76..f3ce8f82c72 100644 --- a/Mage.Sets/src/mage/cards/d/DefenseOfTheHeart.java +++ b/Mage.Sets/src/mage/cards/d/DefenseOfTheHeart.java @@ -1,36 +1,40 @@ package mage.cards.d; -import java.util.Set; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.filter.StaticFilters; -import mage.game.Game; +import mage.filter.common.FilterOpponentsCreaturePermanent; import mage.target.common.TargetCardInLibrary; +import java.util.UUID; + /** - * * @author Plopman */ public final class DefenseOfTheHeart extends CardImpl { + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + new FilterOpponentsCreaturePermanent("an opponent controls three or more creatures"), + ComparisonType.MORE_THAN, 2 + ); + public DefenseOfTheHeart(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}"); // At the beginning of your upkeep, if an opponent controls three or more creatures, sacrifice Defense of the Heart, search your library for up to two creature cards, and put those cards onto the battlefield. Then shuffle your library. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new SacrificeSourceEffect()); - ability.addEffect(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 2, StaticFilters.FILTER_CARD_CREATURE), false)); - DefenseOfTheHeartCondition contition = new DefenseOfTheHeartCondition(); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, contition, "At the beginning of your upkeep, if an opponent controls three or more creatures, sacrifice {this}, search your library for up to two creature cards, put those cards onto the battlefield, then shuffle")); - + Ability ability = new BeginningOfUpkeepTriggeredAbility(new SacrificeSourceEffect()).withInterveningIf(condition); + ability.addEffect(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary( + 2, StaticFilters.FILTER_CARD_CREATURES + )).concatBy(",")); + this.addAbility(ability); } private DefenseOfTheHeart(final DefenseOfTheHeart card) { @@ -41,18 +45,4 @@ public final class DefenseOfTheHeart extends CardImpl { public DefenseOfTheHeart copy() { return new DefenseOfTheHeart(this); } - - static class DefenseOfTheHeartCondition implements Condition { - - @Override - public boolean apply(Game game, Ability source) { - Set opponents = game.getOpponents(source.getControllerId()); - for (UUID uuid : opponents) { - if (game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_CREATURE, uuid, game) >= 3) { - return true; - } - } - return false; - } - } } diff --git a/Mage.Sets/src/mage/cards/d/DeflectingPalm.java b/Mage.Sets/src/mage/cards/d/DeflectingPalm.java index a9f8bdccb89..8d8ed56608d 100644 --- a/Mage.Sets/src/mage/cards/d/DeflectingPalm.java +++ b/Mage.Sets/src/mage/cards/d/DeflectingPalm.java @@ -2,16 +2,15 @@ package mage.cards.d; import mage.abilities.Ability; import mage.abilities.effects.PreventionEffectData; -import mage.abilities.effects.PreventionEffectImpl; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; -import mage.target.TargetSource; +import mage.target.Target; import java.util.UUID; @@ -24,7 +23,12 @@ public final class DeflectingPalm extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}{W}"); // The next time a source of your choice would deal damage to you this turn, prevent that damage. If damage is prevented this way, Deflecting Palm deals that much damage to that source's controller. - this.getSpellAbility().addEffect(new DeflectingPalmEffect()); + this.getSpellAbility().addEffect( + new PreventNextDamageFromChosenSourceEffect( + Duration.EndOfTurn, true, + DeflectingPalmPreventionApplier.instance + ) + ); } private DeflectingPalm(final DeflectingPalm card) { @@ -37,56 +41,24 @@ public final class DeflectingPalm extends CardImpl { } } -class DeflectingPalmEffect extends PreventionEffectImpl { +enum DeflectingPalmPreventionApplier implements PreventNextDamageFromChosenSourceEffect.ApplierOnPrevention { + instance; - private final TargetSource target; - - DeflectingPalmEffect() { - super(Duration.EndOfTurn, Integer.MAX_VALUE, false, false); - this.staticText = "the next time a source of your choice would deal damage to you this turn, " + - "prevent that damage. If damage is prevented this way, " + - "{this} deals that much damage to that source's controller"; - this.target = new TargetSource(); - } - - private DeflectingPalmEffect(final DeflectingPalmEffect effect) { - super(effect); - this.target = effect.target.copy(); - } - - @Override - public DeflectingPalmEffect copy() { - return new DeflectingPalmEffect(this); - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - PreventionEffectData preventionData = preventDamageAction(event, source, game); - this.used = true; - this.discard(); // only one use - if (preventionData.getPreventedDamage() < 1) { - return true; + public boolean apply(PreventionEffectData data, Target target, GameEvent event, Ability source, Game game) { + if (data == null || data.getPreventedDamage() <= 0) { + return false; } + int prevented = data.getPreventedDamage(); UUID objectControllerId = game.getControllerId(target.getFirstTarget()); Player objectController = game.getPlayer(objectControllerId); if (objectController == null) { - return true; + return false; } - objectController.damage(preventionData.getPreventedDamage(), source.getSourceId(), source, game); + objectController.damage(prevented, source.getSourceId(), source, game); return true; } - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - return !this.used - && super.applies(event, source, game) - && event.getTargetId().equals(source.getControllerId()) - && event.getSourceId().equals(target.getFirstTarget()); + public String getText() { + return "If damage is prevented this way, {this} deals that much damage to that source's controller"; } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/d/Defossilize.java b/Mage.Sets/src/mage/cards/d/Defossilize.java index d221733195d..97a3a97adbd 100644 --- a/Mage.Sets/src/mage/cards/d/Defossilize.java +++ b/Mage.Sets/src/mage/cards/d/Defossilize.java @@ -14,6 +14,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; +import mage.util.CardUtil; import java.util.UUID; @@ -66,7 +67,7 @@ class DefossilizeEffect extends OneShotEffect { } player.moveCards(card, Zone.BATTLEFIELD, source, game); game.processAction(); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/d/DelightfulDiscovery.java b/Mage.Sets/src/mage/cards/d/DelightfulDiscovery.java index 742222efb38..756d6f1ceea 100644 --- a/Mage.Sets/src/mage/cards/d/DelightfulDiscovery.java +++ b/Mage.Sets/src/mage/cards/d/DelightfulDiscovery.java @@ -32,7 +32,7 @@ public final class DelightfulDiscovery extends CardImpl { new SpellCostReductionForEachSourceEffect( 1, DelightfulDiscoveryValue.instance ).setCanWorksOnStackOnly(true) - ).setRuleAtTheTop(true).addHint(DelightfulDiscoveryValue.getHint()), new SpellsCastWatcher()); + ).setRuleAtTheTop(true).addHint(DelightfulDiscoveryValue.getHint())); // Scry 2, then draw two cards. this.getSpellAbility().addEffect(new ScryEffect(2, false)); diff --git a/Mage.Sets/src/mage/cards/d/DelinaWildMage.java b/Mage.Sets/src/mage/cards/d/DelinaWildMage.java index 31e0db4035c..8e04bccb92c 100644 --- a/Mage.Sets/src/mage/cards/d/DelinaWildMage.java +++ b/Mage.Sets/src/mage/cards/d/DelinaWildMage.java @@ -57,7 +57,7 @@ class DelinaWildMageEffect extends OneShotEffect { super(Outcome.Benefit); staticText = "choose target creature you control, then roll a d20." + "
    1-14 | Create a tapped and attacking token that's a copy of that creature, " + - "except it's not legendary and it has \"Exile this creature at end of combat.\"" + + "except it's not legendary and it has \"At end of combat, exile this token.\"" + "
    15-20 | Create one of those tokens. You may roll again."; } @@ -81,9 +81,7 @@ class DelinaWildMageEffect extends OneShotEffect { false, 1, true, true ); effect.setIsntLegendary(true); - effect.addAdditionalAbilities(new EndOfCombatTriggeredAbility( - new ExileSourceEffect(), false, "Exile this creature at end of combat." - )); + effect.addAdditionalAbilities(new EndOfCombatTriggeredAbility(new ExileSourceEffect(), false)); effect.setTargetPointer(this.getTargetPointer().copy()); while (true) { int result = player.rollDice(outcome, source, game, 20); diff --git a/Mage.Sets/src/mage/cards/d/DeliveryMoogle.java b/Mage.Sets/src/mage/cards/d/DeliveryMoogle.java new file mode 100644 index 00000000000..f2d46eabd0f --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DeliveryMoogle.java @@ -0,0 +1,51 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.search.SearchLibraryGraveyardPutInHandEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.common.FilterArtifactCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DeliveryMoogle extends CardImpl { + + private static final FilterCard filter = new FilterArtifactCard("an artifact card with mana value 2 or less"); + + static { + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); + } + + public DeliveryMoogle(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.subtype.add(SubType.MOOGLE); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When this creature enters, search your library and/or graveyard for an artifact card with mana value 2 or less, reveal it, and put it into your hand. If you search your library this way, shuffle. + this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryGraveyardPutInHandEffect(filter))); + } + + private DeliveryMoogle(final DeliveryMoogle card) { + super(card); + } + + @Override + public DeliveryMoogle copy() { + return new DeliveryMoogle(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DeluxeDragster.java b/Mage.Sets/src/mage/cards/d/DeluxeDragster.java index eba0ecfca95..b5100fa2b03 100644 --- a/Mage.Sets/src/mage/cards/d/DeluxeDragster.java +++ b/Mage.Sets/src/mage/cards/d/DeluxeDragster.java @@ -20,7 +20,7 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterInstantOrSorceryCard; import mage.filter.predicate.Predicates; import mage.target.common.TargetCardInGraveyard; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -54,7 +54,7 @@ public final class DeluxeDragster extends CardImpl { + ThatSpellGraveyardExileReplacementEffect.RULE_A); Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(effect, false, true); ability.addTarget(new TargetCardInGraveyard(filterCard)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster(true)); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster(true)); this.addAbility(ability); // Crew 2 diff --git a/Mage.Sets/src/mage/cards/d/DemolisherSpawn.java b/Mage.Sets/src/mage/cards/d/DemolisherSpawn.java index 952cff306cb..7fd32cd0fad 100644 --- a/Mage.Sets/src/mage/cards/d/DemolisherSpawn.java +++ b/Mage.Sets/src/mage/cards/d/DemolisherSpawn.java @@ -3,7 +3,6 @@ package mage.cards.d; import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.condition.common.DeliriumCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.dynamicvalue.common.CardTypesInGraveyardCount; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.abilities.keyword.HasteAbility; @@ -37,13 +36,9 @@ public final class DemolisherSpawn extends CardImpl { this.addAbility(HasteAbility.getInstance()); // Delirium -- Whenever Demolisher Spawn attacks, if there are four or more card types among cards in your graveyard, other attacking creatures get +4/+4 until end of turn. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new AttacksTriggeredAbility(new BoostAllEffect( - 4, 4, Duration.EndOfTurn, - StaticFilters.FILTER_ATTACKING_CREATURES, true - )), DeliriumCondition.instance, "Whenever {this} attacks, if there are four or more " + - "card types among cards in your graveyard, other attacking creatures get +4/+4 until end of turn." - ).setAbilityWord(AbilityWord.DELIRIUM).addHint(CardTypesInGraveyardCount.YOU.getHint())); + this.addAbility(new AttacksTriggeredAbility(new BoostAllEffect( + 4, 4, Duration.EndOfTurn, StaticFilters.FILTER_ATTACKING_CREATURES, true + )).withInterveningIf(DeliriumCondition.instance).setAbilityWord(AbilityWord.DELIRIUM).addHint(CardTypesInGraveyardCount.YOU.getHint())); } private DemolisherSpawn(final DemolisherSpawn card) { diff --git a/Mage.Sets/src/mage/cards/d/DemolitionField.java b/Mage.Sets/src/mage/cards/d/DemolitionField.java index b52c289b5e3..5c2837a491a 100644 --- a/Mage.Sets/src/mage/cards/d/DemolitionField.java +++ b/Mage.Sets/src/mage/cards/d/DemolitionField.java @@ -1,7 +1,5 @@ package mage.cards.d; -import java.util.UUID; - import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; @@ -17,19 +15,21 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SuperType; import mage.constants.TargetController; +import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterLandPermanent; import mage.filter.predicate.Predicates; +import mage.target.TargetPermanent; import mage.target.common.TargetCardInLibrary; -import mage.target.common.TargetLandPermanent; + +import java.util.UUID; /** - * * @author weirddan455 */ public final class DemolitionField extends CardImpl { - private static final FilterLandPermanent filter = new FilterLandPermanent("nonbasic land an opponent controls"); + private static final FilterPermanent filter = new FilterLandPermanent("nonbasic land an opponent controls"); static { filter.add(TargetController.OPPONENT.getControllerPredicate()); @@ -56,7 +56,7 @@ public final class DemolitionField extends CardImpl { new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND_A), false, false, true )); - ability.addTarget(new TargetLandPermanent(filter)); + ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/d/DemonWall.java b/Mage.Sets/src/mage/cards/d/DemonWall.java new file mode 100644 index 00000000000..1fb456c0ef4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DemonWall.java @@ -0,0 +1,63 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.SourceHasCounterCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalAsThoughEffect; +import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderSourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.DefenderAbility; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DemonWall extends CardImpl { + + private static final Condition condition = new SourceHasCounterCondition(null); + + public DemonWall(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.DEMON); + this.subtype.add(SubType.WALL); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Defender + this.addAbility(DefenderAbility.getInstance()); + + // Menace + this.addAbility(new MenaceAbility()); + + // As long as this creature has a counter on it, it can attack as though it didn't have defender. + this.addAbility(new SimpleStaticAbility(new ConditionalAsThoughEffect( + new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.WhileOnBattlefield), condition + ).setText("as long as this creature has a counter on it, it can attack as though it didn't have defender"))); + + // {5}{B}: Put two +1/+1 counters on this creature. + this.addAbility(new SimpleActivatedAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance(2)), new ManaCostsImpl<>("{5}{B}") + )); + } + + private DemonWall(final DemonWall card) { + super(card); + } + + @Override + public DemonWall copy() { + return new DemonWall(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DemonicHordes.java b/Mage.Sets/src/mage/cards/d/DemonicHordes.java index 803e2d92d52..5a0ff89ae36 100644 --- a/Mage.Sets/src/mage/cards/d/DemonicHordes.java +++ b/Mage.Sets/src/mage/cards/d/DemonicHordes.java @@ -1,23 +1,20 @@ package mage.cards.d; -import java.util.Locale; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.common.FilterLandPermanent; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; @@ -25,11 +22,12 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; import mage.target.TargetPermanent; -import mage.target.common.TargetLandPermanent; import mage.target.common.TargetOpponent; +import java.util.Locale; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class DemonicHordes extends CardImpl { @@ -97,7 +95,7 @@ class DemonicHordesEffect extends OneShotEffect { if (controller.choose(Outcome.Neutral, choiceOpponent, source, game)) { Player opponent = game.getPlayer(choiceOpponent.getFirstTarget()); if (opponent != null) { - Target chosenLand = new TargetLandPermanent(filterLand); + Target chosenLand = new TargetPermanent(filterLand); chosenLand.withNotTarget(true); if (opponent.chooseTarget(Outcome.Sacrifice, chosenLand, source, game)) { Permanent land = game.getPermanent(chosenLand.getFirstTarget()); diff --git a/Mage.Sets/src/mage/cards/d/DemonicJunker.java b/Mage.Sets/src/mage/cards/d/DemonicJunker.java index 85d6415c796..3133acb7ad8 100644 --- a/Mage.Sets/src/mage/cards/d/DemonicJunker.java +++ b/Mage.Sets/src/mage/cards/d/DemonicJunker.java @@ -1,28 +1,25 @@ package mage.cards.d; -import java.util.*; - import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.constants.Outcome; -import mage.constants.SubType; import mage.abilities.keyword.AffinityForArtifactsAbility; import mage.abilities.keyword.CrewAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.TargetPermanent; -import mage.target.targetadjustment.TargetAdjuster; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; +import java.util.UUID; + /** * * @author Jmlundeen @@ -43,7 +40,9 @@ public final class DemonicJunker extends CardImpl { Ability ability = new EntersBattlefieldTriggeredAbility(new DemonicJunkerEffect() .setTargetPointer(new EachTargetPointer())) .setTriggerPhrase("When this Vehicle enters, "); - this.addAbility(ability.setTargetAdjuster(DemonicJunkerAdjuster.instance)); + ability.addTarget(new TargetCreaturePermanent(0, 1)); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, false)); + this.addAbility(ability); // Crew 2 this.addAbility(new CrewAbility(2)); @@ -60,24 +59,6 @@ public final class DemonicJunker extends CardImpl { } } -enum DemonicJunkerAdjuster implements TargetAdjuster { - instance; - @Override - public void adjustTargets(Ability ability, Game game) { - ability.getTargets().clear(); - for (UUID playerId : game.getState().getPlayersInRange(ability.getControllerId(), game)) { - Player player = game.getPlayer(playerId); - if (player == null) { - continue; - } - String playerName = ability.isControlledBy(playerId) ? "you" : player.getName(); - FilterCreaturePermanent filter = new FilterCreaturePermanent("creature controlled by " + playerName); - filter.add(new ControllerIdPredicate(playerId)); - ability.addTarget(new TargetPermanent(0, 1, filter)); - } - } -} - class DemonicJunkerEffect extends OneShotEffect { DemonicJunkerEffect() { diff --git a/Mage.Sets/src/mage/cards/d/DemonicRising.java b/Mage.Sets/src/mage/cards/d/DemonicRising.java index 8ded22ba9d3..b60875f6ba6 100644 --- a/Mage.Sets/src/mage/cards/d/DemonicRising.java +++ b/Mage.Sets/src/mage/cards/d/DemonicRising.java @@ -1,31 +1,33 @@ - package mage.cards.d; -import java.util.UUID; -import mage.abilities.TriggeredAbility; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; -import mage.abilities.condition.common.CreatureCountCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TargetController; +import mage.constants.ComparisonType; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.permanent.token.DemonToken; +import java.util.UUID; + /** * @author noxx */ public final class DemonicRising extends CardImpl { - private static final String ruleText = "At the beginning of your end step, if you control exactly one creature, create a 5/5 black Demon creature token with flying"; + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + new FilterControlledCreaturePermanent("you control exactly one creature"), + ComparisonType.EQUAL_TO, 1 + ); public DemonicRising(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{B}{B}"); // At the beginning of your end step, if you control exactly one creature, create a 5/5 black Demon creature token with flying. - TriggeredAbility ability = new BeginningOfEndStepTriggeredAbility(new CreateTokenEffect(new DemonToken())); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new CreatureCountCondition(1, TargetController.YOU), ruleText)); + this.addAbility(new BeginningOfEndStepTriggeredAbility(new CreateTokenEffect(new DemonToken())).withInterveningIf(condition)); } private DemonicRising(final DemonicRising card) { diff --git a/Mage.Sets/src/mage/cards/d/DemonicTaskmaster.java b/Mage.Sets/src/mage/cards/d/DemonicTaskmaster.java index d3059c545f2..96bb4c160ac 100644 --- a/Mage.Sets/src/mage/cards/d/DemonicTaskmaster.java +++ b/Mage.Sets/src/mage/cards/d/DemonicTaskmaster.java @@ -20,7 +20,7 @@ import mage.filter.predicate.mageobject.AnotherPredicate; */ public final class DemonicTaskmaster extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("a creature other than Demonic Taskmaster"); + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("a creature other than {this}"); static { filter.add(AnotherPredicate.instance); diff --git a/Mage.Sets/src/mage/cards/d/DenryKlinEditorInChief.java b/Mage.Sets/src/mage/cards/d/DenryKlinEditorInChief.java index 5e2adce6336..2f83f11f11e 100644 --- a/Mage.Sets/src/mage/cards/d/DenryKlinEditorInChief.java +++ b/Mage.Sets/src/mage/cards/d/DenryKlinEditorInChief.java @@ -5,7 +5,6 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.condition.common.SourceHasCountersCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.counter.AddCounterChoiceSourceEffect; import mage.cards.CardImpl; @@ -43,14 +42,9 @@ public class DenryKlinEditorInChief extends CardImpl { // Whenever a nontoken creature you control enters, // if Denry has counters on it, put the same number of each kind of counter on that creature. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldControlledTriggeredAbility( - new DenryKlinEditorInChiefCopyCountersEffect(), - StaticFilters.FILTER_CONTROLLED_CREATURE_NON_TOKEN), - SourceHasCountersCondition.instance, - "Whenever a nontoken creature you control enters, " + - "if Denry has counters on it, put the same number of each kind of counter on that creature.") - ); + this.addAbility(new EntersBattlefieldControlledTriggeredAbility( + new DenryKlinEditorInChiefCopyCountersEffect(), StaticFilters.FILTER_CONTROLLED_CREATURE_NON_TOKEN + ).withInterveningIf(SourceHasCountersCondition.instance)); } @@ -68,6 +62,7 @@ class DenryKlinEditorInChiefCopyCountersEffect extends OneShotEffect { DenryKlinEditorInChiefCopyCountersEffect() { super(Outcome.Benefit); + staticText = "put the same number of each kind of counter on that creature"; } @Override diff --git a/Mage.Sets/src/mage/cards/d/DescendantsPath.java b/Mage.Sets/src/mage/cards/d/DescendantsPath.java index ca765656f63..f4460a25e84 100644 --- a/Mage.Sets/src/mage/cards/d/DescendantsPath.java +++ b/Mage.Sets/src/mage/cards/d/DescendantsPath.java @@ -82,7 +82,7 @@ class DescendantsPathEffect extends OneShotEffect { CardUtil.castSpellWithAttributesForFree(controller, source, game, card); } if (game.getState().getZone(card.getId()) == Zone.LIBRARY) { - controller.putCardsOnBottomOfLibrary(card, game, source, false); + controller.putCardsOnBottomOfLibrary(card, game, source); } return true; } diff --git a/Mage.Sets/src/mage/cards/d/DesecrateReality.java b/Mage.Sets/src/mage/cards/d/DesecrateReality.java index 69f5b8d34a5..ba81cbff43d 100644 --- a/Mage.Sets/src/mage/cards/d/DesecrateReality.java +++ b/Mage.Sets/src/mage/cards/d/DesecrateReality.java @@ -20,7 +20,7 @@ import mage.players.Player; import mage.target.Target; import mage.target.TargetPermanent; import mage.target.common.TargetCardInYourGraveyard; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; import mage.target.targetpointer.FixedTarget; @@ -45,7 +45,7 @@ public final class DesecrateReality extends CardImpl { .setTargetPointer(new EachTargetPointer()) .setText("for each opponent, exile up to one target permanent that player controls with an even mana value.")); this.getSpellAbility().addTarget(new TargetPermanent(0, 1, evenFilter)); - this.getSpellAbility().setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + this.getSpellAbility().setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); // Adamant -- If at least three colorless mana was spent to cast this spell, return a permanent card with an odd mana value from your graveyard to the battlefield. this.getSpellAbility().addEffect(new ConditionalOneShotEffect( diff --git a/Mage.Sets/src/mage/cards/d/DesertsHold.java b/Mage.Sets/src/mage/cards/d/DesertsHold.java index 77e2c8f086c..4253a1dc1d3 100644 --- a/Mage.Sets/src/mage/cards/d/DesertsHold.java +++ b/Mage.Sets/src/mage/cards/d/DesertsHold.java @@ -3,7 +3,6 @@ package mage.cards.d; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.DesertControlledOrGraveyardCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.combat.CantBlockAttackActivateAttachedEffect; @@ -35,11 +34,9 @@ public final class DesertsHold extends CardImpl { this.addAbility(new EnchantAbility(auraTarget)); // When Desert's Hold enters the battlefield, if you control a Desert or there is a Desert card in your graveyard, you gain 3 life. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new GainLifeEffect(3)), - DesertControlledOrGraveyardCondition.instance, "When {this} enters, " + - "if you control a Desert or there is a Desert card in your graveyard, you gain 3 life." - ).addHint(DesertControlledOrGraveyardCondition.getHint())); + this.addAbility(new EntersBattlefieldTriggeredAbility(new GainLifeEffect(3)) + .withInterveningIf(DesertControlledOrGraveyardCondition.instance) + .addHint(DesertControlledOrGraveyardCondition.getHint())); // Enchanted creature can't attack or block, and its activated abilities can't be activated. this.addAbility(new SimpleStaticAbility(new CantBlockAttackActivateAttachedEffect())); diff --git a/Mage.Sets/src/mage/cards/d/DesperateGambit.java b/Mage.Sets/src/mage/cards/d/DesperateGambit.java index 1931ccdc0e3..a33067ba290 100644 --- a/Mage.Sets/src/mage/cards/d/DesperateGambit.java +++ b/Mage.Sets/src/mage/cards/d/DesperateGambit.java @@ -1,10 +1,6 @@ package mage.cards.d; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.PreventionEffectImpl; @@ -14,24 +10,28 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.filter.FilterSource; import mage.game.Game; import mage.game.events.DamageEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.stack.StackObject; -import mage.filter.FilterObject; import mage.players.Player; import mage.target.TargetSource; import mage.util.CardUtil; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + /** - * * @author L_J */ public final class DesperateGambit extends CardImpl { public DesperateGambit(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{R}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}"); // Choose a source you control and flip a coin. If you win the flip, the next time that source would deal damage this turn, it deals double that damage instead. If you lose the flip, the next time it would deal damage this turn, prevent that damage. this.getSpellAbility().addEffect(new DesperateGambitEffect()); @@ -48,7 +48,7 @@ public final class DesperateGambit extends CardImpl { } class DesperateGambitEffect extends PreventionEffectImpl { - + private final TargetSource target; private boolean wonFlip; @@ -122,10 +122,14 @@ class DesperateGambitEffect extends PreventionEffectImpl { } } +/** + * TODO: should not be needed with the proper {@link FilterSource}. + * make sure it works properly with 108.4a if you do + */ class TargetControlledSource extends TargetSource { public TargetControlledSource() { - super(1, 1, new FilterObject("source you control")); + super(1, 1, new FilterSource("source you control")); } private TargetControlledSource(final TargetControlledSource target) { @@ -135,16 +139,16 @@ class TargetControlledSource extends TargetSource { @Override public boolean canChoose(UUID sourceControllerId, Game game) { int count = 0; - for (StackObject stackObject: game.getStack()) { - if (game.getState().getPlayersInRange(sourceControllerId, game).contains(stackObject.getControllerId()) - && Objects.equals(stackObject.getControllerId(), sourceControllerId)) { + for (StackObject stackObject : game.getStack()) { + if (game.getState().getPlayersInRange(sourceControllerId, game).contains(stackObject.getControllerId()) + && Objects.equals(stackObject.getControllerId(), sourceControllerId)) { count++; if (count >= this.minNumberOfTargets) { return true; } } } - for (Permanent permanent: game.getBattlefield().getActivePermanents(sourceControllerId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(sourceControllerId, game)) { if (Objects.equals(permanent.getControllerId(), sourceControllerId)) { count++; if (count >= this.minNumberOfTargets) { @@ -177,13 +181,13 @@ class TargetControlledSource extends TargetSource { @Override public Set possibleTargets(UUID sourceControllerId, Game game) { Set possibleTargets = new HashSet<>(); - for (StackObject stackObject: game.getStack()) { - if (game.getState().getPlayersInRange(sourceControllerId, game).contains(stackObject.getControllerId()) - && Objects.equals(stackObject.getControllerId(), sourceControllerId)) { + for (StackObject stackObject : game.getStack()) { + if (game.getState().getPlayersInRange(sourceControllerId, game).contains(stackObject.getControllerId()) + && Objects.equals(stackObject.getControllerId(), sourceControllerId)) { possibleTargets.add(stackObject.getId()); } } - for (Permanent permanent: game.getBattlefield().getActivePermanents(sourceControllerId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(sourceControllerId, game)) { if (Objects.equals(permanent.getControllerId(), sourceControllerId)) { possibleTargets.add(permanent.getId()); } diff --git a/Mage.Sets/src/mage/cards/d/DestinySpinner.java b/Mage.Sets/src/mage/cards/d/DestinySpinner.java index b520c321f56..d6219c5f349 100644 --- a/Mage.Sets/src/mage/cards/d/DestinySpinner.java +++ b/Mage.Sets/src/mage/cards/d/DestinySpinner.java @@ -51,7 +51,7 @@ public final class DestinySpinner extends CardImpl { // Creature and enchantment spells you control can't be countered. this.addAbility(new SimpleStaticAbility(new CantBeCounteredControlledEffect( - filter, null, Duration.WhileOnBattlefield + filter, Duration.WhileOnBattlefield ))); // {3}{G}: Target land you control becomes an X/X Elemental creature with trample and haste until end of turn, where X is the number of enchantments you control. It's still a land. diff --git a/Mage.Sets/src/mage/cards/d/Detonate.java b/Mage.Sets/src/mage/cards/d/Detonate.java index 042ab4be4c1..0db0424ccf4 100644 --- a/Mage.Sets/src/mage/cards/d/Detonate.java +++ b/Mage.Sets/src/mage/cards/d/Detonate.java @@ -7,6 +7,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.common.FilterArtifactPermanent; +import mage.target.TargetPermanent; import mage.target.common.TargetArtifactPermanent; import mage.target.targetadjustment.XManaValueTargetAdjuster; @@ -22,7 +23,7 @@ public final class Detonate extends CardImpl { // Destroy target artifact with converted mana cost X. It can't be regenerated. Detonate deals X damage to that artifact's controller. this.getSpellAbility().addEffect(new DestroyTargetEffect(true)); - this.getSpellAbility().addTarget(new TargetArtifactPermanent(new FilterArtifactPermanent("artifact with mana value X"))); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactPermanent("artifact with mana value X"))); this.getSpellAbility().addEffect(new DamageTargetControllerEffect(GetXValue.instance, "artifact")); this.getSpellAbility().addTarget(new TargetArtifactPermanent()); this.getSpellAbility().setTargetAdjuster(new XManaValueTargetAdjuster()); diff --git a/Mage.Sets/src/mage/cards/d/DeusOfCalamity.java b/Mage.Sets/src/mage/cards/d/DeusOfCalamity.java index fce48a753a8..9c5a614d177 100644 --- a/Mage.Sets/src/mage/cards/d/DeusOfCalamity.java +++ b/Mage.Sets/src/mage/cards/d/DeusOfCalamity.java @@ -1,7 +1,6 @@ package mage.cards.d; -import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.common.DestroyTargetEffect; @@ -11,22 +10,23 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; +import mage.filter.FilterPermanent; import mage.filter.common.FilterLandPermanent; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.target.Target; -import mage.target.common.TargetLandPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class DeusOfCalamity extends CardImpl { public DeusOfCalamity(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{R/G}{R/G}{R/G}{R/G}{R/G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R/G}{R/G}{R/G}{R/G}{R/G}"); this.subtype.add(SubType.SPIRIT); this.subtype.add(SubType.AVATAR); @@ -74,9 +74,9 @@ class DeusOfCalamityTriggeredAbility extends TriggeredAbilityImpl { if (event.getSourceId().equals(this.getSourceId()) && event.getAmount() > 5 && game.getOpponents(this.getControllerId()).contains(event.getTargetId())) { - FilterLandPermanent filter = new FilterLandPermanent("land of the damaged player"); + FilterPermanent filter = new FilterLandPermanent("land of the damaged player"); filter.add(new ControllerIdPredicate(event.getTargetId())); - Target target = new TargetLandPermanent(filter); + Target target = new TargetPermanent(filter); this.getTargets().clear(); this.addTarget(target); return true; diff --git a/Mage.Sets/src/mage/cards/d/DevotedCaretaker.java b/Mage.Sets/src/mage/cards/d/DevotedCaretaker.java index 40dbee78f9d..28b65b125a1 100644 --- a/Mage.Sets/src/mage/cards/d/DevotedCaretaker.java +++ b/Mage.Sets/src/mage/cards/d/DevotedCaretaker.java @@ -26,7 +26,7 @@ import mage.target.common.TargetControlledPermanent; */ public final class DevotedCaretaker extends CardImpl { - private static final FilterSpell filter = new FilterSpell("instant spells and sorcery spells"); + private static final FilterSpell filter = new FilterSpell("instant spells and from sorcery spells"); static{ filter.add(Predicates.or(CardType.SORCERY.getPredicate(), CardType.INSTANT.getPredicate())); diff --git a/Mage.Sets/src/mage/cards/d/DevouringTendrils.java b/Mage.Sets/src/mage/cards/d/DevouringTendrils.java index 07f16c164de..27b67965980 100644 --- a/Mage.Sets/src/mage/cards/d/DevouringTendrils.java +++ b/Mage.Sets/src/mage/cards/d/DevouringTendrils.java @@ -19,7 +19,6 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; -import mage.target.Target; import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; @@ -44,9 +43,7 @@ public final class DevouringTendrils extends CardImpl { Effect effect = new DamageWithPowerFromOneToAnotherTargetEffect(); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - TargetPermanent target = new TargetPermanent(filter); - target.setTargetTag(2); - this.getSpellAbility().addTarget(target); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); this.getSpellAbility().addEffect(new DevouringTendrilsEffect()); } @@ -79,11 +76,7 @@ class DevouringTendrilsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Target target = source.getTargets().stream() - .filter(t -> t.getTargetTag() == 2) - .findFirst() - .orElseThrow(() -> new IllegalStateException("Expected to find target with tag 2 but found none")); - Permanent permanent = game.getPermanent(target.getFirstTarget()); + Permanent permanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); if (permanent != null) { DelayedTriggeredAbility delayedAbility = new DevouringTendrilsDelayedTriggeredAbility(new MageObjectReference(permanent, game)); game.addDelayedTriggeredAbility(delayedAbility, source); diff --git a/Mage.Sets/src/mage/cards/d/DiamondWeapon.java b/Mage.Sets/src/mage/cards/d/DiamondWeapon.java new file mode 100644 index 00000000000..84cc0093bfb --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DiamondWeapon.java @@ -0,0 +1,51 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.DescendCondition; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.common.PreventCombatDamageToSourceEffect; +import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DiamondWeapon extends CardImpl { + + private static final DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_PERMANENT); + + public DiamondWeapon(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{7}{G}{G}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.ELEMENTAL); + this.power = new MageInt(8); + this.toughness = new MageInt(8); + + // This spell costs {1} less to cast for each permanent card in your graveyard. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue)).addHint(DescendCondition.getHint())); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Immune -- Prevent all combat damage that would be dealt to Diamond Weapon. + this.addAbility(new SimpleStaticAbility(new PreventCombatDamageToSourceEffect(Duration.WhileOnBattlefield)).withFlavorWord("Immune")); + } + + private DiamondWeapon(final DiamondWeapon card) { + super(card); + } + + @Override + public DiamondWeapon copy() { + return new DiamondWeapon(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DiluvianPrimordial.java b/Mage.Sets/src/mage/cards/d/DiluvianPrimordial.java index c3c5422b58a..f00f9bfb707 100644 --- a/Mage.Sets/src/mage/cards/d/DiluvianPrimordial.java +++ b/Mage.Sets/src/mage/cards/d/DiluvianPrimordial.java @@ -19,7 +19,7 @@ import mage.game.Game; import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCardInOpponentsGraveyard; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.FixedTarget; import java.util.UUID; @@ -44,7 +44,7 @@ public final class DiluvianPrimordial extends CardImpl { // When Diluvian Primordial enters the battlefield, for each opponent, you may cast up to one target instant or sorcery card from that player's graveyard without paying its mana cost. If a card cast this way would be put into a graveyard this turn, exile it instead. Ability ability = new EntersBattlefieldTriggeredAbility(new DiluvianPrimordialEffect(), false); ability.addTarget(new TargetCardInOpponentsGraveyard(0, 1, filter)); - ability.setTargetAdjuster(new ForEachOpponentTargetsAdjuster(true)); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(true, true)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/d/DiminisherWitch.java b/Mage.Sets/src/mage/cards/d/DiminisherWitch.java index 2a439102f96..b9420c50703 100644 --- a/Mage.Sets/src/mage/cards/d/DiminisherWitch.java +++ b/Mage.Sets/src/mage/cards/d/DiminisherWitch.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.BargainedCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.CreateRoleAttachedTargetEffect; import mage.abilities.hint.common.BargainCostWasPaidHint; import mage.abilities.keyword.BargainAbility; @@ -34,11 +33,8 @@ public final class DiminisherWitch extends CardImpl { this.addAbility(new BargainAbility()); // When Diminisher Witch enters the battlefield, if it was bargained, create a Cursed Role token attached to target creature an opponent controls. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new CreateRoleAttachedTargetEffect(RoleType.CURSED)), - BargainedCondition.instance, "When {this} enters, if it was bargained, " + - "create a Cursed Role token attached to target creature an opponent controls." - ); + Ability ability = new EntersBattlefieldTriggeredAbility(new CreateRoleAttachedTargetEffect(RoleType.CURSED)) + .withInterveningIf(BargainedCondition.instance); ability.addTarget(new TargetOpponentsCreaturePermanent()); this.addAbility(ability.addHint(BargainCostWasPaidHint.instance)); } diff --git a/Mage.Sets/src/mage/cards/d/DionBahamutsDominant.java b/Mage.Sets/src/mage/cards/d/DionBahamutsDominant.java new file mode 100644 index 00000000000..169722a42e3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DionBahamutsDominant.java @@ -0,0 +1,75 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MyTurnCondition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.ExileAndReturnSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.game.permanent.token.WaylayToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DionBahamutsDominant extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.KNIGHT, ""); + + public DionBahamutsDominant(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.NOBLE); + this.subtype.add(SubType.KNIGHT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + this.secondSideCardClazz = mage.cards.b.BahamutWardenOfLight.class; + + // Dragonfire Dive -- During your turn, Dion and other Knights you control have flying. + Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(FlyingAbility.getInstance()), + MyTurnCondition.instance, "during your turn, {this}" + )); + ability.addEffect(new ConditionalContinuousEffect( + new GainAbilityControlledEffect(FlyingAbility.getInstance(), Duration.WhileControlled, filter), + MyTurnCondition.instance, "and other Knights you control have flying" + )); + this.addAbility(ability.withFlavorWord("Dragonfire Dive")); + + // When Dion enters, create a 2/2 white Knight creature token. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new WaylayToken()))); + + // {4}{W}{W}, {T}: Exile Dion, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery. + this.addAbility(new TransformAbility()); + ability = new ActivateAsSorceryActivatedAbility( + new ExileAndReturnSourceEffect(PutCards.BATTLEFIELD_TRANSFORMED), new ManaCostsImpl<>("{4}{W}{W}") + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private DionBahamutsDominant(final DionBahamutsDominant card) { + super(card); + } + + @Override + public DionBahamutsDominant copy() { + return new DionBahamutsDominant(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DirtyWererat.java b/Mage.Sets/src/mage/cards/d/DirtyWererat.java index 5301799e739..e0ed0f84f33 100644 --- a/Mage.Sets/src/mage/cards/d/DirtyWererat.java +++ b/Mage.Sets/src/mage/cards/d/DirtyWererat.java @@ -43,7 +43,7 @@ public final class DirtyWererat extends CardImpl { // Threshold - As long as seven or more cards are in your graveyard, Dirty Wererat gets +2/+2 and can't block. Ability thresholdAbility = new SimpleStaticAbility(new ConditionalContinuousEffect( new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), - ThresholdCondition.instance, "If seven or more cards are in your graveyard, {this} gets +2/+2" + ThresholdCondition.instance, "As long as seven or more cards are in your graveyard, {this} gets +2/+2" )); thresholdAbility.addEffect(new ConditionalRestrictionEffect( new CantBlockSourceEffect(Duration.WhileOnBattlefield), ThresholdCondition.instance diff --git a/Mage.Sets/src/mage/cards/d/DiscerningFinancier.java b/Mage.Sets/src/mage/cards/d/DiscerningFinancier.java index 6bd7aa7bfd3..a7a004406f0 100644 --- a/Mage.Sets/src/mage/cards/d/DiscerningFinancier.java +++ b/Mage.Sets/src/mage/cards/d/DiscerningFinancier.java @@ -2,18 +2,17 @@ package mage.cards.d; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.OpponentControlsMoreCondition; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.abilities.hint.ConditionHint; import mage.abilities.hint.Hint; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -34,11 +33,11 @@ import java.util.UUID; * @author Susucr */ public final class DiscerningFinancier extends CardImpl { - private static final Condition condition = new OpponentControlsMoreCondition(StaticFilters.FILTER_LAND); + private static final Condition condition = new OpponentControlsMoreCondition(StaticFilters.FILTER_LANDS); private static final Hint hint = new ConditionHint( - condition, "An opponent controls more land than you", null, - "No opponent controls more land than you", null, true + condition, "An opponent controls more lands than you", null, + "No opponent controls more lands than you", null, true ); private static final FilterControlledPermanent filter = @@ -53,14 +52,9 @@ public final class DiscerningFinancier extends CardImpl { this.toughness = new MageInt(3); // At the beginning of your upkeep, if an opponent controls more lands than you, create a Treasure token. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility( - new CreateTokenEffect(new TreasureToken()), false - ), - condition, - "At the beginning of your upkeep, if an opponent controls more lands than you, create a Treasure token." - ).addHint(hint)); - + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new CreateTokenEffect(new TreasureToken()), false + ).withInterveningIf(condition).addHint(hint)); // {2}{W}: Choose another player. That player gains control of target Treasure you control. You draw a card. Ability ability = new SimpleActivatedAbility( diff --git a/Mage.Sets/src/mage/cards/d/DiscordantSpirit.java b/Mage.Sets/src/mage/cards/d/DiscordantSpirit.java new file mode 100644 index 00000000000..eef2f224c88 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DiscordantSpirit.java @@ -0,0 +1,118 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.RemoveAllCountersSourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.constants.WatcherScope; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DiscordantSpirit extends CardImpl { + + public DiscordantSpirit(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{R}"); + + this.subtype.add(SubType.SPIRIT); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // At the beginning of each end step, if it's an opponent's turn, put a +1/+1 counter on this creature for each 1 damage dealt to you this turn. + this.addAbility(new BeginningOfEndStepTriggeredAbility( + TargetController.OPPONENT, + new AddCountersSourceEffect(CounterType.P1P1.createInstance(), DiscordantSpiritValue.instance), + false + ).setTriggerPhrase("At the beginning of each end step, if it's an opponent's turn, ") + .addHint(DiscordantSpiritValue.getHint()), new DiscordantSpiritWatcher()); + + // At the beginning of your end step, remove all +1/+1 counters from this creature. + this.addAbility(new BeginningOfEndStepTriggeredAbility(new RemoveAllCountersSourceEffect(CounterType.P1P1))); + } + + private DiscordantSpirit(final DiscordantSpirit card) { + super(card); + } + + @Override + public DiscordantSpirit copy() { + return new DiscordantSpirit(this); + } +} + +enum DiscordantSpiritValue implements DynamicValue { + instance; + private static final Hint hint = new ValueHint("Damage dealt to you this turn", instance); + + public static Hint getHint() { + return hint; + } + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return DiscordantSpiritWatcher.getCount(game, sourceAbility); + } + + @Override + public DiscordantSpiritValue copy() { + return this; + } + + @Override + public String getMessage() { + return "1 damage dealt to you this turn"; + } + + @Override + public String toString() { + return "1"; + } +} + +class DiscordantSpiritWatcher extends Watcher { + + private final Map map = new HashMap<>(); + + DiscordantSpiritWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.DAMAGED_PLAYER) { + map.compute(event.getTargetId(), (u, i) -> i == null ? event.getAmount() : Integer.sum(i, event.getAmount())); + } + } + + @Override + public void reset() { + super.reset(); + map.clear(); + } + + static int getCount(Game game, Ability source) { + return game + .getState() + .getWatcher(DiscordantSpiritWatcher.class) + .map + .getOrDefault(source.getControllerId(), 0); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DiscoverTheImpossible.java b/Mage.Sets/src/mage/cards/d/DiscoverTheImpossible.java index 5c0e253b2c5..c5ff5e91224 100644 --- a/Mage.Sets/src/mage/cards/d/DiscoverTheImpossible.java +++ b/Mage.Sets/src/mage/cards/d/DiscoverTheImpossible.java @@ -79,7 +79,7 @@ class DiscoverTheImpossibleEffect extends OneShotEffect { player.choose(outcome, cards, target, source, game); Card card = game.getCard(target.getFirstTarget()); if (card == null) { - player.putCardsOnBottomOfLibrary(card, game, source, false); + player.putCardsOnBottomOfLibrary(card, game, source); return true; } player.moveCards(card, Zone.EXILED, source, game); diff --git a/Mage.Sets/src/mage/cards/d/DismantlingWave.java b/Mage.Sets/src/mage/cards/d/DismantlingWave.java index 8a89ae6731d..30bc39bb11e 100644 --- a/Mage.Sets/src/mage/cards/d/DismantlingWave.java +++ b/Mage.Sets/src/mage/cards/d/DismantlingWave.java @@ -10,7 +10,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.StaticFilters; import mage.target.TargetPermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; import java.util.UUID; @@ -28,7 +28,7 @@ public final class DismantlingWave extends CardImpl { .setTargetPointer(new EachTargetPointer()) .setText("For each opponent, destroy up to one target artifact or enchantment that player controls.")); this.getSpellAbility().addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); - this.getSpellAbility().setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + this.getSpellAbility().setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); // Cycling {6}{W}{W} this.addAbility(new CyclingAbility(new ManaCostsImpl<>("{6}{W}{W}"))); diff --git a/Mage.Sets/src/mage/cards/d/DisorientingChoice.java b/Mage.Sets/src/mage/cards/d/DisorientingChoice.java index 29b98c0c493..23c141e82a6 100644 --- a/Mage.Sets/src/mage/cards/d/DisorientingChoice.java +++ b/Mage.Sets/src/mage/cards/d/DisorientingChoice.java @@ -20,7 +20,7 @@ import mage.players.Player; import mage.target.Target; import mage.target.TargetPermanent; import mage.target.common.TargetCardInLibrary; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; /** * @@ -34,7 +34,7 @@ public final class DisorientingChoice extends CardImpl { // For each opponent, choose up to one target artifact or enchantment that player controls. For each permanent chosen this way, its controller may exile it. Then if one or more of the chosen permanents are still on the battlefield, you search your library for up to that many land cards, put them onto the battlefield tapped, then shuffle. this.getSpellAbility().addEffect(new DisorientingChoiceEffect()); this.getSpellAbility().addTarget(new TargetPermanent(0,1, StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); - this.getSpellAbility().setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + this.getSpellAbility().setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); } private DisorientingChoice(final DisorientingChoice card) { diff --git a/Mage.Sets/src/mage/cards/d/DispersalShield.java b/Mage.Sets/src/mage/cards/d/DispersalShield.java index 172ca4223c3..2d20aae1c4d 100644 --- a/Mage.Sets/src/mage/cards/d/DispersalShield.java +++ b/Mage.Sets/src/mage/cards/d/DispersalShield.java @@ -1,9 +1,7 @@ package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.common.HighestManaValueCount; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -13,17 +11,19 @@ import mage.game.Game; import mage.game.stack.Spell; import mage.target.TargetSpell; +import java.util.UUID; + /** - * * @author nigelzor */ public final class DispersalShield extends CardImpl { public DispersalShield(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); // Counter target spell if its converted mana cost is less than or equal to the highest converted mana cost among permanents you control. this.getSpellAbility().addEffect(new DispersalShieldEffect()); + this.getSpellAbility().addHint(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_PERMANENTS.getHint()); this.getSpellAbility().addTarget(new TargetSpell()); } @@ -41,7 +41,7 @@ class DispersalShieldEffect extends OneShotEffect { DispersalShieldEffect() { super(Outcome.Detriment); - staticText = "Counter target spell if its mana value is less than or equal to the highest mana value among permanents you control"; + staticText = "Counter target spell if its mana value is less than or equal to the greatest mana value among permanents you control"; } private DispersalShieldEffect(final DispersalShieldEffect effect) { @@ -55,9 +55,9 @@ class DispersalShieldEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - DynamicValue amount = new HighestManaValueCount(); + int amount = GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_PERMANENTS.calculate(game, source, this); Spell spell = game.getStack().getSpell(getTargetPointer().getFirst(game, source)); - if (spell != null && spell.getManaValue() <= amount.calculate(game, source, this)) { + if (spell != null && spell.getManaValue() <= amount) { return game.getStack().counter(source.getFirstTarget(), source, game); } return false; diff --git a/Mage.Sets/src/mage/cards/d/DistendedMindbender.java b/Mage.Sets/src/mage/cards/d/DistendedMindbender.java index 954e6261132..d12bec48bd7 100644 --- a/Mage.Sets/src/mage/cards/d/DistendedMindbender.java +++ b/Mage.Sets/src/mage/cards/d/DistendedMindbender.java @@ -66,7 +66,7 @@ class DistendedMindbenderEffect extends OneShotEffect { public DistendedMindbenderEffect() { super(Outcome.Discard); this.staticText = "target opponent reveals their hand. " + - "You choose from it a nonland card with mana value 3 or less and a card with mana value 4 or greater." + + "You choose from it a nonland card with mana value 3 or less and a card with mana value 4 or greater. " + "That player discards those cards."; } diff --git a/Mage.Sets/src/mage/cards/d/DivineSacrament.java b/Mage.Sets/src/mage/cards/d/DivineSacrament.java index 3e17d625f4a..10323cef53d 100644 --- a/Mage.Sets/src/mage/cards/d/DivineSacrament.java +++ b/Mage.Sets/src/mage/cards/d/DivineSacrament.java @@ -37,7 +37,7 @@ public final class DivineSacrament extends CardImpl { // Threshold - White creatures get an additional +1/+1 as long as seven or more cards are in your graveyard. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( new BoostAllEffect(1, 1, Duration.WhileOnBattlefield, filter, false), - ThresholdCondition.instance, "If seven or more cards are in your graveyard, white creatures get an additional +1/+1." + ThresholdCondition.instance, "white creatures get an additional +1/+1 as long as seven or more cards are in your graveyard" )).setAbilityWord(AbilityWord.THRESHOLD)); } diff --git a/Mage.Sets/src/mage/cards/d/DivinersPortent.java b/Mage.Sets/src/mage/cards/d/DivinersPortent.java index 4efb58a46f3..ab3c4cbacbc 100644 --- a/Mage.Sets/src/mage/cards/d/DivinersPortent.java +++ b/Mage.Sets/src/mage/cards/d/DivinersPortent.java @@ -6,6 +6,7 @@ import mage.abilities.dynamicvalue.common.GetXValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.RollDieWithResultTableEffect; +import mage.abilities.effects.keyword.ScryEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -35,7 +36,10 @@ public final class DivinersPortent extends CardImpl { effect.addTableEntry(1, 14, new DrawCardSourceControllerEffect(GetXValue.instance)); // 15+ | Scry X, then draw X cards. - effect.addTableEntry(15, Integer.MAX_VALUE, new DivinersPortentEffect()); + effect.addTableEntry(15, Integer.MAX_VALUE, + new ScryEffect(GetXValue.instance), + new DrawCardSourceControllerEffect(GetXValue.instance).concatBy(", then") + ); } private DivinersPortent(final DivinersPortent card) { diff --git a/Mage.Sets/src/mage/cards/d/DodgyJalopy.java b/Mage.Sets/src/mage/cards/d/DodgyJalopy.java index 07c4a72fcec..40d3992ca65 100644 --- a/Mage.Sets/src/mage/cards/d/DodgyJalopy.java +++ b/Mage.Sets/src/mage/cards/d/DodgyJalopy.java @@ -1,12 +1,9 @@ package mage.cards.d; import mage.MageInt; -import mage.MageObject; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.continuous.SetBasePowerSourceEffect; import mage.abilities.keyword.CrewAbility; import mage.abilities.keyword.ScavengeAbility; @@ -16,8 +13,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.StaticFilters; -import mage.game.Game; import java.util.UUID; @@ -36,10 +31,12 @@ public final class DodgyJalopy extends CardImpl { // Trample this.addAbility(TrampleAbility.getInstance()); - // Dodgy Jalopy's power is equal to the highest mana value among creatures you control. + // Dodgy Jalopy's power is equal to the greatest mana value among creatures you control. this.addAbility(new SimpleStaticAbility( - Zone.ALL, new SetBasePowerSourceEffect(DodgyJalopyValue.instance) - )); + Zone.ALL, + new SetBasePowerSourceEffect(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_CREATURES) + .setText("{this}'s power is equal to the greatest mana value among creatures you control.") + ).addHint(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_CREATURES.getHint())); // Crew 3 this.addAbility(new CrewAbility(3)); @@ -56,31 +53,4 @@ public final class DodgyJalopy extends CardImpl { public DodgyJalopy copy() { return new DodgyJalopy(this); } -} - -enum DodgyJalopyValue implements DynamicValue { - instance; - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - return game - .getBattlefield() - .getActivePermanents( - StaticFilters.FILTER_CONTROLLED_CREATURE, - sourceAbility.getControllerId(), sourceAbility, game - ).stream() - .mapToInt(MageObject::getManaValue) - .max() - .orElse(0); - } - - @Override - public DodgyJalopyValue copy() { - return this; - } - - @Override - public String getMessage() { - return "the highest mana value among creatures you control"; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/d/Domestication.java b/Mage.Sets/src/mage/cards/d/Domestication.java index c3cf849cf4e..dec065881ae 100644 --- a/Mage.Sets/src/mage/cards/d/Domestication.java +++ b/Mage.Sets/src/mage/cards/d/Domestication.java @@ -1,52 +1,48 @@ - package mage.cards.d; -import java.util.UUID; +import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.effects.common.continuous.ControlEnchantedEffect; import mage.abilities.keyword.EnchantAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.SubType; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.Optional; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class Domestication extends CardImpl { public Domestication(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}{U}"); this.subtype.add(SubType.AURA); - // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.GainControl)); - Ability ability = new EnchantAbility(auraTarget); - this.addAbility(ability); - + this.addAbility(new EnchantAbility(auraTarget)); + // You control enchanted creature. this.addAbility(new SimpleStaticAbility(new ControlEnchantedEffect())); - + // At the beginning of your end step, if enchanted creature's power is 4 or greater, sacrifice Domestication. - TriggeredAbility ability2 = new BeginningOfEndStepTriggeredAbility(new SacrificeSourceEffect()); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability2, new DomesticationCondition(), "At the beginning of your end step, if enchanted creature's power is 4 or greater, sacrifice {this}")); + this.addAbility(new BeginningOfEndStepTriggeredAbility(new SacrificeSourceEffect()).withInterveningIf(DomesticationCondition.instance)); } private Domestication(final Domestication card) { @@ -59,19 +55,23 @@ public final class Domestication extends CardImpl { } } -class DomesticationCondition implements Condition { - +enum DomesticationCondition implements Condition { + instance; + @Override public boolean apply(Game game, Ability source) { - Permanent enchantment = game.getPermanent(source.getSourceId()); - if (enchantment != null) { - Permanent enchanted = game.getPermanent(enchantment.getAttachedTo()); - if (enchanted != null) { - if (enchanted.getPower().getValue() >= 4) { - return true; - } - } - } - return false; + return Optional + .ofNullable(source.getSourcePermanentIfItStillExists(game)) + .map(Permanent::getAttachedTo) + .map(game::getPermanent) + .map(MageObject::getPower) + .map(MageInt::getValue) + .filter(x -> x >= 4) + .isPresent(); + } + + @Override + public String toString() { + return "enchanted creature's power is 4 or greater"; } } diff --git a/Mage.Sets/src/mage/cards/d/DominatorDrone.java b/Mage.Sets/src/mage/cards/d/DominatorDrone.java index a75ef4545d9..5e0e3ce950a 100644 --- a/Mage.Sets/src/mage/cards/d/DominatorDrone.java +++ b/Mage.Sets/src/mage/cards/d/DominatorDrone.java @@ -2,10 +2,9 @@ package mage.cards.d; import mage.MageInt; -import mage.abilities.TriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.LoseLifeOpponentsEffect; import mage.abilities.keyword.DevoidAbility; import mage.abilities.keyword.IngestAbility; @@ -13,25 +12,27 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.mageobject.ColorlessPredicate; import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.predicate.mageobject.ColorlessPredicate; import java.util.UUID; /** - * * @author LevelX2 */ public final class DominatorDrone extends CardImpl { - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("another colorless creature"); + private static final FilterPermanent filter = new FilterControlledCreaturePermanent("you control another colorless creature"); static { filter.add(AnotherPredicate.instance); filter.add(ColorlessPredicate.instance); } + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + public DominatorDrone(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); this.subtype.add(SubType.ELDRAZI); @@ -46,12 +47,7 @@ public final class DominatorDrone extends CardImpl { this.addAbility(new IngestAbility()); // When Dominator Drone enters the battlefield, if you control another colorless creature, each opponent loses 2 life. - TriggeredAbility triggeredAbility = new EntersBattlefieldTriggeredAbility(new LoseLifeOpponentsEffect(2)); - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - triggeredAbility, - new PermanentsOnTheBattlefieldCondition(filter), - "When {this} enters, if you control another colorless creature, each opponent loses 2 life.")); - + this.addAbility(new EntersBattlefieldTriggeredAbility(new LoseLifeOpponentsEffect(2)).withInterveningIf(condition)); } private DominatorDrone(final DominatorDrone card) { diff --git a/Mage.Sets/src/mage/cards/d/DoomsdayExcruciator.java b/Mage.Sets/src/mage/cards/d/DoomsdayExcruciator.java index 4b3ac468b2a..8df0ed6ae2b 100644 --- a/Mage.Sets/src/mage/cards/d/DoomsdayExcruciator.java +++ b/Mage.Sets/src/mage/cards/d/DoomsdayExcruciator.java @@ -2,18 +2,20 @@ package mage.cards.d; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.CastFromEverywhereSourceCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.FlyingAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.cards.Cards; import mage.cards.CardsImpl; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; import mage.game.Game; import mage.players.Player; @@ -35,16 +37,10 @@ public final class DoomsdayExcruciator extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Doomsday Excruciator enters, if it was cast, each player exiles all but the bottom six cards of their library face down. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new DoomsdayExcruciatorEffect()), - CastFromEverywhereSourceCondition.instance, "When {this} enters, if it was cast, " + - "each player exiles all but the bottom six cards of their library face down." - )); + this.addAbility(new EntersBattlefieldTriggeredAbility(new DoomsdayExcruciatorEffect()).withInterveningIf(CastFromEverywhereSourceCondition.instance)); // At the beginning of your upkeep, draw a card. - this.addAbility(new BeginningOfUpkeepTriggeredAbility( - new DrawCardSourceControllerEffect(1) - )); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new DrawCardSourceControllerEffect(1))); } private DoomsdayExcruciator(final DoomsdayExcruciator card) { @@ -61,6 +57,7 @@ class DoomsdayExcruciatorEffect extends OneShotEffect { DoomsdayExcruciatorEffect() { super(Outcome.Benefit); + staticText = "each player exiles all but the bottom six cards of their library face down"; } private DoomsdayExcruciatorEffect(final DoomsdayExcruciatorEffect effect) { diff --git a/Mage.Sets/src/mage/cards/d/DoorsOfDurin.java b/Mage.Sets/src/mage/cards/d/DoorsOfDurin.java index b7e7095c1e4..c83af125c6a 100644 --- a/Mage.Sets/src/mage/cards/d/DoorsOfDurin.java +++ b/Mage.Sets/src/mage/cards/d/DoorsOfDurin.java @@ -17,6 +17,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -81,7 +82,7 @@ class DoorsOfDurinEffect extends OneShotEffect { return true; } player.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return true; } diff --git a/Mage.Sets/src/mage/cards/d/DoublingChant.java b/Mage.Sets/src/mage/cards/d/DoublingChant.java index d5c1da4dac1..0d3705cf00c 100644 --- a/Mage.Sets/src/mage/cards/d/DoublingChant.java +++ b/Mage.Sets/src/mage/cards/d/DoublingChant.java @@ -67,14 +67,14 @@ class DoublingChantEffect extends OneShotEffect { if (player == null) { return false; } - Set names = game.getBattlefield().getActivePermanents( + List names = game.getBattlefield().getActivePermanents( StaticFilters.FILTER_CONTROLLED_CREATURE, source.getControllerId(), source, game ) .stream() .filter(Objects::nonNull) .map(MageObject::getName) - .collect(Collectors.toSet()); + .collect(Collectors.toList()); TargetCardInLibrary targetCardInLibrary = new DoublingChantTarget(names); player.searchLibrary(targetCardInLibrary, source, game); Cards cards = new CardsImpl(targetCardInLibrary.getTargets()); @@ -88,7 +88,7 @@ class DoublingChantTarget extends TargetCardInLibrary { private final Map nameMap = new HashMap<>(); - DoublingChantTarget(Set names) { + DoublingChantTarget(List names) { super(0, names.size(), makeFilter(names)); this.populateNameMap(names); } @@ -103,13 +103,13 @@ class DoublingChantTarget extends TargetCardInLibrary { return new DoublingChantTarget(this); } - private static FilterCard makeFilter(Set names) { + private static FilterCard makeFilter(List names) { FilterCard filter = new FilterCreatureCard(); filter.add(Predicates.or(names.stream().map(name -> new NamePredicate(name)).collect(Collectors.toSet()))); return filter; } - private void populateNameMap(Set names) { + private void populateNameMap(List names) { names.stream().forEach(name -> this.nameMap.compute(name, CardUtil::setOrIncrementValue)); } diff --git a/Mage.Sets/src/mage/cards/d/DovinsAcuity.java b/Mage.Sets/src/mage/cards/d/DovinsAcuity.java index d9f2fedf31f..07386d65811 100644 --- a/Mage.Sets/src/mage/cards/d/DovinsAcuity.java +++ b/Mage.Sets/src/mage/cards/d/DovinsAcuity.java @@ -4,7 +4,6 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.condition.common.IsMainPhaseCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.ReturnToHandSourceEffect; @@ -20,7 +19,7 @@ import java.util.UUID; */ public final class DovinsAcuity extends CardImpl { - private static final FilterSpell filter = new FilterSpell(); + private static final FilterSpell filter = new FilterSpell("an instant spell"); static { filter.add(CardType.INSTANT.getPredicate()); @@ -35,13 +34,9 @@ public final class DovinsAcuity extends CardImpl { this.addAbility(ability); // Whenever you cast an instant spell during your main phase, you may return Dovin's Acuity to its owner's hand. - this.addAbility(new ConditionalTriggeredAbility( - new SpellCastControllerTriggeredAbility( - new ReturnToHandSourceEffect(true), filter, true - ), IsMainPhaseCondition.YOUR, - "Whenever you cast an instant spell during your main phase, " + - "you may return {this} to its owner's hand." - )); + this.addAbility(new SpellCastControllerTriggeredAbility( + new ReturnToHandSourceEffect(true), filter, true + ).withTriggerCondition(IsMainPhaseCondition.YOUR).setTriggerPhrase("Whenever you cast an instant spell during your main phase, ")); } private DovinsAcuity(final DovinsAcuity card) { @@ -52,4 +47,4 @@ public final class DovinsAcuity extends CardImpl { public DovinsAcuity copy() { return new DovinsAcuity(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/d/DownwindAmbusher.java b/Mage.Sets/src/mage/cards/d/DownwindAmbusher.java index e84ba2f8fb2..97f44727c05 100644 --- a/Mage.Sets/src/mage/cards/d/DownwindAmbusher.java +++ b/Mage.Sets/src/mage/cards/d/DownwindAmbusher.java @@ -11,9 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterOpponentsCreaturePermanent; -import mage.filter.predicate.permanent.WasDealtDamageThisTurnPredicate; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import mage.target.common.TargetOpponentsCreaturePermanent; @@ -24,13 +22,6 @@ import java.util.UUID; */ public final class DownwindAmbusher extends CardImpl { - private static final FilterPermanent filter - = new FilterOpponentsCreaturePermanent("creature an opponent controls that was dealt damage this turn"); - - static { - filter.add(WasDealtDamageThisTurnPredicate.instance); - } - public DownwindAmbusher(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); @@ -48,7 +39,7 @@ public final class DownwindAmbusher extends CardImpl { ability.addTarget(new TargetOpponentsCreaturePermanent()); // * Destroy target creature an opponent controls that was dealt damage this turn. - ability.addMode(new Mode(new DestroyTargetEffect()).addTarget(new TargetPermanent(filter))); + ability.addMode(new Mode(new DestroyTargetEffect()).addTarget(new TargetPermanent(StaticFilters.FILTER_OPPONENTS_CREATURE_DAMAGED_THIS_TURN))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/d/DrafnaFounderOfLatNam.java b/Mage.Sets/src/mage/cards/d/DrafnaFounderOfLatNam.java index 0468bbfe187..caa730ab411 100644 --- a/Mage.Sets/src/mage/cards/d/DrafnaFounderOfLatNam.java +++ b/Mage.Sets/src/mage/cards/d/DrafnaFounderOfLatNam.java @@ -16,7 +16,6 @@ import mage.constants.SuperType; import mage.constants.TargetController; import mage.filter.FilterSpell; import mage.filter.StaticFilters; -import mage.filter.common.FilterArtifactSpell; import mage.target.TargetPermanent; import mage.target.TargetSpell; @@ -27,9 +26,10 @@ import java.util.UUID; */ public final class DrafnaFounderOfLatNam extends CardImpl { - private static final FilterSpell filter = new FilterArtifactSpell("artifact spell you control"); + private static final FilterSpell filter = new FilterSpell("artifact spell you control"); static { + filter.add(CardType.ARTIFACT.getPredicate()); filter.add(TargetController.YOU.getControllerPredicate()); } diff --git a/Mage.Sets/src/mage/cards/d/DragonBreath.java b/Mage.Sets/src/mage/cards/d/DragonBreath.java index 3b5147c563c..5f8c5cfecb8 100644 --- a/Mage.Sets/src/mage/cards/d/DragonBreath.java +++ b/Mage.Sets/src/mage/cards/d/DragonBreath.java @@ -1,27 +1,21 @@ - package mage.cards.d; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAnyReturnSourceFromGraveyardAttachedToItTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ColoredManaCost; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.HasteAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ManaValuePredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -54,7 +48,7 @@ public final class DragonBreath extends CardImpl { this.addAbility(new SimpleActivatedAbility(new BoostEnchantedEffect(1, 0, Duration.EndOfTurn), new ColoredManaCost(ColoredManaSymbol.R))); // When a creature with converted mana cost 6 or greater enters the battlefield, you may return Dragon Breath from your graveyard to the battlefield attached to that creature. - this.addAbility(new EntersBattlefieldAllTriggeredAbility(Zone.GRAVEYARD, new DragonBreathEffect(), filter, true, SetTargetPointer.PERMANENT)); + this.addAbility(new EntersBattlefieldAnyReturnSourceFromGraveyardAttachedToItTriggeredAbility(filter)); } private DragonBreath(final DragonBreath card) { @@ -66,35 +60,3 @@ public final class DragonBreath extends CardImpl { return new DragonBreath(this); } } - -class DragonBreathEffect extends OneShotEffect { - - DragonBreathEffect() { - super(Outcome.Benefit); - this.staticText = "return {this} from your graveyard to the battlefield attached to that creature"; - } - - private DragonBreathEffect(final DragonBreathEffect effect) { - super(effect); - } - - @Override - public DragonBreathEffect copy() { - return new DragonBreathEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Card sourceCard = source.getSourceCardIfItStillExists(game); - Permanent permanent = game.getPermanent(this.getTargetPointer().getFirst(game, source)); - Player controller = game.getPlayer(source.getControllerId()); - if (sourceCard != null && permanent != null && controller != null) { - game.getState().setValue("attachTo:" + sourceCard.getId(), permanent); - if (controller.moveCards(sourceCard, Zone.BATTLEFIELD, source, game)) { - permanent.addAttachment(sourceCard.getId(), source, game); - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/d/DragonFangs.java b/Mage.Sets/src/mage/cards/d/DragonFangs.java index 96de2ba40cf..eebfe8a4327 100644 --- a/Mage.Sets/src/mage/cards/d/DragonFangs.java +++ b/Mage.Sets/src/mage/cards/d/DragonFangs.java @@ -1,28 +1,23 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAnyReturnSourceFromGraveyardAttachedToItTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.TrampleAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ManaValuePredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** * * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) @@ -46,11 +41,13 @@ public final class DragonFangs extends CardImpl { this.addAbility(ability); // Enchanted creature gets +1/+1 and has trample. - this.addAbility(new SimpleStaticAbility(new BoostEnchantedEffect(1, 1, Duration.WhileOnBattlefield))); - this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect(TrampleAbility.getInstance(), AttachmentType.AURA))); + ability = new SimpleStaticAbility(new BoostEnchantedEffect(1, 1, Duration.WhileOnBattlefield)); + ability.addEffect(new GainAbilityAttachedEffect(TrampleAbility.getInstance(), AttachmentType.AURA) + .setText("and has trample")); + this.addAbility(ability); // When a creature with converted mana cost 6 or greater enters the battlefield, you may return Dragon Fangs from your graveyard to the battlefield attached to that creature. - this.addAbility(new EntersBattlefieldAllTriggeredAbility(Zone.GRAVEYARD, new DragonFangsEffect(), filter, true, SetTargetPointer.PERMANENT)); + this.addAbility(new EntersBattlefieldAnyReturnSourceFromGraveyardAttachedToItTriggeredAbility(filter)); } private DragonFangs(final DragonFangs card) { @@ -62,35 +59,3 @@ public final class DragonFangs extends CardImpl { return new DragonFangs(this); } } - -class DragonFangsEffect extends OneShotEffect { - - DragonFangsEffect() { - super(Outcome.Benefit); - this.staticText = "return {this} from your graveyard to the battlefield attached to that creature"; - } - - private DragonFangsEffect(final DragonFangsEffect effect) { - super(effect); - } - - @Override - public DragonFangsEffect copy() { - return new DragonFangsEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Card sourceCard = source.getSourceCardIfItStillExists(game); - Permanent permanent = game.getPermanent(this.getTargetPointer().getFirst(game, source)); - Player controller = game.getPlayer(source.getControllerId()); - if (sourceCard != null && permanent != null && controller != null) { - game.getState().setValue("attachTo:" + sourceCard.getId(), permanent); - if (controller.moveCards(sourceCard, Zone.BATTLEFIELD, source, game)) { - permanent.addAttachment(sourceCard.getId(), source, game); - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/d/DragonScales.java b/Mage.Sets/src/mage/cards/d/DragonScales.java index d8bc4a54e0f..2138f7ef2e3 100644 --- a/Mage.Sets/src/mage/cards/d/DragonScales.java +++ b/Mage.Sets/src/mage/cards/d/DragonScales.java @@ -1,28 +1,23 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAnyReturnSourceFromGraveyardAttachedToItTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.VigilanceAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ManaValuePredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** * * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) @@ -45,12 +40,14 @@ public final class DragonScales extends CardImpl { Ability ability = new EnchantAbility(auraTarget); this.addAbility(ability); - // Enchanted creature gets +1/+2 and has vigilance. - this.addAbility(new SimpleStaticAbility(new BoostEnchantedEffect(1, 2, Duration.WhileOnBattlefield))); - this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect(VigilanceAbility.getInstance(), AttachmentType.AURA))); + // Enchanted creature gets +1/+2 and has vigilance. + ability = new SimpleStaticAbility(new BoostEnchantedEffect(1, 2, Duration.WhileOnBattlefield)); + ability.addEffect(new GainAbilityAttachedEffect(VigilanceAbility.getInstance(), AttachmentType.AURA) + .setText("and has vigilance")); + this.addAbility(ability); // When a creature with converted mana cost 6 or greater enters the battlefield, you may return Dragon Scales from your graveyard to the battlefield attached to that creature. - this.addAbility(new EntersBattlefieldAllTriggeredAbility(Zone.GRAVEYARD, new DragonScalesEffect(), filter, true, SetTargetPointer.PERMANENT)); + this.addAbility(new EntersBattlefieldAnyReturnSourceFromGraveyardAttachedToItTriggeredAbility(filter)); } private DragonScales(final DragonScales card) { @@ -62,35 +59,3 @@ public final class DragonScales extends CardImpl { return new DragonScales(this); } } - -class DragonScalesEffect extends OneShotEffect { - - DragonScalesEffect() { - super(Outcome.Benefit); - this.staticText = "return {this} from your graveyard to the battlefield attached to that creature"; - } - - private DragonScalesEffect(final DragonScalesEffect effect) { - super(effect); - } - - @Override - public DragonScalesEffect copy() { - return new DragonScalesEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Card sourceCard = source.getSourceCardIfItStillExists(game); - Permanent permanent = game.getPermanent(this.getTargetPointer().getFirst(game, source)); - Player controller = game.getPlayer(source.getControllerId()); - if (sourceCard != null && permanent != null && controller != null) { - game.getState().setValue("attachTo:" + sourceCard.getId(), permanent); - if (controller.moveCards(sourceCard, Zone.BATTLEFIELD, source, game)) { - permanent.addAttachment(sourceCard.getId(), source, game); - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/d/DragonShadow.java b/Mage.Sets/src/mage/cards/d/DragonShadow.java index 6b7dc1b3c38..22856333be0 100644 --- a/Mage.Sets/src/mage/cards/d/DragonShadow.java +++ b/Mage.Sets/src/mage/cards/d/DragonShadow.java @@ -1,28 +1,23 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAnyReturnSourceFromGraveyardAttachedToItTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.FearAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ManaValuePredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** * * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) @@ -46,11 +41,13 @@ public final class DragonShadow extends CardImpl { this.addAbility(ability); // Enchanted creature gets +1/+0 and has fear. - this.addAbility(new SimpleStaticAbility(new BoostEnchantedEffect(1, 0, Duration.WhileOnBattlefield))); - this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect(FearAbility.getInstance(), AttachmentType.AURA))); + ability = new SimpleStaticAbility(new BoostEnchantedEffect(1, 0, Duration.WhileOnBattlefield)); + ability.addEffect(new GainAbilityAttachedEffect(FearAbility.getInstance(), AttachmentType.AURA) + .setText("and has fear")); + this.addAbility(ability); // When a creature with converted mana cost 6 or greater enters the battlefield, you may return Dragon Breath from your graveyard to the battlefield attached to that creature. - this.addAbility(new EntersBattlefieldAllTriggeredAbility(Zone.GRAVEYARD, new DragonShadowEffect(), filter, true, SetTargetPointer.PERMANENT)); + this.addAbility(new EntersBattlefieldAnyReturnSourceFromGraveyardAttachedToItTriggeredAbility(filter)); } private DragonShadow(final DragonShadow card) { @@ -62,35 +59,3 @@ public final class DragonShadow extends CardImpl { return new DragonShadow(this); } } - -class DragonShadowEffect extends OneShotEffect { - - DragonShadowEffect() { - super(Outcome.Benefit); - this.staticText = "return {this} from your graveyard to the battlefield attached to that creature"; - } - - private DragonShadowEffect(final DragonShadowEffect effect) { - super(effect); - } - - @Override - public DragonShadowEffect copy() { - return new DragonShadowEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Card sourceCard = source.getSourceCardIfItStillExists(game); - Permanent permanent = game.getPermanent(this.getTargetPointer().getFirst(game, source)); - Player controller = game.getPlayer(source.getControllerId()); - if (sourceCard != null && permanent != null && controller != null) { - game.getState().setValue("attachTo:" + sourceCard.getId(), permanent); - if (controller.moveCards(sourceCard, Zone.BATTLEFIELD, source, game)) { - permanent.addAttachment(sourceCard.getId(), source, game); - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/d/DragonWings.java b/Mage.Sets/src/mage/cards/d/DragonWings.java index b92be8c4d6f..a9de347006c 100644 --- a/Mage.Sets/src/mage/cards/d/DragonWings.java +++ b/Mage.Sets/src/mage/cards/d/DragonWings.java @@ -3,24 +3,19 @@ package mage.cards.d; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAnyReturnSourceFromGraveyardAttachedToItTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.CyclingAbility; import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.FlyingAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ManaValuePredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -52,7 +47,7 @@ public final class DragonWings extends CardImpl { this.addAbility(new CyclingAbility(new ManaCostsImpl<>("{1}{U}"))); // When a creature with converted mana cost 6 or greater enters the battlefield, you may return Dragon Breath from your graveyard to the battlefield attached to that creature. - this.addAbility(new EntersBattlefieldAllTriggeredAbility(Zone.GRAVEYARD, new DragonWingsEffect(), filter, true, SetTargetPointer.PERMANENT)); + this.addAbility(new EntersBattlefieldAnyReturnSourceFromGraveyardAttachedToItTriggeredAbility(filter)); } private DragonWings(final DragonWings card) { @@ -64,35 +59,3 @@ public final class DragonWings extends CardImpl { return new DragonWings(this); } } - -class DragonWingsEffect extends OneShotEffect { - - DragonWingsEffect() { - super(Outcome.Benefit); - this.staticText = "return {this} from your graveyard to the battlefield attached to that creature"; - } - - private DragonWingsEffect(final DragonWingsEffect effect) { - super(effect); - } - - @Override - public DragonWingsEffect copy() { - return new DragonWingsEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Card sourceCard = source.getSourceCardIfItStillExists(game); - Permanent permanent = game.getPermanent(this.getTargetPointer().getFirst(game, source)); - Player controller = game.getPlayer(source.getControllerId()); - if (sourceCard != null && permanent != null && controller != null) { - game.getState().setValue("attachTo:" + sourceCard.getId(), permanent); - if (controller.moveCards(sourceCard, Zone.BATTLEFIELD, source, game)) { - permanent.addAttachment(sourceCard.getId(), source, game); - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/d/DragonmasterOutcast.java b/Mage.Sets/src/mage/cards/d/DragonmasterOutcast.java index 493688e506c..1fc135b7409 100644 --- a/Mage.Sets/src/mage/cards/d/DragonmasterOutcast.java +++ b/Mage.Sets/src/mage/cards/d/DragonmasterOutcast.java @@ -1,38 +1,30 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.CreateTokenEffect; -import mage.abilities.hint.ValueHint; +import mage.abilities.hint.common.LandsYouControlHint; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.ComparisonType; -import mage.filter.FilterPermanent; -import mage.filter.StaticFilters; +import mage.constants.SubType; +import mage.filter.common.FilterLandPermanent; import mage.game.permanent.token.DragonToken2; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class DragonmasterOutcast extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("land"); - - static { - filter.add(CardType.LAND.getPredicate()); - } + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(new FilterLandPermanent("you control six or more lands"), ComparisonType.MORE_THAN, 5); public DragonmasterOutcast(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SHAMAN); @@ -40,11 +32,8 @@ public final class DragonmasterOutcast extends CardImpl { this.toughness = new MageInt(1); // At the beginning of your upkeep, if you control six or more lands, create a 5/5 red Dragon creature token with flying. - TriggeredAbility ability = new BeginningOfUpkeepTriggeredAbility(new CreateTokenEffect(new DragonToken2(), 1)); - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - ability, new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 5), - "At the beginning of your upkeep, if you control six or more lands, create a 5/5 red Dragon creature token with flying." - ).addHint(new ValueHint("Lands you control", new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND)))); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new CreateTokenEffect(new DragonToken2(), 1)) + .withInterveningIf(condition).addHint(LandsYouControlHint.instance)); } private DragonmasterOutcast(final DragonmasterOutcast card) { diff --git a/Mage.Sets/src/mage/cards/d/DranaAndLinvala.java b/Mage.Sets/src/mage/cards/d/DranaAndLinvala.java index 021f6aca581..bf8c376bb13 100644 --- a/Mage.Sets/src/mage/cards/d/DranaAndLinvala.java +++ b/Mage.Sets/src/mage/cards/d/DranaAndLinvala.java @@ -162,12 +162,10 @@ class DranaAndLinvalaManaEffect extends AsThoughEffectImpl implements AsThoughMa return CardUtil .getMainCardId(game, objectId) .equals(source.getSourceId()) - && affectedAbility - .getEffects() - .stream() - .map(effect -> effect.getValue("dranaLinvalaFlag")) - .filter(Boolean.class::isInstance) - .anyMatch(Boolean.class::cast) + && CardUtil + .getEffectValueFromAbility( + affectedAbility, "dranaLinvalaFlag", Boolean.class + ).orElse(false) && source.isControlledBy(playerId); } diff --git a/Mage.Sets/src/mage/cards/d/DreadCacodemon.java b/Mage.Sets/src/mage/cards/d/DreadCacodemon.java index 77e9eb5d708..89279bb0a58 100644 --- a/Mage.Sets/src/mage/cards/d/DreadCacodemon.java +++ b/Mage.Sets/src/mage/cards/d/DreadCacodemon.java @@ -1,11 +1,9 @@ package mage.cards.d; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; +import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.CastFromHandSourcePermanentCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DestroyAllEffect; import mage.abilities.effects.common.TapAllEffect; import mage.cards.CardImpl; @@ -15,23 +13,26 @@ import mage.constants.SubType; import mage.filter.StaticFilters; import mage.watchers.common.CastFromHandWatcher; +import java.util.UUID; + /** * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ public final class DreadCacodemon extends CardImpl { - public DreadCacodemon(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{7}{B}{B}{B}"); + public DreadCacodemon(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{7}{B}{B}{B}"); this.subtype.add(SubType.DEMON); this.power = new MageInt(8); this.toughness = new MageInt(8); // When Dread Cacodemon enters the battlefield, // if you cast it from your hand, destroy all creatures your opponents control, then tap all other creatures you control. - TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new DestroyAllEffect(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURES)); - ability.addEffect(new TapAllEffect(StaticFilters.FILTER_OTHER_CONTROLLED_CREATURES)); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, CastFromHandSourcePermanentCondition.instance, - "When {this} enters, if you cast it from your hand, destroy all creatures your opponents control, then tap all other creatures you control."), new CastFromHandWatcher()); + Ability ability = new EntersBattlefieldTriggeredAbility( + new DestroyAllEffect(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURES) + ).withInterveningIf(CastFromHandSourcePermanentCondition.instance); + ability.addEffect(new TapAllEffect(StaticFilters.FILTER_OTHER_CONTROLLED_CREATURES).concatBy(", then")); + this.addAbility(ability, new CastFromHandWatcher()); } private DreadCacodemon(final DreadCacodemon card) { diff --git a/Mage.Sets/src/mage/cards/d/DreadSlaver.java b/Mage.Sets/src/mage/cards/d/DreadSlaver.java index 8b3cf98e16e..6de44d51cba 100644 --- a/Mage.Sets/src/mage/cards/d/DreadSlaver.java +++ b/Mage.Sets/src/mage/cards/d/DreadSlaver.java @@ -12,8 +12,10 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @author noxx @@ -61,17 +63,18 @@ class DreadSlaverEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { + Card card = game.getCard(getTargetPointer().getFirst(game, source)); + if (controller == null || card == null) { return false; } - Card card = game.getCard(getTargetPointer().getFirst(game, source)); - if (card != null) { - if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { + if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); + if (permanent != null) { ContinuousEffect effect = new AddCreatureTypeAdditionEffect(SubType.ZOMBIE, true); - effect.setTargetPointer(new FixedTarget(card.getId(), game)); + effect.setTargetPointer(new FixedTarget(permanent.getId(), game)); game.addEffect(effect, source); - return true; } + return true; } return false; } diff --git a/Mage.Sets/src/mage/cards/d/DreadmawsIre.java b/Mage.Sets/src/mage/cards/d/DreadmawsIre.java index c5703f64d9d..01c34fc336b 100644 --- a/Mage.Sets/src/mage/cards/d/DreadmawsIre.java +++ b/Mage.Sets/src/mage/cards/d/DreadmawsIre.java @@ -15,7 +15,7 @@ import mage.constants.CardType; import mage.filter.common.FilterArtifactPermanent; import mage.target.TargetPermanent; import mage.target.common.TargetAttackingCreature; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -41,7 +41,7 @@ public final class DreadmawsIre extends CardImpl { effect.setText("have it deal damage equal to its power to target creature that player controls."); Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new DestroyTargetEffect(), false, true); ability.addTarget(new TargetPermanent(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.getSpellAbility().addEffect(new GainAbilityTargetEffect(ability) .setText("and \"Whenever this creature deals combat damage to a player, destroy target artifact that player controls.\"")); diff --git a/Mage.Sets/src/mage/cards/d/DreamSpoilers.java b/Mage.Sets/src/mage/cards/d/DreamSpoilers.java index eb9d1a9f40d..1248ecc0be0 100644 --- a/Mage.Sets/src/mage/cards/d/DreamSpoilers.java +++ b/Mage.Sets/src/mage/cards/d/DreamSpoilers.java @@ -3,8 +3,7 @@ package mage.cards.d; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SpellCastControllerTriggeredAbility; -import mage.abilities.condition.common.OnOpponentsTurnCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.condition.common.OpponentsTurnCondition; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -31,14 +30,11 @@ public final class DreamSpoilers extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - + // Whenever you cast a spell during an opponent's turn, up to one target creature an opponent controls gets -1/-1 until end of turn. - Ability ability = new ConditionalTriggeredAbility( - new SpellCastControllerTriggeredAbility( - new BoostTargetEffect(-1, -1, Duration.EndOfTurn), false - ), OnOpponentsTurnCondition.instance, "Whenever you cast a spell during an opponent's " - + "turn, up to one target creature an opponent controls gets -1/-1 until end of turn." - ); + Ability ability = new SpellCastControllerTriggeredAbility( + new BoostTargetEffect(-1, -1, Duration.EndOfTurn), false + ).withTriggerCondition(OpponentsTurnCondition.instance); ability.addTarget(new TargetOpponentsCreaturePermanent(0, 1)); this.addAbility(ability); } @@ -51,4 +47,4 @@ public final class DreamSpoilers extends CardImpl { public DreamSpoilers copy() { return new DreamSpoilers(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/d/DreamcallerSiren.java b/Mage.Sets/src/mage/cards/d/DreamcallerSiren.java index a0ff63c9091..826b2565f47 100644 --- a/Mage.Sets/src/mage/cards/d/DreamcallerSiren.java +++ b/Mage.Sets/src/mage/cards/d/DreamcallerSiren.java @@ -1,13 +1,12 @@ package mage.cards.d; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; +import mage.abilities.Ability; import mage.abilities.common.CanBlockOnlyFlyingAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.TapTargetEffect; import mage.abilities.keyword.FlashAbility; import mage.abilities.keyword.FlyingAbility; @@ -15,25 +14,26 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.common.TargetNonlandPermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class DreamcallerSiren extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("another Pirate"); + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.PIRATE, "you control another Pirate"); static { - filter.add(SubType.PIRATE.getPredicate()); filter.add(AnotherPredicate.instance); - filter.add(TargetController.YOU.getControllerPredicate()); } + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + public DreamcallerSiren(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{U}"); @@ -52,11 +52,9 @@ public final class DreamcallerSiren extends CardImpl { this.addAbility(new CanBlockOnlyFlyingAbility()); // When Dreamcaller Siren enters the battlefield, if you control another Pirate, tap up to two nonland permanents. - TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new TapTargetEffect()); + Ability ability = new EntersBattlefieldTriggeredAbility(new TapTargetEffect()).withInterveningIf(condition); ability.addTarget(new TargetNonlandPermanent(0, 2, false)); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, - new PermanentsOnTheBattlefieldCondition(filter), - "when {this} enters, if you control another Pirate, tap up to two target nonland permanents.")); + this.addAbility(ability); } private DreamcallerSiren(final DreamcallerSiren card) { diff --git a/Mage.Sets/src/mage/cards/d/Dreamcatcher.java b/Mage.Sets/src/mage/cards/d/Dreamcatcher.java index ac4f20ac4c6..c6c05467045 100644 --- a/Mage.Sets/src/mage/cards/d/Dreamcatcher.java +++ b/Mage.Sets/src/mage/cards/d/Dreamcatcher.java @@ -28,7 +28,7 @@ public final class Dreamcatcher extends CardImpl { // Whenever you cast a Spirit or Arcane spell, you may sacrifice Dreamcatcher. If you do, draw a card. this.addAbility(new SpellCastControllerTriggeredAbility(new DoIfCostPaid( new DrawCardSourceControllerEffect(1), new SacrificeSourceCost() - ), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false)); + ), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, false)); } private Dreamcatcher(final Dreamcatcher card) { diff --git a/Mage.Sets/src/mage/cards/d/DreampodDruid.java b/Mage.Sets/src/mage/cards/d/DreampodDruid.java index f11aafc15a0..5735738308b 100644 --- a/Mage.Sets/src/mage/cards/d/DreampodDruid.java +++ b/Mage.Sets/src/mage/cards/d/DreampodDruid.java @@ -1,12 +1,10 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageInt; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.EnchantedSourceCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -14,12 +12,15 @@ import mage.constants.SubType; import mage.constants.TargetController; import mage.game.permanent.token.SaprolingToken; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class DreampodDruid extends CardImpl { + private static final Condition condition = new EnchantedSourceCondition(); + public DreampodDruid(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); this.subtype.add(SubType.HUMAN); @@ -29,10 +30,9 @@ public final class DreampodDruid extends CardImpl { this.toughness = new MageInt(2); // At the beginning of each upkeep, if Dreampod Druid is enchanted, create a 1/1 green Saproling creature token. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility(TargetController.ANY, new CreateTokenEffect(new SaprolingToken(), 1), false), - new EnchantedSourceCondition(), - "At the beginning of each upkeep, if Dreampod Druid is enchanted, create a 1/1 green Saproling creature token.")); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + TargetController.ANY, new CreateTokenEffect(new SaprolingToken(), 1), false + ).withInterveningIf(condition)); } private DreampodDruid(final DreampodDruid card) { diff --git a/Mage.Sets/src/mage/cards/d/DreamsOfTheDead.java b/Mage.Sets/src/mage/cards/d/DreamsOfTheDead.java index 59b35d7d9ca..521ecc7018a 100644 --- a/Mage.Sets/src/mage/cards/d/DreamsOfTheDead.java +++ b/Mage.Sets/src/mage/cards/d/DreamsOfTheDead.java @@ -25,6 +25,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @@ -82,7 +83,7 @@ class DreamsOfTheDeadEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null && card != null) { if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { - Permanent creature = game.getPermanent(card.getId()); + Permanent creature = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (creature != null) { ContinuousEffect effect = new GainAbilityTargetEffect(new CumulativeUpkeepAbility(new ManaCostsImpl<>("{2}")), Duration.Custom); effect.setTargetPointer(new FixedTarget(creature, game)); diff --git a/Mage.Sets/src/mage/cards/d/DreamspoilerWitches.java b/Mage.Sets/src/mage/cards/d/DreamspoilerWitches.java index eb5e3ce2b9f..6a3a8cc8391 100644 --- a/Mage.Sets/src/mage/cards/d/DreamspoilerWitches.java +++ b/Mage.Sets/src/mage/cards/d/DreamspoilerWitches.java @@ -1,11 +1,9 @@ - package mage.cards.d; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SpellCastControllerTriggeredAbility; -import mage.abilities.condition.common.OnOpponentsTurnCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.condition.common.OpponentsTurnCondition; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -18,13 +16,12 @@ import mage.target.common.TargetCreaturePermanent; import java.util.UUID; /** - * * @author LevelX2 */ public final class DreamspoilerWitches extends CardImpl { public DreamspoilerWitches(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); this.subtype.add(SubType.FAERIE); this.subtype.add(SubType.WIZARD); @@ -33,9 +30,11 @@ public final class DreamspoilerWitches extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); + // Whenever you cast a spell during an opponent's turn, you may have target creature get -1/-1 until end of turn. - Ability ability = new ConditionalTriggeredAbility(new SpellCastControllerTriggeredAbility(new BoostTargetEffect(-1, -1, Duration.EndOfTurn), true), OnOpponentsTurnCondition.instance, - "Whenever you cast a spell during an opponent's turn, you may have target creature get -1/-1 until end of turn."); + Ability ability = new SpellCastControllerTriggeredAbility( + new BoostTargetEffect(-1, -1, Duration.EndOfTurn), true + ).withTriggerCondition(OpponentsTurnCondition.instance); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/d/DrizztDoUrden.java b/Mage.Sets/src/mage/cards/d/DrizztDoUrden.java index 4904c140f5f..fc6d6657afc 100644 --- a/Mage.Sets/src/mage/cards/d/DrizztDoUrden.java +++ b/Mage.Sets/src/mage/cards/d/DrizztDoUrden.java @@ -1,11 +1,11 @@ package mage.cards.d; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.DoubleStrikeAbility; @@ -19,6 +19,7 @@ import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.GuenhwyvarToken; +import mage.util.CardUtil; import java.util.UUID; @@ -43,11 +44,7 @@ public final class DrizztDoUrden extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GuenhwyvarToken()))); // Whenever a creature dies, if it had power greater than Drizzt's power, put a number of +1/+1 counters on Drizzt equal to the difference. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new DiesCreatureTriggeredAbility(new DrizztDoUrdenEffect(), false), - DrizztDoUrdenCondition.instance, "Whenever a creature dies, if it had power greater " + - "than {this}'s power, put a number of +1/+1 counters on {this} equal to the difference." - )); + this.addAbility(new DiesCreatureTriggeredAbility(new DrizztDoUrdenEffect(), false).withInterveningIf(DrizztDoUrdenCondition.instance)); } private DrizztDoUrden(final DrizztDoUrden card) { @@ -66,15 +63,18 @@ enum DrizztDoUrdenCondition implements Condition { @Override public boolean apply(Game game, Ability source) { Permanent sourcePermanent = source.getSourcePermanentOrLKI(game); - Permanent creatureDied = (Permanent) source - .getEffects() - .stream() - .map(effect -> effect.getValue("creatureDied")) - .findFirst() - .orElse(null); return sourcePermanent != null - && creatureDied != null - && creatureDied.getPower().getValue() > sourcePermanent.getPower().getValue(); + && CardUtil + .getEffectValueFromAbility(source, "creatureDied", Permanent.class) + .map(MageObject::getPower) + .map(MageInt::getValue) + .filter(x -> sourcePermanent.getPower().getValue() < x) + .isPresent(); + } + + @Override + public String toString() { + return "it had power greater than {this}'s power"; } } @@ -82,6 +82,7 @@ class DrizztDoUrdenEffect extends OneShotEffect { DrizztDoUrdenEffect() { super(Outcome.Benefit); + staticText = "put a number of +1/+1 counters on {this} equal to the difference"; } private DrizztDoUrdenEffect(final DrizztDoUrdenEffect effect) { diff --git a/Mage.Sets/src/mage/cards/d/DrossGolem.java b/Mage.Sets/src/mage/cards/d/DrossGolem.java index 3a1515721ec..cfc213b6799 100644 --- a/Mage.Sets/src/mage/cards/d/DrossGolem.java +++ b/Mage.Sets/src/mage/cards/d/DrossGolem.java @@ -1,30 +1,30 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageInt; -import mage.abilities.keyword.AffinityForLandTypeAbility; +import mage.abilities.keyword.AffinityAbility; import mage.abilities.keyword.FearAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AffinityType; import mage.constants.CardType; import mage.constants.SubType; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class DrossGolem extends CardImpl { public DrossGolem(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{5}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}"); this.subtype.add(SubType.GOLEM); this.power = new MageInt(3); this.toughness = new MageInt(2); // Affinity for Swamps - this.addAbility(new AffinityForLandTypeAbility(SubType.SWAMP, "Swamps")); - + this.addAbility(new AffinityAbility(AffinityType.SWAMPS)); + // Fear this.addAbility(FearAbility.getInstance()); } diff --git a/Mage.Sets/src/mage/cards/d/DrownerOfTruth.java b/Mage.Sets/src/mage/cards/d/DrownerOfTruth.java index fd085e352f7..8aa6ac54644 100644 --- a/Mage.Sets/src/mage/cards/d/DrownerOfTruth.java +++ b/Mage.Sets/src/mage/cards/d/DrownerOfTruth.java @@ -1,10 +1,8 @@ package mage.cards.d; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.condition.common.ManaWasSpentCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.CastSourceTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.DevoidAbility; @@ -38,12 +36,8 @@ public final class DrownerOfTruth extends ModalDoubleFacedCard { this.getLeftHalfCard().addAbility(new DevoidAbility(this.getLeftHalfCard().getColor())); // When you cast this spell, if {C} was spent to cast it, create two 0/1 colorless Eldrazi Spawn creature tokens with "Sacrifice this creature: Add {C}." - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new CastSourceTriggeredAbility(new CreateTokenEffect(new EldraziSpawnToken(), 2)), - ManaWasSpentCondition.COLORLESS, - "When you cast this spell, if {C} was spent to cast it, " - + "create two 0/1 colorless Eldrazi Spawn creature tokens with \"Sacrifice this creature: Add {C}.\""); - this.getLeftHalfCard().addAbility(ability); + this.getLeftHalfCard().addAbility(new CastSourceTriggeredAbility(new CreateTokenEffect(new EldraziSpawnToken(), 2)) + .withInterveningIf(ManaWasSpentCondition.COLORLESS)); // 2. // Drowned Jungle diff --git a/Mage.Sets/src/mage/cards/d/DrownyardBehemoth.java b/Mage.Sets/src/mage/cards/d/DrownyardBehemoth.java index d5020c06729..058785117b0 100644 --- a/Mage.Sets/src/mage/cards/d/DrownyardBehemoth.java +++ b/Mage.Sets/src/mage/cards/d/DrownyardBehemoth.java @@ -38,7 +38,7 @@ public final class DrownyardBehemoth extends CardImpl { // Drownyard Behemoth has hexproof as long as it entered the battlefield this turn. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( new GainAbilitySourceEffect(HexproofAbility.getInstance(), Duration.WhileOnBattlefield), - SourceEnteredThisTurnCondition.instance, "{this} has hexproof as long as it entered the battlefield this turn" + SourceEnteredThisTurnCondition.DID, "{this} has hexproof as long as it entered the battlefield this turn" ))); } diff --git a/Mage.Sets/src/mage/cards/d/DuelcraftTrainer.java b/Mage.Sets/src/mage/cards/d/DuelcraftTrainer.java index bd4a5bdfc7e..b71fac635c4 100644 --- a/Mage.Sets/src/mage/cards/d/DuelcraftTrainer.java +++ b/Mage.Sets/src/mage/cards/d/DuelcraftTrainer.java @@ -2,16 +2,18 @@ package mage.cards.d; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.abilities.condition.common.CovenCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.hint.common.CovenHint; import mage.abilities.keyword.DoubleStrikeAbility; import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.AbilityWord; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; import mage.target.common.TargetControlledCreaturePermanent; import java.util.UUID; @@ -33,13 +35,9 @@ public final class DuelcraftTrainer extends CardImpl { this.addAbility(FirstStrikeAbility.getInstance()); // Coven — At the beginning of combat on your turn, if you control three or more creatures with different powers, target creature you control gains double strike until end of turn. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new BeginningOfCombatTriggeredAbility(new GainAbilityTargetEffect( - DoubleStrikeAbility.getInstance(), Duration.EndOfTurn - )), CovenCondition.instance, "At the beginning " + - "of combat on your turn, if you control three or more creatures with different powers, " + - "target creature you control gains double strike until end of turn." - ); + Ability ability = new BeginningOfCombatTriggeredAbility( + new GainAbilityTargetEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn) + ).withInterveningIf(CovenCondition.instance); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability.addHint(CovenHint.instance).setAbilityWord(AbilityWord.COVEN)); } diff --git a/Mage.Sets/src/mage/cards/d/DuergarHedgeMage.java b/Mage.Sets/src/mage/cards/d/DuergarHedgeMage.java index b2623d28cb8..9cfae0bab85 100644 --- a/Mage.Sets/src/mage/cards/d/DuergarHedgeMage.java +++ b/Mage.Sets/src/mage/cards/d/DuergarHedgeMage.java @@ -1,40 +1,38 @@ - package mage.cards.d; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.ComparisonType; import mage.constants.SubType; -import mage.filter.common.FilterLandPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.target.common.TargetArtifactPermanent; import mage.target.common.TargetEnchantmentPermanent; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class DuergarHedgeMage extends CardImpl { - private static final FilterLandPermanent filter = new FilterLandPermanent("a Mountain"); - private static final FilterLandPermanent filter2 = new FilterLandPermanent("a Plains"); - - static { - filter.add(SubType.MOUNTAIN.getPredicate()); - filter2.add(SubType.PLAINS.getPredicate()); - } - private static final String rule1 = "When {this} enters, if you control two or more Mountains, you may destroy target artifact."; - private static final String rule2 = "When {this} enters, if you control two or more Plains, you may destroy target enchantment."; + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + new FilterControlledPermanent(SubType.MOUNTAIN, "you control two or more Mountains"), + ComparisonType.MORE_THAN, 1 + ); + private static final Condition condition2 = new PermanentsOnTheBattlefieldCondition( + new FilterControlledPermanent(SubType.PLAINS, "you control two or more Plains"), + ComparisonType.MORE_THAN, 1 + ); public DuergarHedgeMage(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R/W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R/W}"); this.subtype.add(SubType.DWARF); this.subtype.add(SubType.SHAMAN); @@ -42,15 +40,14 @@ public final class DuergarHedgeMage extends CardImpl { this.toughness = new MageInt(2); // When Duergar Hedge-Mage enters the battlefield, if you control two or more Mountains, you may destroy target artifact. - Ability ability = new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), true), new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 1), rule1); + Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), true).withInterveningIf(condition); ability.addTarget(new TargetArtifactPermanent()); this.addAbility(ability); // When Duergar Hedge-Mage enters the battlefield, if you control two or more Plains, you may destroy target enchantment. - Ability ability2 = new ConditionalInterveningIfTriggeredAbility(new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), true), new PermanentsOnTheBattlefieldCondition(filter2, ComparisonType.MORE_THAN, 1), rule2); - ability2.addTarget(new TargetEnchantmentPermanent()); - this.addAbility(ability2); - + ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), true).withInterveningIf(condition2); + ability.addTarget(new TargetEnchantmentPermanent()); + this.addAbility(ability); } private DuergarHedgeMage(final DuergarHedgeMage card) { diff --git a/Mage.Sets/src/mage/cards/d/DunedainRangers.java b/Mage.Sets/src/mage/cards/d/DunedainRangers.java index 2495393c8ec..aab17f5e857 100644 --- a/Mage.Sets/src/mage/cards/d/DunedainRangers.java +++ b/Mage.Sets/src/mage/cards/d/DunedainRangers.java @@ -4,11 +4,9 @@ import mage.MageInt; import mage.abilities.common.LandfallAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.keyword.TheRingTemptsYouEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.ComparisonType; import mage.constants.SubType; @@ -23,15 +21,13 @@ import java.util.UUID; */ public final class DunedainRangers extends CardImpl { - private static final FilterPermanent filter = new FilterControlledPermanent(); + private static final FilterPermanent filter = new FilterControlledPermanent("you don't control a Ring-bearer"); static { filter.add(RingBearerPredicate.instance); } - private static final Condition condition = new PermanentsOnTheBattlefieldCondition( - filter, ComparisonType.EQUAL_TO, 0 - ); + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.EQUAL_TO, 0); public DunedainRangers(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); @@ -42,11 +38,7 @@ public final class DunedainRangers extends CardImpl { this.toughness = new MageInt(4); // Landfall -- Whenever a land you control enters, if you don't control a Ring-bearer, the Ring tempts you. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new LandfallAbility(new TheRingTemptsYouEffect()), - condition, "Whenever a land you control enters, " + - "if you don't control a Ring-bearer, the Ring tempts you." - ).setAbilityWord(AbilityWord.LANDFALL)); + this.addAbility(new LandfallAbility(new TheRingTemptsYouEffect()).withInterveningIf(condition)); } private DunedainRangers(final DunedainRangers card) { diff --git a/Mage.Sets/src/mage/cards/d/DuneriderOutlaw.java b/Mage.Sets/src/mage/cards/d/DuneriderOutlaw.java index 90fcebdf8d6..15038f66b52 100644 --- a/Mage.Sets/src/mage/cards/d/DuneriderOutlaw.java +++ b/Mage.Sets/src/mage/cards/d/DuneriderOutlaw.java @@ -1,32 +1,28 @@ package mage.cards.d; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; -import mage.abilities.TriggeredAbility; -import mage.abilities.common.OnEventTriggeredAbility; import mage.abilities.condition.common.DealtDamageToAnOpponent; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.keyword.ProtectionAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import mage.constants.TargetController; import mage.counters.CounterType; -import mage.game.events.GameEvent; + +import java.util.UUID; /** - * * @author LevelX */ public final class DuneriderOutlaw extends CardImpl { - private static final String ruleText = "At the beginning of each end step, if {this} dealt damage to an opponent this turn, put a +1/+1 counter on it."; - public DuneriderOutlaw(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{B}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{B}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.REBEL); this.subtype.add(SubType.ROGUE); @@ -37,9 +33,14 @@ public final class DuneriderOutlaw extends CardImpl { // Protection from green this.addAbility(ProtectionAbility.from(ObjectColor.GREEN)); + // At the beginning of each end step, if Dunerider Outlaw dealt damage to an opponent this turn, put a +1/+1 counter on it. - TriggeredAbility triggered = new OnEventTriggeredAbility(GameEvent.EventType.END_TURN_STEP_PRE, "beginning of each end step", true, new AddCountersSourceEffect(CounterType.P1P1.createInstance())); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(triggered, new DealtDamageToAnOpponent(), ruleText)); + this.addAbility(new BeginningOfEndStepTriggeredAbility( + TargetController.ANY, + new AddCountersSourceEffect(CounterType.P1P1.createInstance()) + .setText("put a +1/+1 counter on it"), + false, DealtDamageToAnOpponent.instance + )); } private DuneriderOutlaw(final DuneriderOutlaw card) { diff --git a/Mage.Sets/src/mage/cards/d/Duplicant.java b/Mage.Sets/src/mage/cards/d/Duplicant.java index c1a1ebd8a9b..96ba6d2ea3a 100644 --- a/Mage.Sets/src/mage/cards/d/Duplicant.java +++ b/Mage.Sets/src/mage/cards/d/Duplicant.java @@ -94,7 +94,7 @@ class DuplicantContinuousEffect extends ContinuousEffectImpl { DuplicantContinuousEffect() { super(Duration.WhileOnBattlefield, Outcome.BoostCreature); - staticText = "As long as a card exiled with {this} is a creature card, {this} has the power, toughness, and creature types of the last creature card exiled with {this}. It's still a Shapeshifter."; + staticText = "As long as a card exiled with {this} is a creature card, {this} has the power, toughness, and creature types of the last creature card exiled with it. It's still a Shapeshifter."; } private DuplicantContinuousEffect(final DuplicantContinuousEffect effect) { diff --git a/Mage.Sets/src/mage/cards/d/DustToDust.java b/Mage.Sets/src/mage/cards/d/DustToDust.java index 1cdea568ffe..c3d96c41ce4 100644 --- a/Mage.Sets/src/mage/cards/d/DustToDust.java +++ b/Mage.Sets/src/mage/cards/d/DustToDust.java @@ -1,26 +1,24 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.effects.common.ExileTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterArtifactPermanent; import mage.target.common.TargetArtifactPermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class DustToDust extends CardImpl { public DustToDust(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{W}{W}"); // Exile two target artifacts. this.getSpellAbility().addEffect(new ExileTargetEffect()); - this.getSpellAbility().addTarget(new TargetArtifactPermanent(2, 2, new FilterArtifactPermanent("artifacts"), false)); + this.getSpellAbility().addTarget(new TargetArtifactPermanent(2)); } private DustToDust(final DustToDust card) { diff --git a/Mage.Sets/src/mage/cards/d/DwarvenCastleGuard.java b/Mage.Sets/src/mage/cards/d/DwarvenCastleGuard.java new file mode 100644 index 00000000000..1c6b7569fe9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DwarvenCastleGuard.java @@ -0,0 +1,39 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.HeroToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DwarvenCastleGuard extends CardImpl { + + public DwarvenCastleGuard(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.DWARF); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // When this creature dies, create a 1/1 colorless Hero creature token. + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new HeroToken()))); + } + + private DwarvenCastleGuard(final DwarvenCastleGuard card) { + super(card); + } + + @Override + public DwarvenCastleGuard copy() { + return new DwarvenCastleGuard(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DwynensElite.java b/Mage.Sets/src/mage/cards/d/DwynensElite.java index 0e030e313fe..d837c7f1eec 100644 --- a/Mage.Sets/src/mage/cards/d/DwynensElite.java +++ b/Mage.Sets/src/mage/cards/d/DwynensElite.java @@ -1,15 +1,15 @@ package mage.cards.d; import mage.MageInt; -import mage.abilities.TriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.ElfWarriorToken; @@ -21,13 +21,14 @@ import java.util.UUID; */ public final class DwynensElite extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("another Elf"); + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.ELF, "you control another Elf"); static { filter.add(AnotherPredicate.instance); - filter.add(SubType.ELF.getPredicate()); } + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + public DwynensElite(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); this.subtype.add(SubType.ELF); @@ -36,11 +37,7 @@ public final class DwynensElite extends CardImpl { this.toughness = new MageInt(2); // When this creature enters, if you control another Elf, create a 1/1 green Elf Warrior creature token. - TriggeredAbility triggeredAbility = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new ElfWarriorToken())); - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - triggeredAbility, - new PermanentsOnTheBattlefieldCondition(filter), - "When this creature enters, if you control another Elf, create a 1/1 green Elf Warrior creature token.")); + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new ElfWarriorToken())).withInterveningIf(condition)); } private DwynensElite(final DwynensElite card) { diff --git a/Mage.Sets/src/mage/cards/e/EDELonesomeEyebot.java b/Mage.Sets/src/mage/cards/e/EDELonesomeEyebot.java index b98a5f729ea..fa0c05fd665 100644 --- a/Mage.Sets/src/mage/cards/e/EDELonesomeEyebot.java +++ b/Mage.Sets/src/mage/cards/e/EDELonesomeEyebot.java @@ -1,7 +1,5 @@ package mage.cards.e; -import java.util.UUID; - import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; @@ -9,24 +7,25 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.condition.Condition; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.IntPlusDynamicValue; import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.constants.SubType; -import mage.constants.SuperType; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.UUID; + /** * @author Cguy7777 */ @@ -45,12 +44,10 @@ public final class EDELonesomeEyebot extends CardImpl { // ED-E My Love -- Whenever you attack, if the number of attacking creatures is greater than the number of quest counters on // ED-E, Lonesome Eyebot, put a quest counter on it. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new AttacksWithCreaturesTriggeredAbility(new AddCountersSourceEffect(CounterType.QUEST.createInstance()), 1), - EDELonesomeEyebotCondition.instance, - "Whenever you attack, if the number of attacking creatures is " + - "greater than the number of quest counters on {this}, put a quest counter on it." - ).withFlavorWord("ED-E My Love")); + this.addAbility(new AttacksWithCreaturesTriggeredAbility( + new AddCountersSourceEffect(CounterType.QUEST.createInstance()) + .setText("put a quest counter on it"), 1 + ).withInterveningIf(EDELonesomeEyebotCondition.instance).withFlavorWord("ED-E My Love")); // {2}, Sacrifice ED-E: Draw a card, then draw an additional card for each quest counter on ED-E. Ability ability = new SimpleActivatedAbility( @@ -81,10 +78,12 @@ enum EDELonesomeEyebotCondition implements Condition { @Override public boolean apply(Game game, Ability source) { Permanent permanent = source.getSourcePermanentIfItStillExists(game); - if (permanent == null) { - return false; - } + return permanent != null + && attackingCreatureCount.calculate(game, source, null) > permanent.getCounters(game).getCount(CounterType.QUEST); + } - return attackingCreatureCount.calculate(game, source, null) > permanent.getCounters(game).getCount(CounterType.QUEST); + @Override + public String toString() { + return "the number of attacking creatures is greater than the number of quest counters on {this}"; } } diff --git a/Mage.Sets/src/mage/cards/e/Earthlink.java b/Mage.Sets/src/mage/cards/e/Earthlink.java index 3d33712a78b..5f770afb5a4 100644 --- a/Mage.Sets/src/mage/cards/e/Earthlink.java +++ b/Mage.Sets/src/mage/cards/e/Earthlink.java @@ -1,25 +1,18 @@ package mage.cards.e; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.SacrificeEffect; import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.SetTargetPointer; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; /** * @@ -34,7 +27,9 @@ public final class Earthlink extends CardImpl { this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SacrificeSourceUnlessPaysEffect(new ManaCostsImpl<>("{2}")))); // Whenever a creature dies, that creature's controller sacrifices a land. - this.addAbility(new DiesCreatureTriggeredAbility(new EarthlinkEffect(), false, false, true)); + this.addAbility(new DiesCreatureTriggeredAbility( + new SacrificeEffect(StaticFilters.FILTER_LAND, 1, "that creature's controller"), + SetTargetPointer.PLAYER)); } private Earthlink(final Earthlink card) { @@ -46,34 +41,3 @@ public final class Earthlink extends CardImpl { return new Earthlink(this); } } - -class EarthlinkEffect extends OneShotEffect { - - EarthlinkEffect() { - super(Outcome.DrawCard); - this.staticText = "that creature's controller sacrifices a land"; - } - - private EarthlinkEffect(final EarthlinkEffect effect) { - super(effect); - } - - @Override - public EarthlinkEffect copy() { - return new EarthlinkEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = (Permanent) game.getLastKnownInformation(this.getTargetPointer().getFirst(game, source), Zone.BATTLEFIELD); - if (permanent != null) { - Player controller = game.getPlayer(permanent.getControllerId()); - if (controller != null) { - Effect effect = new SacrificeEffect(StaticFilters.FILTER_LAND, 1, "that creature's controller"); - effect.setTargetPointer(new FixedTarget(controller.getId(), game)); - effect.apply(game, source); - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/e/Earthshaker.java b/Mage.Sets/src/mage/cards/e/Earthshaker.java index f1d7f811176..059c8d14fdb 100644 --- a/Mage.Sets/src/mage/cards/e/Earthshaker.java +++ b/Mage.Sets/src/mage/cards/e/Earthshaker.java @@ -34,7 +34,7 @@ public final class Earthshaker extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(5); // Whenever you cast a Spirit or Arcane spell, Earthshaker deals 2 damage to each creature without flying. - this.addAbility(new SpellCastControllerTriggeredAbility(new DamageAllEffect(StaticValue.get(2) , creatureFilter), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new DamageAllEffect(StaticValue.get(2) , creatureFilter), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, false)); } private Earthshaker(final Earthshaker card) { diff --git a/Mage.Sets/src/mage/cards/e/EarwigSquad.java b/Mage.Sets/src/mage/cards/e/EarwigSquad.java index 5e4e6a2a4b5..fa1ed4663f9 100644 --- a/Mage.Sets/src/mage/cards/e/EarwigSquad.java +++ b/Mage.Sets/src/mage/cards/e/EarwigSquad.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.ProwlCostWasPaidCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.search.SearchLibraryAndExileTargetEffect; import mage.abilities.hint.common.ProwlCostWasPaidHint; import mage.abilities.keyword.ProwlAbility; @@ -33,16 +32,11 @@ public final class EarwigSquad extends CardImpl { this.addAbility(new ProwlAbility("{2}{B}")); // When Earwig Squad enters the battlefield, if its prowl cost was paid, search target opponent's library for three cards and exile them. Then that player shuffles their library. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility( - new SearchLibraryAndExileTargetEffect(3, true), false - ), ProwlCostWasPaidCondition.instance, "When {this} enters, " + - "if its prowl cost was paid, search target opponent's library for three cards " + - "and exile them. Then that player shuffles." - ); + Ability ability = new EntersBattlefieldTriggeredAbility( + new SearchLibraryAndExileTargetEffect(3, true), false + ).withInterveningIf(ProwlCostWasPaidCondition.instance); ability.addTarget(new TargetOpponent()); this.addAbility(ability.addHint(ProwlCostWasPaidHint.instance)); - } private EarwigSquad(final EarwigSquad card) { diff --git a/Mage.Sets/src/mage/cards/e/EccentricApprentice.java b/Mage.Sets/src/mage/cards/e/EccentricApprentice.java index ebd24d9c3e4..97625efcfb2 100644 --- a/Mage.Sets/src/mage/cards/e/EccentricApprentice.java +++ b/Mage.Sets/src/mage/cards/e/EccentricApprentice.java @@ -2,14 +2,13 @@ package mage.cards.e; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.CompletedDungeonCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.keyword.VentureIntoTheDungeonEffect; import mage.abilities.hint.common.CurrentDungeonHint; import mage.abilities.keyword.FlyingAbility; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -41,13 +40,8 @@ public final class EccentricApprentice extends CardImpl { .addHint(CurrentDungeonHint.instance)); // At the beginning of combat on your turn, if you've completed a dungeon, up to one target creature becomes a Bird with base power and toughness 1/1 and flying until end of turn. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new BeginningOfCombatTriggeredAbility( - new EccentricApprenticeEffect() - ), CompletedDungeonCondition.instance, "At the beginning of combat on your turn, " + - "if you've completed a dungeon, up to one target creature becomes a Bird " + - "with base power and toughness 1/1 and flying until end of turn." - ).addHint(CompletedDungeonCondition.getHint()); + Ability ability = new BeginningOfCombatTriggeredAbility(new EccentricApprenticeEffect()) + .withInterveningIf(CompletedDungeonCondition.instance).addHint(CompletedDungeonCondition.getHint()); ability.addTarget(new TargetCreaturePermanent(0, 1)); this.addAbility(ability, new CompletedDungeonWatcher()); } @@ -66,6 +60,7 @@ class EccentricApprenticeEffect extends ContinuousEffectImpl { EccentricApprenticeEffect() { super(Duration.EndOfTurn, Outcome.Benefit); + staticText = "up to one target creature becomes a Bird with base power and toughness 1/1 and flying until end of turn"; } private EccentricApprenticeEffect(final EccentricApprenticeEffect effect) { diff --git a/Mage.Sets/src/mage/cards/e/EdenSeatOfTheSanctum.java b/Mage.Sets/src/mage/cards/e/EdenSeatOfTheSanctum.java new file mode 100644 index 00000000000..a0eef76e73e --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EdenSeatOfTheSanctum.java @@ -0,0 +1,62 @@ +package mage.cards.e; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DoWhenCostPaid; +import mage.abilities.effects.common.MillCardsControllerEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.mana.ColorlessManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.common.FilterPermanentCard; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EdenSeatOfTheSanctum extends CardImpl { + + private static final FilterCard filter = new FilterPermanentCard("another target permanent card from your graveyard"); + + static { + filter.add(AnotherPredicate.instance); + } + + public EdenSeatOfTheSanctum(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.subtype.add(SubType.TOWN); + + // {T}: Add {C}. + this.addAbility(new ColorlessManaAbility()); + + // {5}, {T}: Mill two cards. Then you may sacrifice this land. When you do, return another target permanent card from your graveyard to your hand. + Ability ability = new SimpleActivatedAbility(new MillCardsControllerEffect(2), new GenericManaCost(5)); + ability.addCost(new TapSourceCost()); + ReflexiveTriggeredAbility rAbility = new ReflexiveTriggeredAbility( + new ReturnFromGraveyardToHandTargetEffect(), false + ); + rAbility.addTarget(new TargetCardInYourGraveyard(filter)); + ability.addEffect(new DoWhenCostPaid(rAbility, new SacrificeSourceCost(), "Sacrifice this land?").concatBy("Then")); + this.addAbility(ability); + } + + private EdenSeatOfTheSanctum(final EdenSeatOfTheSanctum card) { + super(card); + } + + @Override + public EdenSeatOfTheSanctum copy() { + return new EdenSeatOfTheSanctum(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EdgarKingOfFigaro.java b/Mage.Sets/src/mage/cards/e/EdgarKingOfFigaro.java new file mode 100644 index 00000000000..00a3b337c9c --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EdgarKingOfFigaro.java @@ -0,0 +1,121 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.ArtifactYouControlCount; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.hint.common.ArtifactYouControlHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.FlipCoinsEvent; +import mage.game.events.GameEvent; +import mage.watchers.Watcher; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EdgarKingOfFigaro extends CardImpl { + + public EdgarKingOfFigaro(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ARTIFICER); + this.subtype.add(SubType.NOBLE); + this.power = new MageInt(4); + this.toughness = new MageInt(5); + + // When Edgar enters, draw a card for each artifact you control. + this.addAbility(new EntersBattlefieldTriggeredAbility( + new DrawCardSourceControllerEffect(ArtifactYouControlCount.instance) + ).addHint(ArtifactYouControlHint.instance)); + + // Two-Headed Coin -- The first time you flip one or more coins each turn, those coins come up heads and you win those flips. + this.addAbility(new SimpleStaticAbility(new EdgarKingOfFigaroEffect()) + .withFlavorWord("Two-Headed Coin"), new EdgarKingOfFigaroWatcher()); + } + + private EdgarKingOfFigaro(final EdgarKingOfFigaro card) { + super(card); + } + + @Override + public EdgarKingOfFigaro copy() { + return new EdgarKingOfFigaro(this); + } +} + +class EdgarKingOfFigaroEffect extends ReplacementEffectImpl { + + EdgarKingOfFigaroEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "the first time you flip one or more coins each turn, " + + "those coins come up heads and you win those flips"; + } + + private EdgarKingOfFigaroEffect(final EdgarKingOfFigaroEffect effect) { + super(effect); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + ((FlipCoinsEvent) event).setHeadsAndWon(true); + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.FLIP_COINS; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.isControlledBy(event.getPlayerId()) + && !EdgarKingOfFigaroWatcher.checkPlayer(game, source); + } + + @Override + public EdgarKingOfFigaroEffect copy() { + return new EdgarKingOfFigaroEffect(this); + } +} + +class EdgarKingOfFigaroWatcher extends Watcher { + + private final Set set = new HashSet<>(); + + EdgarKingOfFigaroWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.COIN_FLIPPED) { + set.add(event.getPlayerId()); + } + } + + @Override + public void reset() { + super.reset(); + set.clear(); + } + + static boolean checkPlayer(Game game, Ability source) { + return game + .getState() + .getWatcher(EdgarKingOfFigaroWatcher.class) + .set + .contains(source.getControllerId()); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EdgarMarkov.java b/Mage.Sets/src/mage/cards/e/EdgarMarkov.java index 998b482e9fc..095286e69b7 100644 --- a/Mage.Sets/src/mage/cards/e/EdgarMarkov.java +++ b/Mage.Sets/src/mage/cards/e/EdgarMarkov.java @@ -2,11 +2,9 @@ package mage.cards.e; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.condition.common.SourceOnBattlefieldOrCommandZoneCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.counter.AddCountersAllEffect; import mage.abilities.keyword.FirstStrikeAbility; @@ -44,15 +42,10 @@ public final class EdgarMarkov extends CardImpl { this.toughness = new MageInt(4); // Eminence - Whenever you cast another Vampire spell, if Edgar Markov is in the command zone or on the battlefield, create a 1/1 black Vampire creature token. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new SpellCastControllerTriggeredAbility( - Zone.ALL, new CreateTokenEffect(new EdgarMarkovToken()), - filter2, false, SetTargetPointer.NONE - ), - SourceOnBattlefieldOrCommandZoneCondition.instance, - "Whenever you cast another Vampire spell, if {this} is in the command zone or on the battlefield, create a 1/1 black Vampire creature token."); - ability.setAbilityWord(AbilityWord.EMINENCE); - this.addAbility(ability); + this.addAbility(new SpellCastControllerTriggeredAbility( + Zone.ALL, new CreateTokenEffect(new EdgarMarkovToken()), + filter2, false, SetTargetPointer.NONE + ).withInterveningIf(SourceOnBattlefieldOrCommandZoneCondition.instance).setAbilityWord(AbilityWord.EMINENCE)); // First strike this.addAbility(FirstStrikeAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/e/EdgarMasterMachinist.java b/Mage.Sets/src/mage/cards/e/EdgarMasterMachinist.java new file mode 100644 index 00000000000..6f501530948 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EdgarMasterMachinist.java @@ -0,0 +1,59 @@ +package mage.cards.e; + +import mage.MageIdentifier; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.CastFromGraveyardOnceDuringEachOfYourTurnAbility; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterCard; +import mage.filter.common.FilterArtifactCard; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class EdgarMasterMachinist extends CardImpl { + + private static final FilterCard filter = new FilterArtifactCard("an artifact spell"); + + public EdgarMasterMachinist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ARTIFICER); + this.subtype.add(SubType.NOBLE); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Once during each of your turns, you may cast an artifact spell from your graveyard. If you cast a spell this way, that artifact enters tapped. + Ability ability = new CastFromGraveyardOnceDuringEachOfYourTurnAbility(filter, MageIdentifier.OnceOnYourTurnCastFromGraveyardEntersTapped); + ability.addEffect(new InfoEffect("If you cast a spell this way, that artifact enters tapped")); + this.addAbility(ability); + + // Tools -- Whenever Edgar attacks, it gets +X/+0 until end of turn, where X is the greatest mana value among artifacts you control. + this.addAbility(new AttacksTriggeredAbility( + new BoostSourceEffect(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_ARTIFACTS, StaticValue.get(0), Duration.EndOfTurn, "it") + ).withFlavorWord("Tools").addHint(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_ARTIFACTS.getHint())); + } + + private EdgarMasterMachinist(final EdgarMasterMachinist card) { + super(card); + } + + @Override + public EdgarMasterMachinist copy() { + return new EdgarMasterMachinist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EdgarsAwakening.java b/Mage.Sets/src/mage/cards/e/EdgarsAwakening.java index 5e97ea7cb09..66bf82d5c73 100644 --- a/Mage.Sets/src/mage/cards/e/EdgarsAwakening.java +++ b/Mage.Sets/src/mage/cards/e/EdgarsAwakening.java @@ -56,6 +56,7 @@ class EdgarsAwakeningTriggeredAbility extends TriggeredAbilityImpl { EdgarsAwakeningTriggeredAbility() { super(Zone.ALL, new DoWhenCostPaid(makeAbility(), new ManaCostsImpl<>("{B}"), "Pay {B}?")); + this.setTriggerPhrase("When you discard this card, "); } private EdgarsAwakeningTriggeredAbility(final EdgarsAwakeningTriggeredAbility ability) { @@ -76,10 +77,4 @@ class EdgarsAwakeningTriggeredAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { return this.getSourceId().equals(event.getTargetId()); } - - @Override - public String getRule() { - return "When you discard {this}, you may pay {B}. " + - "When you do, return target creature card from your graveyard to your hand."; - } } diff --git a/Mage.Sets/src/mage/cards/e/EdificeOfAuthority.java b/Mage.Sets/src/mage/cards/e/EdificeOfAuthority.java index da3725d7179..2eb546cec93 100644 --- a/Mage.Sets/src/mage/cards/e/EdificeOfAuthority.java +++ b/Mage.Sets/src/mage/cards/e/EdificeOfAuthority.java @@ -5,19 +5,19 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalActivatedAbility; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.RestrictionEffect; import mage.abilities.effects.common.combat.CantAttackTargetEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.turn.Step; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -27,7 +27,7 @@ import java.util.UUID; */ public final class EdificeOfAuthority extends CardImpl { - private static final String rule = "{1}, {T}: Until your next turn, target creature can't attack or block and its activated abilities can't be activated. Activate only if there are three or more brick counters on {this}."; + private static final Condition condition = new SourceHasCounterCondition(CounterType.BRICK, 3); public EdificeOfAuthority(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); @@ -40,12 +40,10 @@ public final class EdificeOfAuthority extends CardImpl { this.addAbility(ability); // {1}, {T}: Until your next turn, target creature can't attack or block and its activated abilities can't be activated. Activate this ability only if there are three or more brick counter on Edifice of Authority. - Condition condition = new SourceHasCounterCondition(CounterType.BRICK, 3, Integer.MAX_VALUE); - Ability ability2 = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new EdificeOfAuthorityEffect(), new ManaCostsImpl<>("{1}"), condition, rule); + Ability ability2 = new ConditionalActivatedAbility(new EdificeOfAuthorityEffect(), new GenericManaCost(1), condition); ability2.addCost(new TapSourceCost()); ability2.addTarget(new TargetCreaturePermanent()); this.addAbility(ability2); - } private EdificeOfAuthority(final EdificeOfAuthority card) { @@ -58,45 +56,17 @@ public final class EdificeOfAuthority extends CardImpl { } } -class EdificeOfAuthorityEffect extends OneShotEffect { +class EdificeOfAuthorityEffect extends RestrictionEffect { EdificeOfAuthorityEffect() { - super(Outcome.LoseAbility); - } - - public EdificeOfAuthorityEffect(String ruleText) { - super(Outcome.LoseAbility); - staticText = ruleText; + super(Duration.UntilYourNextTurn); + staticText = "until your next turn, target creature can't attack or block and its activated abilities can't be activated"; } private EdificeOfAuthorityEffect(final EdificeOfAuthorityEffect effect) { super(effect); } - @Override - public EdificeOfAuthorityEffect copy() { - return new EdificeOfAuthorityEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - EdificeOfAuthorityRestrictionEffect effect = new EdificeOfAuthorityRestrictionEffect(); - game.addEffect(effect, source); - return true; - } -} - -class EdificeOfAuthorityRestrictionEffect extends RestrictionEffect { - - EdificeOfAuthorityRestrictionEffect() { - super(Duration.Custom); - staticText = ""; - } - - private EdificeOfAuthorityRestrictionEffect(final EdificeOfAuthorityRestrictionEffect effect) { - super(effect); - } - @Override public void init(Ability source, Game game) { super.init(source, game); @@ -108,24 +78,6 @@ class EdificeOfAuthorityRestrictionEffect extends RestrictionEffect { } } - @Override - public boolean isInactive(Ability source, Game game) { - if (game.getPhase().getStep().getType() == PhaseStep.UNTAP - && game.getStep().getStepPart() == Step.StepPart.PRE) { - if (game.isActivePlayer(source.getControllerId()) - || game.getPlayer(source.getControllerId()).hasReachedNextTurnAfterLeaving()) { - for (UUID targetId : this.getTargetPointer().getTargets(game, source)) { - Permanent permanent = game.getPermanent(targetId); - if (permanent != null) { - permanent.addInfo("Can't attack or block and its activated abilities can't be activated." + getId(), "", game); - } - } - return true; - } - } - return false; - } - @Override public boolean applies(Permanent permanent, Ability source, Game game) { return this.getTargetPointer().getTargets(game, source).contains(permanent.getId()); @@ -147,8 +99,8 @@ class EdificeOfAuthorityRestrictionEffect extends RestrictionEffect { } @Override - public EdificeOfAuthorityRestrictionEffect copy() { - return new EdificeOfAuthorityRestrictionEffect(this); + public EdificeOfAuthorityEffect copy() { + return new EdificeOfAuthorityEffect(this); } } diff --git a/Mage.Sets/src/mage/cards/e/Eject.java b/Mage.Sets/src/mage/cards/e/Eject.java new file mode 100644 index 00000000000..c1ffa790716 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/Eject.java @@ -0,0 +1,40 @@ +package mage.cards.e; + +import mage.abilities.common.CantBeCounteredSourceAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetNonlandPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Eject extends CardImpl { + + public Eject(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}"); + + // This spell can't be countered. + this.addAbility(new CantBeCounteredSourceAbility()); + + // Return target nonland permanent to its owner's hand. + this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); + this.getSpellAbility().addTarget(new TargetNonlandPermanent()); + + // Draw a card. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
    ")); + } + + private Eject(final Eject card) { + super(card); + } + + @Override + public Eject copy() { + return new Eject(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/ElderPineOfJukai.java b/Mage.Sets/src/mage/cards/e/ElderPineOfJukai.java index 78a76519a6c..d0186a3e41a 100644 --- a/Mage.Sets/src/mage/cards/e/ElderPineOfJukai.java +++ b/Mage.Sets/src/mage/cards/e/ElderPineOfJukai.java @@ -27,7 +27,7 @@ public final class ElderPineOfJukai extends CardImpl { this.toughness = new MageInt(1); // Whenever you cast a Spirit or Arcane spell, reveal the top three cards of your library. Put all land cards revealed this way into your hand and the rest on the bottom of your library in any order. - this.addAbility(new SpellCastControllerTriggeredAbility(new RevealLibraryPutIntoHandEffect(3, StaticFilters.FILTER_CARD_LANDS, Zone.LIBRARY), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new RevealLibraryPutIntoHandEffect(3, StaticFilters.FILTER_CARD_LANDS, Zone.LIBRARY), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, false)); // Soulshift 2 this.addAbility(new SoulshiftAbility(2)); diff --git a/Mage.Sets/src/mage/cards/e/ElenaTurkRecruit.java b/Mage.Sets/src/mage/cards/e/ElenaTurkRecruit.java index c5fbea1f82f..950294a67a6 100644 --- a/Mage.Sets/src/mage/cards/e/ElenaTurkRecruit.java +++ b/Mage.Sets/src/mage/cards/e/ElenaTurkRecruit.java @@ -13,10 +13,9 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.counters.CounterType; import mage.filter.FilterCard; -import mage.filter.FilterSpell; -import mage.filter.common.FilterHistoricCard; -import mage.filter.common.FilterHistoricSpell; +import mage.filter.StaticFilters; import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.HistoricPredicate; import mage.target.common.TargetCardInYourGraveyard; import java.util.UUID; @@ -26,10 +25,10 @@ import java.util.UUID; */ public final class ElenaTurkRecruit extends CardImpl { - private static final FilterCard filter = new FilterHistoricCard("non-Assassin historic card from your graveyard"); - private static final FilterSpell filter2 = new FilterHistoricSpell(); + private static final FilterCard filter = new FilterCard("non-Assassin historic card from your graveyard"); static { + filter.add(HistoricPredicate.instance); filter.add(Predicates.not(SubType.ASSASSIN.getPredicate())); } @@ -49,7 +48,7 @@ public final class ElenaTurkRecruit extends CardImpl { // Whenever you cast a historic spell, put a +1/+1 counter on Elena. this.addAbility(new SpellCastControllerTriggeredAbility( - new AddCountersSourceEffect(CounterType.P1P1.createInstance()), filter2, false + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), StaticFilters.FILTER_SPELL_HISTORIC, false )); } diff --git a/Mage.Sets/src/mage/cards/e/Elixir.java b/Mage.Sets/src/mage/cards/e/Elixir.java new file mode 100644 index 00000000000..9c687d7cc9e --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/Elixir.java @@ -0,0 +1,80 @@ +package mage.cards.e; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.ExileSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Elixir extends CardImpl { + + public Elixir(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); + + // This artifact enters tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {5}, {T}, Exile this artifact: Shuffle all nonland cards from your graveyard into your library. You gain life equal to the number of cards shuffled into your library this way. + Ability ability = new SimpleActivatedAbility(new ElixirEffect(), new GenericManaCost(5)); + ability.addCost(new TapSourceCost()); + ability.addCost(new ExileSourceCost()); + this.addAbility(ability); + } + + private Elixir(final Elixir card) { + super(card); + } + + @Override + public Elixir copy() { + return new Elixir(this); + } +} + +class ElixirEffect extends OneShotEffect { + + ElixirEffect() { + super(Outcome.Benefit); + staticText = "shuffle all nonland cards from your graveyard into your library. " + + "You gain life equal to the number of cards shuffled into your library this way"; + } + + private ElixirEffect(final ElixirEffect effect) { + super(effect); + } + + @Override + public ElixirEffect copy() { + return new ElixirEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(player.getGraveyard().getCards(StaticFilters.FILTER_CARD_NON_LAND, game)); + player.shuffleCardsToLibrary(cards, game, source); + cards.retainZone(Zone.LIBRARY, game); + player.gainLife(cards.size(), game, source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/e/ElminstersSimulacrum.java b/Mage.Sets/src/mage/cards/e/ElminstersSimulacrum.java index 98770714cc9..0a00f530e51 100644 --- a/Mage.Sets/src/mage/cards/e/ElminstersSimulacrum.java +++ b/Mage.Sets/src/mage/cards/e/ElminstersSimulacrum.java @@ -10,7 +10,7 @@ import mage.constants.Outcome; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; import java.util.UUID; @@ -26,7 +26,7 @@ public final class ElminstersSimulacrum extends CardImpl { // For each opponent, you create a token that's a copy of up to one target creature that player controls. this.getSpellAbility().addEffect(new ElminstersSimulacrumAdjusterEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(0,1)); - this.getSpellAbility().setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + this.getSpellAbility().setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); } private ElminstersSimulacrum(final ElminstersSimulacrum card) { diff --git a/Mage.Sets/src/mage/cards/e/ElspethConquersDeath.java b/Mage.Sets/src/mage/cards/e/ElspethConquersDeath.java index d420c4a7eee..8cdb14c7329 100644 --- a/Mage.Sets/src/mage/cards/e/ElspethConquersDeath.java +++ b/Mage.Sets/src/mage/cards/e/ElspethConquersDeath.java @@ -139,7 +139,7 @@ class ElspethConquersDeathReturnEffect extends OneShotEffect { return false; } player.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/e/ElspethResplendent.java b/Mage.Sets/src/mage/cards/e/ElspethResplendent.java index c3065a7799c..cebcda8a2d3 100644 --- a/Mage.Sets/src/mage/cards/e/ElspethResplendent.java +++ b/Mage.Sets/src/mage/cards/e/ElspethResplendent.java @@ -19,6 +19,7 @@ import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetCreaturePermanent; +import mage.util.CardUtil; import java.util.*; @@ -136,7 +137,7 @@ class ElspethResplendentLookEffect extends OneShotEffect { Card card = game.getCard(target.getFirstTarget()); if (card != null) { player.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { permanent.addCounters(CounterType.SHIELD.createInstance(), source, game); } diff --git a/Mage.Sets/src/mage/cards/e/ElugeTheShorelessSea.java b/Mage.Sets/src/mage/cards/e/ElugeTheShorelessSea.java index 8b87b566f72..49005367aa2 100644 --- a/Mage.Sets/src/mage/cards/e/ElugeTheShorelessSea.java +++ b/Mage.Sets/src/mage/cards/e/ElugeTheShorelessSea.java @@ -118,7 +118,7 @@ class ElugeTheShorelessSeaEffect extends BecomesBasicLandTargetEffect { ElugeTheShorelessSeaEffect() { super(Duration.Custom, false, false, SubType.ISLAND); - staticText = "It's an land is an Island in addition to its other types for as long as it has a flood counter on it"; + staticText = "It's an Island in addition to its other types for as long as it has a flood counter on it"; } private ElugeTheShorelessSeaEffect(final ElugeTheShorelessSeaEffect effect) { diff --git a/Mage.Sets/src/mage/cards/e/ElvishHydromancer.java b/Mage.Sets/src/mage/cards/e/ElvishHydromancer.java index 444264ff141..488b81b9412 100644 --- a/Mage.Sets/src/mage/cards/e/ElvishHydromancer.java +++ b/Mage.Sets/src/mage/cards/e/ElvishHydromancer.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.KickedCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.CreateTokenCopyTargetEffect; import mage.abilities.keyword.KickerAbility; import mage.cards.CardImpl; @@ -32,11 +31,7 @@ public final class ElvishHydromancer extends CardImpl { this.addAbility(new KickerAbility("{3}{U}")); // When Elvish Hydromancer enters the battlefield, if it was kicked, create a token that's a copy of target creature you control. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new CreateTokenCopyTargetEffect()), - KickedCondition.ONCE, "When {this} enters, " + - "if it was kicked, create a token that's a copy of target creature you control." - ); + Ability ability = new EntersBattlefieldTriggeredAbility(new CreateTokenCopyTargetEffect()).withInterveningIf(KickedCondition.ONCE); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/e/EmeraldCharm.java b/Mage.Sets/src/mage/cards/e/EmeraldCharm.java index 780ca514a6c..f784dfef328 100644 --- a/Mage.Sets/src/mage/cards/e/EmeraldCharm.java +++ b/Mage.Sets/src/mage/cards/e/EmeraldCharm.java @@ -1,7 +1,5 @@ - package mage.cards.e; -import java.util.UUID; import mage.abilities.Mode; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.UntapTargetEffect; @@ -12,26 +10,27 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; +import mage.filter.FilterPermanent; import mage.filter.common.FilterEnchantmentPermanent; import mage.filter.predicate.Predicates; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; -import mage.target.common.TargetEnchantmentPermanent; + +import java.util.UUID; /** - * * @author fireshoes */ public final class EmeraldCharm extends CardImpl { - - private static final FilterEnchantmentPermanent filter = new FilterEnchantmentPermanent("non-Aura enchantment"); - + + private static final FilterPermanent filter = new FilterEnchantmentPermanent("non-Aura enchantment"); + static { filter.add(Predicates.not(SubType.AURA.getPredicate())); } public EmeraldCharm(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{G}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}"); // Choose one - Untap target permanent; this.getSpellAbility().addEffect(new UntapTargetEffect()); @@ -39,7 +38,7 @@ public final class EmeraldCharm extends CardImpl { // or destroy target non-Aura enchantment; Mode mode = new Mode(new DestroyTargetEffect()); - mode.addTarget(new TargetEnchantmentPermanent(filter)); + mode.addTarget(new TargetPermanent(filter)); this.getSpellAbility().addMode(mode); // or target creature loses flying until end of turn. diff --git a/Mage.Sets/src/mage/cards/e/EmergentSequence.java b/Mage.Sets/src/mage/cards/e/EmergentSequence.java index a4016017b40..b46cd39f344 100644 --- a/Mage.Sets/src/mage/cards/e/EmergentSequence.java +++ b/Mage.Sets/src/mage/cards/e/EmergentSequence.java @@ -78,26 +78,23 @@ class EmergentSequenceEffect extends OneShotEffect { TargetCardInLibrary target = new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND_A); player.searchLibrary(target, source, game); Card card = player.getLibrary().getCard(target.getFirstTarget(), game); - Permanent permanent = null; if (card != null) { player.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null); - permanent = game.getPermanent(target.getFirstTarget()); } player.shuffleLibrary(source, game); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return true; } - - // boost land game.addEffect(new BecomesCreatureTargetEffect( new FractalToken(), false, true, Duration.Custom ).setTargetPointer(new FixedTarget(permanent, game)), source); - // rules // The last sentence of Emergent Sequence’s ability counts the land it put onto the battlefield. // (2021-04-16) - // no ETB yet, so add +1 manually - int amount = 1 + EmergentSequenceWatcher.getAmount(source.getControllerId(), game); + game.processAction(); + + int amount = EmergentSequenceWatcher.getAmount(source.getControllerId(), game); permanent.addCounters(CounterType.P1P1.createInstance(amount), source.getControllerId(), source, game); return true; } diff --git a/Mage.Sets/src/mage/cards/e/EmergentUltimatum.java b/Mage.Sets/src/mage/cards/e/EmergentUltimatum.java index e2f0d73a582..e9f2af707bc 100644 --- a/Mage.Sets/src/mage/cards/e/EmergentUltimatum.java +++ b/Mage.Sets/src/mage/cards/e/EmergentUltimatum.java @@ -100,7 +100,7 @@ class EmergentUltimatumEffect extends OneShotEffect { opponent.choose(outcome, cards, targetCardInExile, source, game); Card toShuffle = game.getCard(targetCardInExile.getFirstTarget()); if (toShuffle != null) { - player.putCardsOnBottomOfLibrary(toShuffle, game, source, false); + player.putCardsOnBottomOfLibrary(toShuffle, game, source); player.shuffleLibrary(source, game); cards.remove(toShuffle); } diff --git a/Mage.Sets/src/mage/cards/e/EmetSelchOfTheThirdSeat.java b/Mage.Sets/src/mage/cards/e/EmetSelchOfTheThirdSeat.java new file mode 100644 index 00000000000..8b478cbe95e --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EmetSelchOfTheThirdSeat.java @@ -0,0 +1,137 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.BatchTriggeredAbility; +import mage.abilities.TriggeredAbility; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; +import mage.abilities.effects.common.replacement.ThatSpellGraveyardExileReplacementEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.predicate.card.CastFromZonePredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.LifeLostBatchEvent; +import mage.game.events.LifeLostEvent; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +import java.util.List; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EmetSelchOfTheThirdSeat extends CardImpl { + + private static final FilterCard filter = new FilterCard("spells you cast from your graveyard"); + + static { + filter.add(new CastFromZonePredicate(Zone.GRAVEYARD)); + } + + public EmetSelchOfTheThirdSeat(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.ELDER); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Spells you cast from your graveyard cost {2} less to cast. + this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 2))); + + // Whenever one or more opponents lose life, you may cast target instant or sorcery card from your graveyard. If that spell would be put into your graveyard, exile it instead. Do this only once each turn. + this.addAbility(new EmetSelchOfTheThirdSeatAbility()); + } + + private EmetSelchOfTheThirdSeat(final EmetSelchOfTheThirdSeat card) { + super(card); + } + + @Override + public EmetSelchOfTheThirdSeat copy() { + return new EmetSelchOfTheThirdSeat(this); + } +} + +class EmetSelchOfTheThirdSeatAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility { + + EmetSelchOfTheThirdSeatAbility() { + super(Zone.BATTLEFIELD, new EmetSelchOfTheThirdSeatEffect()); + this.setTriggerPhrase("Whenever one or more opponents lose life, "); + this.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY_FROM_YOUR_GRAVEYARD)); + this.setDoOnlyOnceEachTurn(true); + } + + private EmetSelchOfTheThirdSeatAbility(final EmetSelchOfTheThirdSeatAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.LOST_LIFE_BATCH; + } + + @Override + public boolean checkEvent(LifeLostEvent event, Game game) { + return game.getOpponents(getControllerId()).contains(event.getTargetId()); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + List filteredEvents = getFilteredEvents((LifeLostBatchEvent) event, game); + return !filteredEvents.isEmpty() + && CardUtil + .getEventTargets(event) + .stream() + .anyMatch(uuid -> LifeLostBatchEvent.getLifeLostByPlayer(filteredEvents, uuid) > 0); + } + + @Override + public EmetSelchOfTheThirdSeatAbility copy() { + return new EmetSelchOfTheThirdSeatAbility(this); + } +} + +class EmetSelchOfTheThirdSeatEffect extends OneShotEffect { + + EmetSelchOfTheThirdSeatEffect() { + super(Outcome.Benefit); + staticText = "cast target instant or sorcery card from your graveyard. " + + "If that spell would be put into your graveyard, exile it instead"; + } + + private EmetSelchOfTheThirdSeatEffect(final EmetSelchOfTheThirdSeatEffect effect) { + super(effect); + } + + @Override + public EmetSelchOfTheThirdSeatEffect copy() { + return new EmetSelchOfTheThirdSeatEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(getTargetPointer().getFirst(game, source)); + if (player == null || card == null || !CardUtil.castSingle(player, source, game, card)) { + // if the spell isn't cast then the ability can be used again in the same turn + TriggeredAbility.clearDidThisTurn(source, game); + return false; + } + game.addEffect(new ThatSpellGraveyardExileReplacementEffect(true) + .setTargetPointer(new FixedTarget(card, game)), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/e/EmissaryOfTheSleepless.java b/Mage.Sets/src/mage/cards/e/EmissaryOfTheSleepless.java index 5e0d08e6727..15883d31437 100644 --- a/Mage.Sets/src/mage/cards/e/EmissaryOfTheSleepless.java +++ b/Mage.Sets/src/mage/cards/e/EmissaryOfTheSleepless.java @@ -1,12 +1,8 @@ - package mage.cards.e; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.MorbidCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.hint.common.MorbidHint; import mage.abilities.keyword.FlyingAbility; @@ -16,24 +12,25 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.game.permanent.token.SpiritWhiteToken; +import java.util.UUID; + /** - * * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ public final class EmissaryOfTheSleepless extends CardImpl { public EmissaryOfTheSleepless(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}"); this.subtype.add(SubType.SPIRIT); this.power = new MageInt(2); this.toughness = new MageInt(4); // Flying this.addAbility(FlyingAbility.getInstance()); - + // When Emissary of the Sleepless enters the battlefield, if a creature died this turn, create a 1/1 white Spirit creature token with flying. - TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken())); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, MorbidCondition.instance, "When {this} enters, if a creature died this turn, create a 1/1 white Spirit creature token with flying.").addHint(MorbidHint.instance)); + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken())) + .withInterveningIf(MorbidCondition.instance).addHint(MorbidHint.instance)); } private EmissaryOfTheSleepless(final EmissaryOfTheSleepless card) { diff --git a/Mage.Sets/src/mage/cards/e/EmperorOfBones.java b/Mage.Sets/src/mage/cards/e/EmperorOfBones.java index 44b362bfd66..ce8d5af27fe 100644 --- a/Mage.Sets/src/mage/cards/e/EmperorOfBones.java +++ b/Mage.Sets/src/mage/cards/e/EmperorOfBones.java @@ -101,7 +101,7 @@ class EmperorOfBonesEffect extends OneShotEffect { } game.setEnterWithCounters(card.getId(), new Counters().addCounter(CounterType.FINALITY.createInstance())); player.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(CardUtil.getDefaultCardSideForBattlefield(game, card).getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return true; } diff --git a/Mage.Sets/src/mage/cards/e/EmpyrialPlate.java b/Mage.Sets/src/mage/cards/e/EmpyrialPlate.java index 0010967bbc3..0c1ae165c5a 100644 --- a/Mage.Sets/src/mage/cards/e/EmpyrialPlate.java +++ b/Mage.Sets/src/mage/cards/e/EmpyrialPlate.java @@ -24,7 +24,7 @@ public final class EmpyrialPlate extends CardImpl { this.subtype.add(SubType.EQUIPMENT); // Equipped creature gets +1/+1 for each card in your hand. - this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(CardsInControllerHandCount.ANY, CardsInControllerHandCount.ANY))); + this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(CardsInControllerHandCount.ANY_SINGULAR, CardsInControllerHandCount.ANY_SINGULAR))); // Equip {2} this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(2), false)); diff --git a/Mage.Sets/src/mage/cards/e/EnergyVortex.java b/Mage.Sets/src/mage/cards/e/EnergyVortex.java index 85133397453..017a8a5e11a 100644 --- a/Mage.Sets/src/mage/cards/e/EnergyVortex.java +++ b/Mage.Sets/src/mage/cards/e/EnergyVortex.java @@ -3,17 +3,16 @@ package mage.cards.e; import mage.abilities.Ability; import mage.abilities.common.ActivateIfConditionActivatedAbility; import mage.abilities.common.AsEntersBattlefieldAbility; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.IsStepCondition; import mage.abilities.costs.Cost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.dynamicvalue.common.GetXValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ChooseOpponentEffect; import mage.abilities.effects.common.RemoveAllCountersSourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -43,12 +42,10 @@ public final class EnergyVortex extends CardImpl { )); // At the beginning of the chosen player's upkeep, Energy Vortex deals 3 damage to that player unless they pay {1} for each vortex counter on Energy Vortex. - this.addAbility(new ConditionalTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility( - TargetController.ANY, new EnergyVortexEffect(), false - ), EnergyVortexCondition.instance, "At the beginning of the chosen player's upkeep, " + - "{this} deals 3 damage to that player unless they pay {1} for each vortex counter on {this}." - )); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + TargetController.ANY, new EnergyVortexEffect(), false + ).withTriggerCondition(EnergyVortexCondition.instance) + .setTriggerPhrase("At the beginning of the chosen player's upkeep, ")); // {X}: Put X vortex counters on Energy Vortex. Activate this ability only during your upkeep. this.addAbility(new ActivateIfConditionActivatedAbility( @@ -78,12 +75,18 @@ enum EnergyVortexCondition implements Condition { public boolean apply(Game game, Ability source) { return game.getActivePlayerId().equals(game.getState().getValue(source.getSourceId().toString() + ChooseOpponentEffect.VALUE_KEY)); } + + @Override + public String toString() { + return ""; + } } class EnergyVortexEffect extends OneShotEffect { EnergyVortexEffect() { super(Outcome.Benefit); + staticText = "{this} deals 3 damage to that player unless they pay {1} for each vortex counter on {this}"; } private EnergyVortexEffect(final EnergyVortexEffect effect) { @@ -104,9 +107,7 @@ class EnergyVortexEffect extends OneShotEffect { } int counters = permanent.getCounters(game).getCount(CounterType.VORTEX); Cost cost = ManaUtil.createManaCost(counters, false); - if (cost.pay(source, game, source, player.getId(), false)) { - return true; - } - return player.damage(3, source.getSourceId(), source, game) > 0; + return cost.pay(source, game, source, player.getId(), false) + || player.damage(3, source.getSourceId(), source, game) > 0; } } diff --git a/Mage.Sets/src/mage/cards/e/EngineeredExplosives.java b/Mage.Sets/src/mage/cards/e/EngineeredExplosives.java index e61e003a2ec..aa44ac9a643 100644 --- a/Mage.Sets/src/mage/cards/e/EngineeredExplosives.java +++ b/Mage.Sets/src/mage/cards/e/EngineeredExplosives.java @@ -1,23 +1,20 @@ - package mage.cards.e; -import java.util.UUID; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyAllEffect; import mage.abilities.keyword.SunburstAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; import mage.counters.CounterType; +import mage.filter.FilterPermanent; import mage.filter.common.FilterNonlandPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.filter.predicate.mageobject.ManaValueEqualToCountersSourceCountPredicate; + +import java.util.UUID; /** * @@ -25,14 +22,21 @@ import mage.game.permanent.Permanent; */ public final class EngineeredExplosives extends CardImpl { + private static final FilterPermanent filter = new FilterNonlandPermanent( + "each nonland permanent with mana value equal to the number of charge counters on {this}" + ); + static { + filter.add(new ManaValueEqualToCountersSourceCountPredicate(CounterType.CHARGE)); + } public EngineeredExplosives(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{X}"); // Sunburst this.addAbility(new SunburstAbility(this)); + // {2}, Sacrifice Engineered Explosives: Destroy each nonland permanent with converted mana cost equal to the number of charge counters on Engineered Explosives. - Ability ability = new SimpleActivatedAbility(new EngineeredExplosivesEffect(), new ManaCostsImpl<>("{2}")); + Ability ability = new SimpleActivatedAbility(new DestroyAllEffect(filter), new ManaCostsImpl<>("{2}")); ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); } @@ -46,40 +50,3 @@ public final class EngineeredExplosives extends CardImpl { return new EngineeredExplosives(this); } } - -class EngineeredExplosivesEffect extends OneShotEffect { - - private static final FilterNonlandPermanent filter = new FilterNonlandPermanent(); - - - public EngineeredExplosivesEffect() { - super(Outcome.DestroyPermanent); - staticText = "Destroy each nonland permanent with mana value equal to the number of charge counters on Engineered Explosives"; - } - - - private EngineeredExplosivesEffect(final EngineeredExplosivesEffect effect) { - super(effect); - } - - @Override - public EngineeredExplosivesEffect copy() { - return new EngineeredExplosivesEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - MageObject engineeredExplosives = game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if(engineeredExplosives instanceof Permanent){ - int count = ((Permanent)engineeredExplosives).getCounters(game).getCount(CounterType.CHARGE); - for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source, game)) { - if(permanent.getManaValue() == count){ - permanent.destroy(source, game, false); - } - } - return true; - } - return false; - } - -} diff --git a/Mage.Sets/src/mage/cards/e/EnigmaThief.java b/Mage.Sets/src/mage/cards/e/EnigmaThief.java index 5ee64169a29..4d7816322ea 100644 --- a/Mage.Sets/src/mage/cards/e/EnigmaThief.java +++ b/Mage.Sets/src/mage/cards/e/EnigmaThief.java @@ -11,7 +11,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.target.common.TargetNonlandPermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; import java.util.UUID; @@ -40,7 +40,7 @@ public final class EnigmaThief extends CardImpl { .setTargetPointer(new EachTargetPointer()) .setText("for each opponent, return up to one target nonland permanent that player controls to its owner's hand")); ability.addTarget(new TargetNonlandPermanent(0,1)); - ability.setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/e/EomerOfTheRiddermark.java b/Mage.Sets/src/mage/cards/e/EomerOfTheRiddermark.java index e22562d33a1..45826738791 100644 --- a/Mage.Sets/src/mage/cards/e/EomerOfTheRiddermark.java +++ b/Mage.Sets/src/mage/cards/e/EomerOfTheRiddermark.java @@ -3,7 +3,6 @@ package mage.cards.e; import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.condition.common.ControlsCreatureGreatestPowerCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.hint.ConditionHint; import mage.abilities.hint.Hint; @@ -40,12 +39,8 @@ public final class EomerOfTheRiddermark extends CardImpl { this.addAbility(HasteAbility.getInstance()); // Whenever Eomer of the Riddermark attacks, if you control a creature with the greatest power among creatures on the battlefield, create a 1/1 white Human Soldier creature token. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new AttacksTriggeredAbility(new CreateTokenEffect(new HumanSoldierToken())), - ControlsCreatureGreatestPowerCondition.instance, "Whenever {this} attacks, " + - "if you control a creature with the greatest power among creatures on the battlefield, " + - "create a 1/1 white Human Soldier creature token." - ).addHint(hint)); + this.addAbility(new AttacksTriggeredAbility(new CreateTokenEffect(new HumanSoldierToken())) + .withInterveningIf(ControlsCreatureGreatestPowerCondition.instance).addHint(hint)); } private EomerOfTheRiddermark(final EomerOfTheRiddermark card) { diff --git a/Mage.Sets/src/mage/cards/e/EonFrolicker.java b/Mage.Sets/src/mage/cards/e/EonFrolicker.java index a042d7761eb..a11bccab173 100644 --- a/Mage.Sets/src/mage/cards/e/EonFrolicker.java +++ b/Mage.Sets/src/mage/cards/e/EonFrolicker.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.CastFromEverywhereSourceCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.effects.common.continuous.GainAbilityControllerEffect; @@ -43,12 +42,8 @@ public final class EonFrolicker extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Eon Frolicker enters the battlefield, if you cast it, target opponent takes an extra turn after this one. Until your next turn, you and planeswalkers you control gain protection from that player. - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new EonFrolickerEffect()), - CastFromEverywhereSourceCondition.instance, "When {this} enters, " + - "if you cast it, target opponent takes an extra turn after this one. Until your next turn, " + - "you and planeswalkers you control gain protection from that player." - ); + Ability ability = new EntersBattlefieldTriggeredAbility(new EonFrolickerEffect()) + .withInterveningIf(CastFromEverywhereSourceCondition.instance); ability.addTarget(new TargetOpponent()); this.addAbility(ability); } @@ -67,6 +62,8 @@ class EonFrolickerEffect extends OneShotEffect { EonFrolickerEffect() { super(Outcome.Benefit); + staticText = "target opponent takes an extra turn after this one. Until your next turn, " + + "you and planeswalkers you control gain protection from that player"; } private EonFrolickerEffect(final EonFrolickerEffect effect) { diff --git a/Mage.Sets/src/mage/cards/e/EowynShieldmaiden.java b/Mage.Sets/src/mage/cards/e/EowynShieldmaiden.java index 1fc5e8bfb1b..0f0b388d636 100644 --- a/Mage.Sets/src/mage/cards/e/EowynShieldmaiden.java +++ b/Mage.Sets/src/mage/cards/e/EowynShieldmaiden.java @@ -3,15 +3,13 @@ package mage.cards.e; import mage.MageInt; import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -25,16 +23,17 @@ import mage.watchers.Watcher; import java.util.*; /** - * * @author Susucr */ public final class EowynShieldmaiden extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent(SubType.HUMAN, "Humans"); + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + new FilterPermanent(SubType.HUMAN, ""), ComparisonType.MORE_THAN, 5 + ); public EowynShieldmaiden(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{R}{W}"); - + this.supertype.add(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.KNIGHT); @@ -48,27 +47,14 @@ public final class EowynShieldmaiden extends CardImpl { // if another Human entered the battlefield under your control this turn, // create two 2/2 red Human Knight creature tokens with trample and haste. // Then if you control six or more Humans, draw a card. - - TriggeredAbility triggeredAbility = new BeginningOfCombatTriggeredAbility( - Zone.BATTLEFIELD, - TargetController.YOU, new CreateTokenEffect(new HumanKnightToken(), 2), - false - ); - triggeredAbility.addEffect(new ConditionalOneShotEffect( - new DrawCardSourceControllerEffect(1), - new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 5) + Ability ability = new BeginningOfCombatTriggeredAbility( + TargetController.YOU, new CreateTokenEffect(new HumanKnightToken(), 2), false + ).withInterveningIf(EowynShieldmaidenCondition.instance); + ability.addEffect(new ConditionalOneShotEffect( + new DrawCardSourceControllerEffect(1), condition, + "Then if you control six or more Humans, draw a card" )); - - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - triggeredAbility, - EowynShieldmaidenCondition.instance, - "At the beginning of combat on your turn, " - + "if another Human entered the battlefield " - + "under your control this turn, create two " - + "2/2 red Human Knight creature tokens with " - + "trample and haste. " - + "Then if you control six or more Humans, draw a card." - ), new EowynShieldmaidenWatcher()); + this.addAbility(ability, new EowynShieldmaidenWatcher()); } private EowynShieldmaiden(final EowynShieldmaiden card) { @@ -88,7 +74,7 @@ enum EowynShieldmaidenCondition implements Condition { public boolean apply(Game game, Ability source) { EowynShieldmaidenWatcher watcher = game.getState().getWatcher(EowynShieldmaidenWatcher.class); return watcher != null - && watcher.hasPlayerHadAnotherHumanEnterThisTurn( + && watcher.hasPlayerHadAnotherHumanEnterThisTurn( game, source.getSourcePermanentOrLKI(game), source.getControllerId()); @@ -137,11 +123,11 @@ class EowynShieldmaidenWatcher extends Watcher { // we do use MageObjectReference for when eowyn is entering the battlefield // multiple time in the same turn (flickered for instance) MageObjectReference sourceMOR = sourcePermanent == null ? null - : new MageObjectReference(sourcePermanent.getId(), game); + : new MageObjectReference(sourcePermanent.getId(), game); Set setForThePlayer = this.humanEnterings.getOrDefault(playerId, new HashSet<>()); return setForThePlayer.stream().anyMatch( - humanMOR -> !(humanMOR.equals(sourceMOR)) + humanMOR -> !(humanMOR.equals(sourceMOR)) ); } } diff --git a/Mage.Sets/src/mage/cards/e/EpharaGodOfThePolis.java b/Mage.Sets/src/mage/cards/e/EpharaGodOfThePolis.java index 6ea8d040ed6..6febc28ba20 100644 --- a/Mage.Sets/src/mage/cards/e/EpharaGodOfThePolis.java +++ b/Mage.Sets/src/mage/cards/e/EpharaGodOfThePolis.java @@ -2,17 +2,19 @@ package mage.cards.e; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.dynamicvalue.common.DevotionCount; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.LoseCreatureTypeSourceEffect; import mage.abilities.keyword.IndestructibleAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; import mage.game.Game; import mage.game.permanent.Permanent; import mage.watchers.common.PermanentsEnteredBattlefieldWatcher; @@ -40,13 +42,9 @@ public final class EpharaGodOfThePolis extends CardImpl { .addHint(DevotionCount.WU.getHint())); // At the beginning of each upkeep, if you had another creature enter the battlefield under your control last turn, draw a card. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility( - Zone.BATTLEFIELD, TargetController.ANY, new DrawCardSourceControllerEffect(1), - false - ), EpharaGodOfThePolisCondition.instance, "At the beginning of each upkeep, " + - "if you had another creature enter the battlefield under your control last turn, draw a card." - ), new PermanentsEnteredBattlefieldWatcher()); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + TargetController.ANY, new DrawCardSourceControllerEffect(1), false + ).withInterveningIf(EpharaGodOfThePolisCondition.instance), new PermanentsEnteredBattlefieldWatcher()); } private EpharaGodOfThePolis(final EpharaGodOfThePolis card) { @@ -71,4 +69,9 @@ enum EpharaGodOfThePolisCondition implements Condition { && watcher != null && watcher.anotherCreatureEnteredBattlefieldUnderPlayersControlLastTurn(sourcePermanent, game); } + + @Override + public String toString() { + return "you had another creature enter the battlefield under your control last turn"; + } } diff --git a/Mage.Sets/src/mage/cards/e/EpicStruggle.java b/Mage.Sets/src/mage/cards/e/EpicStruggle.java index de3aae322b9..7e0955917b0 100644 --- a/Mage.Sets/src/mage/cards/e/EpicStruggle.java +++ b/Mage.Sets/src/mage/cards/e/EpicStruggle.java @@ -1,34 +1,34 @@ - package mage.cards.e; -import java.util.UUID; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.WinGameSourceControllerEffect; -import mage.abilities.hint.ValueHint; +import mage.abilities.hint.common.CreaturesYouControlHint; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.ComparisonType; -import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; + +import java.util.UUID; /** - * * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ public final class EpicStruggle extends CardImpl { + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + new FilterControlledCreaturePermanent("you control twenty or more creatures"), + ComparisonType.MORE_THAN, 19 + ); + public EpicStruggle(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}{G}"); // At the beginning of your upkeep, if you control twenty or more creatures, you win the game. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility(new WinGameSourceControllerEffect()), - new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_CONTROLLED_CREATURE, ComparisonType.MORE_THAN, 19), - "At the beginning of your upkeep, if you control twenty or more creatures, you win the game." - ).addHint(new ValueHint("Creatures you control", new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_CREATURE)))); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new WinGameSourceControllerEffect()) + .withInterveningIf(condition).addHint(CreaturesYouControlHint.instance)); } private EpicStruggle(final EpicStruggle card) { diff --git a/Mage.Sets/src/mage/cards/e/Epicenter.java b/Mage.Sets/src/mage/cards/e/Epicenter.java index 623498e3ea9..ec78e7f0973 100644 --- a/Mage.Sets/src/mage/cards/e/Epicenter.java +++ b/Mage.Sets/src/mage/cards/e/Epicenter.java @@ -32,7 +32,7 @@ public final class Epicenter extends CardImpl { // Target player sacrifices a land. this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new SacrificeEffect(StaticFilters.FILTER_LAND, 1, "Target player"), - condition, "Target player sacrifices a land" + condition, "Target player sacrifices a land of their choice" )); this.getSpellAbility().addTarget(new TargetPlayer()); diff --git a/Mage.Sets/src/mage/cards/e/ErestorOfTheCouncil.java b/Mage.Sets/src/mage/cards/e/ErestorOfTheCouncil.java index 9e2f1195c0f..770ed22a932 100644 --- a/Mage.Sets/src/mage/cards/e/ErestorOfTheCouncil.java +++ b/Mage.Sets/src/mage/cards/e/ErestorOfTheCouncil.java @@ -5,6 +5,7 @@ import mage.abilities.Ability; import mage.abilities.common.FinishVotingTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.keyword.ScryEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -13,7 +14,6 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.game.Game; import mage.game.permanent.token.TreasureToken; -import mage.players.Player; import java.util.Set; import java.util.UUID; @@ -68,19 +68,16 @@ class ErestorOfTheCouncilEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Set playerIds = (Set) getValue("votedAgainst"); - int count = 0; + int scryCount = 0; for (UUID opponentId : game.getOpponents(source.getControllerId())) { if (playerIds.contains(opponentId)) { - count++; + scryCount++; } else { new TreasureToken().putOntoBattlefield(1, game, source, opponentId); } } - if (count > 0) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - player.scry(count, source, game); - } + if (scryCount > 0) { + new ScryEffect(scryCount).apply(game, source); } return true; } diff --git a/Mage.Sets/src/mage/cards/e/ErgRaiders.java b/Mage.Sets/src/mage/cards/e/ErgRaiders.java index 9a3a486642a..f4a6bf72b5a 100644 --- a/Mage.Sets/src/mage/cards/e/ErgRaiders.java +++ b/Mage.Sets/src/mage/cards/e/ErgRaiders.java @@ -1,16 +1,13 @@ package mage.cards.e; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.InvertCondition; import mage.abilities.condition.common.AttackedThisTurnSourceCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.decorator.ConditionalOneShotEffect; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageControllerEffect; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -18,13 +15,17 @@ import mage.constants.SubType; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.UUID; + /** - * * @author awjackson - * */ public final class ErgRaiders extends CardImpl { + private static final Condition condition = new InvertCondition( + AttackedThisTurnSourceCondition.instance, "{this} didn't attack this turn" + ); + public ErgRaiders(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); this.subtype.add(SubType.HUMAN, SubType.WARRIOR); @@ -32,16 +33,10 @@ public final class ErgRaiders extends CardImpl { this.toughness = new MageInt(3); // At the beginning of your end step, if Erg Raiders didn't attack this turn, Erg Raiders deals 2 damage to you unless it came under your control this turn. - Effect effect = new ConditionalOneShotEffect( - new DamageControllerEffect(2), - ErgRaidersCondition.instance, + this.addAbility(new BeginningOfEndStepTriggeredAbility(new ConditionalOneShotEffect( + new DamageControllerEffect(2), ErgRaidersCondition.instance, "{this} deals 2 damage to you unless it came under your control this turn" - ); - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new BeginningOfEndStepTriggeredAbility(effect), - new InvertCondition(AttackedThisTurnSourceCondition.instance), - "At the beginning of your end step, if {this} didn't attack this turn, {this} deals 2 damage to you unless it came under your control this turn."); - this.addAbility(ability); + )).withInterveningIf(condition)); } private ErgRaiders(final ErgRaiders card) { diff --git a/Mage.Sets/src/mage/cards/e/ErrandRiderOfGondor.java b/Mage.Sets/src/mage/cards/e/ErrandRiderOfGondor.java index 4089f0f0e05..8e97a244f6e 100644 --- a/Mage.Sets/src/mage/cards/e/ErrandRiderOfGondor.java +++ b/Mage.Sets/src/mage/cards/e/ErrandRiderOfGondor.java @@ -85,6 +85,6 @@ class ErrandRiderOfGondorEffect extends OneShotEffect { TargetCard target = new TargetCardInHand(); player.choose(outcome, player.getHand(), target, source, game); Card card = game.getCard(target.getFirstTarget()); - return card == null || player.putCardsOnBottomOfLibrary(card, game, source, false); + return card == null || player.putCardsOnBottomOfLibrary(card, game, source); } } diff --git a/Mage.Sets/src/mage/cards/e/EshkiTemursRoar.java b/Mage.Sets/src/mage/cards/e/EshkiTemursRoar.java index 2e8c907d1ef..ca711ba0931 100644 --- a/Mage.Sets/src/mage/cards/e/EshkiTemursRoar.java +++ b/Mage.Sets/src/mage/cards/e/EshkiTemursRoar.java @@ -75,15 +75,11 @@ enum EshkiTemursRoarCondition implements Condition { @Override public boolean apply(Game game, Ability source) { - return CardUtil.castStream( - source.getEffects() - .stream() - .map(effect -> effect.getValue("spellCast")), - Spell.class - ) - .findFirst() + return CardUtil + .getEffectValueFromAbility(source, "spellCast", Spell.class) .map(Spell::getPower) .map(MageInt::getValue) - .orElse(0) >= amount; + .filter(x -> x >= amount) + .isPresent(); } } diff --git a/Mage.Sets/src/mage/cards/e/EsperOrigins.java b/Mage.Sets/src/mage/cards/e/EsperOrigins.java new file mode 100644 index 00000000000..92025c053fb --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EsperOrigins.java @@ -0,0 +1,91 @@ +package mage.cards.e; + +import mage.abilities.Ability; +import mage.abilities.condition.common.CastFromGraveyardSourceCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.keyword.SurveilEffect; +import mage.abilities.keyword.FlashbackAbility; +import mage.abilities.keyword.TransformAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.counters.Counters; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EsperOrigins extends CardImpl { + + public EsperOrigins(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}"); + this.secondSideCardClazz = mage.cards.s.SummonEsperMaduin.class; + + // Surveil 2. You gain 2 life. If this spell was cast from a graveyard, exile it, then put it onto the battlefield transformed under its owner's control with a finality counter on it. + this.getSpellAbility().addEffect(new SurveilEffect(2, false)); + this.getSpellAbility().addEffect(new GainLifeEffect(2)); + this.getSpellAbility().addEffect(new EsperOriginsEffect()); + this.addAbility(new TransformAbility()); + + // Flashback {3}{G} + this.addAbility(new FlashbackAbility(this, new ManaCostsImpl<>("{3}{G}"))); + } + + private EsperOrigins(final EsperOrigins card) { + super(card); + } + + @Override + public EsperOrigins copy() { + return new EsperOrigins(this); + } +} + +class EsperOriginsEffect extends OneShotEffect { + + EsperOriginsEffect() { + super(Outcome.Benefit); + staticText = "If this spell was cast from a graveyard, exile it, then put it onto the battlefield " + + "transformed under its owner's control with a finality counter on it."; + } + + private EsperOriginsEffect(final EsperOriginsEffect effect) { + super(effect); + } + + @Override + public EsperOriginsEffect copy() { + return new EsperOriginsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (!CastFromGraveyardSourceCondition.instance.apply(game, source)) { + return false; + } + Player player = game.getPlayer(source.getControllerId()); + Spell spell = game.getSpell(source.getId()); + if (player == null || spell == null) { + return false; + } + Card card = spell.getMainCard(); + player.moveCards(card, Zone.EXILED, source, game); + game.setEnterWithCounters(card.getId(), new Counters(CounterType.FINALITY.createInstance())); + game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + card.getId(), Boolean.TRUE); + player.moveCards( + card, Zone.BATTLEFIELD, source, game, false, + false, true, null + ); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/e/EsperTerra.java b/Mage.Sets/src/mage/cards/e/EsperTerra.java index 9397c73f147..884d7dfb4c4 100644 --- a/Mage.Sets/src/mage/cards/e/EsperTerra.java +++ b/Mage.Sets/src/mage/cards/e/EsperTerra.java @@ -6,7 +6,7 @@ import mage.abilities.Ability; import mage.abilities.common.SagaAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenCopyTargetEffect; -import mage.abilities.effects.common.ExileAndReturnSourceEffect; +import mage.abilities.effects.common.ExileSourceAndReturnFaceUpEffect; import mage.abilities.effects.mana.BasicManaEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.HasteAbility; @@ -62,7 +62,7 @@ public final class EsperTerra extends CardImpl { new BasicManaEffect(new Mana( 2, 2, 2, 2, 2, 0, 0, 0 )).setText("add {W}{W}, {U}{U}, {B}{B}, {R}{R}, and {G}{G}"), - new ExileAndReturnSourceEffect(PutCards.BATTLEFIELD)); + new ExileSourceAndReturnFaceUpEffect()); this.addAbility(sagaAbility); // Flying diff --git a/Mage.Sets/src/mage/cards/e/EssenceHarvest.java b/Mage.Sets/src/mage/cards/e/EssenceHarvest.java index 644d9f273ce..ab504b529fd 100644 --- a/Mage.Sets/src/mage/cards/e/EssenceHarvest.java +++ b/Mage.Sets/src/mage/cards/e/EssenceHarvest.java @@ -1,21 +1,16 @@ package mage.cards.e; -import java.util.List; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author North */ public final class EssenceHarvest extends CardImpl { @@ -24,7 +19,15 @@ public final class EssenceHarvest extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}"); // Target player loses X life and you gain X life, where X is the greatest power among creatures you control. - this.getSpellAbility().addEffect(new EssenceHarvestEffect()); + this.getSpellAbility().addEffect( + new LoseLifeTargetEffect(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES) + .setText("target player loses X life") + ); + this.getSpellAbility().addEffect( + new GainLifeEffect(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES) + .setText("and you gain X life, where X is the greatest power among creatures you control") + ); + this.getSpellAbility().addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint()); this.getSpellAbility().addTarget(new TargetPlayer()); } @@ -36,46 +39,4 @@ public final class EssenceHarvest extends CardImpl { public EssenceHarvest copy() { return new EssenceHarvest(this); } -} - -class EssenceHarvestEffect extends OneShotEffect { - - EssenceHarvestEffect() { - super(Outcome.Damage); - this.staticText = "Target player loses X life and you gain X life, where X is the greatest power among creatures you control"; - } - - private EssenceHarvestEffect(final EssenceHarvestEffect effect) { - super(effect); - } - - @Override - public EssenceHarvestEffect copy() { - return new EssenceHarvestEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Player targetPlayer = game.getPlayer(source.getFirstTarget()); - if (controller != null - && targetPlayer != null) { - List creatures = game.getBattlefield().getAllActivePermanents( - StaticFilters.FILTER_PERMANENT_CREATURE, controller.getId(), game); - int amount = 0; - for (Permanent creature : creatures) { - int power = creature.getPower().getValue(); - if (amount < power) { - amount = power; - } - } - - if (amount > 0) { - targetPlayer.loseLife(amount, game, source, false); - controller.gainLife(amount, game, source); - } - return true; - } - return false; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/e/EstinienVarlineau.java b/Mage.Sets/src/mage/cards/e/EstinienVarlineau.java new file mode 100644 index 00000000000..fdcd18ecde3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EstinienVarlineau.java @@ -0,0 +1,150 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.LoseLifeSourceControllerEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.triggers.BeginningOfSecondMainTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.DamagedPlayerEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.watchers.Watcher; + +import java.util.*; + +/** + * @author TheElk801 + */ +public final class EstinienVarlineau extends CardImpl { + + public EstinienVarlineau(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.ELF); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Whenever you cast a noncreature spell, put a +1/+1 counter on Estinien Varlineau. It gains flying until end of turn. + Ability ability = new SpellCastControllerTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), + StaticFilters.FILTER_SPELL_A_NON_CREATURE, false + ); + ability.addEffect(new GainAbilitySourceEffect( + FlyingAbility.getInstance(), Duration.EndOfTurn + ).setText("It gains flying until end of turn")); + this.addAbility(ability); + + // At the beginning of your second main phase, you draw X cards and lose X life, where X is the number of your opponents who were dealt combat damage by Estinien Varlineau or a Dragon this turn. + ability = new BeginningOfSecondMainTriggeredAbility( + new DrawCardSourceControllerEffect(EstinienVarlineauValue.instance) + .setText("you draw X cards"), false + ); + ability.addEffect(new LoseLifeSourceControllerEffect(EstinienVarlineauValue.instance) + .setText("and lose X life, where X is the number of your opponents " + + "who were dealt combat damage by {this} or a Dragon this turn")); + this.addAbility(ability, new EstinienVarlineauWatcher()); + } + + private EstinienVarlineau(final EstinienVarlineau card) { + super(card); + } + + @Override + public EstinienVarlineau copy() { + return new EstinienVarlineau(this); + } +} + +enum EstinienVarlineauValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return EstinienVarlineauWatcher.getCount(game, sourceAbility); + } + + @Override + public EstinienVarlineauValue copy() { + return this; + } + + @Override + public String getMessage() { + return ""; + } + + @Override + public String toString() { + return "X"; + } +} + +class EstinienVarlineauWatcher extends Watcher { + + private final Map> morMap = new HashMap<>(); + private final Set dragonSet = new HashSet<>(); + + EstinienVarlineauWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.DAMAGED_PLAYER) { + return; + } + DamagedPlayerEvent dEvent = (DamagedPlayerEvent) event; + if (!dEvent.isCombatDamage()) { + return; + } + Permanent permanent = game.getPermanent(dEvent.getSourceId()); + if (permanent == null) { + return; + } + morMap.computeIfAbsent( + new MageObjectReference(permanent, game), x -> new HashSet<>() + ).add(dEvent.getTargetId()); + if (permanent.hasSubtype(SubType.DRAGON, game)) { + dragonSet.add(dEvent.getTargetId()); + } + } + + @Override + public void reset() { + super.reset(); + morMap.clear(); + dragonSet.clear(); + } + + static int getCount(Game game, Ability source) { + return game.getState() + .getWatcher(EstinienVarlineauWatcher.class) + .computeCount( + game.getOpponents(source.getControllerId()), + new MageObjectReference(source.getSourcePermanentOrLKI(game), game) + ); + } + + private int computeCount(Set opponents, MageObjectReference mor) { + return opponents + .stream() + .filter(uuid -> dragonSet.contains(uuid) || morMap.getOrDefault(mor, Collections.emptySet()).contains(uuid)) + .mapToInt(x -> 1) + .sum(); + } +} diff --git a/Mage.Sets/src/mage/cards/e/Ether.java b/Mage.Sets/src/mage/cards/e/Ether.java new file mode 100644 index 00000000000..2b22a273dd5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/Ether.java @@ -0,0 +1,38 @@ +package mage.cards.e; + +import mage.abilities.common.delayed.CopyNextSpellDelayedTriggeredAbility; +import mage.abilities.costs.common.ExileSourceCost; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.mana.BasicManaAbility; +import mage.abilities.mana.BlueManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Ether extends CardImpl { + + public Ether(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}{U}"); + + // {T}, Exile this artifact: Add {U}. When you next cast an instant or sorcery spell this turn, copy that spell. You may choose new targets for the copy. + BasicManaAbility ability = new BlueManaAbility(); + ability.addCost(new ExileSourceCost()); + ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new CopyNextSpellDelayedTriggeredAbility())); + ability.setUndoPossible(false); // exiles itself and creates a delayed trigger so undo should be disabled + this.addAbility(ability); + } + + private Ether(final Ether card) { + super(card); + } + + @Override + public Ether copy() { + return new Ether(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EtherWell.java b/Mage.Sets/src/mage/cards/e/EtherWell.java index cb578d4bd75..ddb7ee257f1 100644 --- a/Mage.Sets/src/mage/cards/e/EtherWell.java +++ b/Mage.Sets/src/mage/cards/e/EtherWell.java @@ -65,10 +65,10 @@ class EtherWellEffect extends OneShotEffect { && player.chooseUse(outcome, "Put " + permanent.getLogName() + " on the bottom of its owner's library?", source, game )) { - player.putCardsOnBottomOfLibrary(permanent, game, source, true); + player.putCardsOnBottomOfLibrary(permanent, game, source); return true; } player.putCardsOnTopOfLibrary(new CardsImpl(permanent), game, source, true); return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/e/EtrataTheSilencer.java b/Mage.Sets/src/mage/cards/e/EtrataTheSilencer.java index 849f43f2213..58f5cad072c 100644 --- a/Mage.Sets/src/mage/cards/e/EtrataTheSilencer.java +++ b/Mage.Sets/src/mage/cards/e/EtrataTheSilencer.java @@ -18,7 +18,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -44,7 +44,7 @@ public final class EtrataTheSilencer extends CardImpl { // Whenever Etrata deals combat damage to a player, exile target creature that player controls and put a hit counter on that card. That player loses the game if they own three or more exiled card with hit counters on them. Etrata's owner shuffles Etrata into their library. Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new EtrataTheSilencerEffect(), false, true); ability.addTarget(new TargetPermanent(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/e/EverybodyLives.java b/Mage.Sets/src/mage/cards/e/EverybodyLives.java new file mode 100644 index 00000000000..f719f9fb6b6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EverybodyLives.java @@ -0,0 +1,108 @@ +package mage.cards.e; + +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.keyword.HexproofAbility; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author padfoot + */ +public final class EverybodyLives extends CardImpl { + + public EverybodyLives(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); + + // All creatures gain hexproof and indestructible until end of turn. + this.getSpellAbility().addEffect(new GainAbilityAllEffect( + HexproofAbility.getInstance(), + Duration.EndOfTurn, + StaticFilters.FILTER_PERMANENT_ALL_CREATURES + ).setText("all creatures gain hexproof")); + this.getSpellAbility().addEffect(new GainAbilityAllEffect( + IndestructibleAbility.getInstance(), + Duration.EndOfTurn, + StaticFilters.FILTER_PERMANENT_ALL_CREATURES + ).setText("and indestructible until end of turn")); + + // Players gain hexproof until end of turn. Players can't lose life this turn and players can't lose the game or win the game this turn. + this.getSpellAbility().addEffect(new EverybodyLivesPlayerEffect()); + this.getSpellAbility().addEffect(new EverybodyLivesCantLoseOrWinGameEffect()); + } + + private EverybodyLives(final EverybodyLives card) { + super(card); + } + + @Override + public EverybodyLives copy() { + return new EverybodyLives(this); + } +} + +class EverybodyLivesPlayerEffect extends ContinuousEffectImpl { + + EverybodyLivesPlayerEffect() { + super(Duration.EndOfTurn, Layer.PlayerEffects, SubLayer.NA, Outcome.AddAbility); + this.staticText = "Players gain hexproof until end of turn. Players can't lose life this turn"; + } + + private EverybodyLivesPlayerEffect(final EverybodyLivesPlayerEffect effect) { + super(effect); + } + + @Override + public EverybodyLivesPlayerEffect copy() { + return new EverybodyLivesPlayerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + player.addAbility(HexproofAbility.getInstance()); + player.setCanLoseLife(false); + } + } + return true; + } +} + +class EverybodyLivesCantLoseOrWinGameEffect extends ContinuousRuleModifyingEffectImpl { + + EverybodyLivesCantLoseOrWinGameEffect() { + super(Duration.EndOfTurn, Outcome.Benefit, false, false); + staticText = "and players can't lose the game or win the game this turn"; + } + + private EverybodyLivesCantLoseOrWinGameEffect(final EverybodyLivesCantLoseOrWinGameEffect effect) { + super(effect); + } + + @Override + public EverybodyLivesCantLoseOrWinGameEffect copy() { + return new EverybodyLivesCantLoseOrWinGameEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return (event.getType() == GameEvent.EventType.LOSES || event.getType() == GameEvent.EventType.WINS); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/e/EvilReawakened.java b/Mage.Sets/src/mage/cards/e/EvilReawakened.java index 745c306a11c..ccda5514808 100644 --- a/Mage.Sets/src/mage/cards/e/EvilReawakened.java +++ b/Mage.Sets/src/mage/cards/e/EvilReawakened.java @@ -19,7 +19,7 @@ public final class EvilReawakened extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}"); // Return target creature card from your graveyard to the battlefield with two additional +1/+1 counters on it. - this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldWithCounterTargetEffect(true, CounterType.P1P1.createInstance())); + this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldWithCounterTargetEffect(true, CounterType.P1P1.createInstance(2))); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); } diff --git a/Mage.Sets/src/mage/cards/e/EvolvedSpinoderm.java b/Mage.Sets/src/mage/cards/e/EvolvedSpinoderm.java index 0403b2d3f31..8d9789f0196 100644 --- a/Mage.Sets/src/mage/cards/e/EvolvedSpinoderm.java +++ b/Mage.Sets/src/mage/cards/e/EvolvedSpinoderm.java @@ -2,7 +2,6 @@ package mage.cards.e; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; @@ -15,9 +14,11 @@ import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; import mage.abilities.keyword.HexproofAbility; import mage.abilities.keyword.TrampleAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.SubType; import mage.counters.CounterType; @@ -28,8 +29,8 @@ import java.util.UUID; */ public final class EvolvedSpinoderm extends CardImpl { - private static final Condition condition1 = new SourceHasCounterCondition(CounterType.OIL, 3); - private static final Condition condition2 = new SourceHasCounterCondition(CounterType.OIL, 0, 0); + private static final Condition condition1 = new SourceHasCounterCondition(CounterType.OIL, ComparisonType.OR_LESS, 2); + private static final Condition condition2 = new SourceHasCounterCondition(CounterType.OIL, ComparisonType.EQUAL_TO, 0); public EvolvedSpinoderm(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}"); diff --git a/Mage.Sets/src/mage/cards/e/ExcaliburII.java b/Mage.Sets/src/mage/cards/e/ExcaliburII.java new file mode 100644 index 00000000000..c37c86a9e82 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ExcaliburII.java @@ -0,0 +1,52 @@ +package mage.cards.e; + +import mage.abilities.common.GainLifeControllerTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CountersSourceCount; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.EquipAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ExcaliburII extends CardImpl { + + private static final DynamicValue xValue = new CountersSourceCount(CounterType.CHARGE); + + public ExcaliburII(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.EQUIPMENT); + + // Whenever you gain life, put a charge counter on Excalibur II. + this.addAbility(new GainLifeControllerTriggeredAbility( + new AddCountersSourceEffect(CounterType.CHARGE.createInstance()) + )); + + // Equipped creature gets +1/+1 for each charge counter on Excalibur II. + this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(xValue, xValue))); + + // Equip {3} + this.addAbility(new EquipAbility(3)); + } + + private ExcaliburII(final ExcaliburII card) { + super(card); + } + + @Override + public ExcaliburII copy() { + return new ExcaliburII(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/ExcavationElephant.java b/Mage.Sets/src/mage/cards/e/ExcavationElephant.java index 7006d1bb189..c823d084b20 100644 --- a/Mage.Sets/src/mage/cards/e/ExcavationElephant.java +++ b/Mage.Sets/src/mage/cards/e/ExcavationElephant.java @@ -1,10 +1,9 @@ package mage.cards.e; import mage.MageInt; -import mage.abilities.TriggeredAbility; +import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.KickedCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.keyword.KickerAbility; import mage.cards.CardImpl; @@ -32,13 +31,11 @@ public final class ExcavationElephant extends CardImpl { this.addAbility(new KickerAbility("{1}{W}")); // When Excavation Elephant enters the battlefield, if it was kicked, return target artifact card from your graveyard to your hand. - TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect(), false); + Ability ability = new EntersBattlefieldTriggeredAbility( + new ReturnToHandTargetEffect(), false + ).withInterveningIf(KickedCondition.ONCE); ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_ARTIFACT_FROM_YOUR_GRAVEYARD)); - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - ability, KickedCondition.ONCE, - "When {this} enters, if it was kicked, " - + "return target artifact card from your graveyard to your hand." - )); + this.addAbility(ability); } private ExcavationElephant(final ExcavationElephant card) { diff --git a/Mage.Sets/src/mage/cards/e/ExdeathVoidWarlock.java b/Mage.Sets/src/mage/cards/e/ExdeathVoidWarlock.java new file mode 100644 index 00000000000..4a941ceec6a --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ExdeathVoidWarlock.java @@ -0,0 +1,61 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.CardsInControllerGraveyardCondition; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.TransformAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ExdeathVoidWarlock extends CardImpl { + + private static final Condition condition = new CardsInControllerGraveyardCondition(6, StaticFilters.FILTER_CARD_PERMANENTS); + private static final Hint hint = new ValueHint( + "Permanent cards in your graveyard", + new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_PERMANENT) + ); + + public ExdeathVoidWarlock(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{G}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + this.secondSideCardClazz = mage.cards.n.NeoExdeathDimensionsEnd.class; + + // When Exdeath enters, you gain 3 life. + this.addAbility(new EntersBattlefieldTriggeredAbility(new GainLifeEffect(3))); + + // At the beginning of your end step, if there are six or more permanent cards in your graveyard, transform Exdeath. + this.addAbility(new TransformAbility()); + this.addAbility(new BeginningOfEndStepTriggeredAbility(new TransformSourceEffect()) + .withInterveningIf(condition).addHint(hint)); + } + + private ExdeathVoidWarlock(final ExdeathVoidWarlock card) { + super(card); + } + + @Override + public ExdeathVoidWarlock copy() { + return new ExdeathVoidWarlock(this); + } +} diff --git a/Mage.Sets/src/mage/cards/e/ExperimentOne.java b/Mage.Sets/src/mage/cards/e/ExperimentOne.java index dd3989f1704..b262f1def2a 100644 --- a/Mage.Sets/src/mage/cards/e/ExperimentOne.java +++ b/Mage.Sets/src/mage/cards/e/ExperimentOne.java @@ -1,6 +1,5 @@ package mage.cards.e; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.RemoveCountersSourceCost; @@ -10,11 +9,11 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; import mage.counters.CounterType; +import java.util.UUID; + /** - * * @author Plopman */ public final class ExperimentOne extends CardImpl { @@ -32,7 +31,7 @@ public final class ExperimentOne extends CardImpl { this.addAbility(new EvolveAbility()); //Remove two +1/+1 counters from Experiment One: Regenerate Experiment One. - this.addAbility(new SimpleActivatedAbility(new RegenerateSourceEffect(), new RemoveCountersSourceCost(CounterType.P1P1.createInstance(2)))); + this.addAbility(new SimpleActivatedAbility(new RegenerateSourceEffect("it"), new RemoveCountersSourceCost(CounterType.P1P1.createInstance(2)))); } private ExperimentOne(final ExperimentOne card) { diff --git a/Mage.Sets/src/mage/cards/e/ExplosiveGetaway.java b/Mage.Sets/src/mage/cards/e/ExplosiveGetaway.java index 5410a4bcbc1..4f2f182a89c 100644 --- a/Mage.Sets/src/mage/cards/e/ExplosiveGetaway.java +++ b/Mage.Sets/src/mage/cards/e/ExplosiveGetaway.java @@ -1,43 +1,29 @@ package mage.cards.e; -import java.util.UUID; -import java.util.function.Predicate; - -import mage.abilities.Ability; -import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.effects.common.DamageAllEffect; import mage.abilities.effects.common.ExileReturnBattlefieldNextEndStepTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.predicate.Predicates; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author Jmlundeen */ public final class ExplosiveGetaway extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or creature"); - - static { - filter.add(Predicates.or( - CardType.ARTIFACT.getPredicate(), - CardType.CREATURE.getPredicate() - )); - } public ExplosiveGetaway(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}{W}"); - // Exile up to one target artifact or creature. Return it to the battlefield under its owner's control at the beginning of the next end step. this.getSpellAbility().addEffect(new ExileReturnBattlefieldNextEndStepTargetEffect().withTextThatCard(false)); - this.getSpellAbility().addTarget(new TargetPermanent(0, 1, filter)); + this.getSpellAbility().addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_CREATURE)); + // Explosive Getaway deals 4 damage to each creature. - this.getSpellAbility().addEffect(new DamageAllEffect(4, StaticFilters.FILTER_PERMANENT_ALL_CREATURES).concatBy("
    ")); + this.getSpellAbility().addEffect(new DamageAllEffect(4, StaticFilters.FILTER_PERMANENT_CREATURE).concatBy("
    ")); } private ExplosiveGetaway(final ExplosiveGetaway card) { diff --git a/Mage.Sets/src/mage/cards/e/ExpressiveIteration.java b/Mage.Sets/src/mage/cards/e/ExpressiveIteration.java index 983ec9c9359..2d8c5ad4fe2 100644 --- a/Mage.Sets/src/mage/cards/e/ExpressiveIteration.java +++ b/Mage.Sets/src/mage/cards/e/ExpressiveIteration.java @@ -88,7 +88,7 @@ class ExpressiveIterationEffect extends OneShotEffect { player.choose(outcome, cards, target, source, game); card = game.getCard(target.getFirstTarget()); if (card != null) { - player.putCardsOnBottomOfLibrary(card, game, source, false); + player.putCardsOnBottomOfLibrary(card, game, source); cards.remove(card); } if (cards.isEmpty()) { diff --git a/Mage.Sets/src/mage/cards/e/ExtractionSpecialist.java b/Mage.Sets/src/mage/cards/e/ExtractionSpecialist.java index 9a77fe5e04f..010b2004d7d 100644 --- a/Mage.Sets/src/mage/cards/e/ExtractionSpecialist.java +++ b/Mage.Sets/src/mage/cards/e/ExtractionSpecialist.java @@ -18,6 +18,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -86,7 +87,7 @@ class ExtractionSpecialistEffect extends OneShotEffect { return false; } player.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null && source.getSourcePermanentIfItStillExists(game) != null && source.isControlledBy(game.getControllerId(source.getSourceId()))) { diff --git a/Mage.Sets/src/mage/cards/e/ExtraplanarLens.java b/Mage.Sets/src/mage/cards/e/ExtraplanarLens.java index 57b52b8beeb..2c967c42ce3 100644 --- a/Mage.Sets/src/mage/cards/e/ExtraplanarLens.java +++ b/Mage.Sets/src/mage/cards/e/ExtraplanarLens.java @@ -1,7 +1,6 @@ package mage.cards.e; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.Effect; @@ -14,43 +13,36 @@ import mage.cards.CardSetInfo; import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterLandPermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ManaEvent; import mage.game.events.TappedForManaEvent; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.common.TargetLandPermanent; +import mage.target.TargetPermanent; import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class ExtraplanarLens extends CardImpl { - private static final FilterLandPermanent filter = new FilterLandPermanent("land you control"); - - static { - filter.add(TargetController.YOU.getControllerPredicate()); - } - public ExtraplanarLens(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); // Imprint - When Extraplanar Lens enters the battlefield, you may exile target land you control. Ability ability = new EntersBattlefieldTriggeredAbility(new ExtraplanarLensImprintEffect(), true); ability.setAbilityWord(AbilityWord.IMPRINT); - ability.addTarget(new TargetLandPermanent(filter)); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND)); this.addAbility(ability); // Whenever a land with the same name as the exiled card is tapped for mana, its controller adds one mana of any type that land produced. this.addAbility(new ExtraplanarLensTriggeredAbility()); - } private ExtraplanarLens(final ExtraplanarLens card) { diff --git a/Mage.Sets/src/mage/cards/e/ExtricatorOfSin.java b/Mage.Sets/src/mage/cards/e/ExtricatorOfSin.java index 9187fc51e56..e52dc72a2a3 100644 --- a/Mage.Sets/src/mage/cards/e/ExtricatorOfSin.java +++ b/Mage.Sets/src/mage/cards/e/ExtricatorOfSin.java @@ -1,26 +1,26 @@ package mage.cards.e; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.dynamicvalue.common.CardTypesInGraveyardCount; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.DeliriumCondition; import mage.abilities.costs.common.SacrificeTargetCost; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.dynamicvalue.common.CardTypesInGraveyardCount; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.keyword.TransformAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.EldraziHorrorToken; +import java.util.UUID; + /** * @author LevelX2 */ @@ -47,11 +47,9 @@ public final class ExtricatorOfSin extends CardImpl { // Delirium — At the beginning of your upkeep, if there are four or more card types among cards in your graveyard, transform Extricator of Sin. this.addAbility(new TransformAbility()); - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect()), - DeliriumCondition.instance, - "Delirium — At the beginning of your upkeep, if there are four or more card types among cards in your graveyard, " - + " transform {this}.") + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new TransformSourceEffect()) + .withInterveningIf(DeliriumCondition.instance) + .setAbilityWord(AbilityWord.DELIRIUM) .addHint(CardTypesInGraveyardCount.YOU.getHint())); } diff --git a/Mage.Sets/src/mage/cards/e/EyeOfNidhogg.java b/Mage.Sets/src/mage/cards/e/EyeOfNidhogg.java index cff808d64d4..abd58a38e60 100644 --- a/Mage.Sets/src/mage/cards/e/EyeOfNidhogg.java +++ b/Mage.Sets/src/mage/cards/e/EyeOfNidhogg.java @@ -42,13 +42,13 @@ public final class EyeOfNidhogg extends CardImpl { // Enchanted creature is a black Dragon with base power and toughness 4/2, has flying and deathtouch, and is goaded. Ability ability = new SimpleStaticAbility(new SetCardColorAttachedEffect( ObjectColor.BLACK, Duration.WhileControlled, AttachmentType.AURA - ).setText("enchanted creature is a black")); + ).setText("enchanted creature is a black Dragon")); ability.addEffect(new AddCardSubtypeAttachedEffect( SubType.DRAGON, AttachmentType.AURA - ).setText("Dragon with power")); + ).setText("with base power")); ability.addEffect(new SetBasePowerToughnessAttachedEffect( - 4, 2, - AttachmentType.AURA).setText("and toughness 4/2")); + 4, 2, AttachmentType.AURA + ).setText("and toughness 4/2")); ability.addEffect(new GainAbilityAttachedEffect( FlyingAbility.getInstance(), AttachmentType.AURA ).setText(", has flying")); diff --git a/Mage.Sets/src/mage/cards/e/EyesOfTheWisent.java b/Mage.Sets/src/mage/cards/e/EyesOfTheWisent.java index 4a97eda2c78..648908b4363 100644 --- a/Mage.Sets/src/mage/cards/e/EyesOfTheWisent.java +++ b/Mage.Sets/src/mage/cards/e/EyesOfTheWisent.java @@ -3,9 +3,7 @@ package mage.cards.e; import mage.ObjectColor; import mage.abilities.common.SpellCastOpponentTriggeredAbility; import mage.abilities.condition.common.MyTurnCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; -import mage.abilities.hint.common.MyTurnHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -32,11 +30,9 @@ public final class EyesOfTheWisent extends CardImpl { this.subtype.add(SubType.ELEMENTAL); // Whenever an opponent casts a blue spell during your turn, you may create a 4/4 green Elemental creature token. - this.addAbility(new ConditionalTriggeredAbility( - new SpellCastOpponentTriggeredAbility(new CreateTokenEffect(new Elemental44GreenToken()), filter, true), - MyTurnCondition.instance, - "Whenever an opponent casts a blue spell during your turn, you may create a 4/4 green Elemental creature token." - ).addHint(MyTurnHint.instance)); + this.addAbility(new SpellCastOpponentTriggeredAbility( + new CreateTokenEffect(new Elemental44GreenToken()), filter, true + ).withTriggerCondition(MyTurnCondition.instance)); } private EyesOfTheWisent(final EyesOfTheWisent card) { diff --git a/Mage.Sets/src/mage/cards/f/FabledPassage.java b/Mage.Sets/src/mage/cards/f/FabledPassage.java index ce6a2c90e8f..5301cd4bc35 100644 --- a/Mage.Sets/src/mage/cards/f/FabledPassage.java +++ b/Mage.Sets/src/mage/cards/f/FabledPassage.java @@ -16,9 +16,7 @@ import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.TargetCard; import mage.target.common.TargetCardInLibrary; -import mage.target.targetpointer.FirstTargetPointer; import mage.target.targetpointer.FixedTarget; import java.util.UUID; @@ -53,7 +51,7 @@ class FabledPassageSearchForLandEffect extends OneShotEffect { FabledPassageSearchForLandEffect() { super(Outcome.PutCardInPlay); - staticText = "Search your library for a basic land card, put it onto the battlefield tapped, then shuffle "; + staticText = "Search your library for a basic land card, put it onto the battlefield tapped, then shuffle"; } private FabledPassageSearchForLandEffect(final FabledPassageSearchForLandEffect effect) { @@ -129,4 +127,4 @@ class FabledPassageUntapLandEffect extends OneShotEffect { public FabledPassageUntapLandEffect copy() { return new FabledPassageUntapLandEffect(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/f/FaerieTauntings.java b/Mage.Sets/src/mage/cards/f/FaerieTauntings.java index 1b3579b8ee3..5334baa9d9e 100644 --- a/Mage.Sets/src/mage/cards/f/FaerieTauntings.java +++ b/Mage.Sets/src/mage/cards/f/FaerieTauntings.java @@ -1,9 +1,7 @@ - package mage.cards.f; import mage.abilities.common.SpellCastControllerTriggeredAbility; -import mage.abilities.condition.common.OnOpponentsTurnCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.condition.common.OpponentsTurnCondition; import mage.abilities.effects.common.LoseLifeOpponentsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -13,19 +11,18 @@ import mage.constants.SubType; import java.util.UUID; /** - * * @author Styxo */ public final class FaerieTauntings extends CardImpl { public FaerieTauntings(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.KINDRED,CardType.ENCHANTMENT},"{2}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.KINDRED, CardType.ENCHANTMENT}, "{2}{B}"); this.subtype.add(SubType.FAERIE); // Whenever you cast a spell during an opponent's turn, you may have each opponent lose 1 life - this.addAbility(new ConditionalTriggeredAbility(new SpellCastControllerTriggeredAbility(new LoseLifeOpponentsEffect(1), true), OnOpponentsTurnCondition.instance, - "Whenever you cast a spell during an opponent's turn, you may have each opponent lose 1 life.")); - + this.addAbility(new SpellCastControllerTriggeredAbility( + new LoseLifeOpponentsEffect(1), true + ).withTriggerCondition(OpponentsTurnCondition.instance)); } private FaerieTauntings(final FaerieTauntings card) { @@ -36,4 +33,4 @@ public final class FaerieTauntings extends CardImpl { public FaerieTauntings copy() { return new FaerieTauntings(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/f/FaithboundJudge.java b/Mage.Sets/src/mage/cards/f/FaithboundJudge.java index cbda9137108..0098ae14971 100644 --- a/Mage.Sets/src/mage/cards/f/FaithboundJudge.java +++ b/Mage.Sets/src/mage/cards/f/FaithboundJudge.java @@ -1,21 +1,21 @@ package mage.cards.f; import mage.MageInt; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.decorator.ConditionalAsThoughEffect; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.combat.CanAttackAsThoughItDidntHaveDefenderSourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.keyword.DefenderAbility; import mage.abilities.keyword.DisturbAbility; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.VigilanceAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.Duration; import mage.constants.SubType; import mage.counters.CounterType; @@ -27,7 +27,7 @@ import java.util.UUID; */ public final class FaithboundJudge extends CardImpl { - private static final Condition condition1 = new SourceHasCounterCondition(CounterType.JUDGMENT, 0, 2); + private static final Condition condition1 = new SourceHasCounterCondition(CounterType.JUDGMENT, ComparisonType.OR_LESS, 2); private static final Condition condition2 = new SourceHasCounterCondition(CounterType.JUDGMENT, 3); public FaithboundJudge(UUID ownerId, CardSetInfo setInfo) { @@ -49,18 +49,16 @@ public final class FaithboundJudge extends CardImpl { this.addAbility(VigilanceAbility.getInstance()); // At the beginning of your upkeep, if Faithbound Judge has two or fewer judgment counters on it, put a judgment counter on it. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility( - new AddCountersSourceEffect(CounterType.JUDGMENT.createInstance()), false - ), condition1, "At the beginning of your upkeep, if {this} has " + - "two or fewer judgment counters on it, put a judgment counter on it." - )); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new AddCountersSourceEffect(CounterType.JUDGMENT.createInstance()) + .setText("put a judgment counter on it"), false + ).withInterveningIf(condition1)); // As long as Faithbound Judge has three or more judgment counters on it, it can attack as though it didn't have defender. this.addAbility(new SimpleStaticAbility(new ConditionalAsThoughEffect( new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.WhileOnBattlefield), condition2 - ).setText("as long as {this} has three or more judgment counters on it," + - " it can attack as though it didn't have defender"))); + ).setText("as long as {this} has three or more judgment counters on it, " + + "it can attack as though it didn't have defender"))); // Disturb {5}{W}{W} this.addAbility(new DisturbAbility(this, "{5}{W}{W}")); diff --git a/Mage.Sets/src/mage/cards/f/FaithfulPikemaster.java b/Mage.Sets/src/mage/cards/f/FaithfulPikemaster.java index a990fe5b854..7ec554a3515 100644 --- a/Mage.Sets/src/mage/cards/f/FaithfulPikemaster.java +++ b/Mage.Sets/src/mage/cards/f/FaithfulPikemaster.java @@ -37,7 +37,7 @@ public final class FaithfulPikemaster extends CardImpl { // As long as it's your turn, Faithful Pikemaster has first strike. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( new GainAbilitySourceEffect(FirstStrikeAbility.getInstance(), Duration.WhileOnBattlefield), - MyTurnCondition.instance, "during your turn, {this} has first strike." + MyTurnCondition.instance, "as long as it's your turn, {this} has first strike." )).addHint(MyTurnHint.instance)); } diff --git a/Mage.Sets/src/mage/cards/f/FaithfulSquire.java b/Mage.Sets/src/mage/cards/f/FaithfulSquire.java index 4bdc2d2c819..afdf306f44c 100644 --- a/Mage.Sets/src/mage/cards/f/FaithfulSquire.java +++ b/Mage.Sets/src/mage/cards/f/FaithfulSquire.java @@ -1,36 +1,34 @@ package mage.cards.f; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.OnEventTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.RemoveCountersSourceCost; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.FlipSourceEffect; import mage.abilities.effects.common.PreventDamageToTargetEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.keyword.FlyingAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.SuperType; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.CounterType; import mage.filter.StaticFilters; -import mage.game.events.GameEvent; import mage.game.permanent.token.TokenImpl; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** * @author LevelX2 */ public final class FaithfulSquire extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.KI, 2); + public FaithfulSquire(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{W}"); this.subtype.add(SubType.HUMAN); @@ -42,14 +40,15 @@ public final class FaithfulSquire extends CardImpl { this.flipCardName = "Kaiso, Memory of Loyalty"; // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Faithful Squire. - this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility( + new AddCountersSourceEffect(CounterType.KI.createInstance()), + StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, true + )); // At the beginning of the end step, if there are two or more ki counters on Faithful Squire, you may flip it - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new OnEventTriggeredAbility(GameEvent.EventType.END_TURN_STEP_PRE, "beginning of the end step", true, new FlipSourceEffect(new KaisoMemoryOfLoyaltyToken()), true), - new SourceHasCounterCondition(CounterType.KI, 2, Integer.MAX_VALUE), - "At the beginning of the end step, if there are two or more ki counters on {this}, you may flip it.")); - + this.addAbility(new BeginningOfEndStepTriggeredAbility( + TargetController.NEXT, new FlipSourceEffect(new KaisoMemoryOfLoyaltyToken()).setText("flip it"), true, condition + )); } private FaithfulSquire(final FaithfulSquire card) { diff --git a/Mage.Sets/src/mage/cards/f/FallOfGilGalad.java b/Mage.Sets/src/mage/cards/f/FallOfGilGalad.java index f1632d577dc..3dbe36dec56 100644 --- a/Mage.Sets/src/mage/cards/f/FallOfGilGalad.java +++ b/Mage.Sets/src/mage/cards/f/FallOfGilGalad.java @@ -52,7 +52,7 @@ public final class FallOfGilGalad extends CardImpl { .setTriggerPhrase("When this creature dies, ") ).setText("until end of turn, target creature you control gains \"When this creature dies, draw two cards.\"")); ability.addEffect(new FightTargetsEffect().setText("Then that creature fights up to one other target creature")); - ability.addTarget(new TargetControlledCreaturePermanent()); + ability.addTarget(new TargetControlledCreaturePermanent().setTargetTag(1)); ability.addTarget(new TargetPermanent(StaticFilters.FILTER_ANOTHER_CREATURE_TARGET_2).setTargetTag(2)); }); diff --git a/Mage.Sets/src/mage/cards/f/FandanielTelophoroiAscian.java b/Mage.Sets/src/mage/cards/f/FandanielTelophoroiAscian.java new file mode 100644 index 00000000000..9a07baafd94 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FandanielTelophoroiAscian.java @@ -0,0 +1,116 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.keyword.SurveilEffect; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; +import mage.game.Controllable; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetSacrifice; + +import java.util.*; + +/** + * @author TheElk801 + */ +public final class FandanielTelophoroiAscian extends CardImpl { + + public FandanielTelophoroiAscian(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.ELDER); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(4); + this.toughness = new MageInt(5); + + // Whenever you cast an instant or sorcery spell, surveil 1. + this.addAbility(new SpellCastControllerTriggeredAbility( + new SurveilEffect(1), StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, false + )); + + // At the beginning of your end step, each opponent may sacrifice a nontoken creature of their choice. Each opponent who doesn't loses 2 life for each instant and sorcery card in your graveyard. + this.addAbility(new BeginningOfEndStepTriggeredAbility(new FandanielTelophoroiAscianEffect())); + } + + private FandanielTelophoroiAscian(final FandanielTelophoroiAscian card) { + super(card); + } + + @Override + public FandanielTelophoroiAscian copy() { + return new FandanielTelophoroiAscian(this); + } +} + +class FandanielTelophoroiAscianEffect extends OneShotEffect { + + FandanielTelophoroiAscianEffect() { + super(Outcome.Benefit); + staticText = "each opponent may sacrifice a nontoken creature of their choice. " + + "Each opponent who doesn't loses 2 life for each instant and sorcery card in your graveyard"; + } + + private FandanielTelophoroiAscianEffect(final FandanielTelophoroiAscianEffect effect) { + super(effect); + } + + @Override + public FandanielTelophoroiAscianEffect copy() { + return new FandanielTelophoroiAscianEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Set players = new HashSet<>(); + List permanents = new ArrayList<>(); + for (UUID playerId : game.getOpponents(source.getControllerId())) { + Player player = game.getPlayer(playerId); + if (player == null || !game.getBattlefield().contains( + StaticFilters.FILTER_CONTROLLED_CREATURE_NON_TOKEN, + playerId, source, game, 1 + )) { + continue; + } + TargetSacrifice target = new TargetSacrifice( + 0, 1, StaticFilters.FILTER_CONTROLLED_CREATURE_NON_TOKEN + ); + player.choose(Outcome.Sacrifice, target, source, game); + permanents.add(game.getPermanent(target.getFirstTarget())); + players.add(playerId); + } + permanents.removeIf(Objects::isNull); + for (Permanent permanent : permanents) { + if (permanent.sacrifice(source, game)) { + players.remove(permanent.getControllerId()); + } + } + int count = Optional + .ofNullable(source) + .map(Controllable::getControllerId) + .map(game::getPlayer) + .map(Player::getGraveyard) + .map(g -> 2 * g.count(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, game)) + .orElse(0); + if (count < 1) { + return true; + } + for (UUID playerId : players) { + Optional.ofNullable(playerId) + .map(game::getPlayer) + .ifPresent(player -> player.loseLife(count, game, source, false)); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/f/FangFearlessLCie.java b/Mage.Sets/src/mage/cards/f/FangFearlessLCie.java index cf71fd538a4..6e1c510e378 100644 --- a/Mage.Sets/src/mage/cards/f/FangFearlessLCie.java +++ b/Mage.Sets/src/mage/cards/f/FangFearlessLCie.java @@ -33,7 +33,9 @@ public final class FangFearlessLCie extends CardImpl { this.meldsToClazz = mage.cards.r.RagnarokDivineDeliverance.class; // Whenever one or more cards leave your graveyard, you draw a card and you lose 1 life. This ability triggers only once each turn. - Ability ability = new CardsLeaveGraveyardTriggeredAbility(new DrawCardSourceControllerEffect(1, true)); + Ability ability = new CardsLeaveGraveyardTriggeredAbility( + new DrawCardSourceControllerEffect(1, true) + ).setTriggersLimitEachTurn(1); ability.addEffect(new LoseLifeSourceControllerEffect(1).concatBy("and")); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/f/FaridehDevilsChosen.java b/Mage.Sets/src/mage/cards/f/FaridehDevilsChosen.java index f9ff066ec65..73642c06f44 100644 --- a/Mage.Sets/src/mage/cards/f/FaridehDevilsChosen.java +++ b/Mage.Sets/src/mage/cards/f/FaridehDevilsChosen.java @@ -16,8 +16,8 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.constants.SuperType; import mage.game.Game; +import mage.util.CardUtil; -import java.util.Objects; import java.util.UUID; /** @@ -65,12 +65,9 @@ enum FaridehDevilsChosenCondition implements Condition { @Override public boolean apply(Game game, Ability source) { - return source - .getEffects() - .stream() - .map(effect -> effect.getValue("maxDieRoll")) - .filter(Objects::nonNull) - .mapToInt(Integer.class::cast) - .anyMatch(x -> x >= 10); + return CardUtil + .getEffectValueFromAbility(source, "maxDieRoll", Integer.class) + .filter(x -> x >= 10) + .isPresent(); } } diff --git a/Mage.Sets/src/mage/cards/f/FatalBlow.java b/Mage.Sets/src/mage/cards/f/FatalBlow.java index b8e97964f46..e22bad9a55e 100644 --- a/Mage.Sets/src/mage/cards/f/FatalBlow.java +++ b/Mage.Sets/src/mage/cards/f/FatalBlow.java @@ -1,34 +1,25 @@ - package mage.cards.f; -import java.util.UUID; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.WasDealtDamageThisTurnPredicate; -import mage.target.common.TargetCreaturePermanent; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author Quercitron */ public final class FatalBlow extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature that was dealt damage this turn"); - - static { - filter.add(WasDealtDamageThisTurnPredicate.instance); - } - public FatalBlow(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}"); // Destroy target creature that was dealt damage this turn. It can't be regenerated. this.getSpellAbility().addEffect(new DestroyTargetEffect(true)); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_DAMAGED_THIS_TURN)); } private FatalBlow(final FatalBlow card) { diff --git a/Mage.Sets/src/mage/cards/f/FatalPush.java b/Mage.Sets/src/mage/cards/f/FatalPush.java index cf50d901f9b..967dc20c598 100644 --- a/Mage.Sets/src/mage/cards/f/FatalPush.java +++ b/Mage.Sets/src/mage/cards/f/FatalPush.java @@ -47,7 +47,7 @@ class FatalPushEffect extends OneShotEffect { super(Outcome.DestroyPermanent); this.staticText = "Destroy target creature if it has mana value 2 or less.
    " + AbilityWord.REVOLT.formatWord() + "Destroy that creature if it has mana value 4 " + - "or less instead if a permanent you controlled left the battlefield this turn"; + "or less instead if a permanent left the battlefield under your control this turn"; } private FatalPushEffect(final FatalPushEffect effect) { diff --git a/Mage.Sets/src/mage/cards/f/FathomFleetCutthroat.java b/Mage.Sets/src/mage/cards/f/FathomFleetCutthroat.java index a045da13c83..a91953a16ae 100644 --- a/Mage.Sets/src/mage/cards/f/FathomFleetCutthroat.java +++ b/Mage.Sets/src/mage/cards/f/FathomFleetCutthroat.java @@ -1,7 +1,6 @@ package mage.cards.f; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -10,24 +9,16 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.WasDealtDamageThisTurnPredicate; -import mage.target.common.TargetCreaturePermanent; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class FathomFleetCutthroat extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls that was dealt damage this turn"); - - static { - filter.add(WasDealtDamageThisTurnPredicate.instance); - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public FathomFleetCutthroat(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); @@ -38,7 +29,7 @@ public final class FathomFleetCutthroat extends CardImpl { // When Fathom Fleet Cutthroat enters the battlefield, destroy target creature an opponent controls that was dealt damage this turn. Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), false); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_OPPONENTS_CREATURE_DAMAGED_THIS_TURN)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/f/FearOfSleepParalysis.java b/Mage.Sets/src/mage/cards/f/FearOfSleepParalysis.java index db977eb225d..eab4edbe21d 100644 --- a/Mage.Sets/src/mage/cards/f/FearOfSleepParalysis.java +++ b/Mage.Sets/src/mage/cards/f/FearOfSleepParalysis.java @@ -38,7 +38,7 @@ public final class FearOfSleepParalysis extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Eerie -- Whenever Fear of Sleep Paralysis or another enchantment you control enters and whenever you fully unlock a Room, tap up to one target creature and put a stun counter on it. - Ability ability = new EerieAbility(new TapTargetEffect()); + Ability ability = new EerieAbility(new TapTargetEffect()).setTriggerPhrase("Whenever this creature or another enchantment you control enters and whenever you fully unlock a Room, "); ability.addEffect(new AddCountersTargetEffect(CounterType.STUN.createInstance()).setText("and put a stun counter on it")); ability.addTarget(new TargetCreaturePermanent(0, 1)); this.addAbility(ability); @@ -89,4 +89,4 @@ class FearOfSleepParalysisEffect extends ReplacementEffectImpl { return target != null && event.getData().equals(CounterType.STUN.getName()) && !target.getControllerId().equals(source.getControllerId()); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/f/FeastOfWorms.java b/Mage.Sets/src/mage/cards/f/FeastOfWorms.java index 06f07b76567..2392a2cb76c 100644 --- a/Mage.Sets/src/mage/cards/f/FeastOfWorms.java +++ b/Mage.Sets/src/mage/cards/f/FeastOfWorms.java @@ -69,7 +69,7 @@ class FeastOfWormsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(id); + Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); Player targetPlayer = null; if (permanent != null) { targetPlayer = game.getPlayer(permanent.getControllerId()); diff --git a/Mage.Sets/src/mage/cards/f/FeatherTheRedeemed.java b/Mage.Sets/src/mage/cards/f/FeatherTheRedeemed.java index fcc6e8e1b54..47cc4950e48 100644 --- a/Mage.Sets/src/mage/cards/f/FeatherTheRedeemed.java +++ b/Mage.Sets/src/mage/cards/f/FeatherTheRedeemed.java @@ -95,7 +95,7 @@ class FeatherTheRedeemedTriggeredAbility extends TriggeredAbilityImpl { if (permanent != null && permanent.isCreature(game) && permanent.isControlledBy(getControllerId())) { this.getEffects().clear(); - this.addEffect(new FeatherTheRedeemedEffect(new MageObjectReference(spell.getCard(), game))); + this.addEffect(new FeatherTheRedeemedEffect(spell, game)); return true; } } @@ -106,7 +106,7 @@ class FeatherTheRedeemedTriggeredAbility extends TriggeredAbilityImpl { if (permanent != null && permanent.isCreature(game) && permanent.isControlledBy(getControllerId())) { this.getEffects().clear(); - this.addEffect(new FeatherTheRedeemedEffect(new MageObjectReference(spell.getCard(), game))); + this.addEffect(new FeatherTheRedeemedEffect(spell, game)); return true; } } @@ -125,21 +125,25 @@ class FeatherTheRedeemedTriggeredAbility extends TriggeredAbilityImpl { class FeatherTheRedeemedEffect extends ReplacementEffectImpl { - private final MageObjectReference mor; - - FeatherTheRedeemedEffect(MageObjectReference mor) { + // we store both Spell and Card to work properly on split cards. + private final MageObjectReference morSpell; + private final MageObjectReference morCard; + + FeatherTheRedeemedEffect(Spell spell, Game game) { super(Duration.OneUse, Outcome.Benefit); - this.mor = mor; + this.morSpell = new MageObjectReference(spell.getCard(), game); + this.morCard = new MageObjectReference(spell.getMainCard(), game); } private FeatherTheRedeemedEffect(final FeatherTheRedeemedEffect effect) { super(effect); - this.mor = effect.mor; + this.morSpell = effect.morSpell; + this.morCard = effect.morCard; } @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Spell sourceSpell = game.getStack().getSpell(event.getTargetId()); + Spell sourceSpell = morSpell.getSpell(game); if (sourceSpell == null || sourceSpell.isCopy()) { return false; } @@ -162,15 +166,11 @@ class FeatherTheRedeemedEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { ZoneChangeEvent zEvent = ((ZoneChangeEvent) event); - if (zEvent.getFromZone() != Zone.STACK - || zEvent.getToZone() != Zone.GRAVEYARD - || event.getSourceId() == null - || !event.getSourceId().equals(event.getTargetId()) - || !mor.equals(new MageObjectReference(event.getTargetId(), game))) { - return false; - } - return true; - } + return Zone.STACK.equals(zEvent.getFromZone()) + && Zone.GRAVEYARD.equals(zEvent.getToZone()) + && morSpell.refersTo(event.getSourceId(), game) // this is how we check that the spell resolved properly (and was not countered or the like) + && morCard.refersTo(event.getTargetId(), game); // this is how we check that the card being moved is the one we want. + } @Override public FeatherTheRedeemedEffect copy() { diff --git a/Mage.Sets/src/mage/cards/f/FecundGreenshell.java b/Mage.Sets/src/mage/cards/f/FecundGreenshell.java index 3a88f596fd6..78362c58dd3 100644 --- a/Mage.Sets/src/mage/cards/f/FecundGreenshell.java +++ b/Mage.Sets/src/mage/cards/f/FecundGreenshell.java @@ -1,7 +1,5 @@ package mage.cards.f; -import java.util.UUID; - import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; @@ -11,24 +9,27 @@ import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.hint.common.LandsYouControlHint; -import mage.cards.Card; -import mage.constants.*; import mage.abilities.keyword.ReachAbility; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.ToughnessGreaterThanPowerPredicate; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** * @author Cguy7777 */ public final class FecundGreenshell extends CardImpl { - private static final FilterCreaturePermanent filter - = new FilterCreaturePermanent("creature you control with toughness greater than its power"); + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("creature you control with toughness greater than its power"); static { filter.add(ToughnessGreaterThanPowerPredicate.instance); @@ -59,7 +60,7 @@ public final class FecundGreenshell extends CardImpl { // Whenever Fecund Greenshell or another creature you control with toughness greater than its power enters, // look at the top card of your library. If it's a land card, you may put it onto the battlefield tapped. Otherwise, put it into your hand. this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility( - new FecundGreenshellEffect(), filter, false, true)); + new FecundGreenshellEffect(), filter, false, false)); } private FecundGreenshell(final FecundGreenshell card) { diff --git a/Mage.Sets/src/mage/cards/f/FerocityOfTheUnderworld.java b/Mage.Sets/src/mage/cards/f/FerocityOfTheUnderworld.java index 694eebb77ad..adcdf0e805c 100644 --- a/Mage.Sets/src/mage/cards/f/FerocityOfTheUnderworld.java +++ b/Mage.Sets/src/mage/cards/f/FerocityOfTheUnderworld.java @@ -1,7 +1,5 @@ - package mage.cards.f; -import java.util.UUID; import mage.abilities.Mode; import mage.abilities.effects.common.CopyTargetStackObjectEffect; import mage.abilities.effects.common.DestroyTargetEffect; @@ -13,12 +11,13 @@ import mage.constants.ComparisonType; import mage.filter.StaticFilters; import mage.filter.common.FilterNonlandPermanent; import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.target.TargetPermanent; import mage.target.TargetSpell; import mage.target.common.TargetCardInYourGraveyard; -import mage.target.common.TargetNonlandPermanent; + +import java.util.UUID; /** - * * @author Styxo */ public final class FerocityOfTheUnderworld extends CardImpl { @@ -30,11 +29,11 @@ public final class FerocityOfTheUnderworld extends CardImpl { } public FerocityOfTheUnderworld(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{B}{R}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}{R}{G}"); // Choose one - Destroy target nonland permanent with converted mana cost 3 or less. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetNonlandPermanent(filterMode1)); + this.getSpellAbility().addTarget(new TargetPermanent(filterMode1)); // Copy target instant or sorcery spell. You may choose new targets for the copy. Mode mode = new Mode(new CopyTargetStackObjectEffect()); diff --git a/Mage.Sets/src/mage/cards/f/FiddleheadKami.java b/Mage.Sets/src/mage/cards/f/FiddleheadKami.java index 6017d582d04..3e843e39d52 100644 --- a/Mage.Sets/src/mage/cards/f/FiddleheadKami.java +++ b/Mage.Sets/src/mage/cards/f/FiddleheadKami.java @@ -25,7 +25,7 @@ public final class FiddleheadKami extends CardImpl { this.toughness = new MageInt(3); // Whenever you cast a Spirit or Arcane spell, regenerate Fiddlehead Kami. - this.addAbility(new SpellCastControllerTriggeredAbility(new RegenerateSourceEffect(), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new RegenerateSourceEffect(), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, false)); } private FiddleheadKami(final FiddleheadKami card) { diff --git a/Mage.Sets/src/mage/cards/f/FieldOfRuin.java b/Mage.Sets/src/mage/cards/f/FieldOfRuin.java index 0eb5368b767..683e422d895 100644 --- a/Mage.Sets/src/mage/cards/f/FieldOfRuin.java +++ b/Mage.Sets/src/mage/cards/f/FieldOfRuin.java @@ -1,7 +1,5 @@ - package mage.cards.f; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; @@ -13,26 +11,24 @@ import mage.abilities.mana.ColorlessManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.cards.CardsImpl; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.SuperType; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.constants.*; +import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterLandPermanent; import mage.filter.predicate.Predicates; import mage.game.Game; import mage.players.Player; +import mage.target.TargetPermanent; import mage.target.common.TargetCardInLibrary; -import mage.target.common.TargetLandPermanent; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class FieldOfRuin extends CardImpl { - private static final FilterLandPermanent filter = new FilterLandPermanent("nonbasic land an opponent controls"); + private static final FilterPermanent filter = new FilterLandPermanent("nonbasic land an opponent controls"); static { filter.add(TargetController.OPPONENT.getControllerPredicate()); @@ -50,7 +46,7 @@ public final class FieldOfRuin extends CardImpl { ability.addCost(new ManaCostsImpl<>("{2}")); ability.addCost(new SacrificeSourceCost()); ability.addEffect(new FieldOfRuinEffect()); - ability.addTarget(new TargetLandPermanent(filter)); + ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/f/FierceInvocation.java b/Mage.Sets/src/mage/cards/f/FierceInvocation.java index 175959b8312..fee6b97a158 100644 --- a/Mage.Sets/src/mage/cards/f/FierceInvocation.java +++ b/Mage.Sets/src/mage/cards/f/FierceInvocation.java @@ -17,6 +17,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @@ -64,7 +65,7 @@ class FierceInvocationEffect extends OneShotEffect { Card card = controller.getLibrary().getFromTop(game); if (card != null) { new ManifestEffect(1).apply(game, source); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { Effect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance(2)); effect.setTargetPointer(new FixedTarget(permanent, game)); diff --git a/Mage.Sets/src/mage/cards/f/FieryAnnihilation.java b/Mage.Sets/src/mage/cards/f/FieryAnnihilation.java index 6b54ad299ca..ad81f8376c0 100644 --- a/Mage.Sets/src/mage/cards/f/FieryAnnihilation.java +++ b/Mage.Sets/src/mage/cards/f/FieryAnnihilation.java @@ -7,8 +7,8 @@ import mage.abilities.effects.common.ExileTargetIfDiesEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; import mage.filter.FilterPermanent; -import mage.filter.common.FilterEquipmentPermanent; import mage.filter.predicate.ObjectSourcePlayer; import mage.filter.predicate.ObjectSourcePlayerPredicate; import mage.game.Game; @@ -26,7 +26,7 @@ import java.util.UUID; */ public final class FieryAnnihilation extends CardImpl { - private static final FilterPermanent filter = new FilterEquipmentPermanent("Equipment attached to that creature"); + private static final FilterPermanent filter = new FilterPermanent(SubType.EQUIPMENT, "Equipment attached to that creature"); static { filter.add(FieryAnnihilationPredicate.instance); diff --git a/Mage.Sets/src/mage/cards/f/FightOn.java b/Mage.Sets/src/mage/cards/f/FightOn.java new file mode 100644 index 00000000000..ddc76272ba1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FightOn.java @@ -0,0 +1,35 @@ +package mage.cards.f; + +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FightOn extends CardImpl { + + public FightOn(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}"); + + // Return up to two target creature cards from your graveyard to your hand. + this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard( + 0, 2, StaticFilters.FILTER_CARD_CREATURES_YOUR_GRAVEYARD + )); + } + + private FightOn(final FightOn card) { + super(card); + } + + @Override + public FightOn copy() { + return new FightOn(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/Filth.java b/Mage.Sets/src/mage/cards/f/Filth.java index 324e7eee31f..3c7c0b9e14d 100644 --- a/Mage.Sets/src/mage/cards/f/Filth.java +++ b/Mage.Sets/src/mage/cards/f/Filth.java @@ -23,7 +23,7 @@ import mage.filter.common.FilterControlledPermanent; */ public final class Filth extends CardImpl { - private static final String ruleText = "As long as Filth is in your graveyard and you control a Swamp, creatures you control have swampwalk"; + private static final String ruleText = "As long as this card is in your graveyard and you control a Swamp, creatures you control have swampwalk"; private static final FilterControlledPermanent filter = new FilterControlledPermanent("Swamp"); diff --git a/Mage.Sets/src/mage/cards/f/FinalStingFaerie.java b/Mage.Sets/src/mage/cards/f/FinalStingFaerie.java index ebc23fc4f30..2ba5fcf62c2 100644 --- a/Mage.Sets/src/mage/cards/f/FinalStingFaerie.java +++ b/Mage.Sets/src/mage/cards/f/FinalStingFaerie.java @@ -1,7 +1,5 @@ - package mage.cards.f; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -11,24 +9,18 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.WasDealtDamageThisTurnPredicate; -import mage.target.common.TargetCreaturePermanent; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author fireshoes */ public final class FinalStingFaerie extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature that was dealt damage this turn"); - - static { - filter.add(WasDealtDamageThisTurnPredicate.instance); - } public FinalStingFaerie(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); this.subtype.add(SubType.FAERIE); this.subtype.add(SubType.ASSASSIN); this.power = new MageInt(2); @@ -36,11 +28,11 @@ public final class FinalStingFaerie extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); - + // When Final-Sting Faerie enters the battlefield, destroy target creature that was dealt damage this turn. Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect()); - ability.addTarget(new TargetCreaturePermanent(filter)); - this.addAbility(ability); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_DAMAGED_THIS_TURN)); + this.addAbility(ability); } private FinalStingFaerie(final FinalStingFaerie card) { diff --git a/Mage.Sets/src/mage/cards/f/FireMagic.java b/Mage.Sets/src/mage/cards/f/FireMagic.java index 91bb0da53b4..3ecab5d1e90 100644 --- a/Mage.Sets/src/mage/cards/f/FireMagic.java +++ b/Mage.Sets/src/mage/cards/f/FireMagic.java @@ -24,7 +24,7 @@ public final class FireMagic extends CardImpl { // * Fire -- {0} -- Fire Magic deals 1 damage to each creature. this.getSpellAbility().addEffect(new DamageAllEffect(1, StaticFilters.FILTER_PERMANENT_CREATURE)); - this.getSpellAbility().withFirstModeCost(new GenericManaCost(1)); + this.getSpellAbility().withFirstModeCost(new GenericManaCost(0)); this.getSpellAbility().withFirstModeFlavorWord("Fire"); // * Fira -- {2} -- Fire Magic deals 2 damage to each creature. diff --git a/Mage.Sets/src/mage/cards/f/FireProphecy.java b/Mage.Sets/src/mage/cards/f/FireProphecy.java index 2c9e488c09a..0a031453de8 100644 --- a/Mage.Sets/src/mage/cards/f/FireProphecy.java +++ b/Mage.Sets/src/mage/cards/f/FireProphecy.java @@ -73,7 +73,7 @@ class FireProphecyEffect extends OneShotEffect { if (card == null) { return false; } - if (player.putCardsOnBottomOfLibrary(card, game, source, false)) { + if (player.putCardsOnBottomOfLibrary(card, game, source)) { player.drawCards(1, source, game); } return true; diff --git a/Mage.Sets/src/mage/cards/f/FirefluxSquad.java b/Mage.Sets/src/mage/cards/f/FirefluxSquad.java index 93d1eefc6fe..3f4603ddc2a 100644 --- a/Mage.Sets/src/mage/cards/f/FirefluxSquad.java +++ b/Mage.Sets/src/mage/cards/f/FirefluxSquad.java @@ -18,6 +18,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; +import mage.util.CardUtil; import java.util.UUID; @@ -102,11 +103,11 @@ class FirefluxSquadEffect extends OneShotEffect { return player.putCardsOnBottomOfLibrary(cards, game, source, false); } player.moveCards(toBattlefield, Zone.BATTLEFIELD, source, game, true, false, true, null); - permanent = game.getPermanent(toBattlefield.getId()); + permanent = CardUtil.getPermanentFromCardPutToBattlefield(toBattlefield, game); if (permanent != null) { cards.remove(toBattlefield); game.getCombat().addAttackingCreature(permanent.getId(), game); } return player.putCardsOnBottomOfLibrary(cards, game, source, false); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/f/FiresOfInvention.java b/Mage.Sets/src/mage/cards/f/FiresOfInvention.java index 9185d0eb268..4517681986a 100644 --- a/Mage.Sets/src/mage/cards/f/FiresOfInvention.java +++ b/Mage.Sets/src/mage/cards/f/FiresOfInvention.java @@ -10,7 +10,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; import mage.filter.FilterCard; -import mage.filter.predicate.card.ManaValueLessThanControlledLandCountPredicate; +import mage.filter.predicate.mageobject.ManaValueLessThanControlledLandCountPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.watchers.common.CastSpellLastTurnWatcher; diff --git a/Mage.Sets/src/mage/cards/f/FlameBurst.java b/Mage.Sets/src/mage/cards/f/FlameBurst.java index dc4842490a4..a0b5f4939a7 100644 --- a/Mage.Sets/src/mage/cards/f/FlameBurst.java +++ b/Mage.Sets/src/mage/cards/f/FlameBurst.java @@ -64,7 +64,7 @@ public final class FlameBurst extends CardImpl { class CountAsFlameBurstAbility extends SimpleStaticAbility { public CountAsFlameBurstAbility() { - super(Zone.GRAVEYARD, new InfoEffect("If {this} is in a graveyard, effects from spells named Flame Burst count it as a card named Flame Burst")); + super(Zone.GRAVEYARD, new InfoEffect("If this card is in a graveyard, effects from spells named Flame Burst count it as a card named Flame Burst")); } private CountAsFlameBurstAbility(CountAsFlameBurstAbility ability) { diff --git a/Mage.Sets/src/mage/cards/f/Flash.java b/Mage.Sets/src/mage/cards/f/Flash.java index 1b7310b5ef1..d2b94341c78 100644 --- a/Mage.Sets/src/mage/cards/f/Flash.java +++ b/Mage.Sets/src/mage/cards/f/Flash.java @@ -80,7 +80,7 @@ class FlashEffect extends OneShotEffect { } game.processAction(); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { permanent.sacrifice(source, game); } diff --git a/Mage.Sets/src/mage/cards/f/FlayerOfTheHatebound.java b/Mage.Sets/src/mage/cards/f/FlayerOfTheHatebound.java index 0afa1b2edf9..1f32bf5ba9b 100644 --- a/Mage.Sets/src/mage/cards/f/FlayerOfTheHatebound.java +++ b/Mage.Sets/src/mage/cards/f/FlayerOfTheHatebound.java @@ -82,7 +82,7 @@ class FlayerTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever Flayer of the Hatebound or another creature enters the battlefield from your graveyard, that creature deals damage equal to its power to any target."; + return "Whenever {this} or another creature enters from your graveyard, that creature deals damage equal to its power to any target."; } @Override diff --git a/Mage.Sets/src/mage/cards/f/Flickerform.java b/Mage.Sets/src/mage/cards/f/Flickerform.java index 4b1366dd476..235671b5ddb 100644 --- a/Mage.Sets/src/mage/cards/f/Flickerform.java +++ b/Mage.Sets/src/mage/cards/f/Flickerform.java @@ -29,6 +29,7 @@ import mage.players.Player; import mage.target.Target; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import mage.util.CardUtil; /** * @@ -154,45 +155,44 @@ class FlickerformReturnEffect extends OneShotEffect { } ExileZone exileZone = game.getExile().getExileZone(exileZoneId); Card enchantedCard = exileZone.get(enchantedCardId, game); - //skip if exiled card is missing - if (enchantedCard != null) { - Player owner = game.getPlayer(enchantedCard.getOwnerId()); - //skip if card's owner is missing - if (owner != null) { - owner.moveCards(enchantedCard, Zone.BATTLEFIELD, source, game); - Permanent newPermanent = game.getPermanent(enchantedCardId); - if (newPermanent != null) { - Set toBattlefieldAttached = new HashSet(); - for (Card enchantment : exileZone.getCards(game)) { - if (filterAura.match(enchantment, game)) { - boolean canTarget = false; - for (Target target : enchantment.getSpellAbility().getTargets()) { - Filter filter = target.getFilter(); - if (filter.match(newPermanent, game)) { - canTarget = true; - break; - } - } - if (!canTarget) { - // Aura stays exiled - continue; - } - game.getState().setValue("attachTo:" + enchantment.getId(), newPermanent); + if (enchantedCard == null) { + return false; + } + Player owner = game.getPlayer(enchantedCard.getOwnerId()); + if (owner == null) { + return false; + } + owner.moveCards(enchantedCard, Zone.BATTLEFIELD, source, game); + Permanent newPermanent = CardUtil.getPermanentFromCardPutToBattlefield(enchantedCard, game); + if (newPermanent != null) { + Set toBattlefieldAttached = new HashSet<>(); + for (Card enchantment : exileZone.getCards(game)) { + if (filterAura.match(enchantment, game)) { + boolean canTarget = false; + for (Target target : enchantment.getSpellAbility().getTargets()) { + Filter filter = target.getFilter(); + if (filter.match(newPermanent, game)) { + canTarget = true; + break; } - toBattlefieldAttached.add(enchantment); } - if (!toBattlefieldAttached.isEmpty()) { - controller.moveCards(toBattlefieldAttached, Zone.BATTLEFIELD, source, game); - for (Card card : toBattlefieldAttached) { - if (game.getState().getZone(card.getId()) == Zone.BATTLEFIELD) { - newPermanent.addAttachment(card.getId(), source, game); - } - } + if (!canTarget) { + // Aura stays exiled + continue; + } + game.getState().setValue("attachTo:" + enchantment.getId(), newPermanent); + } + toBattlefieldAttached.add(enchantment); + } + if (!toBattlefieldAttached.isEmpty()) { + controller.moveCards(toBattlefieldAttached, Zone.BATTLEFIELD, source, game); + for (Card card : toBattlefieldAttached) { + if (game.getState().getZone(card.getId()) == Zone.BATTLEFIELD) { + newPermanent.addAttachment(card.getId(), source, game); } } - return true; } } - return false; + return true; } } diff --git a/Mage.Sets/src/mage/cards/f/FlourishingHunter.java b/Mage.Sets/src/mage/cards/f/FlourishingHunter.java index 89fc37c1059..e72a2a0ff26 100644 --- a/Mage.Sets/src/mage/cards/f/FlourishingHunter.java +++ b/Mage.Sets/src/mage/cards/f/FlourishingHunter.java @@ -2,7 +2,7 @@ package mage.cards.f; import mage.MageInt; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.dynamicvalue.common.GreatestToughnessAmongControlledCreaturesValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.GainLifeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -26,9 +26,9 @@ public final class FlourishingHunter extends CardImpl { // When Flourishing Hunter enters the battlefield, you gain life equal to the greatest toughness among other creatures you control. this.addAbility(new EntersBattlefieldTriggeredAbility( - new GainLifeEffect(GreatestToughnessAmongControlledCreaturesValue.OTHER) + new GainLifeEffect(GreatestAmongPermanentsValue.TOUGHNESS_OTHER_CONTROLLED_CREATURES) .setText("you gain life equal to the greatest toughness among other creatures you control") - ).addHint(GreatestToughnessAmongControlledCreaturesValue.OTHER.getHint())); + ).addHint(GreatestAmongPermanentsValue.TOUGHNESS_OTHER_CONTROLLED_CREATURES.getHint())); } private FlourishingHunter(final FlourishingHunter card) { diff --git a/Mage.Sets/src/mage/cards/f/FootstepsOfTheGoryo.java b/Mage.Sets/src/mage/cards/f/FootstepsOfTheGoryo.java index dd04d4f00ee..30c89b3c630 100644 --- a/Mage.Sets/src/mage/cards/f/FootstepsOfTheGoryo.java +++ b/Mage.Sets/src/mage/cards/f/FootstepsOfTheGoryo.java @@ -20,6 +20,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @@ -69,7 +70,7 @@ class FootstepsOfTheGoryoEffect extends OneShotEffect { Card card = game.getCard(getTargetPointer().getFirst(game, source)); if (card != null) { if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { // Sacrifice it at end of turn Effect sacrificeEffect = new SacrificeTargetEffect("Sacrifice that creature at the beginning of next end step", source.getControllerId()); diff --git a/Mage.Sets/src/mage/cards/f/ForTheCommonGood.java b/Mage.Sets/src/mage/cards/f/ForTheCommonGood.java index 1d90d44e6ad..ac25c6345e0 100644 --- a/Mage.Sets/src/mage/cards/f/ForTheCommonGood.java +++ b/Mage.Sets/src/mage/cards/f/ForTheCommonGood.java @@ -50,7 +50,7 @@ public final class ForTheCommonGood extends CardImpl { IndestructibleAbility.getInstance(), Duration.UntilYourNextTurn, StaticFilters.FILTER_PERMANENT_TOKENS ).concatBy("Then")); - this.getSpellAbility().addEffect(new GainLifeEffect(xValue)); + this.getSpellAbility().addEffect(new GainLifeEffect(xValue).setText("You gain 1 life for each token you control")); this.getSpellAbility().addHint(hint); } diff --git a/Mage.Sets/src/mage/cards/f/ForgeOfHeroes.java b/Mage.Sets/src/mage/cards/f/ForgeOfHeroes.java index 25347b5a4e1..a667c878c46 100644 --- a/Mage.Sets/src/mage/cards/f/ForgeOfHeroes.java +++ b/Mage.Sets/src/mage/cards/f/ForgeOfHeroes.java @@ -1,11 +1,9 @@ package mage.cards.f; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.mana.ColorlessManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -19,14 +17,15 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class ForgeOfHeroes extends CardImpl { private static final FilterPermanent filter - = new FilterPermanent("commander that entered the battlefield this turn"); + = new FilterPermanent("commander that entered this turn"); static { filter.add(CommanderPredicate.instance); @@ -40,10 +39,7 @@ public final class ForgeOfHeroes extends CardImpl { this.addAbility(new ColorlessManaAbility()); // {T}: Choose target commander that entered the battlefield this turn. Put a +1/+1 counter on it if it's a creature and a loyalty counter on it if it's a planeswalker. - Ability ability = new SimpleActivatedAbility( - new ForgeOfHeroesEffect(), - new TapSourceCost() - ); + Ability ability = new SimpleActivatedAbility(new ForgeOfHeroesEffect(), new TapSourceCost()); ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); } @@ -62,7 +58,7 @@ class ForgeOfHeroesEffect extends OneShotEffect { ForgeOfHeroesEffect() { super(Outcome.Benefit); - this.staticText = "choose target commander that entered the battlefield this turn. " + this.staticText = "choose target commander that entered this turn. " + "Put a +1/+1 counter on it if it's a creature " + "and a loyalty counter on it if it's a planeswalker"; } @@ -78,20 +74,15 @@ class ForgeOfHeroesEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getFirstTarget()); + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (permanent == null) { return false; } if (permanent.isCreature(game)) { - new AddCountersTargetEffect( - CounterType.P1P1.createInstance() - ).apply(game, source); - } else if (permanent.isPlaneswalker(game)) { - new AddCountersTargetEffect( - CounterType.LOYALTY.createInstance() - ).apply(game, source); - } else { - return false; + permanent.addCounters(CounterType.P1P1.createInstance(), source, game); + } + if (permanent.isPlaneswalker(game)) { + permanent.addCounters(CounterType.LOYALTY.createInstance(), source, game); } return true; } diff --git a/Mage.Sets/src/mage/cards/f/FortunateFew.java b/Mage.Sets/src/mage/cards/f/FortunateFew.java index 0d31a7693a9..3afedd7c1c1 100644 --- a/Mage.Sets/src/mage/cards/f/FortunateFew.java +++ b/Mage.Sets/src/mage/cards/f/FortunateFew.java @@ -1,9 +1,6 @@ package mage.cards.f; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; @@ -18,10 +15,13 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; -import mage.target.common.TargetNonlandPermanent; +import mage.target.TargetPermanent; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; /** - * * @author spjspj */ public final class FortunateFew extends CardImpl { @@ -73,7 +73,7 @@ class FortunateFewEffect extends OneShotEffect { filter.add(Predicates.not(new PermanentIdPredicate(chosenPerm.getId()))); } - Target target = new TargetNonlandPermanent(filter); + Target target = new TargetPermanent(filter); target.withNotTarget(true); if (player.choose(Outcome.Exile, target, source, game)) { Permanent permanent = game.getPermanent(target.getFirstTarget()); diff --git a/Mage.Sets/src/mage/cards/f/FreelanceMuscle.java b/Mage.Sets/src/mage/cards/f/FreelanceMuscle.java index 84c193b2beb..8d7939504fc 100644 --- a/Mage.Sets/src/mage/cards/f/FreelanceMuscle.java +++ b/Mage.Sets/src/mage/cards/f/FreelanceMuscle.java @@ -1,20 +1,14 @@ package mage.cards.f; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.AttacksOrBlocksTriggeredAbility; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.continuous.BoostSourceEffect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.filter.StaticFilters; -import mage.game.Game; import java.util.UUID; @@ -23,10 +17,6 @@ import java.util.UUID; */ public final class FreelanceMuscle extends CardImpl { - private static final Hint hint = new ValueHint( - "Greatest power and/or toughness among other creatures you control", FreelanceMuscleValue.instance - ); - public FreelanceMuscle(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}"); @@ -37,9 +27,10 @@ public final class FreelanceMuscle extends CardImpl { // Whenever Freelance Muscle attacks or blocks, it gets +X/+X until end of turn, where X is the greatest power and/or toughness among other creatures you control. this.addAbility(new AttacksOrBlocksTriggeredAbility(new BoostSourceEffect( - FreelanceMuscleValue.instance, FreelanceMuscleValue.instance, + GreatestAmongPermanentsValue.POWER_OR_TOUGHNESS_OTHER_CONTROLLED_CREATURES, + GreatestAmongPermanentsValue.POWER_OR_TOUGHNESS_OTHER_CONTROLLED_CREATURES, Duration.EndOfTurn, "it" - ), false).addHint(hint)); + ), false).addHint(GreatestAmongPermanentsValue.POWER_OR_TOUGHNESS_OTHER_CONTROLLED_CREATURES.getHint())); } private FreelanceMuscle(final FreelanceMuscle card) { @@ -50,40 +41,4 @@ public final class FreelanceMuscle extends CardImpl { public FreelanceMuscle copy() { return new FreelanceMuscle(this); } -} - -enum FreelanceMuscleValue implements DynamicValue { - instance; - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - return game - .getBattlefield() - .getActivePermanents( - StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE, - sourceAbility.getControllerId(), sourceAbility, game - ) - .stream() - .mapToInt(permanent -> Math.max( - permanent.getPower().getValue(), - permanent.getToughness().getValue() - )) - .max() - .orElse(0); - } - - @Override - public FreelanceMuscleValue copy() { - return this; - } - - @Override - public String toString() { - return "X"; - } - - @Override - public String getMessage() { - return "the greatest power and/or toughness among other creatures you control"; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/f/FrenziedGorespawn.java b/Mage.Sets/src/mage/cards/f/FrenziedGorespawn.java index e18d8b20a1a..e8dc1de6f29 100644 --- a/Mage.Sets/src/mage/cards/f/FrenziedGorespawn.java +++ b/Mage.Sets/src/mage/cards/f/FrenziedGorespawn.java @@ -16,7 +16,7 @@ import mage.game.Game; import mage.game.events.DefenderAttackedEvent; import mage.game.events.GameEvent; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.FixedTargets; import java.util.UUID; @@ -37,7 +37,7 @@ public final class FrenziedGorespawn extends CardImpl { Ability ability = new EntersBattlefieldTriggeredAbility(new GoadTargetEffect() .setText("for each opponent, goad target creature that player controls")); ability.addTarget(new TargetCreaturePermanent()); - ability.setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); this.addAbility(ability); // Whenever one or more creatures attack one of your opponents, those creatures gain menace until end of turn. diff --git a/Mage.Sets/src/mage/cards/f/FriendlyRivalry.java b/Mage.Sets/src/mage/cards/f/FriendlyRivalry.java index a04058f3c4b..892eed11554 100644 --- a/Mage.Sets/src/mage/cards/f/FriendlyRivalry.java +++ b/Mage.Sets/src/mage/cards/f/FriendlyRivalry.java @@ -1,23 +1,26 @@ package mage.cards.f; -import java.util.UUID; - import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SuperType; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.target.Target; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.UUID; + /** - * * @author Susucr */ public final class FriendlyRivalry extends CardImpl { @@ -31,20 +34,18 @@ public final class FriendlyRivalry extends CardImpl { public FriendlyRivalry(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}{G}"); - - // Target creature you control and up to one other target legendary creature you control - // each deal damage equal to their power to target creature you don't control. + // Target creature you control and up to one other target legendary creature you control each deal damage equal to their power to target creature you don't control. this.getSpellAbility().addEffect(new FriendlyRivalryEffect()); TargetControlledCreaturePermanent target1 = new TargetControlledCreaturePermanent(); - this.getSpellAbility().addTarget(target1.setTargetTag(1)); + this.getSpellAbility().addTarget(target1.setTargetTag(1).withChooseHint("to deal damage")); TargetControlledCreaturePermanent target2 = new TargetControlledCreaturePermanent(0, 1, filter2, false); - this.getSpellAbility().addTarget(target2.setTargetTag(2)); + this.getSpellAbility().addTarget(target2.setTargetTag(2).withChooseHint("to deal damage")); TargetCreaturePermanent target3 = new TargetCreaturePermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL); - this.getSpellAbility().addTarget(target3); + this.getSpellAbility().addTarget(target3.setTargetTag(3).withChooseHint("to take damage")); } private FriendlyRivalry(final FriendlyRivalry card) { @@ -62,7 +63,7 @@ class FriendlyRivalryEffect extends OneShotEffect { FriendlyRivalryEffect() { super(Outcome.Benefit); staticText = "Target creature you control and up to one other target legendary " + - "creature you control each deal damage equal to their power to target creature you don't control."; + "creature you control each deal damage equal to their power to target creature you don't control."; } private FriendlyRivalryEffect(final FriendlyRivalryEffect effect) { @@ -77,34 +78,31 @@ class FriendlyRivalryEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { int size = source.getTargets().size(); - if (size < 2) { + if (size < 3) { + throw new IllegalArgumentException("Wrong code usage. Lost targets list, must has 3, but found: " + source.getTargets()); + } + + List toDealDamage = new ArrayList<>(); + source.getTargets().getTargetsByTag(1).stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .forEach(toDealDamage::add); + source.getTargets().getTargetsByTag(2).stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .forEach(toDealDamage::add); + Permanent toTakeDamage = source.getTargets().getTargetsByTag(3).stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .findFirst().orElse(null); + if (toDealDamage.isEmpty() || toTakeDamage == null) { return false; } - Target damageTarget1 = source.getTargets().get(0); - Target damageTarget2 = size == 3 ? source.getTargets().get(1) : null; + toDealDamage.forEach(permanent -> { + toTakeDamage.damage(permanent.getPower().getValue(), permanent.getId(), source, game, false, true); + }); - Target destTarget = source.getTargets().get(size-1); - if ((damageTarget1.getTargets().isEmpty() && (damageTarget2 == null || damageTarget2.getTargets().isEmpty())) - || destTarget.getTargets().isEmpty()) { - return false; - } - - Permanent permanentDamage1 = damageTarget1.getTargets().isEmpty() ? null - : game.getPermanent(damageTarget1.getTargets().get(0)); - Permanent permanentDamage2 = damageTarget2 == null || damageTarget2.getTargets().isEmpty() ? null - : game.getPermanent(damageTarget2.getTargets().get(0)); - Permanent permanentDest = game.getPermanent(destTarget.getTargets().get(0)); - if (permanentDest == null){ - return false; - } - - if (permanentDamage1 != null) { - permanentDest.damage(permanentDamage1.getPower().getValue(), permanentDamage1.getId(), source, game, false, true); - } - if (permanentDamage2 != null) { - permanentDest.damage(permanentDamage2.getPower().getValue(), permanentDamage2.getId(), source, game, false, true); - } return true; } } diff --git a/Mage.Sets/src/mage/cards/f/Frightcrawler.java b/Mage.Sets/src/mage/cards/f/Frightcrawler.java index 4ab8b4411fb..fbf25b0a792 100644 --- a/Mage.Sets/src/mage/cards/f/Frightcrawler.java +++ b/Mage.Sets/src/mage/cards/f/Frightcrawler.java @@ -36,7 +36,7 @@ public final class Frightcrawler extends CardImpl { // Threshold - As long as seven or more cards are in your graveyard, Frightcrawler gets +2/+2 and can't block. Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), - ThresholdCondition.instance, "If seven or more cards are in your graveyard, {this} gets +2/+2 " + ThresholdCondition.instance, "As long as seven or more cards are in your graveyard, {this} gets +2/+2" )); ability.addEffect(new ConditionalRestrictionEffect( new CantBlockSourceEffect(Duration.WhileOnBattlefield), diff --git a/Mage.Sets/src/mage/cards/f/FromFatherToSon.java b/Mage.Sets/src/mage/cards/f/FromFatherToSon.java new file mode 100644 index 00000000000..5efb5d11515 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FromFatherToSon.java @@ -0,0 +1,53 @@ +package mage.cards.f; + +import mage.abilities.condition.common.CastFromGraveyardSourceCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.abilities.keyword.FlashbackAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FromFatherToSon extends CardImpl { + + private static final FilterCard filter = new FilterCard("Vehicle card"); + + static { + filter.add(SubType.VEHICLE.getPredicate()); + } + + public FromFatherToSon(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{W}"); + + // Search your library for a Vehicle card, reveal it, and put it into your hand. If this spell was cast from a graveyard, put that card onto the battlefield instead. Then shuffle. + this.getSpellAbility().addEffect(new ConditionalOneShotEffect( + new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter)), + new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true), + CastFromGraveyardSourceCondition.instance, "search your library for a Vehicle card, " + + "reveal it, and put it into your hand. If this spell was cast from a graveyard, " + + "put that card onto the battlefield instead. Then shuffle" + )); + + // Flashback {4}{W}{W}{W} + this.addAbility(new FlashbackAbility(this, new ManaCostsImpl<>("{4}{W}{W}{W}"))); + } + + private FromFatherToSon(final FromFatherToSon card) { + super(card); + } + + @Override + public FromFatherToSon copy() { + return new FromFatherToSon(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FungalSprouting.java b/Mage.Sets/src/mage/cards/f/FungalSprouting.java index 0853e074cc3..59c9abc60df 100644 --- a/Mage.Sets/src/mage/cards/f/FungalSprouting.java +++ b/Mage.Sets/src/mage/cards/f/FungalSprouting.java @@ -1,16 +1,16 @@ package mage.cards.f; -import java.util.UUID; -import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.game.permanent.token.SaprolingToken; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class FungalSprouting extends CardImpl { @@ -19,8 +19,8 @@ public final class FungalSprouting extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{G}"); // Create X 1/1 green Saproling creature tokens, where X is the greatest power among creatures you control. - this.getSpellAbility().addEffect(new CreateTokenEffect(new SaprolingToken(), GreatestPowerAmongControlledCreaturesValue.instance)); - this.getSpellAbility().addHint(GreatestPowerAmongControlledCreaturesValue.getHint()); + this.getSpellAbility().addEffect(new CreateTokenEffect(new SaprolingToken(), GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES)); + this.getSpellAbility().addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint()); } private FungalSprouting(final FungalSprouting card) { diff --git a/Mage.Sets/src/mage/cards/f/FungusElemental.java b/Mage.Sets/src/mage/cards/f/FungusElemental.java index 2267ade74cc..19dd31405ec 100644 --- a/Mage.Sets/src/mage/cards/f/FungusElemental.java +++ b/Mage.Sets/src/mage/cards/f/FungusElemental.java @@ -15,7 +15,6 @@ import mage.constants.CardType; import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterControlledPermanent; -import mage.target.common.TargetControlledPermanent; /** * @@ -42,7 +41,7 @@ public final class FungusElemental extends CardImpl { Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P2P2.createInstance()), new ManaCostsImpl<>("{G}"), - SourceEnteredThisTurnCondition.instance + SourceEnteredThisTurnCondition.DID ); ability.addCost(new SacrificeTargetCost(filter)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/f/FuriousSpinesplitter.java b/Mage.Sets/src/mage/cards/f/FuriousSpinesplitter.java new file mode 100644 index 00000000000..bfae9505ed1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FuriousSpinesplitter.java @@ -0,0 +1,82 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.game.Game; +import mage.watchers.common.AmountOfDamageAPlayerReceivedThisTurnWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FuriousSpinesplitter extends CardImpl { + + public FuriousSpinesplitter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R/G}{R/G}"); + + this.subtype.add(SubType.OGRE); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // At the beginning of your end step, put a +1/+1 counter on Furious Spinesplitter for each opponent that was dealt damage this turn. + this.addAbility(new BeginningOfEndStepTriggeredAbility(new AddCountersSourceEffect( + CounterType.P1P1.createInstance(), FuriousSpinesplitterValue.instance + )), new AmountOfDamageAPlayerReceivedThisTurnWatcher()); + } + + private FuriousSpinesplitter(final FuriousSpinesplitter card) { + super(card); + } + + @Override + public FuriousSpinesplitter copy() { + return new FuriousSpinesplitter(this); + } +} + +enum FuriousSpinesplitterValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return game + .getOpponents(sourceAbility.getControllerId()) + .stream() + .map(game + .getState() + .getWatcher(AmountOfDamageAPlayerReceivedThisTurnWatcher.class) + ::getAmountOfDamageReceivedThisTurn) + .mapToInt(x -> Math.min(x, 1)) + .sum(); + } + + @Override + public FuriousSpinesplitterValue copy() { + return this; + } + + @Override + public String getMessage() { + return "opponent that was dealt damage this turn"; + } + + @Override + public String toString() { + return "1"; + } +} diff --git a/Mage.Sets/src/mage/cards/g/Gaelicat.java b/Mage.Sets/src/mage/cards/g/Gaelicat.java new file mode 100644 index 00000000000..994f332958d --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/Gaelicat.java @@ -0,0 +1,60 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.common.ArtifactYouControlHint; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Gaelicat extends CardImpl { + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + StaticFilters.FILTER_PERMANENT_ARTIFACT, + ComparisonType.MORE_THAN, 1, true + ); + + public Gaelicat(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.CAT); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // As long as you control two or more artifacts, this creature gets +2/+0. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostSourceEffect(2, 0, Duration.WhileOnBattlefield), + condition, "as long as you control two or more artifacts, this creature gets +2/+0" + )).addHint(ArtifactYouControlHint.instance)); + } + + private Gaelicat(final Gaelicat card) { + super(card); + } + + @Override + public Gaelicat copy() { + return new Gaelicat(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GaiusVanBaelsar.java b/Mage.Sets/src/mage/cards/g/GaiusVanBaelsar.java new file mode 100644 index 00000000000..3f281e03e78 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GaiusVanBaelsar.java @@ -0,0 +1,51 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.SacrificeAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GaiusVanBaelsar extends CardImpl { + + public GaiusVanBaelsar(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // When Gaius van Baelsar enters, choose one -- + // * Each player sacrifices a creature token of their choice. + Ability ability = new EntersBattlefieldTriggeredAbility(new SacrificeAllEffect(StaticFilters.FILTER_CREATURE_TOKEN)); + + // * Each player sacrifices a nontoken creature of their choice. + ability.addMode(new Mode(new SacrificeAllEffect(StaticFilters.FILTER_CREATURE_NON_TOKEN))); + + // * Each player sacrifices an enchantment of their choice. + ability.addMode(new Mode(new SacrificeAllEffect(StaticFilters.FILTER_PERMANENT_ENCHANTMENT))); + this.addAbility(ability); + } + + private GaiusVanBaelsar(final GaiusVanBaelsar card) { + super(card); + } + + @Override + public GaiusVanBaelsar copy() { + return new GaiusVanBaelsar(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GalecasterColossus.java b/Mage.Sets/src/mage/cards/g/GalecasterColossus.java index 2d712bbad85..80642ff1d0c 100644 --- a/Mage.Sets/src/mage/cards/g/GalecasterColossus.java +++ b/Mage.Sets/src/mage/cards/g/GalecasterColossus.java @@ -1,7 +1,6 @@ package mage.cards.g; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -12,15 +11,15 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.TargetController; -import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterNonlandPermanent; import mage.filter.predicate.permanent.TappedPredicate; +import mage.target.TargetPermanent; import mage.target.common.TargetControlledPermanent; -import mage.target.common.TargetNonlandPermanent; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class GalecasterColossus extends CardImpl { @@ -44,7 +43,7 @@ public final class GalecasterColossus extends CardImpl { // Tap an untapped Wizard you control: Return target nonland permanent you don't control to its owner's hand. Ability ability = new SimpleActivatedAbility(new ReturnToHandTargetEffect(), new TapTargetCost(new TargetControlledPermanent(1, 1, filter2, true))); - ability.addTarget(new TargetNonlandPermanent(filter)); + ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GandalfOfTheSecretFire.java b/Mage.Sets/src/mage/cards/g/GandalfOfTheSecretFire.java index 48b78922b53..8e11db87318 100644 --- a/Mage.Sets/src/mage/cards/g/GandalfOfTheSecretFire.java +++ b/Mage.Sets/src/mage/cards/g/GandalfOfTheSecretFire.java @@ -87,16 +87,20 @@ class GandalfOfTheSecretFireTriggeredAbility extends TriggeredAbilityImpl { class GandalfOfTheSecretFireEffect extends ReplacementEffectImpl { - private final MageObjectReference mor; + // we store both Spell and Card to work properly on split cards. + private final MageObjectReference morSpell; + private final MageObjectReference morCard; GandalfOfTheSecretFireEffect(Spell spell, Game game) { super(Duration.OneUse, Outcome.Benefit); - this.mor = new MageObjectReference(spell.getCard(), game); + this.morSpell = new MageObjectReference(spell.getCard(), game); + this.morCard = new MageObjectReference(spell.getMainCard(), game); } private GandalfOfTheSecretFireEffect(final GandalfOfTheSecretFireEffect effect) { super(effect); - this.mor = effect.mor; + this.morSpell = effect.morSpell; + this.morCard = effect.morCard; } @Override @@ -107,7 +111,7 @@ class GandalfOfTheSecretFireEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player controller = game.getPlayer(source.getControllerId()); - Spell sourceSpell = game.getStack().getSpell(event.getTargetId()); + Spell sourceSpell = morSpell.getSpell(game); if (controller == null || sourceSpell == null || sourceSpell.isCopy()) { return false; } @@ -124,14 +128,9 @@ class GandalfOfTheSecretFireEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { ZoneChangeEvent zEvent = ((ZoneChangeEvent) event); - if (zEvent.getFromZone() != Zone.STACK - || zEvent.getToZone() != Zone.GRAVEYARD - || event.getSourceId() == null - || !event.getSourceId().equals(event.getTargetId()) - || !mor.equals(new MageObjectReference(event.getTargetId(), game))) { - return false; - } - Spell spell = game.getStack().getSpell(mor.getSourceId()); - return spell != null && spell.isInstantOrSorcery(game); + return Zone.STACK.equals(zEvent.getFromZone()) + && Zone.GRAVEYARD.equals(zEvent.getToZone()) + && morSpell.refersTo(event.getSourceId(), game) // this is how we check that the spell resolved properly (and was not countered or the like) + && morCard.refersTo(event.getTargetId(), game); // this is how we check that the card being moved is the one we want. } } diff --git a/Mage.Sets/src/mage/cards/g/GarnetPrincessOfAlexandria.java b/Mage.Sets/src/mage/cards/g/GarnetPrincessOfAlexandria.java new file mode 100644 index 00000000000..4f1a787167e --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GarnetPrincessOfAlexandria.java @@ -0,0 +1,102 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetPermanent; + +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GarnetPrincessOfAlexandria extends CardImpl { + + public GarnetPrincessOfAlexandria(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.NOBLE); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Whenever Garnet attacks, you may remove a lore counter from each of any number of Sagas you control. Put a +1/+1 counter on Garnet for each lore counter removed this way. + this.addAbility(new AttacksTriggeredAbility(new GarnetPrincessOfAlexandriaEffect())); + } + + private GarnetPrincessOfAlexandria(final GarnetPrincessOfAlexandria card) { + super(card); + } + + @Override + public GarnetPrincessOfAlexandria copy() { + return new GarnetPrincessOfAlexandria(this); + } +} + +class GarnetPrincessOfAlexandriaEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.SAGA, "Sagas you control"); + + static { + filter.add(CounterType.LORE.getPredicate()); + } + + GarnetPrincessOfAlexandriaEffect() { + super(Outcome.Benefit); + staticText = "you may remove a lore counter from each of any number of Sagas you control. " + + "Put a +1/+1 counter on {this} for each lore counter removed this way"; + } + + private GarnetPrincessOfAlexandriaEffect(final GarnetPrincessOfAlexandriaEffect effect) { + super(effect); + } + + @Override + public GarnetPrincessOfAlexandriaEffect copy() { + return new GarnetPrincessOfAlexandriaEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + TargetPermanent target = new TargetPermanent(0, Integer.MAX_VALUE, filter, true); + target.withChooseHint("to remove lore counters from"); + player.choose(outcome, target, source, game); + int count = target.getTargets() + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .mapToInt(permanent -> permanent.removeCounters(CounterType.LORE.createInstance(), source, game)) + .sum(); + if (count < 1) { + return false; + } + Optional.ofNullable(source.getSourcePermanentIfItStillExists(game)) + .map(permanent -> permanent.addCounters(CounterType.P1P1.createInstance(count), source, game)); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GarrukPrimalHunter.java b/Mage.Sets/src/mage/cards/g/GarrukPrimalHunter.java index 3f1e67e7d4f..4cdc493257e 100644 --- a/Mage.Sets/src/mage/cards/g/GarrukPrimalHunter.java +++ b/Mage.Sets/src/mage/cards/g/GarrukPrimalHunter.java @@ -1,28 +1,23 @@ package mage.cards.g; -import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Outcome; import mage.constants.SuperType; -import mage.filter.StaticFilters; import mage.filter.common.FilterControlledLandPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.game.permanent.token.BeastToken; import mage.game.permanent.token.WurmToken; -import mage.players.Player; + +import java.util.UUID; /** - * * @author Loki */ public final class GarrukPrimalHunter extends CardImpl { @@ -30,7 +25,7 @@ public final class GarrukPrimalHunter extends CardImpl { private static final FilterControlledPermanent filter = new FilterControlledLandPermanent(); public GarrukPrimalHunter(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.PLANESWALKER},"{2}{G}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{G}{G}{G}"); this.supertype.add(SuperType.LEGENDARY); this.subtype.add(SubType.GARRUK); @@ -40,7 +35,10 @@ public final class GarrukPrimalHunter extends CardImpl { this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new BeastToken()), 1)); // -3: Draw cards equal to the greatest power among creatures you control. - this.addAbility(new LoyaltyAbility(new GarrukPrimalHunterEffect(), -3)); + this.addAbility(new LoyaltyAbility( + new DrawCardSourceControllerEffect(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES) + .setText("Draw cards equal to the greatest power among creatures you control"), + -3).addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint())); // -6: Create a 6/6 green Wurm creature token for each land you control. this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new WurmToken(), new PermanentsOnBattlefieldCount(filter)), -6)); @@ -55,38 +53,4 @@ public final class GarrukPrimalHunter extends CardImpl { return new GarrukPrimalHunter(this); } -} - -class GarrukPrimalHunterEffect extends OneShotEffect { - - GarrukPrimalHunterEffect() { - super(Outcome.DrawCard); - staticText = "Draw cards equal to the greatest power among creatures you control"; - } - - private GarrukPrimalHunterEffect(final GarrukPrimalHunterEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - int amount = 0; - for (Permanent p : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_CONTROLLED_CREATURE, source.getControllerId(), game)) { - if (p.getPower().getValue() > amount) { - amount = p.getPower().getValue(); - } - } - player.drawCards(amount, source, game); - return true; - } - return false; - } - - @Override - public GarrukPrimalHunterEffect copy() { - return new GarrukPrimalHunterEffect(this); - } - -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java b/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java index 74074c02bd1..35f1bdb7619 100644 --- a/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java +++ b/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java @@ -12,19 +12,11 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.cards.CardsImpl; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.other.AnotherTargetPredicate; import mage.game.Game; import mage.players.Player; -import mage.target.common.TargetControlledCreaturePermanent; -import mage.target.common.TargetCreaturePermanent; +import mage.target.TargetPermanent; import java.util.UUID; @@ -49,14 +41,8 @@ public final class GarrukSavageHerald extends CardImpl { effect.setText("Target creature you control deals damage equal to its power to another target creature"); Ability minusAbility = new LoyaltyAbility(effect, -2); - TargetControlledCreaturePermanent controlledCreature = new TargetControlledCreaturePermanent(); - controlledCreature.setTargetTag(1); - minusAbility.addTarget(controlledCreature); - - FilterCreaturePermanent filter = new FilterCreaturePermanent(); - filter.add(new AnotherTargetPredicate(2)); - TargetCreaturePermanent anotherTargetCreature = new TargetCreaturePermanent(filter); - minusAbility.addTarget(anotherTargetCreature.withChooseHint("another creature to deal damage to")); + minusAbility.addTarget(new TargetPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE).setTargetTag(1)); + minusAbility.addTarget(new TargetPermanent(StaticFilters.FILTER_ANOTHER_CREATURE_TARGET_2).setTargetTag(2)); this.addAbility(minusAbility); @@ -107,7 +93,7 @@ class GarrukSavageHeraldEffect extends OneShotEffect { if (card.isCreature(game)) { return player.moveCards(card, Zone.HAND, source, game); } else { - return player.putCardsOnBottomOfLibrary(card, game, source, false); + return player.putCardsOnBottomOfLibrary(card, game, source); } } } diff --git a/Mage.Sets/src/mage/cards/g/GarruksUprising.java b/Mage.Sets/src/mage/cards/g/GarruksUprising.java index 180629f5d1f..98af2de7350 100644 --- a/Mage.Sets/src/mage/cards/g/GarruksUprising.java +++ b/Mage.Sets/src/mage/cards/g/GarruksUprising.java @@ -1,10 +1,9 @@ package mage.cards.g; -import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.FerociousCondition; -import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.hint.common.FerociousHint; @@ -17,18 +16,17 @@ import mage.constants.Duration; import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; import java.util.UUID; /** - * * @author htrajan */ public final class GarruksUprising extends CardImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent("a creature with power 4 or greater"); + private static final FilterPermanent filter = new FilterControlledCreaturePermanent("a creature you control with power 4 or greater"); static { filter.add(new PowerPredicate(ComparisonType.MORE_THAN, 3)); @@ -38,9 +36,9 @@ public final class GarruksUprising extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); // When Garruk's Uprising enters the battlefield, if you control a creature with power 4 or greater, draw a card. - this.addAbility(new EntersBattlefieldTriggeredAbility(new ConditionalOneShotEffect( - new DrawCardSourceControllerEffect(1), FerociousCondition.instance)) - .addHint(FerociousHint.instance)); + this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1)) + .withInterveningIf(FerociousCondition.instance) + .addHint(FerociousHint.instance)); // Creatures you control have trample. this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( @@ -49,7 +47,7 @@ public final class GarruksUprising extends CardImpl { ))); // Whenever a creature with power 4 or greater you control enters, draw a card. - this.addAbility(new EntersBattlefieldControlledTriggeredAbility( + this.addAbility(new EntersBattlefieldAllTriggeredAbility( Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), filter, false )); } diff --git a/Mage.Sets/src/mage/cards/g/GateColossus.java b/Mage.Sets/src/mage/cards/g/GateColossus.java index c83c89ec368..9b88eee5c47 100644 --- a/Mage.Sets/src/mage/cards/g/GateColossus.java +++ b/Mage.Sets/src/mage/cards/g/GateColossus.java @@ -2,13 +2,12 @@ package mage.cards.g; import mage.MageInt; import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.AffinityEffect; import mage.abilities.effects.common.PutOnLibrarySourceEffect; -import mage.abilities.hint.common.GateYouControlHint; +import mage.abilities.keyword.AffinityAbility; import mage.abilities.keyword.DauntAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AffinityType; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; @@ -21,8 +20,7 @@ import java.util.UUID; */ public final class GateColossus extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.GATE, "Gates"); - private static final FilterControlledPermanent filter2 = new FilterControlledPermanent(SubType.GATE); + private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.GATE); public GateColossus(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{8}"); @@ -31,8 +29,8 @@ public final class GateColossus extends CardImpl { this.power = new MageInt(8); this.toughness = new MageInt(8); - // This spell costs {1} less to cast for each Gate you control. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filter)).addHint(GateYouControlHint.instance)); + // Affinity for Gates + this.addAbility(new AffinityAbility(AffinityType.GATES)); // Gate Colossus can't be blocked by creatures with power 2 or less. this.addAbility(new DauntAbility()); @@ -42,7 +40,7 @@ public final class GateColossus extends CardImpl { Zone.GRAVEYARD, new PutOnLibrarySourceEffect( true, "put this card from your graveyard on top of your library" - ), filter2, true + ), filter, true )); } diff --git a/Mage.Sets/src/mage/cards/g/GatebreakerRam.java b/Mage.Sets/src/mage/cards/g/GatebreakerRam.java index bcb422f859b..8043d5e2d62 100644 --- a/Mage.Sets/src/mage/cards/g/GatebreakerRam.java +++ b/Mage.Sets/src/mage/cards/g/GatebreakerRam.java @@ -10,7 +10,7 @@ import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; -import mage.abilities.hint.common.GateYouControlHint; +import mage.abilities.hint.common.GatesYouControlHint; import mage.abilities.keyword.TrampleAbility; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; @@ -51,7 +51,7 @@ public final class GatebreakerRam extends CardImpl { this.addAbility(new SimpleStaticAbility( new BoostSourceEffect(xValue, xValue, Duration.WhileOnBattlefield) .setText("{this} gets +1/+1 for each Gate you control.") - ).addHint(GateYouControlHint.instance)); + ).addHint(GatesYouControlHint.instance)); // As long as you control two or more Gates, Gatebreaker Ram has vigilance and trample. Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( diff --git a/Mage.Sets/src/mage/cards/g/GatekeeperGargoyle.java b/Mage.Sets/src/mage/cards/g/GatekeeperGargoyle.java index 6878a0aa607..82a802d0aa3 100644 --- a/Mage.Sets/src/mage/cards/g/GatekeeperGargoyle.java +++ b/Mage.Sets/src/mage/cards/g/GatekeeperGargoyle.java @@ -4,7 +4,7 @@ import mage.MageInt; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.dynamicvalue.common.GateYouControlCount; import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.abilities.hint.common.GateYouControlHint; +import mage.abilities.hint.common.GatesYouControlHint; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -35,7 +35,7 @@ public final class GatekeeperGargoyle extends CardImpl { CounterType.P1P1.createInstance(), GateYouControlCount.instance, true ), "with a +1/+1 counter on it for each Gate you control" - ).addHint(GateYouControlHint.instance)); + ).addHint(GatesYouControlHint.instance)); } private GatekeeperGargoyle(final GatekeeperGargoyle card) { diff --git a/Mage.Sets/src/mage/cards/g/GattaAndLuzzu.java b/Mage.Sets/src/mage/cards/g/GattaAndLuzzu.java new file mode 100644 index 00000000000..b763e715cba --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GattaAndLuzzu.java @@ -0,0 +1,87 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.PreventionEffectImpl; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GattaAndLuzzu extends CardImpl { + + public GattaAndLuzzu(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // When Gatta and Luzzu enters, choose target creature you control. If damage would be dealt to that creature this turn, prevent that damage and put that many +1/+1 counters on it. + Ability ability = new EntersBattlefieldTriggeredAbility(new GattaAndLuzzuEffect()); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + private GattaAndLuzzu(final GattaAndLuzzu card) { + super(card); + } + + @Override + public GattaAndLuzzu copy() { + return new GattaAndLuzzu(this); + } +} + +class GattaAndLuzzuEffect extends PreventionEffectImpl { + + GattaAndLuzzuEffect() { + super(Duration.EndOfTurn, Integer.MAX_VALUE, false, false); + staticText = "choose target creature you control. If damage would be dealt " + + "to that creature this turn, prevent that damage and put that many +1/+1 counters on it"; + } + + private GattaAndLuzzuEffect(final GattaAndLuzzuEffect effect) { + super(effect); + } + + @Override + public GattaAndLuzzuEffect copy() { + return new GattaAndLuzzuEffect(this); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return super.applies(event, source, game) + && event.getTargetId().equals(getTargetPointer().getFirst(game, source)); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Optional.ofNullable(getTargetPointer().getFirst(game, source)) + .map(game::getPermanent) + .ifPresent(permanent -> permanent.addCounters( + CounterType.P1P1.createInstance(event.getAmount()), source, game + )); + return super.replaceEvent(event, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GempalmIncinerator.java b/Mage.Sets/src/mage/cards/g/GempalmIncinerator.java index cc03249ec59..75f6e4a7e32 100644 --- a/Mage.Sets/src/mage/cards/g/GempalmIncinerator.java +++ b/Mage.Sets/src/mage/cards/g/GempalmIncinerator.java @@ -37,7 +37,8 @@ public final class GempalmIncinerator extends CardImpl { // Cycling {1}{R} this.addAbility(new CyclingAbility(new ManaCostsImpl<>("{1}{R}"))); // When you cycle Gempalm Incinerator, you may have it deal X damage to target creature, where X is the number of Goblins on the battlefield. - Ability ability = new CycleTriggeredAbility(new DamageTargetEffect(new PermanentsOnBattlefieldCount(filter)),true); + Ability ability = new CycleTriggeredAbility(new DamageTargetEffect(new PermanentsOnBattlefieldCount(filter)) + .setText("you may have it deal X damage to target creature, where X is the number of Goblins on the battlefield"),true); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GemstoneMine.java b/Mage.Sets/src/mage/cards/g/GemstoneMine.java index 50c2aa87f78..16f1407978d 100644 --- a/Mage.Sets/src/mage/cards/g/GemstoneMine.java +++ b/Mage.Sets/src/mage/cards/g/GemstoneMine.java @@ -1,9 +1,9 @@ package mage.cards.g; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.decorator.ConditionalOneShotEffect; @@ -13,16 +13,20 @@ import mage.abilities.mana.AnyColorManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.counters.CounterType; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class GemstoneMine extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.MINING, ComparisonType.EQUAL_TO, 0); + public GemstoneMine(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // Gemstone Mine enters the battlefield with three mining counters on it. this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.MINING.createInstance(3)), @@ -31,7 +35,10 @@ public final class GemstoneMine extends CardImpl { // {T}, Remove a mining counter from Gemstone Mine: Add one mana of any color. If there are no mining counters on Gemstone Mine, sacrifice it. Ability ability = new AnyColorManaAbility(); ability.addCost(new RemoveCountersSourceCost(CounterType.MINING.createInstance(1))); - ability.addEffect(new ConditionalOneShotEffect(new SacrificeSourceEffect(), new SourceHasCounterCondition(CounterType.MINING, 0, 0), "If there are no mining counters on {this}, sacrifice it")); + ability.addEffect(new ConditionalOneShotEffect( + new SacrificeSourceEffect(), condition, + "If there are no mining counters on {this}, sacrifice it" + )); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GeneralLeoCristophe.java b/Mage.Sets/src/mage/cards/g/GeneralLeoCristophe.java index d81a9a1e6f9..a42d9e34429 100644 --- a/Mage.Sets/src/mage/cards/g/GeneralLeoCristophe.java +++ b/Mage.Sets/src/mage/cards/g/GeneralLeoCristophe.java @@ -44,7 +44,9 @@ public final class GeneralLeoCristophe extends CardImpl { // When General Leo Cristophe enters, return up to one target creature card with mana value 3 or less from your graveyard to the battlefield. Then put a +1/+1 counter on General Leo Cristophe for each creature you control. Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect()); ability.addTarget(new TargetCardInYourGraveyard(0, 1, filter)); - ability.addEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance(), CreaturesYouControlCount.instance)); + ability.addEffect(new AddCountersSourceEffect( + CounterType.P1P1.createInstance(), CreaturesYouControlCount.instance + ).setText("Then put a +1/+1 counter on {this} for each creature you control")); this.addAbility(ability.addHint(CreaturesYouControlHint.instance)); } diff --git a/Mage.Sets/src/mage/cards/g/GenjiGlove.java b/Mage.Sets/src/mage/cards/g/GenjiGlove.java new file mode 100644 index 00000000000..4d9d885cdc9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GenjiGlove.java @@ -0,0 +1,53 @@ +package mage.cards.g; + +import mage.abilities.Ability; +import mage.abilities.common.AttacksAttachedTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.FirstCombatPhaseCondition; +import mage.abilities.effects.common.AdditionalCombatPhaseEffect; +import mage.abilities.effects.common.UntapAttachedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.abilities.keyword.EquipAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GenjiGlove extends CardImpl { + + public GenjiGlove(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{5}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Equipped creature has double strike. + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( + DoubleStrikeAbility.getInstance(), AttachmentType.EQUIPMENT + ))); + + // Whenever equipped creature attacks, if it's the first combat phase of the turn, untap it. After this phase, there is an additional combat phase. + Ability ability = new AttacksAttachedTriggeredAbility(new UntapAttachedEffect().setText("untap it")) + .withInterveningIf(FirstCombatPhaseCondition.instance); + ability.addEffect(new AdditionalCombatPhaseEffect()); + this.addAbility(ability); + + // Equip {3} + this.addAbility(new EquipAbility(3)); + } + + private GenjiGlove(final GenjiGlove card) { + super(card); + } + + @Override + public GenjiGlove copy() { + return new GenjiGlove(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GenjuOfTheCedars.java b/Mage.Sets/src/mage/cards/g/GenjuOfTheCedars.java index b461656469a..82be93e1edf 100644 --- a/Mage.Sets/src/mage/cards/g/GenjuOfTheCedars.java +++ b/Mage.Sets/src/mage/cards/g/GenjuOfTheCedars.java @@ -1,53 +1,55 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.DiesAttachedTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.abilities.effects.common.continuous.BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterPermanent; import mage.filter.common.FilterLandPermanent; import mage.game.permanent.token.TokenImpl; import mage.target.TargetPermanent; -import mage.target.common.TargetLandPermanent; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class GenjuOfTheCedars extends CardImpl { - private static final FilterLandPermanent filter = new FilterLandPermanent(SubType.FOREST, "Forest"); + private static final FilterPermanent filter = new FilterLandPermanent(SubType.FOREST, "Forest"); public GenjuOfTheCedars(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{G}"); this.subtype.add(SubType.AURA); // Enchant Forest - TargetPermanent auraTarget = new TargetLandPermanent(filter); + TargetPermanent auraTarget = new TargetPermanent(filter); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.PutCreatureInPlay)); - Ability ability = new EnchantAbility(auraTarget); - this.addAbility(ability); + this.addAbility(new EnchantAbility(auraTarget)); // {2}: Enchanted Forest becomes a 4/4 green Spirit creature until end of turn. It's still a land. - Ability ability2 = new SimpleActivatedAbility(new BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect(new SpiritToken(), "Enchanted Forest becomes a 4/4 green Spirit creature until end of turn. It's still a land", Duration.EndOfTurn), new GenericManaCost(2)); - this.addAbility(ability2); + this.addAbility(new SimpleActivatedAbility(new BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect( + new SpiritToken(), "Enchanted Forest becomes a 4/4 green " + + "Spirit creature until end of turn. It's still a land", Duration.EndOfTurn + ), new GenericManaCost(2))); // When enchanted Forest is put into a graveyard, you may return Genju of the Cedars from your graveyard to your hand. - Effect effect = new ReturnToHandSourceEffect(false, true); - effect.setText("you may return {this} from your graveyard to your hand"); - Ability ability3 = new DiesAttachedTriggeredAbility(effect, "enchanted Forest", true, false); - this.addAbility(ability3); + this.addAbility(new DiesAttachedTriggeredAbility( + new ReturnToHandSourceEffect(false, true) + .setText("you may return {this} from your graveyard to your hand"), + "enchanted Forest", true, false + )); } private GenjuOfTheCedars(final GenjuOfTheCedars card) { diff --git a/Mage.Sets/src/mage/cards/g/GenjuOfTheFalls.java b/Mage.Sets/src/mage/cards/g/GenjuOfTheFalls.java index 6068e52e51e..607d7552a5c 100644 --- a/Mage.Sets/src/mage/cards/g/GenjuOfTheFalls.java +++ b/Mage.Sets/src/mage/cards/g/GenjuOfTheFalls.java @@ -1,13 +1,10 @@ package mage.cards.g; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.DiesAttachedTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.abilities.effects.common.continuous.BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect; @@ -15,40 +12,45 @@ import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.common.FilterLandPermanent; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterPermanent; import mage.game.permanent.token.TokenImpl; import mage.target.TargetPermanent; -import mage.target.common.TargetLandPermanent; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class GenjuOfTheFalls extends CardImpl { - private static final FilterLandPermanent FILTER = new FilterLandPermanent(SubType.ISLAND, "Island"); + private static final FilterPermanent FILTER = new FilterPermanent(SubType.ISLAND, "Island"); public GenjuOfTheFalls(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{U}"); this.subtype.add(SubType.AURA); // Enchant Island - TargetPermanent auraTarget = new TargetLandPermanent(FILTER); + TargetPermanent auraTarget = new TargetPermanent(FILTER); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.PutCreatureInPlay)); - Ability ability = new EnchantAbility(auraTarget); - this.addAbility(ability); + this.addAbility(new EnchantAbility(auraTarget)); // {2}: Enchanted Island becomes a 3/2 blue Spirit creature with flying until end of turn. It's still a land. - Ability ability2 = new SimpleActivatedAbility(new BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect(new SpiritToken(), "Enchanted Island becomes a 3/2 blue Spirit creature with flying until end of turn. It's still a land", Duration.EndOfTurn), new GenericManaCost(2)); - this.addAbility(ability2); + this.addAbility(new SimpleActivatedAbility(new BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect( + new SpiritToken(), "Enchanted Island becomes a 3/2 blue Spirit " + + "creature with flying until end of turn. It's still a land", Duration.EndOfTurn + ), new GenericManaCost(2))); // When enchanted Island is put into a graveyard, you may return Genju of the Falls from your graveyard to your hand. TargetPermanent auraTarget = new TargetLandPermanent(filter); - Effect effect = new ReturnToHandSourceEffect(false, true); - effect.setText("you may return {this} from your graveyard to your hand"); - Ability ability3 = new DiesAttachedTriggeredAbility(effect, "enchanted Island", true, false); - this.addAbility(ability3); + this.addAbility(new DiesAttachedTriggeredAbility( + new ReturnToHandSourceEffect(false, true) + .setText("you may return {this} from your graveyard to your hand"), + "enchanted Island", true, false + )); } private GenjuOfTheFalls(final GenjuOfTheFalls card) { diff --git a/Mage.Sets/src/mage/cards/g/GenjuOfTheFens.java b/Mage.Sets/src/mage/cards/g/GenjuOfTheFens.java index c4df15c0a11..28c88f9480f 100644 --- a/Mage.Sets/src/mage/cards/g/GenjuOfTheFens.java +++ b/Mage.Sets/src/mage/cards/g/GenjuOfTheFens.java @@ -1,14 +1,10 @@ - package mage.cards.g; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.DiesAttachedTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.abilities.effects.common.continuous.BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect; @@ -16,40 +12,45 @@ import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.common.FilterLandPermanent; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterPermanent; import mage.game.permanent.token.TokenImpl; import mage.target.TargetPermanent; -import mage.target.common.TargetLandPermanent; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class GenjuOfTheFens extends CardImpl { - private static final FilterLandPermanent FILTER = new FilterLandPermanent(SubType.SWAMP, "Swamp"); + private static final FilterPermanent FILTER = new FilterPermanent(SubType.SWAMP, "Swamp"); public GenjuOfTheFens(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{B}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{B}"); this.subtype.add(SubType.AURA); // Enchant Swamp - TargetPermanent auraTarget = new TargetLandPermanent(FILTER); + TargetPermanent auraTarget = new TargetPermanent(FILTER); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.PutCreatureInPlay)); - Ability ability = new EnchantAbility(auraTarget); - this.addAbility(ability); + this.addAbility(new EnchantAbility(auraTarget)); // {2}: Until end of turn, enchanted Swamp becomes a 2/2 black Spirit creature with "{B}: This creature gets +1/+1 until end of turn." It's still a land. - Ability ability2 = new SimpleActivatedAbility(new BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect(new SpiritToken(), "Until end of turn, enchanted Swamp becomes a 2/2 black Spirit creature with \"{B}: This creature gets +1/+1 until end of turn.\" It's still a land", Duration.EndOfTurn), new GenericManaCost(2)); - this.addAbility(ability2); + this.addAbility(new SimpleActivatedAbility(new BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect( + new SpiritToken(), "Until end of turn, enchanted Swamp becomes a 2/2 black Spirit creature " + + "with \"{B}: This creature gets +1/+1 until end of turn.\" It's still a land", Duration.EndOfTurn + ), new GenericManaCost(2))); // When enchanted Swamp is put into a graveyard, you may return Genju of the Fens from your graveyard to your hand. - Effect effect = new ReturnToHandSourceEffect(false, true); - effect.setText("you may return {this} from your graveyard to your hand"); - Ability ability3 = new DiesAttachedTriggeredAbility(effect, "enchanted Swamp", true, false); - this.addAbility(ability3); + this.addAbility(new DiesAttachedTriggeredAbility( + new ReturnToHandSourceEffect(false, true) + .setText("you may return {this} from your graveyard to your hand"), + "enchanted Swamp", true, false + )); } private GenjuOfTheFens(final GenjuOfTheFens card) { diff --git a/Mage.Sets/src/mage/cards/g/GenjuOfTheFields.java b/Mage.Sets/src/mage/cards/g/GenjuOfTheFields.java index 14455a304aa..38575aa807b 100644 --- a/Mage.Sets/src/mage/cards/g/GenjuOfTheFields.java +++ b/Mage.Sets/src/mage/cards/g/GenjuOfTheFields.java @@ -7,7 +7,6 @@ import mage.abilities.common.DiesAttachedTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.dynamicvalue.common.SavedDamageValue; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.ReturnToHandSourceEffect; @@ -17,10 +16,9 @@ import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterLandPermanent; +import mage.filter.FilterPermanent; import mage.game.permanent.token.TokenImpl; import mage.target.TargetPermanent; -import mage.target.common.TargetLandPermanent; import java.util.UUID; @@ -29,33 +27,34 @@ import java.util.UUID; */ public final class GenjuOfTheFields extends CardImpl { - private static final FilterLandPermanent FILTER = new FilterLandPermanent(SubType.PLAINS, "Plains"); + private static final FilterPermanent FILTER = new FilterPermanent(SubType.PLAINS, "Plains"); public GenjuOfTheFields(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); this.subtype.add(SubType.AURA); // Enchant Plains - TargetPermanent auraTarget = new TargetLandPermanent(FILTER); + TargetPermanent auraTarget = new TargetPermanent(FILTER); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.PutCreatureInPlay)); - Ability ability = new EnchantAbility(auraTarget); - this.addAbility(ability); + this.addAbility(new EnchantAbility(auraTarget)); // {2}: Until end of turn, enchanted Plains becomes a 2/5 white Spirit creature with "Whenever this creature deals damage, its controller gains that much life." It's still a land. - Effect effect = new BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect(new SpiritToken(), - "Until end of turn, enchanted Plains becomes a 2/5 white Spirit creature", Duration.EndOfTurn); - Ability ability2 = new SimpleActivatedAbility(effect, new GenericManaCost(2)); - effect = new GainAbilityAttachedEffect(new DealsDamageSourceTriggeredAbility(new GainLifeEffect(SavedDamageValue.MUCH)), AttachmentType.AURA, Duration.EndOfTurn); - effect.setText("with \"Whenever this creature deals damage, its controller gains that much life.\" It's still a land"); - ability2.addEffect(effect); - this.addAbility(ability2); + Ability ability = new SimpleActivatedAbility(new BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect( + new SpiritToken(), "Until end of turn, enchanted Plains " + + "becomes a 2/5 white Spirit creature", Duration.EndOfTurn + ), new GenericManaCost(2)); + ability.addEffect(new GainAbilityAttachedEffect( + new DealsDamageSourceTriggeredAbility(new GainLifeEffect(SavedDamageValue.MUCH)), AttachmentType.AURA, Duration.EndOfTurn + ).setText("with \"Whenever this creature deals damage, its controller gains that much life.\" It's still a land")); + this.addAbility(ability); // When enchanted Plains is put into a graveyard, you may return Genju of the Fields from your graveyard to your hand. - Effect effect2 = new ReturnToHandSourceEffect(false, true); - effect2.setText("you may return {this} from your graveyard to your hand"); - Ability ability3 = new DiesAttachedTriggeredAbility(effect2, "enchanted Plains", true, false); - this.addAbility(ability3); + this.addAbility(new DiesAttachedTriggeredAbility( + new ReturnToHandSourceEffect(false, true) + .setText("you may return {this} from your graveyard to your hand"), + "enchanted Plains", true, false + )); } private GenjuOfTheFields(final GenjuOfTheFields card) { diff --git a/Mage.Sets/src/mage/cards/g/GenjuOfTheSpires.java b/Mage.Sets/src/mage/cards/g/GenjuOfTheSpires.java index 5f37cf5ab32..11ff060fb6b 100644 --- a/Mage.Sets/src/mage/cards/g/GenjuOfTheSpires.java +++ b/Mage.Sets/src/mage/cards/g/GenjuOfTheSpires.java @@ -1,53 +1,55 @@ package mage.cards.g; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.DiesAttachedTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.abilities.effects.common.continuous.BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.common.FilterLandPermanent; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterPermanent; import mage.game.permanent.token.TokenImpl; import mage.target.TargetPermanent; -import mage.target.common.TargetLandPermanent; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class GenjuOfTheSpires extends CardImpl { - private static final FilterLandPermanent FILTER = new FilterLandPermanent(SubType.MOUNTAIN, "Mountain"); + private static final FilterPermanent FILTER = new FilterPermanent(SubType.MOUNTAIN, "Mountain"); public GenjuOfTheSpires(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{R}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{R}"); this.subtype.add(SubType.AURA); // Enchant Mountain - TargetPermanent auraTarget = new TargetLandPermanent(FILTER); + TargetPermanent auraTarget = new TargetPermanent(FILTER); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.PutCreatureInPlay)); - Ability ability = new EnchantAbility(auraTarget); - this.addAbility(ability); + this.addAbility(new EnchantAbility(auraTarget)); // {2}: Enchanted Mountain becomes a 6/1 red Spirit creature until end of turn. It's still a land. - Ability ability2 = new SimpleActivatedAbility(new BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect(new SpiritToken(), "Enchanted Mountain becomes a 6/1 red Spirit creature until end of turn. It's still a land", Duration.EndOfTurn), new GenericManaCost(2)); - this.addAbility(ability2); + this.addAbility(new SimpleActivatedAbility(new BecomesCreatureAttachedWithActivatedAbilityOrSpellEffect( + new SpiritToken(), "Enchanted Mountain becomes a 6/1 " + + "red Spirit creature until end of turn. It's still a land", Duration.EndOfTurn + ), new GenericManaCost(2))); // When enchanted Mountain is put into a graveyard, you may return Genju of the Spires from your graveyard to your hand. - Effect effect = new ReturnToHandSourceEffect(false, true); - effect.setText("you may return {this} from your graveyard to your hand"); - Ability ability3 = new DiesAttachedTriggeredAbility(effect, "enchanted Mountain", true, false); - this.addAbility(ability3); + this.addAbility(new DiesAttachedTriggeredAbility( + new ReturnToHandSourceEffect(false, true) + .setText("you may return {this} from your graveyard to your hand"), + "enchanted Mountain", true, false + )); } private GenjuOfTheSpires(final GenjuOfTheSpires card) { @@ -69,6 +71,7 @@ public final class GenjuOfTheSpires extends CardImpl { power = new MageInt(6); toughness = new MageInt(1); } + private SpiritToken(final SpiritToken token) { super(token); } diff --git a/Mage.Sets/src/mage/cards/g/GeralfTheFleshwright.java b/Mage.Sets/src/mage/cards/g/GeralfTheFleshwright.java index c51a6f279e9..a9073c08a54 100644 --- a/Mage.Sets/src/mage/cards/g/GeralfTheFleshwright.java +++ b/Mage.Sets/src/mage/cards/g/GeralfTheFleshwright.java @@ -116,7 +116,7 @@ class GeralfTheFleshwrightWatcher extends Watcher { } Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); - if (permanent == null || !permanent.getSubtype().contains(SubType.ZOMBIE)) { + if (permanent == null || !permanent.hasSubtype(SubType.ZOMBIE, game)) { return; } diff --git a/Mage.Sets/src/mage/cards/g/GhaltaAndMavren.java b/Mage.Sets/src/mage/cards/g/GhaltaAndMavren.java index de5fa5f2ed6..e2c4e58d68f 100644 --- a/Mage.Sets/src/mage/cards/g/GhaltaAndMavren.java +++ b/Mage.Sets/src/mage/cards/g/GhaltaAndMavren.java @@ -1,14 +1,16 @@ package mage.cards.g; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValuePositiveHint; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -30,13 +32,17 @@ import java.util.UUID; */ public final class GhaltaAndMavren extends CardImpl { - static final FilterPermanent filter = new FilterAttackingCreature("other attacking creatures"); + private static final FilterPermanent filter = new FilterAttackingCreature("other attacking creatures"); static { filter.add(AnotherPredicate.instance); } - private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, null); + static final GreatestAmongPermanentsValue xValue = new GreatestAmongPermanentsValue(GreatestAmongPermanentsValue.Quality.Power, filter); + private static final Hint hint = new ValuePositiveHint("Greatest power among other attacking creatures", xValue); + + private static final DynamicValue xValue2 = new PermanentsOnBattlefieldCount(filter, null); + private static final Hint hint2 = new ValuePositiveHint("Number of other attacking creatures", xValue2); public GhaltaAndMavren(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{G}{W}{W}"); @@ -53,9 +59,11 @@ public final class GhaltaAndMavren extends CardImpl { // Whenever you attack, choose one -- // * Create a tapped and attacking X/X green Dinosaur creature token with trample, where X is the greatest power among other attacking creatures. Ability ability = new AttacksWithCreaturesTriggeredAbility(new GhaltaAndMavrenEffect(), 1); + ability.addHint(hint); // * Create X 1/1 white Vampire creature tokens with lifelink, where X is the number of other attacking creatures. - ability.addMode(new Mode(new CreateTokenEffect(new IxalanVampireToken(), xValue))); + ability.addMode(new Mode(new CreateTokenEffect(new IxalanVampireToken(), xValue2))); + ability.addHint(hint2); this.addAbility(ability); } @@ -88,16 +96,7 @@ class GhaltaAndMavrenEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - int power = game - .getBattlefield() - .getActivePermanents(GhaltaAndMavren.filter, source.getControllerId(), source, game) - .stream() - .map(MageObject::getPower) - .mapToInt(MageInt::getValue) - .max() - .orElse(0); - return new DinosaurXXToken(power).putOntoBattlefield( - 1, game, source, source.getControllerId(), true, true - ); + int power = GhaltaAndMavren.xValue.calculate(game, source, this); + return new CreateTokenEffect(new DinosaurXXToken(power), 1, true, true).apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/g/GhastlyRemains.java b/Mage.Sets/src/mage/cards/g/GhastlyRemains.java index 4abb971a068..4dd8ddeaccc 100644 --- a/Mage.Sets/src/mage/cards/g/GhastlyRemains.java +++ b/Mage.Sets/src/mage/cards/g/GhastlyRemains.java @@ -35,7 +35,7 @@ public final class GhastlyRemains extends CardImpl { // At the beginning of your upkeep, if Ghastly Remains is in your graveyard, you may pay {B}{B}{B}. If you do, return Ghastly Remains to your hand. this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.GRAVEYARD, - TargetController.YOU, new DoIfCostPaid(new ReturnSourceFromGraveyardToHandEffect().setText("return {this} to your hand"), new ManaCostsImpl<>("{B}{B}{B}")), + TargetController.YOU, new DoIfCostPaid(new ReturnSourceFromGraveyardToHandEffect().setText("return it to your hand"), new ManaCostsImpl<>("{B}{B}{B}")), false).withInterveningIf(SourceInGraveyardCondition.instance)); } diff --git a/Mage.Sets/src/mage/cards/g/GiftOfImmortality.java b/Mage.Sets/src/mage/cards/g/GiftOfImmortality.java index be6eff2ac28..42167e6db99 100644 --- a/Mage.Sets/src/mage/cards/g/GiftOfImmortality.java +++ b/Mage.Sets/src/mage/cards/g/GiftOfImmortality.java @@ -23,6 +23,7 @@ import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @@ -86,7 +87,7 @@ class GiftOfImmortalityEffect extends OneShotEffect { } controller.moveCards(card, Zone.BATTLEFIELD, source, game, false, false, true, null); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/g/Gigantoad.java b/Mage.Sets/src/mage/cards/g/Gigantoad.java new file mode 100644 index 00000000000..c465ca37104 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/Gigantoad.java @@ -0,0 +1,51 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.common.LandsYouControlHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Gigantoad extends CardImpl { + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, ComparisonType.MORE_THAN, 6 + ); + + public Gigantoad(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.FROG); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // As long as you control seven or more lands, this creature gets +2/+2. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), condition, + "as long as you control seven or more lands, this creature gets +2/+2" + )).addHint(LandsYouControlHint.instance)); + } + + private Gigantoad(final Gigantoad card) { + super(card); + } + + @Override + public Gigantoad copy() { + return new Gigantoad(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/Gigapede.java b/Mage.Sets/src/mage/cards/g/Gigapede.java index 0e936cc5eb9..5b18c91032b 100644 --- a/Mage.Sets/src/mage/cards/g/Gigapede.java +++ b/Mage.Sets/src/mage/cards/g/Gigapede.java @@ -34,7 +34,7 @@ public final class Gigapede extends CardImpl { // At the beginning of your upkeep, if Gigapede is in your graveyard, you may discard a card. If you do, return Gigapede to your hand. this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.GRAVEYARD, - TargetController.YOU, new DoIfCostPaid(new ReturnSourceFromGraveyardToHandEffect().setText("return {this} to your hand"), new DiscardCardCost()), + TargetController.YOU, new DoIfCostPaid(new ReturnSourceFromGraveyardToHandEffect().setText("return this card to your hand"), new DiscardCardCost()), false).withInterveningIf(SourceInGraveyardCondition.instance)); } diff --git a/Mage.Sets/src/mage/cards/g/GiottKingOfTheDwarves.java b/Mage.Sets/src/mage/cards/g/GiottKingOfTheDwarves.java new file mode 100644 index 00000000000..f140303f621 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GiottKingOfTheDwarves.java @@ -0,0 +1,61 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GiottKingOfTheDwarves extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledPermanent("Dwarf you control enters and whenever an Equipment you control"); + + static { + filter.add(Predicates.or( + SubType.DWARF.getPredicate(), + SubType.EQUIPMENT.getPredicate() + )); + } + + public GiottKingOfTheDwarves(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.DWARF); + this.subtype.add(SubType.NOBLE); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Double strike + this.addAbility(DoubleStrikeAbility.getInstance()); + + // Whenever Giott or another Dwarf you control enters and whenever an Equipment you control enters, you may discard a card. If you do, draw a card. + this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility(new DoIfCostPaid( + new DrawCardSourceControllerEffect(1), new DiscardCardCost() + ), filter, false, false)); + } + + private GiottKingOfTheDwarves(final GiottKingOfTheDwarves card) { + super(card); + } + + @Override + public GiottKingOfTheDwarves copy() { + return new GiottKingOfTheDwarves(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GisaAndGeralf.java b/Mage.Sets/src/mage/cards/g/GisaAndGeralf.java index b3520a300a4..a059536425a 100644 --- a/Mage.Sets/src/mage/cards/g/GisaAndGeralf.java +++ b/Mage.Sets/src/mage/cards/g/GisaAndGeralf.java @@ -1,7 +1,7 @@ package mage.cards.g; import mage.MageInt; -import mage.abilities.common.CastFromGraveyardOnceEachTurnAbility; +import mage.abilities.common.CastFromGraveyardOnceDuringEachOfYourTurnAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.MillCardsControllerEffect; import mage.cards.CardImpl; @@ -9,17 +9,18 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; +import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; import java.util.UUID; /** - * * @author fireshoes */ public final class GisaAndGeralf extends CardImpl { - private static final FilterCreatureCard filter = new FilterCreatureCard("a Zombie creature spell"); + private static final FilterCard filter = new FilterCreatureCard("a Zombie creature spell"); + static { filter.add(SubType.ZOMBIE.getPredicate()); } @@ -36,7 +37,7 @@ public final class GisaAndGeralf extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new MillCardsControllerEffect(4))); // Once during each of your turns, you may cast a Zombie creature spell from your graveyard - this.addAbility(new CastFromGraveyardOnceEachTurnAbility(filter)); + this.addAbility(new CastFromGraveyardOnceDuringEachOfYourTurnAbility(filter)); } private GisaAndGeralf(final GisaAndGeralf card) { diff --git a/Mage.Sets/src/mage/cards/g/GlaiveOfTheGuildpact.java b/Mage.Sets/src/mage/cards/g/GlaiveOfTheGuildpact.java index 42effa53c6a..2e2861158c3 100644 --- a/Mage.Sets/src/mage/cards/g/GlaiveOfTheGuildpact.java +++ b/Mage.Sets/src/mage/cards/g/GlaiveOfTheGuildpact.java @@ -6,7 +6,7 @@ import mage.abilities.dynamicvalue.common.GateYouControlCount; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.continuous.BoostEquippedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; -import mage.abilities.hint.common.GateYouControlHint; +import mage.abilities.hint.common.GatesYouControlHint; import mage.abilities.keyword.EquipAbility; import mage.abilities.keyword.MenaceAbility; import mage.abilities.keyword.VigilanceAbility; @@ -36,7 +36,7 @@ public final class GlaiveOfTheGuildpact extends CardImpl { ability.addEffect(new GainAbilityAttachedEffect( new MenaceAbility(), AttachmentType.EQUIPMENT ).setText("and menace. (A creature with menace can't be blocked except by two or more creatures.)")); - ability.addHint(GateYouControlHint.instance); + ability.addHint(GatesYouControlHint.instance); this.addAbility(ability); // Equip {3} diff --git a/Mage.Sets/src/mage/cards/g/GlamerSpinners.java b/Mage.Sets/src/mage/cards/g/GlamerSpinners.java index bc39fc2721d..ca9d183bce6 100644 --- a/Mage.Sets/src/mage/cards/g/GlamerSpinners.java +++ b/Mage.Sets/src/mage/cards/g/GlamerSpinners.java @@ -1,7 +1,6 @@ package mage.cards.g; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; @@ -16,17 +15,18 @@ import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.filter.predicate.permanent.PermanentIdPredicate; +import mage.game.Controllable; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.Target; import mage.target.TargetPermanent; -import java.util.LinkedList; +import java.util.Objects; +import java.util.Optional; import java.util.UUID; /** - * @author jeffwadsworth + * @author TheElk801 */ public final class GlamerSpinners extends CardImpl { @@ -48,7 +48,6 @@ public final class GlamerSpinners extends CardImpl { Ability ability = new EntersBattlefieldTriggeredAbility(new GlamerSpinnersEffect(), false); ability.addTarget(new TargetPermanent()); this.addAbility(ability); - } private GlamerSpinners(final GlamerSpinners card) { @@ -79,69 +78,43 @@ class GlamerSpinnersEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - /* - 5/1/2008 When Glamer Spinners enters the battlefield, you target only one permanent: the one that will be losing its Auras. You don't choose the permanent that will be receiving the Auras until the ability resolves. - 5/1/2008 You may target a permanent that has no Auras enchanting it. - 5/1/2008 When the ability resolves, you choose the permanent that will be receiving the Auras. It can't be the targeted permanent, it must have the same controller as the targeted permanent, and it must be able to be enchanted by all the Auras attached to the targeted permanent. If you can't choose a permanent that meets all those criteria, the Auras won't move. - */ - Permanent targetPermanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - Player controller = game.getPlayer(source.getControllerId()); - Permanent glamerSpinners = game.getPermanent(source.getSourceId()); - if (targetPermanent != null - && controller != null - && glamerSpinners != null) { - boolean passed = true; - FilterPermanent filterChoice = new FilterPermanent("a different permanent with the same controller as the target to attach the enchantments to"); - filterChoice.add(new ControllerIdPredicate(targetPermanent.getControllerId())); - filterChoice.add(Predicates.not(new PermanentIdPredicate(targetPermanent.getId()))); - - Target chosenPermanentToAttachAuras = new TargetPermanent(filterChoice); - chosenPermanentToAttachAuras.withNotTarget(true); - - LinkedList auras = new LinkedList<>(); - auras.addAll(targetPermanent.getAttachments()); - if (source.getSourceObjectZoneChangeCounter() == glamerSpinners.getZoneChangeCounter(game) // not blinked - && chosenPermanentToAttachAuras.canChoose(source.getControllerId(), source, game) - && controller.choose(Outcome.Neutral, chosenPermanentToAttachAuras, source, game)) { - Permanent permanentToAttachAuras = game.getPermanent(chosenPermanentToAttachAuras.getFirstTarget()); - if (permanentToAttachAuras != null) { - for (UUID auraId : auras) { - Permanent aura = game.getPermanent(auraId); - if (aura != null - && passed) { - // Check the target filter - Target target = aura.getSpellAbility().getTargets().get(0); - if (target instanceof TargetPermanent) { - if (!target.getFilter().match(permanentToAttachAuras, game)) { - passed = false; - } - } - // Check for protection - MageObject auraObject = game.getObject(auraId); - if (auraObject != null) { - if (permanentToAttachAuras.cantBeAttachedBy(auraObject, source, game, true)) { - passed = false; - } - } - } - } - if (passed) { - LinkedList aurasToAttach = new LinkedList<>(); - aurasToAttach.addAll(auras); - - for (UUID auraId : aurasToAttach) { - Permanent auraToAttachToPermanent = game.getPermanent(auraId); - targetPermanent.removeAttachment(auraToAttachToPermanent.getId(), source, game); - permanentToAttachAuras.addAttachment(auraToAttachToPermanent.getId(), source, game); - } - return true; - } - game.informPlayers("Glamer Spinners" + ": No enchantments were moved from the target permanent."); - } - } - return true; + if (targetPermanent == null + || targetPermanent + .getAttachments() + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .noneMatch(p -> p.hasSubtype(SubType.AURA, game))) { + return false; } - return false; + FilterPermanent filter = new FilterPermanent( + "permanent" + Optional + .ofNullable(targetPermanent) + .map(Controllable::getControllerId) + .map(game::getPlayer) + .map(Player::getName) + .map(s -> " controlled by" + s) + .orElse("") + ); + filter.add(new ControllerIdPredicate(targetPermanent.getControllerId())); + filter.add(Predicates.not(new PermanentIdPredicate(targetPermanent.getId()))); + if (!game.getBattlefield().contains(filter, source.getControllerId(), source, game, 1)) { + return false; + } + TargetPermanent target = new TargetPermanent(filter); + target.withNotTarget(true); + Optional.ofNullable(source) + .map(Controllable::getControllerId) + .map(game::getPlayer) + .ifPresent(player -> player.choose(outcome, target, source, game)); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent == null) { + return false; + } + for (UUID attachmentId : targetPermanent.getAttachments()) { + permanent.addAttachment(attachmentId, source, game); + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/g/GlenElendraPranksters.java b/Mage.Sets/src/mage/cards/g/GlenElendraPranksters.java index d58c306ebce..7a1d3d03a84 100644 --- a/Mage.Sets/src/mage/cards/g/GlenElendraPranksters.java +++ b/Mage.Sets/src/mage/cards/g/GlenElendraPranksters.java @@ -1,11 +1,9 @@ - package mage.cards.g; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SpellCastControllerTriggeredAbility; -import mage.abilities.condition.common.OnOpponentsTurnCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.condition.common.OpponentsTurnCondition; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -17,7 +15,6 @@ import mage.target.common.TargetControlledCreaturePermanent; import java.util.UUID; /** - * * @author Markedagain */ public final class GlenElendraPranksters extends CardImpl { @@ -31,11 +28,11 @@ public final class GlenElendraPranksters extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); + // Whenever you cast a spell during an opponent's turn, you may return target creature you control to its owner's hand. - Ability ability = new ConditionalTriggeredAbility( - new SpellCastControllerTriggeredAbility(new ReturnToHandTargetEffect(), true), OnOpponentsTurnCondition.instance, - "Whenever you cast a spell during an opponent's turn, you may return target creature you control to its owner's hand." - ); + Ability ability = new SpellCastControllerTriggeredAbility( + new ReturnToHandTargetEffect(), true + ).withTriggerCondition(OpponentsTurnCondition.instance); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GlintRaker.java b/Mage.Sets/src/mage/cards/g/GlintRaker.java index b86899fa2b5..e69c8348d81 100644 --- a/Mage.Sets/src/mage/cards/g/GlintRaker.java +++ b/Mage.Sets/src/mage/cards/g/GlintRaker.java @@ -3,8 +3,7 @@ package mage.cards.g; import mage.MageInt; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.common.HighestManaValueCount; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.dynamicvalue.common.SavedDamageValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.RevealLibraryPickControllerEffect; @@ -25,8 +24,6 @@ import java.util.UUID; */ public final class GlintRaker extends CardImpl { - private static final DynamicValue xValue = new HighestManaValueCount(StaticFilters.FILTER_PERMANENT_ARTIFACTS); - public GlintRaker(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); @@ -39,8 +36,9 @@ public final class GlintRaker extends CardImpl { // Glint Raker gets +X/+0, where X is the highest mana value among artifacts you control. this.addAbility(new SimpleStaticAbility(new BoostSourceEffect( - xValue, StaticValue.get(0), Duration.WhileOnBattlefield - ))); + GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_ARTIFACTS, + StaticValue.get(0), Duration.WhileOnBattlefield + )).addHint(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_ARTIFACTS.getHint())); // Whenever Glint Raker deals combat damage to a player, you may reveal that many cards from the top of your library. Put an artifact card revealed this way into your hand and the rest into your graveyard. this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/g/GlintWeaver.java b/Mage.Sets/src/mage/cards/g/GlintWeaver.java index 626cd6f719c..3726a2f60e9 100644 --- a/Mage.Sets/src/mage/cards/g/GlintWeaver.java +++ b/Mage.Sets/src/mage/cards/g/GlintWeaver.java @@ -3,7 +3,7 @@ package mage.cards.g; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.dynamicvalue.common.GreatestToughnessAmongControlledCreaturesValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.counter.DistributeCountersEffect; import mage.abilities.keyword.ReachAbility; @@ -32,10 +32,10 @@ public final class GlintWeaver extends CardImpl { // When Glint Weaver enters the battlefield, distribute three +1/+1 counters among one, two, or three target creatures, then you gain life equal to the greatest toughness among creatures you control. Ability ability = new EntersBattlefieldTriggeredAbility(new DistributeCountersEffect()); - ability.addEffect(new GainLifeEffect(GreatestToughnessAmongControlledCreaturesValue.ALL) + ability.addEffect(new GainLifeEffect(GreatestAmongPermanentsValue.TOUGHNESS_CONTROLLED_CREATURES) .setText(", then you gain life equal to the greatest toughness among creatures you control")); ability.addTarget(new TargetCreaturePermanentAmount(3)); - this.addAbility(ability.addHint(GreatestToughnessAmongControlledCreaturesValue.ALL.getHint())); + this.addAbility(ability.addHint(GreatestAmongPermanentsValue.TOUGHNESS_CONTROLLED_CREATURES.getHint())); } private GlintWeaver(final GlintWeaver card) { diff --git a/Mage.Sets/src/mage/cards/g/GloinDwarfEmissary.java b/Mage.Sets/src/mage/cards/g/GloinDwarfEmissary.java index a305271e9a5..f8124235f41 100644 --- a/Mage.Sets/src/mage/cards/g/GloinDwarfEmissary.java +++ b/Mage.Sets/src/mage/cards/g/GloinDwarfEmissary.java @@ -13,9 +13,8 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledPermanent; -import mage.filter.common.FilterHistoricSpell; import mage.game.permanent.token.TreasureToken; import mage.target.common.TargetCreaturePermanent; @@ -26,7 +25,6 @@ import java.util.UUID; */ public final class GloinDwarfEmissary extends CardImpl { - private static final FilterSpell filter = new FilterHistoricSpell(); private static final FilterControlledPermanent filter2 = new FilterControlledPermanent(SubType.TREASURE, "a Treasure"); public GloinDwarfEmissary(UUID ownerId, CardSetInfo setInfo) { @@ -40,7 +38,7 @@ public final class GloinDwarfEmissary extends CardImpl { // Whenever you cast a historic spell, create a Treasure token. This ability triggers only once each turn. this.addAbility(new SpellCastControllerTriggeredAbility( - new CreateTokenEffect(new TreasureToken()), filter, false + new CreateTokenEffect(new TreasureToken()), StaticFilters.FILTER_SPELL_HISTORIC, false ).setTriggersLimitEachTurn(1)); // {T}, Sacrifice a Treasure: Goad target creature. diff --git a/Mage.Sets/src/mage/cards/g/Glory.java b/Mage.Sets/src/mage/cards/g/Glory.java index 295de6d07dc..2ade98d96bb 100644 --- a/Mage.Sets/src/mage/cards/g/Glory.java +++ b/Mage.Sets/src/mage/cards/g/Glory.java @@ -32,7 +32,7 @@ public final class Glory extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // {2}{W}: Choose a color. Creatures you control gain protection from the chosen color until end of turn. Activate this ability only if Glory is in your graveyard. Effect effect = new GainProtectionFromColorAllEffect(Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES); - effect.setText("Choose a color. Creatures you control gain protection from the chosen color until end of turn. Activate only if {this} is in your graveyard."); + effect.setText("Choose a color. Creatures you control gain protection from the chosen color until end of turn. Activate only if this card is in your graveyard."); this.addAbility(new SimpleActivatedAbility(Zone.GRAVEYARD, effect, new ManaCostsImpl<>("{2}{W}"))); } diff --git a/Mage.Sets/src/mage/cards/g/GnostroVoiceOfTheCrags.java b/Mage.Sets/src/mage/cards/g/GnostroVoiceOfTheCrags.java index 4452a3e2678..24b17831deb 100644 --- a/Mage.Sets/src/mage/cards/g/GnostroVoiceOfTheCrags.java +++ b/Mage.Sets/src/mage/cards/g/GnostroVoiceOfTheCrags.java @@ -7,18 +7,17 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.hint.Hint; import mage.abilities.hint.ValueHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.SuperType; import mage.game.Game; -import mage.players.Player; import mage.target.common.TargetCreaturePermanent; import mage.watchers.common.CastSpellLastTurnWatcher; @@ -29,6 +28,8 @@ import java.util.UUID; */ public final class GnostroVoiceOfTheCrags extends CardImpl { + private static final Hint hint = new ValueHint("Number of spells you've cast this turn", GnostroVoiceOfTheCragsValue.instance); + public GnostroVoiceOfTheCrags(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{R}{W}"); @@ -39,8 +40,8 @@ public final class GnostroVoiceOfTheCrags extends CardImpl { // {T}: Choose one. X is the number of spells you've cast this turn. // • Scry X. - Ability ability = new SimpleActivatedAbility(new GnostroVoiceOfTheCragsEffect(), new TapSourceCost()); - ability.addHint(new ValueHint("Number of spells you've cast this turn", GnostroVoiceOfTheCragsValue.instance)); + Ability ability = new SimpleActivatedAbility(new ScryEffect(GnostroVoiceOfTheCragsValue.instance), new TapSourceCost()); + ability.addHint(hint); ability.getModes().setChooseText("choose one. X is the number of spells you've cast this turn."); // • Gnostro, Voice of the Crags deals X damage to target creature. @@ -78,36 +79,12 @@ enum GnostroVoiceOfTheCragsValue implements DynamicValue { return instance; } + public String toString() { + return "X"; + } + @Override public String getMessage() { return ""; } -} - -class GnostroVoiceOfTheCragsEffect extends OneShotEffect { - - GnostroVoiceOfTheCragsEffect() { - super(Outcome.Benefit); - staticText = "scry X"; - } - - private GnostroVoiceOfTheCragsEffect(final GnostroVoiceOfTheCragsEffect effect) { - super(effect); - } - - @Override - public GnostroVoiceOfTheCragsEffect copy() { - return new GnostroVoiceOfTheCragsEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player == null) { - return false; - } - return player.scry( - GnostroVoiceOfTheCragsValue.instance.calculate(game, source, this), source, game - ); - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/g/GoblinArtisans.java b/Mage.Sets/src/mage/cards/g/GoblinArtisans.java index e9f7d247e3a..d134b26b94b 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinArtisans.java +++ b/Mage.Sets/src/mage/cards/g/GoblinArtisans.java @@ -14,7 +14,6 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.FilterSpell; -import mage.filter.common.FilterArtifactSpell; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.Spell; @@ -58,17 +57,18 @@ public final class GoblinArtisans extends CardImpl { class GoblinArtisansTarget extends TargetSpell { - private static final FilterSpell filter = new FilterArtifactSpell( + private static final FilterSpell filterSpell = new FilterSpell( "target artifact spell you control that isn't the target " + "of an ability from another creature named Goblin Artisans" ); static { - filter.add(TargetController.YOU.getOwnerPredicate()); + filterSpell.add(CardType.ARTIFACT.getPredicate()); + filterSpell.add(TargetController.YOU.getOwnerPredicate()); } GoblinArtisansTarget() { - super(filter); + super(filterSpell); } private GoblinArtisansTarget(final GoblinArtisansTarget target) { diff --git a/Mage.Sets/src/mage/cards/g/GoblinAssassin.java b/Mage.Sets/src/mage/cards/g/GoblinAssassin.java index 24c2febb2c8..92f12f026e0 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinAssassin.java +++ b/Mage.Sets/src/mage/cards/g/GoblinAssassin.java @@ -54,7 +54,7 @@ class GoblinAssassinTriggeredEffect extends OneShotEffect { GoblinAssassinTriggeredEffect() { super(Outcome.Sacrifice); - staticText = "each player flips a coin. Each player whose coin comes up tails sacrifices a creature"; + staticText = "each player flips a coin. Each player whose coin comes up tails sacrifices a creature of their choice"; } private GoblinAssassinTriggeredEffect(final GoblinAssassinTriggeredEffect effect) { diff --git a/Mage.Sets/src/mage/cards/g/GoblinTraprunner.java b/Mage.Sets/src/mage/cards/g/GoblinTraprunner.java index dceeef319b6..8ab3b7760e0 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinTraprunner.java +++ b/Mage.Sets/src/mage/cards/g/GoblinTraprunner.java @@ -10,10 +10,11 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import mage.game.Controllable; import mage.game.Game; import mage.game.permanent.token.GoblinToken; -import mage.players.Player; +import java.util.Optional; import java.util.UUID; /** @@ -50,17 +51,16 @@ enum GoblinTraprunnerValue implements DynamicValue { @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { - Player player = game.getPlayer(sourceAbility.getControllerId()); - if (player == null) { - return 0; - } - int count = 0; - for (int i = 0; i < 3; i++) { - if (player.flipCoin(sourceAbility, game, true)) { - count++; - } - } - return count; + return Optional + .ofNullable(sourceAbility) + .map(Controllable::getControllerId) + .map(game::getPlayer) + .map(player -> player + .flipCoins(sourceAbility, game, 3, true) + .stream() + .mapToInt(x -> x ? 1 : 0) + .sum() + ).orElse(0); } @Override diff --git a/Mage.Sets/src/mage/cards/g/GoblinWarStrike.java b/Mage.Sets/src/mage/cards/g/GoblinWarStrike.java index e5d4bfd3a2a..ddb23d994ee 100644 --- a/Mage.Sets/src/mage/cards/g/GoblinWarStrike.java +++ b/Mage.Sets/src/mage/cards/g/GoblinWarStrike.java @@ -29,7 +29,7 @@ public final class GoblinWarStrike extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}"); // Goblin War Strike deals damage equal to the number of Goblins you control to target player. - this.getSpellAbility().addEffect(new DamageTargetEffect(new PermanentsOnBattlefieldCount(filter))); + this.getSpellAbility().addEffect(new DamageTargetEffect(new PermanentsOnBattlefieldCount(filter, null))); this.getSpellAbility().addTarget(new TargetPlayerOrPlaneswalker()); } diff --git a/Mage.Sets/src/mage/cards/g/GodEternalBontu.java b/Mage.Sets/src/mage/cards/g/GodEternalBontu.java index f83e63254a8..b4a0236931f 100644 --- a/Mage.Sets/src/mage/cards/g/GodEternalBontu.java +++ b/Mage.Sets/src/mage/cards/g/GodEternalBontu.java @@ -12,9 +12,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.SuperType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -59,12 +57,6 @@ public final class GodEternalBontu extends CardImpl { class GodEternalBontuEffect extends OneShotEffect { - private static final FilterPermanent filter = new FilterControlledPermanent("other permanents you control"); - - static { - filter.add(AnotherPredicate.instance); - } - GodEternalBontuEffect() { super(Outcome.Benefit); staticText = "sacrifice any number of other permanents, then draw that many cards."; @@ -85,7 +77,7 @@ class GodEternalBontuEffect extends OneShotEffect { if (player == null) { return false; } - Target target = new TargetSacrifice(0, Integer.MAX_VALUE, filter); + Target target = new TargetSacrifice(0, Integer.MAX_VALUE, StaticFilters.FILTER_OTHER_CONTROLLED_PERMANENTS); if (!player.choose(outcome, target, source, game)) { return false; } diff --git a/Mage.Sets/src/mage/cards/g/Godsend.java b/Mage.Sets/src/mage/cards/g/Godsend.java index 7f07da8d55c..ccf9d841a03 100644 --- a/Mage.Sets/src/mage/cards/g/Godsend.java +++ b/Mage.Sets/src/mage/cards/g/Godsend.java @@ -167,7 +167,7 @@ class GodsendRuleModifyingEffect extends ContinuousRuleModifyingEffectImpl { GodsendRuleModifyingEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); - staticText = "Your opponents can't cast cards with the same name as cards exiled with {this}"; + staticText = "Your opponents can't cast spells with the same name as a card exiled with {this}"; } private GodsendRuleModifyingEffect(final GodsendRuleModifyingEffect effect) { diff --git a/Mage.Sets/src/mage/cards/g/GogoMasterOfMimicry.java b/Mage.Sets/src/mage/cards/g/GogoMasterOfMimicry.java new file mode 100644 index 00000000000..dbc2f0b5191 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GogoMasterOfMimicry.java @@ -0,0 +1,88 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.VariableManaCost; +import mage.abilities.dynamicvalue.common.GetXValue; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterStackObject; +import mage.filter.common.FilterActivatedOrTriggeredAbility; +import mage.game.Game; +import mage.game.stack.StackObject; +import mage.target.common.TargetActivatedOrTriggeredAbility; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GogoMasterOfMimicry extends CardImpl { + + private static final FilterStackObject filter + = new FilterActivatedOrTriggeredAbility("activated or triggered ability you control"); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + } + + public GogoMasterOfMimicry(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // {X}{X}, {T}: Copy target activated or triggered ability you control X times. You may choose new targets for the copy. This ability can't be copied, and X can't be 0. + Ability ability = new SimpleActivatedAbility(new GogoMasterOfMimicryCopyEffect(), new ManaCostsImpl<>("{X}{X}")); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetActivatedOrTriggeredAbility(filter)); + CardUtil.castStream(ability.getCosts(), VariableManaCost.class).forEach(cost -> cost.setMinX(1)); + this.addAbility(ability.withCanBeCopied(false)); + } + + private GogoMasterOfMimicry(final GogoMasterOfMimicry card) { + super(card); + } + + @Override + public GogoMasterOfMimicry copy() { + return new GogoMasterOfMimicry(this); + } +} + +class GogoMasterOfMimicryCopyEffect extends OneShotEffect { + + GogoMasterOfMimicryCopyEffect() { + super(Outcome.Benefit); + staticText = "copy target activated or triggered ability you control X times. " + + "You may choose new targets for the copies. This ability can't be copied and X can't be 0"; + } + + private GogoMasterOfMimicryCopyEffect(final GogoMasterOfMimicryCopyEffect effect) { + super(effect); + } + + @Override + public GogoMasterOfMimicryCopyEffect copy() { + return new GogoMasterOfMimicryCopyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + int amount = GetXValue.instance.calculate(game, source, this); + StackObject stackObject = game.getStack().getStackObject(getTargetPointer().getFirst(game, source)); + if (amount < 1 || stackObject == null) { + return false; + } + stackObject.createCopyOnStack(game, source, source.getControllerId(), true, amount); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GohnTownOfRuin.java b/Mage.Sets/src/mage/cards/g/GohnTownOfRuin.java new file mode 100644 index 00000000000..77ef0965350 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GohnTownOfRuin.java @@ -0,0 +1,39 @@ +package mage.cards.g; + +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GohnTownOfRuin extends CardImpl { + + public GohnTownOfRuin(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.subtype.add(SubType.TOWN); + + // This land enters tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {B} or {G}. + this.addAbility(new BlackManaAbility()); + this.addAbility(new GreenManaAbility()); + } + + private GohnTownOfRuin(final GohnTownOfRuin card) { + super(card); + } + + @Override + public GohnTownOfRuin copy() { + return new GohnTownOfRuin(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GolbezCrystalCollector.java b/Mage.Sets/src/mage/cards/g/GolbezCrystalCollector.java new file mode 100644 index 00000000000..d4e79a069d1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GolbezCrystalCollector.java @@ -0,0 +1,104 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.effects.keyword.SurveilEffect; +import mage.abilities.hint.common.ArtifactYouControlHint; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.game.Game; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GolbezCrystalCollector extends CardImpl { + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + new FilterControlledArtifactPermanent("you control four or more artifacts"), ComparisonType.MORE_THAN, 3 + ); + private static final Condition condition2 = new PermanentsOnTheBattlefieldCondition( + StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT, ComparisonType.MORE_THAN, 7 + ); + + public GolbezCrystalCollector(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // Whenever an artifact you control enters, surveil 1. + this.addAbility(new EntersBattlefieldAllTriggeredAbility( + new SurveilEffect(1), StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT + )); + + // At the beginning of your end step, if you control four or more artifacts, return target creature card from your graveyard to your hand. Then if you control eight or more artifacts, each opponent loses life equal to that card's power. + Ability ability = new BeginningOfEndStepTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect()).withInterveningIf(condition); + ability.addEffect(new ConditionalOneShotEffect( + new LoseLifeOpponentsEffect(GolbezCrystalCollectorValue.instance), condition2, + "Then if you control eight or more artifacts, each opponent loses life equal to that card's power" + )); + ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); + this.addAbility(ability.addHint(ArtifactYouControlHint.instance)); + } + + private GolbezCrystalCollector(final GolbezCrystalCollector card) { + super(card); + } + + @Override + public GolbezCrystalCollector copy() { + return new GolbezCrystalCollector(this); + } +} + +enum GolbezCrystalCollectorValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return Optional + .ofNullable(effect.getTargetPointer().getFirst(game, sourceAbility)) + .map(game::getPermanent) + .map(MageObject::getPower) + .map(MageInt::getValue) + .orElse(0); + } + + @Override + public GolbezCrystalCollectorValue copy() { + return this; + } + + @Override + public String getMessage() { + return ""; + } + + @Override + public String toString() { + return "1"; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GoldwardensGambit.java b/Mage.Sets/src/mage/cards/g/GoldwardensGambit.java index 15d5e32dd4f..8d521308131 100644 --- a/Mage.Sets/src/mage/cards/g/GoldwardensGambit.java +++ b/Mage.Sets/src/mage/cards/g/GoldwardensGambit.java @@ -1,18 +1,17 @@ package mage.cards.g; import mage.abilities.Ability; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.AffinityEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.AffinityAbility; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.common.FilterControlledPermanent; +import mage.constants.AffinityType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.RebelRedToken; @@ -28,18 +27,11 @@ import java.util.UUID; */ public final class GoldwardensGambit extends CardImpl { - private static final FilterControlledPermanent filter - = new FilterControlledPermanent(SubType.EQUIPMENT, "Equipment"); - - private static final Hint hint = new ValueHint( - "Equipment you control", new PermanentsOnBattlefieldCount(filter) - ); - public GoldwardensGambit(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{6}{R}{R}"); // Affinity for Equipment - this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filter)).addHint(hint)); + this.addAbility(new AffinityAbility(AffinityType.EQUIPMENT)); // Create five 2/2 red Rebel creature tokens. They gain haste until end of turn. For each of those tokens, you may attach an Equipment you control to it. this.getSpellAbility().addEffect(new GoldwardensGambitEffect()); @@ -53,10 +45,6 @@ public final class GoldwardensGambit extends CardImpl { public GoldwardensGambit copy() { return new GoldwardensGambit(this); } - - public static FilterControlledPermanent getFilter() { - return filter; - } } class GoldwardensGambitEffect extends OneShotEffect { @@ -87,7 +75,7 @@ class GoldwardensGambitEffect extends OneShotEffect { game.addEffect(new GainAbilityTargetEffect( HasteAbility.getInstance(), Duration.EndOfTurn ).setTargetPointer(new FixedTargets(token, game)), source); - if (game.getBattlefield().count(GoldwardensGambit.getFilter(), source.getControllerId(), source, game) < 1) { + if (game.getBattlefield().count(StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT, source.getControllerId(), source, game) < 1) { return true; } for (UUID tokenId : token.getLastAddedTokenIds()) { @@ -98,7 +86,7 @@ class GoldwardensGambitEffect extends OneShotEffect { )) { continue; } - TargetPermanent target = new TargetPermanent(0, 1, GoldwardensGambit.getFilter(), true); + TargetPermanent target = new TargetPermanent(0, 1, StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT, true); player.choose(Outcome.BoostCreature, target, source, game); permanent.addAttachment(target.getFirstTarget(), source, game); } diff --git a/Mage.Sets/src/mage/cards/g/GondGate.java b/Mage.Sets/src/mage/cards/g/GondGate.java index 8949ba5b79a..572361eea6b 100644 --- a/Mage.Sets/src/mage/cards/g/GondGate.java +++ b/Mage.Sets/src/mage/cards/g/GondGate.java @@ -1,19 +1,16 @@ package mage.cards.g; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.EnterUntappedAllEffect; import mage.abilities.mana.AnyColorLandsProduceManaAbility; import mage.abilities.mana.ColorlessManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; import java.util.UUID; @@ -22,7 +19,8 @@ import java.util.UUID; */ public final class GondGate extends CardImpl { - private static final FilterPermanent filter = new FilterControlledPermanent(SubType.GATE, "Gate"); + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.GATE, "Gates you control"); + private static final FilterPermanent filter2 = new FilterControlledPermanent(SubType.GATE, "Gate"); public GondGate(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); @@ -30,13 +28,13 @@ public final class GondGate extends CardImpl { this.subtype.add(SubType.GATE); // Gates you control enter the battlefield untapped. - this.addAbility(new SimpleStaticAbility(new GondGateEffect())); + this.addAbility(new SimpleStaticAbility(new EnterUntappedAllEffect(filter))); // {T}: Add {C}. this.addAbility(new ColorlessManaAbility()); // {T}: Add one mana of any color that a Gate you control could produce. - this.addAbility(new AnyColorLandsProduceManaAbility(TargetController.YOU, true, filter)); + this.addAbility(new AnyColorLandsProduceManaAbility(TargetController.YOU, true, filter2)); } private GondGate(final GondGate card) { @@ -48,52 +46,3 @@ public final class GondGate extends CardImpl { return new GondGate(this); } } - -class GondGateEffect extends ReplacementEffectImpl { - - GondGateEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "Gates you control enter the battlefield untapped"; - } - - private GondGateEffect(final GondGateEffect effect) { - super(effect); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); - if (target != null) { - target.setTapped(false); - } - - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - Permanent sourceObject = game.getPermanent(source.getSourceId()); - if (sourceObject == null) { - return false; - } - - Permanent targetObject = ((EntersTheBattlefieldEvent) event).getTarget(); - if (targetObject == null) { - return false; - } - - return !sourceObject.getId().equals(targetObject.getId()) - && targetObject.isControlledBy(source.getControllerId()) - && targetObject.hasSubtype(SubType.GATE, game); - } - - @Override - public GondGateEffect copy() { - return new GondGateEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/g/GongagaReactorTown.java b/Mage.Sets/src/mage/cards/g/GongagaReactorTown.java new file mode 100644 index 00000000000..687d03db1f3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GongagaReactorTown.java @@ -0,0 +1,39 @@ +package mage.cards.g; + +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.abilities.mana.RedManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GongagaReactorTown extends CardImpl { + + public GongagaReactorTown(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.subtype.add(SubType.TOWN); + + // This land enters tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {R} or {G}. + this.addAbility(new RedManaAbility()); + this.addAbility(new GreenManaAbility()); + } + + private GongagaReactorTown(final GongagaReactorTown card) { + super(card); + } + + @Override + public GongagaReactorTown copy() { + return new GongagaReactorTown(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GoobbueGardener.java b/Mage.Sets/src/mage/cards/g/GoobbueGardener.java new file mode 100644 index 00000000000..335d6b51803 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GoobbueGardener.java @@ -0,0 +1,37 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.mana.GreenManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GoobbueGardener extends CardImpl { + + public GoobbueGardener(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.subtype.add(SubType.PLANT); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // {T}: Add {G}. + this.addAbility(new GreenManaAbility()); + } + + private GoobbueGardener(final GoobbueGardener card) { + super(card); + } + + @Override + public GoobbueGardener copy() { + return new GoobbueGardener(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GorexTheTombshell.java b/Mage.Sets/src/mage/cards/g/GorexTheTombshell.java index 09770ddd69c..691fac1f249 100644 --- a/Mage.Sets/src/mage/cards/g/GorexTheTombshell.java +++ b/Mage.Sets/src/mage/cards/g/GorexTheTombshell.java @@ -128,11 +128,22 @@ class GorexTheTombshellReturnEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + // exile zone corresponds to object on stack + // relative zcc depends on object zone (battlefield for attacks trigger, graveyard for dies) + // so try both zcc offsets to find zone ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId( game, source.getSourceId(), source.getSourceObjectZoneChangeCounter() - 1 )); - if (player == null || exileZone == null || exileZone.isEmpty()) { - return false; + if (exileZone == null || exileZone.isEmpty()) { + exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId( + game, source.getSourceId(), source.getSourceObjectZoneChangeCounter() - 2 + )); + if (exileZone == null || exileZone.isEmpty()) { + return false; + } } return player.moveCards(exileZone.getRandom(game), Zone.HAND, source, game); } diff --git a/Mage.Sets/src/mage/cards/g/GoryosVengeance.java b/Mage.Sets/src/mage/cards/g/GoryosVengeance.java index 33a9d0421e3..61d3dde5201 100644 --- a/Mage.Sets/src/mage/cards/g/GoryosVengeance.java +++ b/Mage.Sets/src/mage/cards/g/GoryosVengeance.java @@ -21,6 +21,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -83,7 +84,7 @@ class GoryosVengeanceEffect extends OneShotEffect { if (card == null || !controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { return false; } - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/g/GossipsTalent.java b/Mage.Sets/src/mage/cards/g/GossipsTalent.java index 0d975785caa..d347e6b334a 100644 --- a/Mage.Sets/src/mage/cards/g/GossipsTalent.java +++ b/Mage.Sets/src/mage/cards/g/GossipsTalent.java @@ -2,7 +2,7 @@ package mage.cards.g; import mage.abilities.Ability; import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; -import mage.abilities.common.OneOrMoreCombatDamagePlayerTriggeredAbility; +import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility; import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.ExileThenReturnTargetEffect; @@ -13,7 +13,10 @@ import mage.abilities.keyword.ClassLevelAbility; import mage.abilities.keyword.ClassReminderAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SetTargetPointer; +import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterAttackingCreature; @@ -27,7 +30,7 @@ import java.util.UUID; */ public final class GossipsTalent extends CardImpl { - private static final FilterPermanent filter = new FilterAttackingCreature("attacking creature with power 3 or less can't be blocked this turn"); + private static final FilterPermanent filter = new FilterAttackingCreature("attacking creature with power 3 or less"); static { filter.add(new PowerPredicate(ComparisonType.FEWER_THAN, 4)); @@ -59,11 +62,10 @@ public final class GossipsTalent extends CardImpl { // Whenever a creature you control deals combat damage to a player, you may exile it, then return it to the battlefield under its owner's control. this.addAbility(new SimpleStaticAbility(new GainClassAbilitySourceEffect( - new OneOrMoreCombatDamagePlayerTriggeredAbility( - Zone.BATTLEFIELD, + new DealsDamageToAPlayerAllTriggeredAbility( new ExileThenReturnTargetEffect(false, false) .setText("exile it, then return it to the battlefield under its owner's control"), - StaticFilters.FILTER_PERMANENT_CREATURE, SetTargetPointer.PERMANENT, true + StaticFilters.FILTER_CONTROLLED_CREATURE, true, SetTargetPointer.PERMANENT, true ), 3 ))); } diff --git a/Mage.Sets/src/mage/cards/g/GrahaTia.java b/Mage.Sets/src/mage/cards/g/GrahaTia.java new file mode 100644 index 00000000000..98070387bd2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GrahaTia.java @@ -0,0 +1,49 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GrahaTia extends CardImpl { + + public GrahaTia(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.CAT); + this.subtype.add(SubType.ARCHER); + this.power = new MageInt(3); + this.toughness = new MageInt(5); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // The Allagan Eye - Whenever one or more other creatures and/or artifacts you control die, draw a card. This ability triggers only once each turn. + this.addAbility(new DiesCreatureTriggeredAbility( + new DrawCardSourceControllerEffect(1), false, + StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE_OR_ARTIFACT + ).setTriggerPhrase("Whenever one or more other creatures and/or artifacts you control die, ") + .setTriggersLimitEachTurn(1).withFlavorWord("The Allagan Eye")); + } + + private GrahaTia(final GrahaTia card) { + super(card); + } + + @Override + public GrahaTia copy() { + return new GrahaTia(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GrahaTiaScionReborn.java b/Mage.Sets/src/mage/cards/g/GrahaTiaScionReborn.java new file mode 100644 index 00000000000..7fafb181bf0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GrahaTiaScionReborn.java @@ -0,0 +1,123 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.token.HeroToken; +import mage.game.permanent.token.Token; +import mage.game.stack.Spell; +import mage.util.CardUtil; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GrahaTiaScionReborn extends CardImpl { + + public GrahaTiaScionReborn(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{U}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.CAT); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Throw Wide the Gates -- Whenever you cast a noncreature spell, you may pay X life, where X is that spell's mana value. If you do, create a 1/1 colorless Hero creature token and put X +1/+1 counters on it. Do this only once each turn. + this.addAbility(new SpellCastControllerTriggeredAbility(new DoIfCostPaid( + new GrahaTiaScionRebornEffect(), + new PayLifeCost( + GrahaTiaScionRebornValue.instance, + "X life, where X is that spell's mana value" + ) + ), StaticFilters.FILTER_SPELL_A_NON_CREATURE, false).setDoOnlyOnceEachTurn(true).withFlavorWord("Throw Wide the Gates")); + } + + private GrahaTiaScionReborn(final GrahaTiaScionReborn card) { + super(card); + } + + @Override + public GrahaTiaScionReborn copy() { + return new GrahaTiaScionReborn(this); + } +} + +enum GrahaTiaScionRebornValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return CardUtil.getEffectValueFromAbility(sourceAbility, "spellCast", Spell.class) + .map(Spell::getManaValue) + .orElse(0); + } + + @Override + public GrahaTiaScionRebornValue copy() { + return this; + } + + @Override + public String getMessage() { + return ""; + } + + @Override + public String toString() { + return "X"; + } +} + +class GrahaTiaScionRebornEffect extends OneShotEffect { + + GrahaTiaScionRebornEffect() { + super(Outcome.Benefit); + staticText = "create a 1/1 colorless Hero creature token and put X +1/+1 counters on it"; + } + + private GrahaTiaScionRebornEffect(final GrahaTiaScionRebornEffect effect) { + super(effect); + } + + @Override + public GrahaTiaScionRebornEffect copy() { + return new GrahaTiaScionRebornEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Token token = new HeroToken(); + token.putOntoBattlefield(1, game, source); + int count = GrahaTiaScionRebornValue.instance.calculate(game, source, this); + if (count < 1) { + return true; + } + for (UUID tokenId : token.getLastAddedTokenIds()) { + Optional.ofNullable(tokenId) + .map(game::getPermanent) + .ifPresent(permanent -> permanent.addCounters(CounterType.P1P1.createInstance(count), source, game)); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GranPulseOchu.java b/Mage.Sets/src/mage/cards/g/GranPulseOchu.java new file mode 100644 index 00000000000..5f2f4cb4e41 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GranPulseOchu.java @@ -0,0 +1,54 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.common.DescendCondition; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GranPulseOchu extends CardImpl { + + private static final DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_PERMANENT); + + public GranPulseOchu(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}"); + + this.subtype.add(SubType.PLANT); + this.subtype.add(SubType.BEAST); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // {8}: Until end of turn, this creature gets +1/+1 for each permanent card in your graveyard. + this.addAbility(new SimpleActivatedAbility( + new BoostSourceEffect(xValue, xValue, Duration.EndOfTurn) + .setText("until end of turn, this creature gets +1/+1 for each permanent card in your graveyard"), + new GenericManaCost(8) + ).addHint(DescendCondition.getHint())); + } + + private GranPulseOchu(final GranPulseOchu card) { + super(card); + } + + @Override + public GranPulseOchu copy() { + return new GranPulseOchu(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GraspOfFate.java b/Mage.Sets/src/mage/cards/g/GraspOfFate.java index 6a69b11781c..b54a04d7a27 100644 --- a/Mage.Sets/src/mage/cards/g/GraspOfFate.java +++ b/Mage.Sets/src/mage/cards/g/GraspOfFate.java @@ -7,7 +7,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.target.common.TargetNonlandPermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; import java.util.UUID; @@ -26,7 +26,7 @@ public final class GraspOfFate extends CardImpl { .setText("for each opponent, exile up to one target nonland permanent that player controls until {this} leaves the battlefield") ); ability.addTarget(new TargetNonlandPermanent(0,1)); - ability.setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GravenLore.java b/Mage.Sets/src/mage/cards/g/GravenLore.java index 9b0f03830dd..ee3008377c0 100644 --- a/Mage.Sets/src/mage/cards/g/GravenLore.java +++ b/Mage.Sets/src/mage/cards/g/GravenLore.java @@ -2,6 +2,8 @@ package mage.cards.g; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.keyword.ScryEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -60,10 +62,8 @@ class GravenLoreEffect extends OneShotEffect { return false; } int snow = ManaPaidSourceWatcher.getSnowPaid(source.getId(), game); - if (snow > 0) { - player.scry(snow, source, game); - } - player.drawCards(3, source, game); - return true; + boolean result = new ScryEffect(snow).apply(game, source); + result |= new DrawCardSourceControllerEffect(3).apply(game, source); + return result; } } diff --git a/Mage.Sets/src/mage/cards/g/Gravestorm.java b/Mage.Sets/src/mage/cards/g/Gravestorm.java index ffd40b75211..97b6d925b4a 100644 --- a/Mage.Sets/src/mage/cards/g/Gravestorm.java +++ b/Mage.Sets/src/mage/cards/g/Gravestorm.java @@ -47,7 +47,7 @@ class GravestormEffect extends OneShotEffect { GravestormEffect() { super(Outcome.Exile); - this.staticText = "At the beginning of your upkeep, target opponent may exile a card from their graveyard. If that player doesn't, you may draw a card."; + this.staticText = "target opponent may exile a card from their graveyard. If that player doesn't, you may draw a card."; } private GravestormEffect(final GravestormEffect effect) { diff --git a/Mage.Sets/src/mage/cards/g/GreasefangOkibaBoss.java b/Mage.Sets/src/mage/cards/g/GreasefangOkibaBoss.java index 31de0775b43..31d7d797b58 100644 --- a/Mage.Sets/src/mage/cards/g/GreasefangOkibaBoss.java +++ b/Mage.Sets/src/mage/cards/g/GreasefangOkibaBoss.java @@ -21,6 +21,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @@ -83,7 +84,7 @@ class GreasefangOkibaBossEffect extends OneShotEffect { return false; } controller.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { ContinuousEffect hasteEffect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); hasteEffect.setTargetPointer(new FixedTarget(permanent, game)); diff --git a/Mage.Sets/src/mage/cards/g/GreaterRealmOfPreservation.java b/Mage.Sets/src/mage/cards/g/GreaterRealmOfPreservation.java index 74a405ba4ad..c3b9ef78ae7 100644 --- a/Mage.Sets/src/mage/cards/g/GreaterRealmOfPreservation.java +++ b/Mage.Sets/src/mage/cards/g/GreaterRealmOfPreservation.java @@ -1,38 +1,38 @@ package mage.cards.g; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author L_J */ public final class GreaterRealmOfPreservation extends CardImpl { - private static final FilterObject filter = new FilterObject("black or red source"); - static{ + private static final FilterSource filter = new FilterSource("black or red source"); + + static { filter.add(Predicates.or(new ColorPredicate(ObjectColor.BLACK), new ColorPredicate(ObjectColor.RED))); } public GreaterRealmOfPreservation(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); // {1}{W}: The next time a black or red source of your choice would deal damage to you this turn, prevent that damage. - Effect effect = new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn, filter); + Effect effect = new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, true, filter); this.addAbility(new SimpleActivatedAbility(effect, new ManaCostsImpl<>("{1}{W}"))); } diff --git a/Mage.Sets/src/mage/cards/g/GremlinMine.java b/Mage.Sets/src/mage/cards/g/GremlinMine.java index 6f6cb61a922..cf095bcd157 100644 --- a/Mage.Sets/src/mage/cards/g/GremlinMine.java +++ b/Mage.Sets/src/mage/cards/g/GremlinMine.java @@ -1,7 +1,6 @@ package mage.cards.g; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; @@ -15,17 +14,17 @@ import mage.choices.Choice; import mage.choices.ChoiceImpl; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterArtifactPermanent; import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.common.TargetArtifactPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author North */ public final class GremlinMine extends CardImpl { @@ -45,13 +44,13 @@ public final class GremlinMine extends CardImpl { Ability ability = new SimpleActivatedAbility(new DamageTargetEffect(4, "it"), new ManaCostsImpl<>("{1}")); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); - ability.addTarget(new TargetArtifactPermanent(filterCreature)); + ability.addTarget(new TargetPermanent(filterCreature)); this.addAbility(ability); // {1}, {tap}, Sacrifice Gremlin Mine: Remove up to four charge counters from target noncreature artifact. ability = new SimpleActivatedAbility(new GremlinMineEffect(), new ManaCostsImpl<>("{1}")); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); - ability.addTarget(new TargetArtifactPermanent(filterNonCreature)); + ability.addTarget(new TargetPermanent(filterNonCreature)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GrimoireOfTheDead.java b/Mage.Sets/src/mage/cards/g/GrimoireOfTheDead.java index 8749db312cf..8e2a7644924 100644 --- a/Mage.Sets/src/mage/cards/g/GrimoireOfTheDead.java +++ b/Mage.Sets/src/mage/cards/g/GrimoireOfTheDead.java @@ -22,10 +22,10 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInHand; import mage.target.targetpointer.FixedTargets; +import mage.util.CardUtil; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.UUID; +import java.util.*; +import java.util.stream.Collectors; /** * @author BetaSteward @@ -90,7 +90,11 @@ class GrimoireOfTheDeadEffect extends OneShotEffect { creatureCards.addAll(player.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURE, game)); } controller.moveCards(creatureCards, Zone.BATTLEFIELD, source, game); - game.addEffect(new GrimoireOfTheDeadEffect2().setTargetPointer(new FixedTargets(creatureCards, game)), source); + List permanents = creatureCards.stream() + .map(c -> CardUtil.getPermanentFromCardPutToBattlefield(c, game)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + game.addEffect(new GrimoireOfTheDeadEffect2().setTargetPointer(new FixedTargets(permanents, game)), source); return true; } @@ -103,7 +107,7 @@ class GrimoireOfTheDeadEffect extends OneShotEffect { class GrimoireOfTheDeadEffect2 extends ContinuousEffectImpl { - public GrimoireOfTheDeadEffect2() { + GrimoireOfTheDeadEffect2() { super(Duration.Custom, Outcome.Neutral); } diff --git a/Mage.Sets/src/mage/cards/g/GripOfPhyresis.java b/Mage.Sets/src/mage/cards/g/GripOfPhyresis.java index 667d617b0bc..1539e72599e 100644 --- a/Mage.Sets/src/mage/cards/g/GripOfPhyresis.java +++ b/Mage.Sets/src/mage/cards/g/GripOfPhyresis.java @@ -7,8 +7,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.SubType; -import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.PhyrexianGermToken; @@ -23,13 +22,6 @@ import java.util.UUID; */ public final class GripOfPhyresis extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("Equipment"); - - static { - filter.add(CardType.ARTIFACT.getPredicate()); - filter.add(SubType.EQUIPMENT.getPredicate()); - } - public GripOfPhyresis(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}"); @@ -37,7 +29,7 @@ public final class GripOfPhyresis extends CardImpl { GainControlTargetEffect effect = new GainControlTargetEffect(Duration.EndOfGame, true); effect.setText("Gain control of target Equipment"); this.getSpellAbility().addEffect(effect); - Target targetEquipment = new TargetPermanent(filter); + Target targetEquipment = new TargetPermanent(StaticFilters.FILTER_PERMANENT_EQUIPMENT); this.getSpellAbility().addTarget(targetEquipment); this.getSpellAbility().addEffect(new GripOfPhyresisEffect()); } diff --git a/Mage.Sets/src/mage/cards/g/GroomsFinery.java b/Mage.Sets/src/mage/cards/g/GroomsFinery.java index 0ea971de78a..e398ff87908 100644 --- a/Mage.Sets/src/mage/cards/g/GroomsFinery.java +++ b/Mage.Sets/src/mage/cards/g/GroomsFinery.java @@ -17,12 +17,9 @@ import mage.constants.AttachmentType; import mage.constants.CardType; import mage.constants.SubType; import mage.filter.FilterPermanent; -import mage.filter.common.FilterEquipmentPermanent; -import mage.filter.predicate.ObjectSourcePlayer; -import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.filter.StaticFilters; import mage.filter.predicate.mageobject.NamePredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.filter.predicate.permanent.AttachedToPredicate; import java.util.UUID; @@ -31,11 +28,11 @@ import java.util.UUID; */ public final class GroomsFinery extends CardImpl { - private static final FilterPermanent filter = new FilterEquipmentPermanent(); + private static final FilterPermanent filter = new FilterPermanent(SubType.EQUIPMENT, ""); static { filter.add(new NamePredicate("Bride's Gown")); - filter.add(GroomsFineryPredicate.instance); + filter.add(new AttachedToPredicate(StaticFilters.FILTER_CONTROLLED_CREATURE)); } private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter, false); @@ -75,15 +72,3 @@ public final class GroomsFinery extends CardImpl { return new GroomsFinery(this); } } - -enum GroomsFineryPredicate implements ObjectSourcePlayerPredicate { - instance; - - @Override - public boolean apply(ObjectSourcePlayer input, Game game) { - Permanent permanent = game.getPermanent(input.getObject().getAttachedTo()); - return permanent != null - && permanent.isCreature(game) - && permanent.isControlledBy(input.getPlayerId()); - } -} diff --git a/Mage.Sets/src/mage/cards/g/GruesomeEncore.java b/Mage.Sets/src/mage/cards/g/GruesomeEncore.java index d8f55cec1f5..dd794449349 100644 --- a/Mage.Sets/src/mage/cards/g/GruesomeEncore.java +++ b/Mage.Sets/src/mage/cards/g/GruesomeEncore.java @@ -22,6 +22,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInOpponentsGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -78,7 +79,7 @@ class GruesomeEncoreEffect extends OneShotEffect { Card card = game.getCard(getTargetPointer().getFirst(game, source)); if (card != null) { controller.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); effect.setTargetPointer(new FixedTarget(permanent, game)); diff --git a/Mage.Sets/src/mage/cards/g/GryffsBoon.java b/Mage.Sets/src/mage/cards/g/GryffsBoon.java index 5f56a7ade08..213309ba88c 100644 --- a/Mage.Sets/src/mage/cards/g/GryffsBoon.java +++ b/Mage.Sets/src/mage/cards/g/GryffsBoon.java @@ -66,7 +66,7 @@ class GryffsBoonEffect extends OneShotEffect { GryffsBoonEffect() { super(Outcome.PutCardInPlay); - staticText = "Return {this} from your graveyard to the battlefield attached to target creature"; + staticText = "Return this card from your graveyard to the battlefield attached to target creature"; } private GryffsBoonEffect(final GryffsBoonEffect effect) { diff --git a/Mage.Sets/src/mage/cards/g/GuadosalamFarplaneGateway.java b/Mage.Sets/src/mage/cards/g/GuadosalamFarplaneGateway.java new file mode 100644 index 00000000000..abb01ad844f --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GuadosalamFarplaneGateway.java @@ -0,0 +1,39 @@ +package mage.cards.g; + +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GuadosalamFarplaneGateway extends CardImpl { + + public GuadosalamFarplaneGateway(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.subtype.add(SubType.TOWN); + + // This land enters tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {G} or {U}. + this.addAbility(new GreenManaAbility()); + this.addAbility(new BlueManaAbility()); + } + + private GuadosalamFarplaneGateway(final GuadosalamFarplaneGateway card) { + super(card); + } + + @Override + public GuadosalamFarplaneGateway copy() { + return new GuadosalamFarplaneGateway(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GuardianOfSolitude.java b/Mage.Sets/src/mage/cards/g/GuardianOfSolitude.java index 0782d6fa957..f05935cace1 100644 --- a/Mage.Sets/src/mage/cards/g/GuardianOfSolitude.java +++ b/Mage.Sets/src/mage/cards/g/GuardianOfSolitude.java @@ -27,7 +27,7 @@ public final class GuardianOfSolitude extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(2); - Ability ability = new SpellCastControllerTriggeredAbility(new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false); + Ability ability = new SpellCastControllerTriggeredAbility(new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, false); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GuffRewritesHistory.java b/Mage.Sets/src/mage/cards/g/GuffRewritesHistory.java index 4cceae42326..89fda11ce5b 100644 --- a/Mage.Sets/src/mage/cards/g/GuffRewritesHistory.java +++ b/Mage.Sets/src/mage/cards/g/GuffRewritesHistory.java @@ -1,6 +1,5 @@ package mage.cards.g; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.*; @@ -8,15 +7,13 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicate; import mage.filter.predicate.Predicates; -import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; import mage.target.TargetPermanent; -import mage.target.targetadjustment.TargetAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; import mage.util.CardUtil; @@ -27,6 +24,15 @@ import java.util.*; */ public final class GuffRewritesHistory extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent("target nonenchantment, nonland permanent"); + + static { + filter.add(Predicates.and( + Predicates.not(CardType.ENCHANTMENT.getPredicate()), + Predicates.not(CardType.LAND.getPredicate()) + )); + } + public GuffRewritesHistory(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); @@ -41,8 +47,8 @@ public final class GuffRewritesHistory extends CardImpl { + "nonland card, then puts the rest on the bottom of their library in a random order. " + "Each player may cast the nonland card they exiled without paying its mana cost.") ); - - this.getSpellAbility().setTargetAdjuster(GuffRewritesHistoryAdjuster.instance); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, false)); } private GuffRewritesHistory(final GuffRewritesHistory card) { @@ -55,33 +61,6 @@ public final class GuffRewritesHistory extends CardImpl { } } -enum GuffRewritesHistoryAdjuster implements TargetAdjuster { - instance; - private static final Predicate predicate = - Predicates.and( - Predicates.not(CardType.ENCHANTMENT.getPredicate()), - Predicates.not(CardType.LAND.getPredicate()) - ); - - @Override - public void adjustTargets(Ability ability, Game game) { - ability.getTargets().clear(); - for (UUID playerId : game.getState().getPlayersInRange(ability.getControllerId(), game)) { - Player player = game.getPlayer(playerId); - if (player == null) { - continue; - } - FilterPermanent filter = new FilterPermanent( - "nonenchantment, nonland permanent " - + (ability.isControlledBy(playerId) ? "you control" : "controlled by " + player.getName()) - ); - filter.add(predicate); - filter.add(new ControllerIdPredicate(playerId)); - ability.addTarget(new TargetPermanent(filter)); - } - } -} - class GuffRewritesHistoryEffect extends OneShotEffect { GuffRewritesHistoryEffect() { diff --git a/Mage.Sets/src/mage/cards/g/GuidelightOptimizer.java b/Mage.Sets/src/mage/cards/g/GuidelightOptimizer.java index 344860c6e58..3d9d9504de3 100644 --- a/Mage.Sets/src/mage/cards/g/GuidelightOptimizer.java +++ b/Mage.Sets/src/mage/cards/g/GuidelightOptimizer.java @@ -53,7 +53,7 @@ class ArtifactOrActivatedManaBuilder extends ConditionalManaBuilder { @Override public String getRule() { - return "Spend this mana only to cast an artifact spells or activate an ability"; + return "Spend this mana only to cast an artifact spell or activate an ability"; } } @@ -61,7 +61,7 @@ class ArtifactOrActivatedConditionalMana extends ConditionalMana { public ArtifactOrActivatedConditionalMana(Mana mana) { super(mana); - staticText = "Spend this mana only to cast an artifact spells or activate an ability"; + staticText = "Spend this mana only to cast an artifact spell or activate an ability"; addCondition(new ArtifactOrActivatedManaCondition()); } } @@ -80,4 +80,4 @@ class ArtifactOrActivatedManaCondition extends ManaCondition implements Conditio public boolean apply(Game game, Ability source, UUID originalId, mage.abilities.costs.Cost costsToPay) { return apply(game, source); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/g/Gurzigost.java b/Mage.Sets/src/mage/cards/g/Gurzigost.java index e4a3d39e8af..dfbf9edb8b6 100644 --- a/Mage.Sets/src/mage/cards/g/Gurzigost.java +++ b/Mage.Sets/src/mage/cards/g/Gurzigost.java @@ -44,7 +44,7 @@ public final class Gurzigost extends CardImpl { // {G}{G}, Discard a card: You may have Gurzigost assign its combat damage this turn as though it weren't blocked. Effect effect = new GainAbilitySourceEffect(DamageAsThoughNotBlockedAbility.getInstance(), Duration.EndOfTurn); - effect.setText("You may have Gurzigost assign its combat damage this turn as though it weren't blocked"); + effect.setText("You may have {this} assign its combat damage this turn as though it weren't blocked"); Ability ability = new SimpleActivatedAbility(effect, new ManaCostsImpl<>("{G}{G}")); ability.addCost(new DiscardCardCost()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/g/GysahlGreens.java b/Mage.Sets/src/mage/cards/g/GysahlGreens.java new file mode 100644 index 00000000000..c72140ee111 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GysahlGreens.java @@ -0,0 +1,36 @@ +package mage.cards.g; + +import java.util.UUID; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.FlashbackAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.permanent.token.ChocoboToken; + +/** + * + * @author TheElk801 + */ +public final class GysahlGreens extends CardImpl { + + public GysahlGreens(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}"); + + // Create a 2/2 green Bird creature token with "Whenever a land you control enters, this token gets +1/+0 until end of turn." + this.getSpellAbility().addEffect(new CreateTokenEffect(new ChocoboToken())); + + // Flashback {6}{G} + this.addAbility(new FlashbackAbility(this, new ManaCostsImpl<>("{6}{G}"))); + } + + private GysahlGreens(final GysahlGreens card) { + super(card); + } + + @Override + public GysahlGreens copy() { + return new GysahlGreens(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HaazdaShieldMate.java b/Mage.Sets/src/mage/cards/h/HaazdaShieldMate.java index 3fb2048ceac..fd27b07966d 100644 --- a/Mage.Sets/src/mage/cards/h/HaazdaShieldMate.java +++ b/Mage.Sets/src/mage/cards/h/HaazdaShieldMate.java @@ -1,28 +1,27 @@ package mage.cards.h; -import java.util.UUID; import mage.MageInt; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; + +import java.util.UUID; /** - * * @author anonymous */ public final class HaazdaShieldMate extends CardImpl { public HaazdaShieldMate(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SOLDIER); this.power = new MageInt(1); @@ -30,9 +29,9 @@ public final class HaazdaShieldMate extends CardImpl { // At the beginning of your upkeep, sacrifice Haazda Shield Mate unless you pay {W}{W}. this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SacrificeSourceUnlessPaysEffect(new ManaCostsImpl<>("{W}{W}")))); - + // {W}: The next time a source of your choice would deal damage to you this turn, prevent that damage. - this.addAbility(new SimpleActivatedAbility(new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn), new ManaCostsImpl<>("{W}"))); + this.addAbility(new SimpleActivatedAbility(new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, true), new ManaCostsImpl<>("{W}"))); } private HaazdaShieldMate(final HaazdaShieldMate card) { diff --git a/Mage.Sets/src/mage/cards/h/HadesSorcererOfEld.java b/Mage.Sets/src/mage/cards/h/HadesSorcererOfEld.java index 46935cb71a0..270bb430e07 100644 --- a/Mage.Sets/src/mage/cards/h/HadesSorcererOfEld.java +++ b/Mage.Sets/src/mage/cards/h/HadesSorcererOfEld.java @@ -1,16 +1,19 @@ package mage.cards.h; -import java.util.UUID; import mage.MageInt; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.abilities.keyword.VigilanceAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MyTurnCondition; +import mage.abilities.decorator.ConditionalAsThoughEffect; import mage.abilities.effects.common.replacement.GraveyardFromAnywhereExileReplacementEffect; import mage.abilities.effects.common.ruleModifying.PlayFromGraveyardControllerEffect; +import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; + +import java.util.UUID; /** * @author balazskristof @@ -19,7 +22,7 @@ public final class HadesSorcererOfEld extends CardImpl { public HadesSorcererOfEld(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); - + this.supertype.add(SuperType.LEGENDARY); this.subtype.add(SubType.AVATAR); this.power = new MageInt(6); @@ -33,7 +36,9 @@ public final class HadesSorcererOfEld extends CardImpl { this.addAbility(VigilanceAbility.getInstance()); // Echo of the Lost -- During your turn you may play cards from your graveyard. - this.addAbility(new SimpleStaticAbility(PlayFromGraveyardControllerEffect.playCards()).withFlavorWord("Echo of the Lost")); + this.addAbility(new SimpleStaticAbility(new ConditionalAsThoughEffect( + PlayFromGraveyardControllerEffect.playCards(), MyTurnCondition.instance + ).setText("during your turn, you may play cards from your graveyard")).withFlavorWord("Echo of the Lost")); // If a card or token would be put into your graveyard from anywhere, exile it instead. this.addAbility(new SimpleStaticAbility(new GraveyardFromAnywhereExileReplacementEffect(true, true))); diff --git a/Mage.Sets/src/mage/cards/h/HaltOrder.java b/Mage.Sets/src/mage/cards/h/HaltOrder.java index 9152886692a..51c9a15d69c 100644 --- a/Mage.Sets/src/mage/cards/h/HaltOrder.java +++ b/Mage.Sets/src/mage/cards/h/HaltOrder.java @@ -1,25 +1,31 @@ - package mage.cards.h; -import java.util.UUID; import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterArtifactSpell; +import mage.filter.FilterSpell; import mage.target.TargetSpell; +import java.util.UUID; + /** * * @author Loki */ public final class HaltOrder extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("artifact spell"); + static { + filter.add(CardType.ARTIFACT.getPredicate()); + } + public HaltOrder (UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{U}"); // Counter target artifact spell. Draw a card. - this.getSpellAbility().addTarget(new TargetSpell(new FilterArtifactSpell())); + this.getSpellAbility().addTarget(new TargetSpell(filter)); this.getSpellAbility().addEffect(new CounterTargetEffect()); this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
    ")); } diff --git a/Mage.Sets/src/mage/cards/h/HalvarGodOfBattle.java b/Mage.Sets/src/mage/cards/h/HalvarGodOfBattle.java index d8a6968651a..ddebad921d6 100644 --- a/Mage.Sets/src/mage/cards/h/HalvarGodOfBattle.java +++ b/Mage.Sets/src/mage/cards/h/HalvarGodOfBattle.java @@ -2,23 +2,23 @@ package mage.cards.h; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.abilities.common.DiesAttachedTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachTargetToTargetEffect; import mage.abilities.effects.common.continuous.BoostEquippedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.DoubleStrikeAbility; import mage.abilities.keyword.EquipAbility; import mage.abilities.keyword.VigilanceAbility; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.Card; import mage.cards.CardSetInfo; import mage.cards.ModalDoubleFacedCard; import mage.constants.*; import mage.filter.FilterPermanent; -import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.ObjectSourcePlayer; import mage.filter.predicate.ObjectSourcePlayerPredicate; @@ -32,6 +32,7 @@ import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; import java.util.Objects; +import java.util.Optional; import java.util.UUID; /** @@ -40,12 +41,12 @@ import java.util.UUID; public final class HalvarGodOfBattle extends ModalDoubleFacedCard { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - private static final FilterPermanent filter2 = new FilterPermanent("aura or equipment attached to a creature you control"); + private static final FilterPermanent filter2 = new FilterPermanent("Aura or Equipment attached to a creature you control"); static { filter.add(Predicates.or(EnchantedPredicate.instance, EquippedPredicate.instance)); filter2.add(Predicates.or(SubType.AURA.getPredicate(), SubType.EQUIPMENT.getPredicate())); - filter2.add(new HalvarGodOfBattlePredicate(StaticFilters.FILTER_CONTROLLED_CREATURE)); + filter2.add(HalvarGodOfBattlePredicate.instance); } public HalvarGodOfBattle(UUID ownerId, CardSetInfo setInfo) { @@ -63,12 +64,13 @@ public final class HalvarGodOfBattle extends ModalDoubleFacedCard { // Creatures you control that are enchanted or equipped have double strike. this.getLeftHalfCard().addAbility(new SimpleStaticAbility( - new GainAbilityControlledEffect(DoubleStrikeAbility.getInstance(), Duration.WhileOnBattlefield, filter + new GainAbilityControlledEffect( + DoubleStrikeAbility.getInstance(), Duration.WhileOnBattlefield, filter ).setText("Creatures you control that are enchanted or equipped have double strike") )); // At the beginning of each combat, you may attach target Aura or Equipment attached to a creature you control to target creature you control. - Ability ability = new BeginningOfCombatTriggeredAbility(TargetController.ANY, new HalvarGodOfBattleEffect(), false); + Ability ability = new BeginningOfCombatTriggeredAbility(TargetController.ANY, new AttachTargetToTargetEffect(), true); ability.addTarget(new TargetPermanent(filter2)); ability.addTarget(new TargetControlledCreaturePermanent()); this.getLeftHalfCard().addAbility(ability); @@ -101,49 +103,6 @@ public final class HalvarGodOfBattle extends ModalDoubleFacedCard { } } -class HalvarGodOfBattleEffect extends OneShotEffect { - - HalvarGodOfBattleEffect() { - super(Outcome.BoostCreature); - staticText = "you may attach target Aura or Equipment attached to a creature you control to target creature you control"; - } - - private HalvarGodOfBattleEffect(final HalvarGodOfBattleEffect effect) { - super(effect); - } - - @Override - public HalvarGodOfBattleEffect copy() { - return new HalvarGodOfBattleEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent attachment = game.getPermanent(source.getTargets().get(0).getFirstTarget()); - Permanent creature = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if (controller != null && attachment != null && creature != null && creature.isControlledBy(controller.getId())) { - Permanent oldCreature = game.getPermanent(attachment.getAttachedTo()); - if (oldCreature != null && oldCreature.isControlledBy(controller.getId()) && !oldCreature.equals(creature)) { - if (creature.cantBeAttachedBy(attachment, source, game, true)) { - game.informPlayers(attachment.getLogName() + " was not attached to " + creature.getLogName() - + " because it's not a legal target"); - return false; - } - if (controller.chooseUse(Outcome.BoostCreature, "Attach " + attachment.getLogName() - + " to " + creature.getLogName() + "?", source, game)) { - oldCreature.removeAttachment(attachment.getId(), source, game); - creature.addAttachment(attachment.getId(), source, game); - game.informPlayers(attachment.getLogName() + " was unattached from " + oldCreature.getLogName() - + " and attached to " + creature.getLogName()); - return true; - } - } - } - return false; - } -} - class SwordOfTheRealmsEffect extends OneShotEffect { SwordOfTheRealmsEffect() { @@ -174,23 +133,17 @@ class SwordOfTheRealmsEffect extends OneShotEffect { } } -class HalvarGodOfBattlePredicate implements ObjectSourcePlayerPredicate { - - private final FilterPermanent filter; - - public HalvarGodOfBattlePredicate(FilterPermanent filter) { - this.filter = filter; - } +enum HalvarGodOfBattlePredicate implements ObjectSourcePlayerPredicate { + instance; @Override public boolean apply(ObjectSourcePlayer input, Game game) { - UUID attachedTo = input.getObject().getAttachedTo(); - Permanent permanent = game.getPermanent(attachedTo); - return permanent != null && filter.match(permanent, input.getPlayerId(), input.getSource(), game); - } - - @Override - public String toString() { - return "attached to " + filter.getMessage(); + return Optional + .ofNullable(input) + .map(ObjectSourcePlayer::getObject) + .map(Permanent::getAttachedTo) + .map(game::getPermanent) + .map(permanent -> permanent.isControlledBy(input.getPlayerId())) + .orElse(false); } } diff --git a/Mage.Sets/src/mage/cards/h/HammerOfRuin.java b/Mage.Sets/src/mage/cards/h/HammerOfRuin.java index 9992e039c5d..9ceb1aecafc 100644 --- a/Mage.Sets/src/mage/cards/h/HammerOfRuin.java +++ b/Mage.Sets/src/mage/cards/h/HammerOfRuin.java @@ -14,10 +14,9 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterEquipmentPermanent; +import mage.filter.FilterPermanent; import mage.target.TargetPermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -26,8 +25,8 @@ import java.util.UUID; */ public final class HammerOfRuin extends CardImpl { - private static final FilterEquipmentPermanent filter - = new FilterEquipmentPermanent("Equipment that player controls"); + private static final FilterPermanent filter + = new FilterPermanent(SubType.EQUIPMENT, "Equipment that player controls"); public HammerOfRuin(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); @@ -39,7 +38,7 @@ public final class HammerOfRuin extends CardImpl { // Whenever equipped creature deals combat damage to a player, you may destroy target Equipment that player controls. Ability ability = new DealsDamageToAPlayerAttachedTriggeredAbility(new DestroyTargetEffect(), "equipped creature", true, true); ability.addTarget(new TargetPermanent(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); // Equip {2} diff --git a/Mage.Sets/src/mage/cards/h/HammerheadTyrant.java b/Mage.Sets/src/mage/cards/h/HammerheadTyrant.java index 0e10903c3a9..eab93495e3e 100644 --- a/Mage.Sets/src/mage/cards/h/HammerheadTyrant.java +++ b/Mage.Sets/src/mage/cards/h/HammerheadTyrant.java @@ -70,19 +70,10 @@ enum HammerheadTyrantPredicate implements ObjectSourcePlayerPredicate @Override public boolean apply(ObjectSourcePlayer input, Game game) { - return input - .getObject() - .getManaValue() - <= CardUtil - .castStream( - input.getSource() - .getEffects() - .stream() - .map(effect -> effect.getValue("spellCast")), - Spell.class - ) - .findFirst() + return CardUtil + .getEffectValueFromAbility(input.getSource(), "spellCast", Spell.class) .map(Spell::getManaValue) - .orElse(-1); + .filter(x -> x >= input.getObject().getManaValue()) + .isPresent(); } } diff --git a/Mage.Sets/src/mage/cards/h/HammersOfMoradin.java b/Mage.Sets/src/mage/cards/h/HammersOfMoradin.java index 3059c91311b..77e864246af 100644 --- a/Mage.Sets/src/mage/cards/h/HammersOfMoradin.java +++ b/Mage.Sets/src/mage/cards/h/HammersOfMoradin.java @@ -10,7 +10,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; import java.util.UUID; @@ -38,7 +38,7 @@ public final class HammersOfMoradin extends CardImpl { .setText("for each opponent, tap up to one target creature that player controls") ); ability.addTarget(new TargetCreaturePermanent(0,1)); - ability.setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/h/HandThatFeeds.java b/Mage.Sets/src/mage/cards/h/HandThatFeeds.java index 74f22ba1cab..32c2508c24d 100644 --- a/Mage.Sets/src/mage/cards/h/HandThatFeeds.java +++ b/Mage.Sets/src/mage/cards/h/HandThatFeeds.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.condition.common.DeliriumCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.dynamicvalue.common.CardTypesInGraveyardCount; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; @@ -31,12 +30,12 @@ public final class HandThatFeeds extends CardImpl { this.toughness = new MageInt(2); // Delirium -- Whenever Hand That Feeds attacks while there are four or more card types among cards in your graveyard, it gets +2/+0 and gains menace until end of turn. - Ability ability = new ConditionalTriggeredAbility( - new AttacksTriggeredAbility(new BoostSourceEffect(2, 0, Duration.EndOfTurn)), - DeliriumCondition.instance, "Whenever {this} attacks while there are four or more " + - "card types among cards in your graveyard, it gets +2/+0 and gains menace until end of turn." - ); - ability.addEffect(new GainAbilitySourceEffect(new MenaceAbility(false), Duration.EndOfTurn)); + Ability ability = new AttacksTriggeredAbility( + new BoostSourceEffect(2, 0, Duration.EndOfTurn).setText("it gets +2/+0") + ).withTriggerCondition(DeliriumCondition.instance); + ability.addEffect(new GainAbilitySourceEffect( + new MenaceAbility(false), Duration.EndOfTurn + ).setText("and gains menace until end of turn")); this.addAbility(ability.setAbilityWord(AbilityWord.DELIRIUM).addHint(CardTypesInGraveyardCount.YOU.getHint())); } diff --git a/Mage.Sets/src/mage/cards/h/HansEriksson.java b/Mage.Sets/src/mage/cards/h/HansEriksson.java index b89a7d5ea46..3ff28910d61 100644 --- a/Mage.Sets/src/mage/cards/h/HansEriksson.java +++ b/Mage.Sets/src/mage/cards/h/HansEriksson.java @@ -23,6 +23,7 @@ import mage.target.common.TargetPlayerOrPlaneswalker; import java.util.UUID; import mage.abilities.effects.Effect; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @author TheElk801 @@ -88,7 +89,7 @@ class HansErikssonEffect extends OneShotEffect { return player.moveCards(card, Zone.HAND, source, game); } player.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return true; } diff --git a/Mage.Sets/src/mage/cards/h/HareApparent.java b/Mage.Sets/src/mage/cards/h/HareApparent.java index ef347f862e8..151733fc391 100644 --- a/Mage.Sets/src/mage/cards/h/HareApparent.java +++ b/Mage.Sets/src/mage/cards/h/HareApparent.java @@ -47,6 +47,8 @@ public final class HareApparent extends CardImpl { // When this creature enters, create a number of 1/1 white Rabbit creature tokens equal to the number of other creatures you control named Hare Apparent. this.addAbility(new EntersBattlefieldTriggeredAbility( new CreateTokenEffect(new RabbitToken(), xValue) + .setText("create a number of 1/1 white Rabbit creature tokens equal " + + "to the number of other creatures you control named Hare Apparent") ).addHint(hint)); // A deck can have any number of cards named Hare Apparent. diff --git a/Mage.Sets/src/mage/cards/h/HaruOnna.java b/Mage.Sets/src/mage/cards/h/HaruOnna.java index 718971ba425..3c8c988759e 100644 --- a/Mage.Sets/src/mage/cards/h/HaruOnna.java +++ b/Mage.Sets/src/mage/cards/h/HaruOnna.java @@ -29,7 +29,7 @@ public final class HaruOnna extends CardImpl { // When Haru-Onna enters the battlefield, draw a card. this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1))); // Whenever you cast a Spirit or Arcane spell, you may return Haru-Onna to its owner's hand. - this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, true)); } private HaruOnna(final HaruOnna card) { diff --git a/Mage.Sets/src/mage/cards/h/HashatonScarabsFist.java b/Mage.Sets/src/mage/cards/h/HashatonScarabsFist.java index 0423996ec28..135de070b3a 100644 --- a/Mage.Sets/src/mage/cards/h/HashatonScarabsFist.java +++ b/Mage.Sets/src/mage/cards/h/HashatonScarabsFist.java @@ -83,7 +83,7 @@ class HashatonScarabsFistEffect extends OneShotEffect { HashatonScarabsFistEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "create a tapped token that`s a copy of that card, except it`s a 4/4 black Zombie"; + this.staticText = "create a tapped token that's a copy of that card, except it's a 4/4 black Zombie"; } private HashatonScarabsFistEffect(OneShotEffect effect) { diff --git a/Mage.Sets/src/mage/cards/h/HasteMagic.java b/Mage.Sets/src/mage/cards/h/HasteMagic.java new file mode 100644 index 00000000000..7faab784597 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HasteMagic.java @@ -0,0 +1,38 @@ +package mage.cards.h; + +import mage.abilities.effects.common.ExileTopXMayPlayUntilEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HasteMagic extends CardImpl { + + public HasteMagic(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); + + // Target creature gets +3/+1 and gains haste until end of turn. Exile the top card of your library. You may play it until your next end step. + this.getSpellAbility().addEffect(new BoostTargetEffect(3, 1).setText("target creature gets +3/+1")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance()).setText("and gains haste until end of turn")); + this.getSpellAbility().addEffect(new ExileTopXMayPlayUntilEffect(1, Duration.UntilYourNextEndStep)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private HasteMagic(final HasteMagic card) { + super(card); + } + + @Override + public HasteMagic copy() { + return new HasteMagic(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HatchetBully.java b/Mage.Sets/src/mage/cards/h/HatchetBully.java index 4c753e09863..84d5ad768e3 100644 --- a/Mage.Sets/src/mage/cards/h/HatchetBully.java +++ b/Mage.Sets/src/mage/cards/h/HatchetBully.java @@ -3,24 +3,16 @@ package mage.cards.h; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; +import mage.abilities.costs.common.PutCountersTargetCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; import mage.counters.CounterType; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.Target; import mage.target.common.TargetAnyTarget; -import mage.target.common.TargetControlledCreaturePermanent; import java.util.UUID; @@ -38,15 +30,11 @@ public final class HatchetBully extends CardImpl { this.toughness = new MageInt(3); // {2}{R}, {tap}, Put a -1/-1 counter on a creature you control: Hatchet Bully deals 2 damage to any target. - Ability ability = new SimpleActivatedAbility(new HatchetBullyEffect(), new ManaCostsImpl<>("{2}{R}")); + Ability ability = new SimpleActivatedAbility(new DamageTargetEffect(2), new ManaCostsImpl<>("{2}{R}")); ability.addCost(new TapSourceCost()); - ability.addCost(new HatchetBullyCost()); + ability.addCost(new PutCountersTargetCost(CounterType.M1M1.createInstance())); ability.addTarget(new TargetAnyTarget()); - Target target = new TargetControlledCreaturePermanent(); - target.withNotTarget(true); - ability.addTarget(target); this.addAbility(ability); - } private HatchetBully(final HatchetBully card) { @@ -58,64 +46,3 @@ public final class HatchetBully extends CardImpl { return new HatchetBully(this); } } - -class HatchetBullyCost extends CostImpl { - - public HatchetBullyCost() { - this.text = "Put a -1/-1 counter on a creature you control"; - } - - private HatchetBullyCost(final HatchetBullyCost cost) { - super(cost); - } - - @Override - public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { - return true; - } - - @Override - public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - Permanent permanent = game.getPermanent(ability.getTargets().get(1).getFirstTarget()); - if (permanent != null) { - permanent.addCounters(CounterType.M1M1.createInstance(), controllerId, ability, game); - this.paid = true; - } - return paid; - } - - @Override - public HatchetBullyCost copy() { - return new HatchetBullyCost(this); - } -} - -class HatchetBullyEffect extends OneShotEffect { - - HatchetBullyEffect() { - super(Outcome.Damage); - staticText = "{this} deals 2 damage to any target"; - } - - private HatchetBullyEffect(final HatchetBullyEffect effect) { - super(effect); - } - - @Override - public HatchetBullyEffect copy() { - return new HatchetBullyEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent != null) { - permanent.damage(2, source.getSourceId(), source, game, false, true); - } - Player player = game.getPlayer(source.getFirstTarget()); - if (player != null) { - player.damage(2, source.getSourceId(), source, game); - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/h/HauntedScreen.java b/Mage.Sets/src/mage/cards/h/HauntedScreen.java index 773a04ee2f7..24bb9b85055 100644 --- a/Mage.Sets/src/mage/cards/h/HauntedScreen.java +++ b/Mage.Sets/src/mage/cards/h/HauntedScreen.java @@ -46,7 +46,8 @@ public final class HauntedScreen extends CardImpl { ); ability.addEffect(new BecomesCreatureSourceEffect(new CreatureToken( 0, 0, "0/0 Spirit creature", SubType.SPIRIT - ), CardType.ARTIFACT, Duration.Custom).withKeepCreatureSubtypes(true)); + ), CardType.ARTIFACT, Duration.Custom).withKeepCreatureSubtypes(true) + .setText("It becomes a 0/0 Spirit creature in addition to its other types")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/h/HavengulMystery.java b/Mage.Sets/src/mage/cards/h/HavengulMystery.java index bb1dbe2aa08..24cca60c3b1 100644 --- a/Mage.Sets/src/mage/cards/h/HavengulMystery.java +++ b/Mage.Sets/src/mage/cards/h/HavengulMystery.java @@ -91,7 +91,7 @@ class HavengulMysteryEffect extends OneShotEffect { return false; } player.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/h/HaviTheAllFather.java b/Mage.Sets/src/mage/cards/h/HaviTheAllFather.java index e79e460bb93..f5d28e0b4d2 100644 --- a/Mage.Sets/src/mage/cards/h/HaviTheAllFather.java +++ b/Mage.Sets/src/mage/cards/h/HaviTheAllFather.java @@ -23,9 +23,9 @@ import mage.constants.SuperType; import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.filter.common.FilterCreatureCard; -import mage.filter.common.FilterHistoricCard; import mage.filter.predicate.ObjectSourcePlayer; import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.filter.predicate.mageobject.HistoricPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCardInYourGraveyard; @@ -38,19 +38,20 @@ import java.util.UUID; */ public final class HaviTheAllFather extends CardImpl { - private static final Condition condition = new CardsInControllerGraveyardCondition( - 4, new FilterHistoricCard("historic cards") - ); - private static final Hint hint = new ValueHint( - "Historic cards in your graveyard", new CardsInControllerGraveyardCount(new FilterHistoricCard()) - ); + private static final FilterCard filterHistoric = new FilterCard("historic cards"); private static final FilterCard filter = new FilterCreatureCard("legendary creature card with lesser mana value from your graveyard"); static { + filterHistoric.add(HistoricPredicate.instance); filter.add(SuperType.LEGENDARY.getPredicate()); filter.add(HaviTheAllFatherPredicate.instance); } + private static final Condition condition = new CardsInControllerGraveyardCondition(4, filterHistoric); + private static final Hint hint = new ValueHint( + "Historic cards in your graveyard", new CardsInControllerGraveyardCount(filterHistoric) + ); + public HaviTheAllFather(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{G}{W}"); diff --git a/Mage.Sets/src/mage/cards/h/HavocEater.java b/Mage.Sets/src/mage/cards/h/HavocEater.java index b7557b22603..e034d056590 100644 --- a/Mage.Sets/src/mage/cards/h/HavocEater.java +++ b/Mage.Sets/src/mage/cards/h/HavocEater.java @@ -16,7 +16,7 @@ import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; import java.util.Objects; @@ -44,7 +44,7 @@ public final class HavocEater extends CardImpl { .setTargetPointer(new EachTargetPointer())); ability.addEffect(new HavocEaterEffect()); ability.addTarget(new TargetCreaturePermanent(0, 1)); - this.addAbility(ability.setTargetAdjuster(new ForEachOpponentTargetsAdjuster())); + this.addAbility(ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true))); } private HavocEater(final HavocEater card) { diff --git a/Mage.Sets/src/mage/cards/h/HaythamKenway.java b/Mage.Sets/src/mage/cards/h/HaythamKenway.java index d18d4cc70b8..3360303fa41 100644 --- a/Mage.Sets/src/mage/cards/h/HaythamKenway.java +++ b/Mage.Sets/src/mage/cards/h/HaythamKenway.java @@ -17,7 +17,7 @@ import mage.constants.SuperType; import mage.filter.FilterCard; import mage.filter.common.FilterCreaturePermanent; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; import java.util.UUID; @@ -59,7 +59,7 @@ public final class HaythamKenway extends CardImpl { .setText("for each opponent, exile up to one target creature that player controls until {this} leaves the battlefield") ); ability2.addTarget(new TargetCreaturePermanent(0, 1)); - ability2.setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + ability2.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); this.addAbility(ability2); } diff --git a/Mage.Sets/src/mage/cards/h/HazardrootHerbalist.java b/Mage.Sets/src/mage/cards/h/HazardrootHerbalist.java index c7589565095..927905b73f1 100644 --- a/Mage.Sets/src/mage/cards/h/HazardrootHerbalist.java +++ b/Mage.Sets/src/mage/cards/h/HazardrootHerbalist.java @@ -2,7 +2,7 @@ package mage.cards.h; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.AttacksCreatureYouControlTriggeredAbility; +import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.TargetObjectMatchesFilterCondition; import mage.abilities.decorator.ConditionalOneShotEffect; @@ -35,7 +35,7 @@ public final class HazardrootHerbalist extends CardImpl { this.toughness = new MageInt(4); // Whenever you attack, target creature you control gets +1/+0 until end of turn. If that creature is a token, it also gains deathtouch until end of turn. - Ability ability = new AttacksCreatureYouControlTriggeredAbility(new BoostTargetEffect(1, 0)); + Ability ability = new AttacksWithCreaturesTriggeredAbility(new BoostTargetEffect(1, 0), 1); ability.addEffect(new ConditionalOneShotEffect( new AddContinuousEffectToGame(new GainAbilityTargetEffect(DeathtouchAbility.getInstance())), condition, "if that creature is a token, it also gains deathtouch until end of turn" diff --git a/Mage.Sets/src/mage/cards/h/HazoretsMonument.java b/Mage.Sets/src/mage/cards/h/HazoretsMonument.java index 049ea10a9f5..5da178ce98a 100644 --- a/Mage.Sets/src/mage/cards/h/HazoretsMonument.java +++ b/Mage.Sets/src/mage/cards/h/HazoretsMonument.java @@ -1,7 +1,6 @@ package mage.cards.h; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; @@ -13,27 +12,23 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SuperType; -import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author fireshoes */ public final class HazoretsMonument extends CardImpl { private static final FilterCard filter = new FilterCard("Red creature spells"); - private static final FilterSpell filter2 = new FilterSpell("a creature spell"); static { filter.add(Predicates.and(new ColorPredicate(ObjectColor.RED), CardType.CREATURE.getPredicate())); } - static { - filter2.add(CardType.CREATURE.getPredicate()); - } public HazoretsMonument(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); @@ -44,7 +39,10 @@ public final class HazoretsMonument extends CardImpl { this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 1))); // Whenever you cast a creature spell, you may discard a card. If you do, draw a card. - this.addAbility(new SpellCastControllerTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new DiscardCardCost()), filter2, false)); + this.addAbility(new SpellCastControllerTriggeredAbility( + new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new DiscardCardCost()), + StaticFilters.FILTER_SPELL_A_CREATURE, false + )); } private HazoretsMonument(final HazoretsMonument card) { diff --git a/Mage.Sets/src/mage/cards/h/HeavenlyBlademaster.java b/Mage.Sets/src/mage/cards/h/HeavenlyBlademaster.java index 3201bb0c792..5ad11358399 100644 --- a/Mage.Sets/src/mage/cards/h/HeavenlyBlademaster.java +++ b/Mage.Sets/src/mage/cards/h/HeavenlyBlademaster.java @@ -14,7 +14,10 @@ import mage.abilities.keyword.DoubleStrikeAbility; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledPermanent; @@ -32,6 +35,11 @@ import java.util.UUID; */ public final class HeavenlyBlademaster extends CardImpl { + private static final DynamicValue totalAmount = new AdditiveDynamicValue( + new EquipmentAttachedCount(1), + new AuraAttachedCount(1) + ); + public HeavenlyBlademaster(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}"); @@ -46,15 +54,9 @@ public final class HeavenlyBlademaster extends CardImpl { this.addAbility(DoubleStrikeAbility.getInstance()); // When Heavenly Blademaster enters the battlefield, you may attach any number of Auras and Equipment you control to it. - this.addAbility(new EntersBattlefieldTriggeredAbility( - new HeavenlyBlademasterEffect(), true - )); + this.addAbility(new EntersBattlefieldTriggeredAbility(new HeavenlyBlademasterEffect())); // Other creatures you control get +1/+1 for each Aura and Equipment attached to Heavenly Blademaster. - DynamicValue totalAmount = new AdditiveDynamicValue( - new EquipmentAttachedCount(1), - new AuraAttachedCount(1) - ); this.addAbility(new SimpleStaticAbility( new BoostControlledEffect( totalAmount, totalAmount, Duration.WhileOnBattlefield, @@ -101,33 +103,16 @@ class HeavenlyBlademasterEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); Player player = game.getPlayer(source.getControllerId()); - if (sourcePermanent == null || player == null) { + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); + if (player == null || sourcePermanent == null) { return false; } Target target = new TargetPermanent(0, Integer.MAX_VALUE, filter, true); - if (!player.choose(outcome, target, source, game)) { - return false; + player.choose(outcome, target, source, game); + for (UUID targetId : target.getTargets()) { + sourcePermanent.addAttachment(targetId, source, game); } - target.getTargets().stream().map( - attachmentId -> game.getPermanent(attachmentId) - ).filter( - attachment -> attachment != null - ).forEachOrdered((attachment) -> { - if (!sourcePermanent.cantBeAttachedBy(attachment, source, game, true)) { - if (attachment.getAttachedTo() != sourcePermanent.getId()) { - if (attachment.getAttachedTo() != null) { - Permanent fromPermanent = game.getPermanent(attachment.getAttachedTo()); - if (fromPermanent != null) { - fromPermanent.removeAttachment(attachment.getId(), source, game); - } - } - } - sourcePermanent.addAttachment(attachment.getId(), source, game); - game.informPlayers(attachment.getLogName() + " was attached to " + sourcePermanent.getLogName()); - } - }); return true; } } diff --git a/Mage.Sets/src/mage/cards/h/Hecteyes.java b/Mage.Sets/src/mage/cards/h/Hecteyes.java new file mode 100644 index 00000000000..0eec9691d13 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/Hecteyes.java @@ -0,0 +1,39 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.discard.DiscardEachPlayerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Hecteyes extends CardImpl { + + public Hecteyes(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.OOZE); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // When this creature enters, each opponent discards a card. + this.addAbility(new EntersBattlefieldTriggeredAbility(new DiscardEachPlayerEffect(TargetController.OPPONENT))); + } + + private Hecteyes(final Hecteyes card) { + super(card); + } + + @Override + public Hecteyes copy() { + return new Hecteyes(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HelixPinnacle.java b/Mage.Sets/src/mage/cards/h/HelixPinnacle.java index 23dc34ade0b..ea6cdbe8dd4 100644 --- a/Mage.Sets/src/mage/cards/h/HelixPinnacle.java +++ b/Mage.Sets/src/mage/cards/h/HelixPinnacle.java @@ -1,29 +1,28 @@ package mage.cards.h; -import java.util.UUID; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.dynamicvalue.common.GetXValue; import mage.abilities.effects.common.WinGameSourceControllerEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.keyword.ShroudAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; import mage.counters.CounterType; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class HelixPinnacle extends CardImpl { - static final String rule = "at the beginning of your upkeep, if there are 100 or more tower counters on Helix Pinnacle, you win the game"; + private static final Condition condition = new SourceHasCounterCondition(CounterType.TOWER, 100); public HelixPinnacle(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{G}"); @@ -32,16 +31,12 @@ public final class HelixPinnacle extends CardImpl { this.addAbility(ShroudAbility.getInstance()); // {X}: Put X tower counters on Helix Pinnacle. - this.addAbility(new SimpleActivatedAbility( - new AddCountersSourceEffect(CounterType.TOWER.createInstance(), GetXValue.instance, true), - new ManaCostsImpl<>("{X}"))); + this.addAbility(new SimpleActivatedAbility(new AddCountersSourceEffect( + CounterType.TOWER.createInstance(), GetXValue.instance, true + ), new ManaCostsImpl<>("{X}"))); // At the beginning of your upkeep, if there are 100 or more tower counters on Helix Pinnacle, you win the game. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility(new WinGameSourceControllerEffect()), - new SourceHasCounterCondition(CounterType.TOWER, 100, Integer.MAX_VALUE), - rule)); - + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new WinGameSourceControllerEffect()).withInterveningIf(condition)); } private HelixPinnacle(final HelixPinnacle card) { diff --git a/Mage.Sets/src/mage/cards/h/HellkiteCourser.java b/Mage.Sets/src/mage/cards/h/HellkiteCourser.java index 9edad9883b4..aa8a646a657 100644 --- a/Mage.Sets/src/mage/cards/h/HellkiteCourser.java +++ b/Mage.Sets/src/mage/cards/h/HellkiteCourser.java @@ -20,6 +20,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetCard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -90,7 +91,7 @@ class HellkiteCourserEffect extends OneShotEffect { return false; } player.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/h/HellspurBrute.java b/Mage.Sets/src/mage/cards/h/HellspurBrute.java index 59417867a30..3106b271c65 100644 --- a/Mage.Sets/src/mage/cards/h/HellspurBrute.java +++ b/Mage.Sets/src/mage/cards/h/HellspurBrute.java @@ -1,19 +1,13 @@ package mage.cards.h; import mage.MageInt; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.common.AffinityEffect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.AffinityAbility; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AffinityType; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.mageobject.OutlawPredicate; import java.util.UUID; @@ -22,16 +16,6 @@ import java.util.UUID; */ public final class HellspurBrute extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("outlaws"); - - static { - filter.add(OutlawPredicate.instance); - } - - private static final Hint hint = new ValueHint( - "Outlaws you control", new PermanentsOnBattlefieldCount(filter) - ); - public HellspurBrute(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}"); @@ -41,7 +25,7 @@ public final class HellspurBrute extends CardImpl { this.toughness = new MageInt(4); // Affinity for outlaws - this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filter)).addHint(hint)); + this.addAbility(new AffinityAbility(AffinityType.OUTLAWS)); // Trample this.addAbility(TrampleAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/h/HelmOfTheHost.java b/Mage.Sets/src/mage/cards/h/HelmOfTheHost.java index fea2ab7368c..44feb75b148 100644 --- a/Mage.Sets/src/mage/cards/h/HelmOfTheHost.java +++ b/Mage.Sets/src/mage/cards/h/HelmOfTheHost.java @@ -54,7 +54,7 @@ class HelmOfTheHostEffect extends OneShotEffect { HelmOfTheHostEffect() { super(Outcome.PutCreatureInPlay); - this.staticText = "create a token that's a copy of equipped creature, except the token isn't legendary if equipped creature is legendary. That token gains haste."; + this.staticText = "create a token that's a copy of equipped creature, except the token isn't legendary. That token gains haste."; } private HelmOfTheHostEffect(final HelmOfTheHostEffect effect) { diff --git a/Mage.Sets/src/mage/cards/h/HermitOfTheNatterknolls.java b/Mage.Sets/src/mage/cards/h/HermitOfTheNatterknolls.java index 867500314b2..ede6c5f95e4 100644 --- a/Mage.Sets/src/mage/cards/h/HermitOfTheNatterknolls.java +++ b/Mage.Sets/src/mage/cards/h/HermitOfTheNatterknolls.java @@ -4,9 +4,7 @@ import mage.MageInt; import mage.abilities.common.SpellCastOpponentTriggeredAbility; import mage.abilities.common.WerewolfFrontTriggeredAbility; import mage.abilities.condition.common.MyTurnCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.abilities.hint.common.MyTurnHint; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -31,11 +29,9 @@ public final class HermitOfTheNatterknolls extends CardImpl { this.secondSideCardClazz = mage.cards.l.LoneWolfOfTheNatterknolls.class; // Whenever an opponent casts a spell during your turn, draw a card. - this.addAbility(new ConditionalTriggeredAbility( - new SpellCastOpponentTriggeredAbility(new DrawCardSourceControllerEffect(1), StaticFilters.FILTER_SPELL_A, false), - MyTurnCondition.instance, - "Whenever an opponent casts a spell during your turn, draw a card." - ).addHint(MyTurnHint.instance)); + this.addAbility(new SpellCastOpponentTriggeredAbility( + new DrawCardSourceControllerEffect(1), StaticFilters.FILTER_SPELL_A, false + ).withTriggerCondition(MyTurnCondition.instance)); // At the beginning of each upkeep, if no spells were cast last turn, transform Hermit of the Natterknolls. this.addAbility(new TransformAbility()); diff --git a/Mage.Sets/src/mage/cards/h/HickoryWoodlot.java b/Mage.Sets/src/mage/cards/h/HickoryWoodlot.java index 56bb98e3b48..be830460a6f 100644 --- a/Mage.Sets/src/mage/cards/h/HickoryWoodlot.java +++ b/Mage.Sets/src/mage/cards/h/HickoryWoodlot.java @@ -1,11 +1,9 @@ - package mage.cards.h; -import java.util.UUID; import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; -import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.costs.common.TapSourceCost; @@ -17,17 +15,21 @@ import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.Zone; import mage.counters.CounterType; +import java.util.UUID; + /** - * * @author Plopman */ public final class HickoryWoodlot extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.DEPLETION, ComparisonType.EQUAL_TO, 0); + public HickoryWoodlot(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // Hickory Woodlot enters the battlefield tapped with two depletion counters on it. Ability etbAbility = new EntersBattlefieldAbility( @@ -35,10 +37,14 @@ public final class HickoryWoodlot extends CardImpl { ); etbAbility.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance(2))); this.addAbility(etbAbility); + // {tap}, Remove a depletion counter from Hickory Woodlot: Add {G}{G}. If there are no depletion counters on Hickory Woodlot, sacrifice it. Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, Mana.GreenMana(2), new TapSourceCost()); ability.addCost(new RemoveCountersSourceCost(CounterType.DEPLETION.createInstance(1))); - ability.addEffect(new ConditionalOneShotEffect(new SacrificeSourceEffect(), new SourceHasCounterCondition(CounterType.DEPLETION, 0,0), "If there are no depletion counters on {this}, sacrifice it")); + ability.addEffect(new ConditionalOneShotEffect( + new SacrificeSourceEffect(), condition, + "If there are no depletion counters on {this}, sacrifice it" + )); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/h/HiddenGuerrillas.java b/Mage.Sets/src/mage/cards/h/HiddenGuerrillas.java index 142ac9511d6..04110c2ed4d 100644 --- a/Mage.Sets/src/mage/cards/h/HiddenGuerrillas.java +++ b/Mage.Sets/src/mage/cards/h/HiddenGuerrillas.java @@ -1,4 +1,3 @@ - package mage.cards.h; import mage.MageInt; @@ -11,10 +10,10 @@ import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterSpell; import mage.filter.StaticFilters; -import mage.filter.common.FilterArtifactSpell; import mage.game.permanent.token.TokenImpl; import java.util.UUID; @@ -26,12 +25,18 @@ import java.util.UUID; */ public final class HiddenGuerrillas extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("an artifact spell"); + static { + filter.add(CardType.ARTIFACT.getPredicate()); + } + public HiddenGuerrillas(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{G}"); // When an opponent casts an artifact spell, if Hidden Guerrillas is an enchantment, Hidden Guerrillas becomes a 5/3 Soldier creature with trample. TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(new BecomesCreatureSourceEffect(new HiddenGuerrillasSoldier(), null, Duration.WhileOnBattlefield), - new FilterArtifactSpell(), false); + filter, false); this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT), "When an opponent casts an artifact spell, if {this} is an enchantment, {this} becomes a 5/3 Soldier creature with trample.")); } diff --git a/Mage.Sets/src/mage/cards/h/HideousTaskmaster.java b/Mage.Sets/src/mage/cards/h/HideousTaskmaster.java index 2d6e59333d9..445b0dcaa23 100644 --- a/Mage.Sets/src/mage/cards/h/HideousTaskmaster.java +++ b/Mage.Sets/src/mage/cards/h/HideousTaskmaster.java @@ -16,7 +16,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; import java.util.UUID; @@ -42,7 +42,7 @@ public final class HideousTaskmaster extends CardImpl { new GainControlTargetEffect(Duration.EndOfTurn).setTargetPointer(new EachTargetPointer()).setText( "for each opponent, gain control of up to one target creature that player controls until end of turn")); ability.addTarget(new TargetCreaturePermanent(0, 1)); - ability.setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); ability.addEffect( new UntapTargetEffect().setTargetPointer(new EachTargetPointer()).setText("Untap those creatures")); ability.addEffect(new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn) diff --git a/Mage.Sets/src/mage/cards/h/HikariTwilightGuardian.java b/Mage.Sets/src/mage/cards/h/HikariTwilightGuardian.java index db8f86378b5..8574ca9249d 100644 --- a/Mage.Sets/src/mage/cards/h/HikariTwilightGuardian.java +++ b/Mage.Sets/src/mage/cards/h/HikariTwilightGuardian.java @@ -33,7 +33,7 @@ public final class HikariTwilightGuardian extends CardImpl { // Whenever you cast a Spirit or Arcane spell, you may exile Hikari, Twilight Guardian. If you do, return it to the battlefield under its owner's control at the beginning of the next end step. Effect effect = new ExileReturnBattlefieldOwnerNextEndStepSourceEffect(); effect.setText("you may exile {this}. If you do, return it to the battlefield under its owner's control at the beginning of the next end step"); - this.addAbility(new SpellCastControllerTriggeredAbility(effect, StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(effect, StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, true)); } private HikariTwilightGuardian(final HikariTwilightGuardian card) { diff --git a/Mage.Sets/src/mage/cards/h/HillGigas.java b/Mage.Sets/src/mage/cards/h/HillGigas.java new file mode 100644 index 00000000000..22821b13109 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HillGigas.java @@ -0,0 +1,45 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.MountaincyclingAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HillGigas extends CardImpl { + + public HillGigas(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}"); + + this.subtype.add(SubType.GIANT); + this.power = new MageInt(5); + this.toughness = new MageInt(4); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Mountaincycling {2} + this.addAbility(new MountaincyclingAbility(new ManaCostsImpl<>("{2}"))); + } + + private HillGigas(final HillGigas card) { + super(card); + } + + @Override + public HillGigas copy() { + return new HillGigas(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HiredMuscle.java b/Mage.Sets/src/mage/cards/h/HiredMuscle.java index d3cfa699ad1..b9d9947d089 100644 --- a/Mage.Sets/src/mage/cards/h/HiredMuscle.java +++ b/Mage.Sets/src/mage/cards/h/HiredMuscle.java @@ -1,37 +1,34 @@ package mage.cards.h; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.OnEventTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.RemoveCountersSourceCost; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.FlipSourceEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.keyword.FearAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.SuperType; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.CounterType; import mage.filter.StaticFilters; -import mage.game.events.GameEvent; import mage.game.permanent.token.TokenImpl; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** * @author LevelX2 */ public final class HiredMuscle extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.KI, 2); public HiredMuscle(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}"); @@ -44,13 +41,15 @@ public final class HiredMuscle extends CardImpl { this.flipCardName = "Scarmaker"; // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Hired Muscle. - this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility( + new AddCountersSourceEffect(CounterType.KI.createInstance()), + StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, true + )); // At the beginning of the end step, if there are two or more ki counters on Hired Muscle, you may flip it. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new OnEventTriggeredAbility(GameEvent.EventType.END_TURN_STEP_PRE, "beginning of the end step", true, new FlipSourceEffect(new Scarmaker()), true), - new SourceHasCounterCondition(CounterType.KI, 2, Integer.MAX_VALUE), - "At the beginning of the end step, if there are two or more ki counters on {this}, you may flip it.")); + this.addAbility(new BeginningOfEndStepTriggeredAbility( + TargetController.NEXT, new FlipSourceEffect(new Scarmaker()).setText("flip it"), true, condition + )); } private HiredMuscle(final HiredMuscle card) { @@ -81,6 +80,7 @@ class Scarmaker extends TokenImpl { ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } + private Scarmaker(final Scarmaker token) { super(token); } diff --git a/Mage.Sets/src/mage/cards/h/HixusPrisonWarden.java b/Mage.Sets/src/mage/cards/h/HixusPrisonWarden.java index 9118193e599..4fe5e3243fa 100644 --- a/Mage.Sets/src/mage/cards/h/HixusPrisonWarden.java +++ b/Mage.Sets/src/mage/cards/h/HixusPrisonWarden.java @@ -36,7 +36,7 @@ public final class HixusPrisonWarden extends CardImpl { this.addAbility(new ConditionalInterveningIfTriggeredAbility(new DealsDamageToYouAllTriggeredAbility( StaticFilters.FILTER_PERMANENT_CREATURE, new ExileUntilSourceLeavesEffect(), true ).setTriggerPhrase("Whenever a creature deals combat damage to you, if {this} entered the battlefield this turn, "), - SourceEnteredThisTurnCondition.instance, null + SourceEnteredThisTurnCondition.DID, null )); } diff --git a/Mage.Sets/src/mage/cards/h/HoardersOverflow.java b/Mage.Sets/src/mage/cards/h/HoardersOverflow.java index 5e56f53f01d..c4e5b1e300c 100644 --- a/Mage.Sets/src/mage/cards/h/HoardersOverflow.java +++ b/Mage.Sets/src/mage/cards/h/HoardersOverflow.java @@ -37,7 +37,7 @@ public final class HoardersOverflow extends CardImpl { // {1}{R}, Sacrifice Hoarder's Overflow: Discard your hand, then draw cards equal to the number of stash counters on Hoarder's Overflow. Ability ability = new SimpleActivatedAbility(new DiscardHandControllerEffect(), new ManaCostsImpl<>("{1}{R}")); ability.addCost(new SacrificeSourceCost()); - ability.addEffect(new DrawCardSourceControllerEffect(new CountersSourceCount(CounterType.STASH)).concatBy(", then")); + ability.addEffect(new DrawCardSourceControllerEffect(new CountersSourceCount(CounterType.STASH)).setText(", then draw cards equal to the number of stash counters on {this}")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/h/HokoriDustDrinker.java b/Mage.Sets/src/mage/cards/h/HokoriDustDrinker.java index a9fcf0ff3c2..969cb7db4cc 100644 --- a/Mage.Sets/src/mage/cards/h/HokoriDustDrinker.java +++ b/Mage.Sets/src/mage/cards/h/HokoriDustDrinker.java @@ -1,24 +1,22 @@ - package mage.cards.h; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DontUntapInControllersUntapStepAllEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.StaticFilters; -import mage.filter.common.FilterLandPermanent; -import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; -import mage.target.common.TargetLandPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** * @author LevelX2 @@ -26,7 +24,7 @@ import mage.target.common.TargetLandPermanent; public final class HokoriDustDrinker extends CardImpl { public HokoriDustDrinker(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}"); this.supertype.add(SuperType.LEGENDARY); this.subtype.add(SubType.SPIRIT); @@ -71,18 +69,16 @@ class HokoriDustDrinkerUntapEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(game.getActivePlayerId()); - FilterLandPermanent filter = new FilterLandPermanent("land you control"); - filter.add(new ControllerIdPredicate(game.getActivePlayerId())); - Target target = new TargetLandPermanent(filter); - if (player != null && player.chooseTarget(Outcome.Untap, target, source, game)) { - for (UUID landId : target.getTargets()) { - Permanent land = game.getPermanent(landId); - if (land != null) { - land.untap(game); - } - } - return true; + Target target = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND); + if (player == null || !player.chooseTarget(Outcome.Untap, target, source, game)) { + return false; } - return false; + for (UUID landId : target.getTargets()) { + Permanent land = game.getPermanent(landId); + if (land != null) { + land.untap(game); + } + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/h/HoldTheGates.java b/Mage.Sets/src/mage/cards/h/HoldTheGates.java index b4ff9cf5c6e..e89b9f9ef44 100644 --- a/Mage.Sets/src/mage/cards/h/HoldTheGates.java +++ b/Mage.Sets/src/mage/cards/h/HoldTheGates.java @@ -6,13 +6,12 @@ import mage.abilities.dynamicvalue.common.GateYouControlCount; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; -import mage.abilities.hint.common.GateYouControlHint; +import mage.abilities.hint.common.GatesYouControlHint; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; import mage.filter.StaticFilters; import java.util.UUID; @@ -33,7 +32,7 @@ public final class HoldTheGates extends CardImpl { new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURES_CONTROLLED) .setText("and have vigilance") ); - ability.addHint(GateYouControlHint.instance); + ability.addHint(GatesYouControlHint.instance); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/h/Homarid.java b/Mage.Sets/src/mage/cards/h/Homarid.java index 6bac43f3795..5e45a30c479 100644 --- a/Mage.Sets/src/mage/cards/h/Homarid.java +++ b/Mage.Sets/src/mage/cards/h/Homarid.java @@ -1,57 +1,63 @@ - package mage.cards.h; -import java.util.UUID; import mage.MageInt; import mage.abilities.StateTriggeredAbility; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.dynamicvalue.common.CountersSourceCount; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.RemoveAllCountersSourceEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.CounterType; import mage.game.Game; import mage.game.events.GameEvent; +import java.util.UUID; + /** - * * @author LoneFox */ public final class Homarid extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.TIDE, ComparisonType.EQUAL_TO, 1); + private static final Condition condition2 = new SourceHasCounterCondition(CounterType.TIDE, ComparisonType.EQUAL_TO, 3); + public Homarid(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); this.subtype.add(SubType.HOMARID); this.power = new MageInt(2); this.toughness = new MageInt(2); // Homarid enters the battlefield with a tide counter on it. - this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.TIDE.createInstance()), - "with a tide counter on it.")); - // At the beginning of your upkeep, put a tide counter on Homarid. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new AddCountersSourceEffect(CounterType.TIDE.createInstance()) + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.TIDE.createInstance()), "with a tide counter on it." )); + + // At the beginning of your upkeep, put a tide counter on Homarid. + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new AddCountersSourceEffect(CounterType.TIDE.createInstance()) + )); + // As long as there is exactly one tide counter on Homarid, it gets -1/-1. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( - new BoostSourceEffect(-1, -1, Duration.WhileOnBattlefield), new SourceHasCounterCondition(CounterType.TIDE, 1, 1), - "As long as there is exactly one tide counter on {this}, it gets -1/-1."))); + new BoostSourceEffect(-1, -1, Duration.WhileOnBattlefield), + condition, "As long as there is exactly one tide counter on {this}, it gets -1/-1."))); + // As long as there are exactly three tide counters on Homarid, it gets +1/+1. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( - new BoostSourceEffect(1, 1, Duration.WhileOnBattlefield), new SourceHasCounterCondition(CounterType.TIDE, 3, 3), - "As long as there are exactly three tide counters on {this}, it gets +1/+1."))); + new BoostSourceEffect(1, 1, Duration.WhileOnBattlefield), + condition2, "As long as there are exactly three tide counters on {this}, it gets +1/+1." + ))); + // Whenever there are four or more tide counters on Homarid, remove all tide counters from it. - this.addAbility(new HomaridTriggeredAbility(new RemoveAllCountersSourceEffect(CounterType.TIDE))); + this.addAbility(new HomaridTriggeredAbility()); } private Homarid(final Homarid card) { @@ -66,8 +72,8 @@ public final class Homarid extends CardImpl { class HomaridTriggeredAbility extends StateTriggeredAbility { - HomaridTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect); + HomaridTriggeredAbility() { + super(Zone.BATTLEFIELD, new RemoveAllCountersSourceEffect(CounterType.TIDE).setText("remove all tide counters from it")); setTriggerPhrase("Whenever there are four or more tide counters on {this}, "); } diff --git a/Mage.Sets/src/mage/cards/h/HoodedAssassin.java b/Mage.Sets/src/mage/cards/h/HoodedAssassin.java index cb6f2002fa8..8cb7ddc9a65 100644 --- a/Mage.Sets/src/mage/cards/h/HoodedAssassin.java +++ b/Mage.Sets/src/mage/cards/h/HoodedAssassin.java @@ -1,7 +1,6 @@ package mage.cards.h; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.Mode; @@ -13,24 +12,18 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.WasDealtDamageThisTurnPredicate; -import mage.target.common.TargetCreaturePermanent; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class HoodedAssassin extends CardImpl { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature that was dealt damage this turn"); - static { - filter.add(WasDealtDamageThisTurnPredicate.instance); - } - public HoodedAssassin(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.ASSASSIN); this.power = new MageInt(1); @@ -41,9 +34,9 @@ public final class HoodedAssassin extends CardImpl { Ability ability = new EntersBattlefieldTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false); // * Destroy target creature that was dealt damage this turn. Mode mode = new Mode(new DestroyTargetEffect()); - mode.addTarget(new TargetCreaturePermanent(filter)); + mode.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_DAMAGED_THIS_TURN)); ability.addMode(mode); - this.addAbility(ability); + this.addAbility(ability); } private HoodedAssassin(final HoodedAssassin card) { diff --git a/Mage.Sets/src/mage/cards/h/HopeEstheim.java b/Mage.Sets/src/mage/cards/h/HopeEstheim.java new file mode 100644 index 00000000000..0b53b059cb2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HopeEstheim.java @@ -0,0 +1,48 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.dynamicvalue.common.ControllerGainedLifeCount; +import mage.abilities.effects.common.MillCardsEachPlayerEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HopeEstheim extends CardImpl { + + public HopeEstheim(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // At the beginning of your end step, each opponent mills X cards, where X is the amount of life you gained this turn. + this.addAbility(new BeginningOfEndStepTriggeredAbility(new MillCardsEachPlayerEffect( + ControllerGainedLifeCount.instance, TargetController.OPPONENT + ))); + } + + private HopeEstheim(final HopeEstheim card) { + super(card); + } + + @Override + public HopeEstheim copy() { + return new HopeEstheim(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HorizonSeed.java b/Mage.Sets/src/mage/cards/h/HorizonSeed.java index be6ed2dfa74..68803b5f947 100644 --- a/Mage.Sets/src/mage/cards/h/HorizonSeed.java +++ b/Mage.Sets/src/mage/cards/h/HorizonSeed.java @@ -25,7 +25,7 @@ public final class HorizonSeed extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); - Ability ability = new SpellCastControllerTriggeredAbility(new RegenerateTargetEffect(), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false); + Ability ability = new SpellCastControllerTriggeredAbility(new RegenerateTargetEffect(), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, false); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/h/HowlpackPiper.java b/Mage.Sets/src/mage/cards/h/HowlpackPiper.java index 37ad6c14dd9..577c869a305 100644 --- a/Mage.Sets/src/mage/cards/h/HowlpackPiper.java +++ b/Mage.Sets/src/mage/cards/h/HowlpackPiper.java @@ -21,6 +21,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetCardInHand; +import mage.util.CardUtil; import java.util.UUID; @@ -92,7 +93,7 @@ class HowlpackPiperEffect extends OneShotEffect { return false; } player.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); if (permanent == null || sourcePermanent == null) { return true; diff --git a/Mage.Sets/src/mage/cards/h/HowlsquadHeavy.java b/Mage.Sets/src/mage/cards/h/HowlsquadHeavy.java index 9ad6dc1e633..84f03204171 100644 --- a/Mage.Sets/src/mage/cards/h/HowlsquadHeavy.java +++ b/Mage.Sets/src/mage/cards/h/HowlsquadHeavy.java @@ -9,7 +9,7 @@ import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.combat.AttacksIfAbleTargetEffect; -import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.abilities.hint.Hint; import mage.abilities.hint.ValueHint; import mage.abilities.keyword.HasteAbility; @@ -22,7 +22,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.SubType; -import mage.filter.StaticFilters; +import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.game.Game; import mage.game.permanent.token.GoblinToken; @@ -36,7 +36,8 @@ import java.util.UUID; */ public final class HowlsquadHeavy extends CardImpl { - private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(new FilterControlledPermanent(SubType.GOBLIN)); + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.GOBLIN); + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); private static final Hint hint = new ValueHint("Goblins you control", xValue); public HowlsquadHeavy(UUID ownerId, CardSetInfo setInfo) { @@ -51,10 +52,9 @@ public final class HowlsquadHeavy extends CardImpl { this.addAbility(new StartYourEnginesAbility()); // Other Goblins you control have haste. - this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( - HasteAbility.getInstance(), Duration.WhileOnBattlefield, - StaticFilters.FILTER_PERMANENT_CREATURE_GOBLINS, true - ))); + this.addAbility(new SimpleStaticAbility(new GainAbilityAllEffect( + HasteAbility.getInstance(), Duration.WhileOnBattlefield, filter, true + ).setText("other Goblins you control have haste"))); // At the beginning of combat on your turn, create a 1/1 red Goblin creature token. That token attacks this combat if able. this.addAbility(new BeginningOfCombatTriggeredAbility(new HowlsquadHeavyEffect())); diff --git a/Mage.Sets/src/mage/cards/h/HraesvelgrOfTheFirstBrood.java b/Mage.Sets/src/mage/cards/h/HraesvelgrOfTheFirstBrood.java index d9b052fecb4..6f82992ade6 100644 --- a/Mage.Sets/src/mage/cards/h/HraesvelgrOfTheFirstBrood.java +++ b/Mage.Sets/src/mage/cards/h/HraesvelgrOfTheFirstBrood.java @@ -1,26 +1,27 @@ package mage.cards.h; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.combat.CantBeBlockedTargetEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; -import mage.abilities.meta.OrTriggeredAbility; -import mage.constants.SubType; -import mage.constants.SuperType; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.VigilanceAbility; -import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.keyword.WardAbility; +import mage.abilities.meta.OrTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.constants.Zone; import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** * @author balazskristof */ @@ -28,7 +29,7 @@ public final class HraesvelgrOfTheFirstBrood extends CardImpl { public HraesvelgrOfTheFirstBrood(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}"); - + this.supertype.add(SuperType.LEGENDARY); this.subtype.add(SubType.ELDER); this.subtype.add(SubType.DRAGON); @@ -52,7 +53,7 @@ public final class HraesvelgrOfTheFirstBrood extends CardImpl { ); ability.addEffect(new CantBeBlockedTargetEffect().setText("can't be blocked this turn").concatBy("and")); ability.addTarget(new TargetCreaturePermanent()); - this.addAbility(ability); + this.addAbility(ability.withFlavorWord("Shiva's Aid")); } private HraesvelgrOfTheFirstBrood(final HraesvelgrOfTheFirstBrood card) { diff --git a/Mage.Sets/src/mage/cards/h/HuatliTheSunsHeart.java b/Mage.Sets/src/mage/cards/h/HuatliTheSunsHeart.java index 37853526cdd..39419cdb5a4 100644 --- a/Mage.Sets/src/mage/cards/h/HuatliTheSunsHeart.java +++ b/Mage.Sets/src/mage/cards/h/HuatliTheSunsHeart.java @@ -2,7 +2,7 @@ package mage.cards.h; import mage.abilities.LoyaltyAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.common.GreatestToughnessAmongControlledCreaturesValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.ruleModifying.CombatDamageByToughnessControlledEffect; import mage.cards.CardImpl; @@ -30,9 +30,9 @@ public final class HuatliTheSunsHeart extends CardImpl { // -3: You gain life equal to the greatest toughness among creatures you control. this.addAbility(new LoyaltyAbility(new GainLifeEffect( - GreatestToughnessAmongControlledCreaturesValue.ALL, + GreatestAmongPermanentsValue.TOUGHNESS_CONTROLLED_CREATURES, "You gain life equal to the greatest toughness among creatures you control" - ), -3).addHint(GreatestToughnessAmongControlledCreaturesValue.ALL.getHint())); + ), -3).addHint(GreatestAmongPermanentsValue.TOUGHNESS_CONTROLLED_CREATURES.getHint())); } private HuatliTheSunsHeart(final HuatliTheSunsHeart card) { diff --git a/Mage.Sets/src/mage/cards/h/HuatliWarriorPoet.java b/Mage.Sets/src/mage/cards/h/HuatliWarriorPoet.java index 3fc694b3eb2..6694bc4e6fa 100644 --- a/Mage.Sets/src/mage/cards/h/HuatliWarriorPoet.java +++ b/Mage.Sets/src/mage/cards/h/HuatliWarriorPoet.java @@ -3,7 +3,7 @@ package mage.cards.h; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.dynamicvalue.common.GetXValue; -import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; @@ -35,10 +35,12 @@ public final class HuatliWarriorPoet extends CardImpl { this.setStartingLoyalty(3); // +2: You gain life equal to the greatest power among creatures you control. - this.addAbility(new LoyaltyAbility(new GainLifeEffect( - GreatestPowerAmongControlledCreaturesValue.instance, - "You gain life equal to the greatest power among creatures you control" - ), 2).addHint(GreatestPowerAmongControlledCreaturesValue.getHint())); + this.addAbility(new LoyaltyAbility( + new GainLifeEffect( + GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES, + "You gain life equal to the greatest power among creatures you control" + ), 2 + ).addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint())); // 0: Create a 3/3 green Dinosaur creature token with trample. this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new DinosaurToken()), 0)); diff --git a/Mage.Sets/src/mage/cards/h/HydaelynTheMothercrystal.java b/Mage.Sets/src/mage/cards/h/HydaelynTheMothercrystal.java new file mode 100644 index 00000000000..eee3edefdf4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HydaelynTheMothercrystal.java @@ -0,0 +1,89 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Controllable; +import mage.game.Game; +import mage.target.TargetPermanent; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HydaelynTheMothercrystal extends CardImpl { + + public HydaelynTheMothercrystal(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.GOD); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + this.nightCard = true; + this.color.setWhite(true); + + // Indestructible + this.addAbility(IndestructibleAbility.getInstance()); + + // Blessing of Light -- At the beginning of combat on your turn, put a +1/+1 counter on another target creature you control. Until your next turn, it gains indestructible. If that creature is legendary, draw a card. + Ability ability = new BeginningOfCombatTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + ability.addEffect(new GainAbilityTargetEffect( + IndestructibleAbility.getInstance(), Duration.UntilYourNextTurn + ).setText("Until your next turn, it gains indestructible")); + ability.addEffect(new HydaelynTheMothercrystalEffect()); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_ANOTHER_TARGET_CREATURE_YOU_CONTROL)); + this.addAbility(ability.withFlavorWord("Blessing of Light")); + } + + private HydaelynTheMothercrystal(final HydaelynTheMothercrystal card) { + super(card); + } + + @Override + public HydaelynTheMothercrystal copy() { + return new HydaelynTheMothercrystal(this); + } +} + +class HydaelynTheMothercrystalEffect extends OneShotEffect { + + HydaelynTheMothercrystalEffect() { + super(Outcome.Benefit); + staticText = "If that creature is legendary, draw a card"; + } + + private HydaelynTheMothercrystalEffect(final HydaelynTheMothercrystalEffect effect) { + super(effect); + } + + @Override + public HydaelynTheMothercrystalEffect copy() { + return new HydaelynTheMothercrystalEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return Optional.ofNullable(getTargetPointer().getFirst(game, source)) + .map(game::getPermanent) + .filter(permanent -> permanent.isLegendary(game)) + .isPresent() + && Optional + .ofNullable(source) + .map(Controllable::getControllerId) + .map(game::getPlayer) + .filter(player -> player.drawCards(1, source, game) > 0) + .isPresent(); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HyperionBlacksmith.java b/Mage.Sets/src/mage/cards/h/HyperionBlacksmith.java index d0f32ace70d..72d43bac2c7 100644 --- a/Mage.Sets/src/mage/cards/h/HyperionBlacksmith.java +++ b/Mage.Sets/src/mage/cards/h/HyperionBlacksmith.java @@ -1,7 +1,6 @@ package mage.cards.h; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -12,12 +11,12 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.TargetController; -import mage.constants.Zone; import mage.filter.common.FilterArtifactPermanent; -import mage.target.common.TargetArtifactPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author fireshoes */ public final class HyperionBlacksmith extends CardImpl { @@ -29,7 +28,7 @@ public final class HyperionBlacksmith extends CardImpl { } public HyperionBlacksmith(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{R}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.ARTIFICER); this.power = new MageInt(2); @@ -37,7 +36,7 @@ public final class HyperionBlacksmith extends CardImpl { // {tap}: You may tap or untap target artifact an opponent controls. Ability ability = new SimpleActivatedAbility(new MayTapOrUntapTargetEffect(), new TapSourceCost()); - ability.addTarget(new TargetArtifactPermanent(filter)); + ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/i/IceCauldron.java b/Mage.Sets/src/mage/cards/i/IceCauldron.java index b97b3bf8bcb..5e85395a159 100644 --- a/Mage.Sets/src/mage/cards/i/IceCauldron.java +++ b/Mage.Sets/src/mage/cards/i/IceCauldron.java @@ -14,8 +14,8 @@ import mage.abilities.decorator.ConditionalActivatedAbility; import mage.abilities.effects.AsThoughEffect; import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.mana.ManaEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.mana.ManaEffect; import mage.abilities.mana.SimpleManaAbility; import mage.cards.Card; import mage.cards.CardImpl; @@ -38,13 +38,16 @@ import java.util.UUID; */ public final class IceCauldron extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.CHARGE, ComparisonType.EQUAL_TO, 0); + public IceCauldron(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); // {X}, {T}: Put a charge counter on Ice Cauldron and exile a nonland card from your hand. You may cast that card for as long as it remains exiled. Note the type and amount of mana spent to pay this activation cost. Activate this ability only if there are no charge counters on Ice Cauldron. - ConditionalActivatedAbility ability = new ConditionalActivatedAbility( - Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.CHARGE.createInstance(), true), new ManaCostsImpl<>("{X}"), new SourceHasCounterCondition(CounterType.CHARGE, 0, 0)); - ability.addEffect(new IceCauldronExileEffect()); + Ability ability = new ConditionalActivatedAbility( + Zone.BATTLEFIELD, new IceCauldronExileEffect(), new ManaCostsImpl<>("{X}"), condition + ); + ability.addEffect(new AddCountersSourceEffect(CounterType.CHARGE.createInstance(), true)); ability.addEffect(new IceCauldronNoteManaEffect()); ability.addCost(new TapSourceCost()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/i/IceMagic.java b/Mage.Sets/src/mage/cards/i/IceMagic.java index 95850e347cc..ab9d2bee0fa 100644 --- a/Mage.Sets/src/mage/cards/i/IceMagic.java +++ b/Mage.Sets/src/mage/cards/i/IceMagic.java @@ -28,7 +28,7 @@ public final class IceMagic extends CardImpl { // * Blizzard -- {0} -- Return target creature to its owner's hand. this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - this.getSpellAbility().withFirstModeCost(new GenericManaCost(2)); + this.getSpellAbility().withFirstModeCost(new GenericManaCost(0)); this.getSpellAbility().withFirstModeFlavorWord("Blizzard"); // * Blizzara -- {2} -- Target creature's owner puts it on their choice of the top or bottom of their library. diff --git a/Mage.Sets/src/mage/cards/i/IcebreakerKraken.java b/Mage.Sets/src/mage/cards/i/IcebreakerKraken.java index 3440213a5c2..ea59ea744de 100644 --- a/Mage.Sets/src/mage/cards/i/IcebreakerKraken.java +++ b/Mage.Sets/src/mage/cards/i/IcebreakerKraken.java @@ -4,22 +4,18 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.ReturnToHandChosenControlledPermanentCost; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.AffinityEffect; import mage.abilities.effects.common.DontUntapInPlayersNextUntapStepAllEffect; import mage.abilities.effects.common.ReturnToHandSourceEffect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.AffinityAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AffinityType; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.Zone; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledLandPermanent; import mage.filter.common.FilterControlledPermanent; @@ -39,8 +35,6 @@ public final class IcebreakerKraken extends CardImpl { filter.add(SuperType.SNOW.getPredicate()); } - private static final Hint hint = new ValueHint("Snow lands you control", new PermanentsOnBattlefieldCount(filter)); - public IcebreakerKraken(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{10}{U}{U}"); @@ -49,8 +43,8 @@ public final class IcebreakerKraken extends CardImpl { this.power = new MageInt(8); this.toughness = new MageInt(8); - // This spell costs {1} less to cast for each snow land you control. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filter)).addHint(hint)); + // Affinity for snow lands + this.addAbility(new AffinityAbility(AffinityType.SNOW_LANDS)); // When Icebreaker Kraken enters the battlefield, artifacts and creatures target opponent controls don't untap during that player's next untap step. Effect effect = new DontUntapInPlayersNextUntapStepAllEffect(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_CREATURE); diff --git a/Mage.Sets/src/mage/cards/i/Ichorid.java b/Mage.Sets/src/mage/cards/i/Ichorid.java index 73048fd93be..48f3bc90432 100644 --- a/Mage.Sets/src/mage/cards/i/Ichorid.java +++ b/Mage.Sets/src/mage/cards/i/Ichorid.java @@ -29,7 +29,7 @@ import java.util.UUID; */ public final class Ichorid extends CardImpl { - private static final FilterCard filter = new FilterCreatureCard("a black creature card other than {this}"); + private static final FilterCard filter = new FilterCreatureCard("a black creature card other than this card"); static { filter.add(AnotherPredicate.instance); @@ -51,7 +51,7 @@ public final class Ichorid extends CardImpl { // At the beginning of your upkeep, if Ichorid is in your graveyard, you may exile a black creature card other than Ichorid from your graveyard. If you do, return Ichorid to the battlefield. this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.GRAVEYARD, TargetController.YOU, new DoIfCostPaid( - new ReturnSourceFromGraveyardToBattlefieldEffect().setText("return {this} to the battlefield"), + new ReturnSourceFromGraveyardToBattlefieldEffect().setText("return this card to the battlefield"), new ExileFromGraveCost(new TargetCardInYourGraveyard(filter)) ), false).withInterveningIf(SourceInGraveyardCondition.instance)); } diff --git a/Mage.Sets/src/mage/cards/i/IfritWardenOfInferno.java b/Mage.Sets/src/mage/cards/i/IfritWardenOfInferno.java index f1de8a189dd..25f9b3ccc9f 100644 --- a/Mage.Sets/src/mage/cards/i/IfritWardenOfInferno.java +++ b/Mage.Sets/src/mage/cards/i/IfritWardenOfInferno.java @@ -50,10 +50,11 @@ public final class IfritWardenOfInferno extends CardImpl { // II, III -- Brimstone -- Add {R}{R}{R}{R}. If Ifrit has three or more lore counters on it, exile it, then return it to the battlefield sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II, SagaChapter.CHAPTER_III, ability -> { - ability.addEffect(new BasicManaEffect(Mana.RedMana(5))); + ability.addEffect(new BasicManaEffect(Mana.RedMana(4))); ability.addEffect(new ConditionalOneShotEffect( new ExileAndReturnSourceEffect(PutCards.BATTLEFIELD), condition, - "If {this} has three or more lore counters on it, exile it, then return it to the battlefield" + "If {this} has three or more lore counters on it, exile it, " + + "then return it to the battlefield (front face up.)." )); ability.withFlavorWord("Brimstone"); }); diff --git a/Mage.Sets/src/mage/cards/i/IgnisScientia.java b/Mage.Sets/src/mage/cards/i/IgnisScientia.java new file mode 100644 index 00000000000..9395513a14a --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IgnisScientia.java @@ -0,0 +1,90 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.token.TreasureToken; +import mage.players.Player; +import mage.target.common.TargetCardInGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class IgnisScientia extends CardImpl { + + public IgnisScientia(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ADVISOR); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // When Ignis Scientia enters, look at the top six cards of your library. You may put a land card from among them onto the battlefield tapped. Put the rest on the bottom of your library in a random order. + this.addAbility(new EntersBattlefieldTriggeredAbility(new LookLibraryAndPickControllerEffect( + 6, 1, StaticFilters.FILTER_CARD_LAND_A, + PutCards.BATTLEFIELD_TAPPED, PutCards.BOTTOM_RANDOM + ))); + + // I've Come Up with a New Recipe! -- {1}{G}{U}, {T}: Exile target card from a graveyard. If a creature card was exiled this way, create a Food token. + Ability ability = new SimpleActivatedAbility(new IgnisScientiaEffect(), new ManaCostsImpl<>("{1}{G}{U}")); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetCardInGraveyard()); + this.addAbility(ability.withFlavorWord("I've Come Up with a New Recipe!")); + } + + private IgnisScientia(final IgnisScientia card) { + super(card); + } + + @Override + public IgnisScientia copy() { + return new IgnisScientia(this); + } +} + +class IgnisScientiaEffect extends OneShotEffect { + + IgnisScientiaEffect() { + super(Outcome.Benefit); + staticText = "exile target card from a graveyard. If a creature card was exiled this way, create a Food token"; + } + + private IgnisScientiaEffect(final IgnisScientiaEffect effect) { + super(effect); + } + + @Override + public IgnisScientiaEffect copy() { + return new IgnisScientiaEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(getTargetPointer().getFirst(game, source)); + if (player == null || card == null) { + return false; + } + boolean flag = card.isCreature(game); + player.moveCards(card, Zone.EXILED, source, game); + if (flag) { + new TreasureToken().putOntoBattlefield(1, game, source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/i/IlMhegPixie.java b/Mage.Sets/src/mage/cards/i/IlMhegPixie.java new file mode 100644 index 00000000000..a383d5310c3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IlMhegPixie.java @@ -0,0 +1,41 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.keyword.SurveilEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class IlMhegPixie extends CardImpl { + + public IlMhegPixie(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + + this.subtype.add(SubType.FAERIE); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever this creature attacks, surveil 1. + this.addAbility(new AttacksTriggeredAbility(new SurveilEffect(1))); + } + + private IlMhegPixie(final IlMhegPixie card) { + super(card); + } + + @Override + public IlMhegPixie copy() { + return new IlMhegPixie(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/IlhargTheRazeBoar.java b/Mage.Sets/src/mage/cards/i/IlhargTheRazeBoar.java index 7515f26bc15..bdcba733714 100644 --- a/Mage.Sets/src/mage/cards/i/IlhargTheRazeBoar.java +++ b/Mage.Sets/src/mage/cards/i/IlhargTheRazeBoar.java @@ -20,6 +20,7 @@ import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetCardInHand; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -89,7 +90,7 @@ class IlhargTheRazeBoarEffect extends OneShotEffect { return false; } player.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, true, null); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/i/IlluminorSzeras.java b/Mage.Sets/src/mage/cards/i/IlluminorSzeras.java index 03a334df08f..421b82c30a4 100644 --- a/Mage.Sets/src/mage/cards/i/IlluminorSzeras.java +++ b/Mage.Sets/src/mage/cards/i/IlluminorSzeras.java @@ -6,7 +6,7 @@ import mage.abilities.Ability; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.common.HighestCMCOfPermanentValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.dynamicvalue.common.SacrificeCostManaValue; import mage.abilities.mana.DynamicManaAbility; import mage.cards.CardImpl; @@ -24,9 +24,6 @@ import java.util.UUID; public final class IlluminorSzeras extends CardImpl { private static final DynamicValue xValue = SacrificeCostManaValue.CREATURE; - private static final DynamicValue netValue = new HighestCMCOfPermanentValue( - StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE, true - ); public IlluminorSzeras(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}{B}"); @@ -39,7 +36,8 @@ public final class IlluminorSzeras extends CardImpl { // Secrets of the Soul -- {T}, Sacrifice another creature: Add an amount of {B} equal to the sacrificed creature's mana value. Ability ability = new DynamicManaAbility( Mana.BlackMana(1), xValue, new TapSourceCost(), "add an amount of {B} " + - "equal to the sacrificed creature's mana value", false, netValue + "equal to the sacrificed creature's mana value", false, + GreatestAmongPermanentsValue.MANAVALUE_OTHER_CONTROLLED_CREATURES ); ability.addCost(new SacrificeTargetCost(StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE)); this.addAbility(ability.withFlavorWord("Secrets of the Soul")); diff --git a/Mage.Sets/src/mage/cards/i/ImpetuousProtege.java b/Mage.Sets/src/mage/cards/i/ImpetuousProtege.java index 09783527e9f..65c3adbec35 100644 --- a/Mage.Sets/src/mage/cards/i/ImpetuousProtege.java +++ b/Mage.Sets/src/mage/cards/i/ImpetuousProtege.java @@ -1,31 +1,40 @@ package mage.cards.i; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; +import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.Hint; import mage.abilities.keyword.PartnerWithAbility; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.TargetController; +import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.TappedPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class ImpetuousProtege extends CardImpl { + private static final FilterPermanent filter = new FilterCreaturePermanent("tapped creatures your opponents control"); + + static { + filter.add(TappedPredicate.TAPPED); + filter.add(TargetController.OPPONENT.getControllerPredicate()); + } + + private static final GreatestAmongPermanentsValue xValue = new GreatestAmongPermanentsValue(GreatestAmongPermanentsValue.Quality.Power, filter); + private static final Hint hint = xValue.getHint(); + public ImpetuousProtege(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); @@ -38,7 +47,9 @@ public final class ImpetuousProtege extends CardImpl { this.addAbility(new PartnerWithAbility("Proud Mentor")); // Whenever Impetuous Protege attacks, it gets +X/+0 until end of turn, where X is the greatest power among tapped creatures your opponents control - this.addAbility(new AttacksTriggeredAbility(new ImpetuousProtegeEffect(), false)); + this.addAbility(new AttacksTriggeredAbility( + new BoostSourceEffect(xValue, StaticValue.get(0), Duration.EndOfTurn, "it") + ).addHint(hint)); } private ImpetuousProtege(final ImpetuousProtege card) { @@ -49,38 +60,4 @@ public final class ImpetuousProtege extends CardImpl { public ImpetuousProtege copy() { return new ImpetuousProtege(this); } -} - -class ImpetuousProtegeEffect extends OneShotEffect { - - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); - - static { - filter.add(TappedPredicate.TAPPED); - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - - ImpetuousProtegeEffect() { - super(Outcome.Benefit); - this.staticText = "it gets +X/+0 until end of turn, where X is the greatest power among tapped creatures your opponents control"; - } - - private ImpetuousProtegeEffect(final ImpetuousProtegeEffect effect) { - super(effect); - } - - @Override - public ImpetuousProtegeEffect copy() { - return new ImpetuousProtegeEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - int maxPower = 0; - for (Permanent creature : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source, game)) { - maxPower = Math.max(maxPower, creature.getPower().getValue()); - } - game.addEffect(new BoostSourceEffect(maxPower, 0, Duration.EndOfTurn), source); - return true; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/i/ImpromptuRaid.java b/Mage.Sets/src/mage/cards/i/ImpromptuRaid.java index 36315657699..269c70f4304 100644 --- a/Mage.Sets/src/mage/cards/i/ImpromptuRaid.java +++ b/Mage.Sets/src/mage/cards/i/ImpromptuRaid.java @@ -28,6 +28,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @@ -90,7 +91,7 @@ class ImpromptuRaidEffect extends OneShotEffect { return true; } if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); effect.setTargetPointer(new FixedTarget(permanent, game)); diff --git a/Mage.Sets/src/mage/cards/i/InSearchOfGreatness.java b/Mage.Sets/src/mage/cards/i/InSearchOfGreatness.java index 13c14cc92ec..ead1c1d471c 100644 --- a/Mage.Sets/src/mage/cards/i/InSearchOfGreatness.java +++ b/Mage.Sets/src/mage/cards/i/InSearchOfGreatness.java @@ -1,10 +1,9 @@ package mage.cards.i; -import mage.MageObject; -import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.OneShotEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.cards.CardsImpl; @@ -12,7 +11,6 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.constants.Outcome; import mage.filter.FilterCard; -import mage.filter.StaticFilters; import mage.filter.common.FilterPermanentCard; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.filter.predicate.mageobject.PermanentPredicate; @@ -20,7 +18,6 @@ import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; -import java.util.Objects; import java.util.UUID; /** @@ -31,12 +28,10 @@ public final class InSearchOfGreatness extends CardImpl { public InSearchOfGreatness(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{G}{G}"); - // At the beginning of your upkeep, you may cast a permanent spell from your hand with converted mana cost - // equal to 1 plus the highest converted mana cost among other permanents you control - // without paying its mana cost. If you don't, scry 1. + // At the beginning of your upkeep, you may cast a permanent spell from your hand with converted mana cost equal to 1 plus the greatest mana value among other permanents you control without paying its mana cost. If you don't, scry 1. this.addAbility(new BeginningOfUpkeepTriggeredAbility( new InSearchOfGreatnessEffect() - )); + ).addHint(GreatestAmongPermanentsValue.MANAVALUE_OTHER_CONTROLLED_PERMANENTS.getHint())); } private InSearchOfGreatness(final InSearchOfGreatness card) { @@ -73,16 +68,7 @@ class InSearchOfGreatnessEffect extends OneShotEffect { if (controller == null) { return false; } - MageObjectReference sourceRef = new MageObjectReference(source); - int manaValue = game - .getBattlefield() - .getActivePermanents(StaticFilters.FILTER_CONTROLLED_PERMANENT, controller.getId(), game) - .stream() - .filter(Objects::nonNull) - .filter(permanent -> !sourceRef.refersTo(permanent, game)) - .mapToInt(MageObject::getManaValue) - .max() - .orElse(0); + int manaValue = GreatestAmongPermanentsValue.MANAVALUE_OTHER_CONTROLLED_PERMANENTS.calculate(game, source, this); FilterCard filter = new FilterPermanentCard(); filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, manaValue + 1)); filter.add(PermanentPredicate.instance); diff --git a/Mage.Sets/src/mage/cards/i/InTheDarknessBindThem.java b/Mage.Sets/src/mage/cards/i/InTheDarknessBindThem.java index bc164b51053..5a7aa39133c 100644 --- a/Mage.Sets/src/mage/cards/i/InTheDarknessBindThem.java +++ b/Mage.Sets/src/mage/cards/i/InTheDarknessBindThem.java @@ -16,7 +16,7 @@ import mage.constants.SagaChapter; import mage.constants.SubType; import mage.game.permanent.token.WraithToken; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; import java.util.UUID; @@ -58,7 +58,7 @@ public final class InTheDarknessBindThem extends CardImpl { ability.getEffects().setTargetPointer(new EachTargetPointer()); ability.addTarget(new TargetCreaturePermanent(0,1)); - ability.setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); } ); diff --git a/Mage.Sets/src/mage/cards/i/InfectiousRage.java b/Mage.Sets/src/mage/cards/i/InfectiousRage.java index 7d07b642f06..ecf4aba4374 100644 --- a/Mage.Sets/src/mage/cards/i/InfectiousRage.java +++ b/Mage.Sets/src/mage/cards/i/InfectiousRage.java @@ -63,7 +63,7 @@ class InfectiousRageReattachEffect extends OneShotEffect { InfectiousRageReattachEffect() { super(Outcome.PutCardInPlay); - this.staticText = "choose a creature at random {this} can enchant. Return {this} to the battlefield attached to that creature."; + this.staticText = "choose a creature at random {this} can enchant. Return this card to the battlefield attached to that creature."; } private InfectiousRageReattachEffect(final InfectiousRageReattachEffect effect) { diff --git a/Mage.Sets/src/mage/cards/i/InfernalDenizen.java b/Mage.Sets/src/mage/cards/i/InfernalDenizen.java index d9394c77581..1f2ce602ea9 100644 --- a/Mage.Sets/src/mage/cards/i/InfernalDenizen.java +++ b/Mage.Sets/src/mage/cards/i/InfernalDenizen.java @@ -116,7 +116,7 @@ class InfernalDenizenEffect extends OneShotEffect { filter2.add(new ControllerIdPredicate(player.getId())); TargetCreaturePermanent targetCreature = new TargetCreaturePermanent(1, 1, filter2, true); targetCreature.setTargetController(opponent.getId()); - if (targetCreature.canChoose(id, source, game) + if (targetCreature.canChoose(source.getControllerId(), source, game) && opponent.chooseUse(Outcome.GainControl, "Gain control of a creature?", source, game) && opponent.chooseTarget(Outcome.GainControl, targetCreature, source, game)) { ConditionalContinuousEffect giveEffect = new ConditionalContinuousEffect( diff --git a/Mage.Sets/src/mage/cards/i/InfernalKirin.java b/Mage.Sets/src/mage/cards/i/InfernalKirin.java index cd98eedc89a..32bf5bf9e7a 100644 --- a/Mage.Sets/src/mage/cards/i/InfernalKirin.java +++ b/Mage.Sets/src/mage/cards/i/InfernalKirin.java @@ -37,7 +37,7 @@ public final class InfernalKirin extends CardImpl { // Whenever you cast a Spirit or Arcane spell, target player reveals their hand and discards all cards with that spell's converted mana cost. Ability ability = new SpellCastControllerTriggeredAbility( - new InfernalKirinEffect(), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, + new InfernalKirinEffect(), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, false, SetTargetPointer.SPELL ); ability.addTarget(new TargetPlayer()); diff --git a/Mage.Sets/src/mage/cards/i/InfernalVessel.java b/Mage.Sets/src/mage/cards/i/InfernalVessel.java index 41408b1b061..5050807b7ba 100644 --- a/Mage.Sets/src/mage/cards/i/InfernalVessel.java +++ b/Mage.Sets/src/mage/cards/i/InfernalVessel.java @@ -17,6 +17,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -94,7 +95,7 @@ class InfernalVesselReturnEffect extends OneShotEffect { effect.setTargetPointer(new FixedTarget(card, game)); effect.apply(game, source); game.processAction(); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { game.addEffect(new AddCardSubTypeTargetEffect( SubType.DEMON, Duration.Custom diff --git a/Mage.Sets/src/mage/cards/i/InitiateOfBlood.java b/Mage.Sets/src/mage/cards/i/InitiateOfBlood.java index b7f63cb6eee..6ccbc724a7d 100644 --- a/Mage.Sets/src/mage/cards/i/InitiateOfBlood.java +++ b/Mage.Sets/src/mage/cards/i/InitiateOfBlood.java @@ -1,10 +1,9 @@ package mage.cards.i; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.delayed.WhenTargetDiesDelayedTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.delayed.WhenTargetDiesDelayedTriggeredAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.DamageTargetEffect; @@ -14,24 +13,19 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.WasDealtDamageThisTurnPredicate; +import mage.filter.StaticFilters; import mage.game.permanent.token.TokenImpl; -import mage.target.common.TargetCreaturePermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** * @author awjackson */ public final class InitiateOfBlood extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature that was dealt damage this turn"); - - static { - filter.add(WasDealtDamageThisTurnPredicate.instance); - } - public InitiateOfBlood(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); this.subtype.add(SubType.OGRE, SubType.SHAMAN); this.power = new MageInt(2); @@ -45,7 +39,7 @@ public final class InitiateOfBlood extends CardImpl { ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new WhenTargetDiesDelayedTriggeredAbility( new FlipSourceEffect(new GokaTheUnjust()).setText("flip {this}") ))); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_DAMAGED_THIS_TURN)); this.addAbility(ability); } @@ -61,12 +55,6 @@ public final class InitiateOfBlood extends CardImpl { class GokaTheUnjust extends TokenImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature that was dealt damage this turn"); - - static { - filter.add(WasDealtDamageThisTurnPredicate.instance); - } - GokaTheUnjust() { super("Goka the Unjust", ""); this.supertype.add(SuperType.LEGENDARY); @@ -78,9 +66,10 @@ class GokaTheUnjust extends TokenImpl { // {T}: Goka the Unjust deals 4 damage to target creature that was dealt damage this turn. Ability ability = new SimpleActivatedAbility(new DamageTargetEffect(4), new TapSourceCost()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_DAMAGED_THIS_TURN)); this.addAbility(ability); } + private GokaTheUnjust(final GokaTheUnjust token) { super(token); } diff --git a/Mage.Sets/src/mage/cards/i/InkEyesServantOfOni.java b/Mage.Sets/src/mage/cards/i/InkEyesServantOfOni.java index d9ae50f79ff..d2c6655563d 100644 --- a/Mage.Sets/src/mage/cards/i/InkEyesServantOfOni.java +++ b/Mage.Sets/src/mage/cards/i/InkEyesServantOfOni.java @@ -13,10 +13,9 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; import mage.target.common.TargetCardInGraveyard; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -43,7 +42,7 @@ public final class InkEyesServantOfOni extends CardImpl { // Whenever Ink-Eyes, Servant of Oni deals combat damage to a player, you may put target creature card from that player's graveyard onto the battlefield under your control. Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(), true, true); ability.addTarget(new TargetCardInGraveyard(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster(true)); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster(true)); this.addAbility(ability); // {1}{B}: Regenerate Ink-Eyes. diff --git a/Mage.Sets/src/mage/cards/i/InnocenceKami.java b/Mage.Sets/src/mage/cards/i/InnocenceKami.java index 9c6479f242c..cc2e3ae2e6e 100644 --- a/Mage.Sets/src/mage/cards/i/InnocenceKami.java +++ b/Mage.Sets/src/mage/cards/i/InnocenceKami.java @@ -16,7 +16,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.ColoredManaSymbol; -import mage.constants.Zone; import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanent; @@ -35,7 +34,7 @@ public final class InnocenceKami extends CardImpl { ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); - this.addAbility(new SpellCastControllerTriggeredAbility(new UntapSourceEffect(), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new UntapSourceEffect(), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, false)); } private InnocenceKami(final InnocenceKami card) { diff --git a/Mage.Sets/src/mage/cards/i/InsomniaCrownCity.java b/Mage.Sets/src/mage/cards/i/InsomniaCrownCity.java new file mode 100644 index 00000000000..07dc9a11ce2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InsomniaCrownCity.java @@ -0,0 +1,39 @@ +package mage.cards.i; + +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class InsomniaCrownCity extends CardImpl { + + public InsomniaCrownCity(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.subtype.add(SubType.TOWN); + + // This land enters tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {W} or {B}. + this.addAbility(new WhiteManaAbility()); + this.addAbility(new BlackManaAbility()); + } + + private InsomniaCrownCity(final InsomniaCrownCity card) { + super(card); + } + + @Override + public InsomniaCrownCity copy() { + return new InsomniaCrownCity(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/Insubordination.java b/Mage.Sets/src/mage/cards/i/Insubordination.java index 7e077dd2270..cda3cdac181 100644 --- a/Mage.Sets/src/mage/cards/i/Insubordination.java +++ b/Mage.Sets/src/mage/cards/i/Insubordination.java @@ -1,25 +1,23 @@ package mage.cards.i; -import java.util.UUID; -import mage.constants.SubType; -import mage.target.common.TargetCreaturePermanent; -import mage.abilities.Ability; -import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.condition.common.DidNotAttackThisTurnEnchantedCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.DamageAttachedControllerEffect; -import mage.constants.Outcome; -import mage.target.TargetPermanent; import mage.abilities.keyword.EnchantAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.TargetController; -import mage.watchers.common.AttackedThisTurnWatcher; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class Insubordination extends CardImpl { @@ -33,16 +31,16 @@ public final class Insubordination extends CardImpl { TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); - Ability ability = new EnchantAbility(auraTarget); - this.addAbility(ability); + this.addAbility(new EnchantAbility(auraTarget)); // At the beginning of the end step of enchanted creature's controller, Insubordination deals 2 damage to that player unless that creature attacked this turn. - this.addAbility(new ConditionalTriggeredAbility( - new AtTheBeginOfNextEndStepDelayedTriggeredAbility( - new DamageAttachedControllerEffect(2), - TargetController.CONTROLLER_ATTACHED_TO), - DidNotAttackThisTurnEnchantedCondition.instance, - "At the beginning of the end step of enchanted creature's controller, {this} deals 2 damage to that player unless that creature attacked this turn.")); + this.addAbility(new BeginningOfEndStepTriggeredAbility( + TargetController.CONTROLLER_ATTACHED_TO, + new ConditionalOneShotEffect( + new DamageAttachedControllerEffect(2), DidNotAttackThisTurnEnchantedCondition.instance, + "{this} deals 2 damage to that player unless that creature attacked this turn" + ), false + ).setTriggerPhrase("At the beginning of the end step of enchanted creature's controller, ")); } private Insubordination(final Insubordination card) { diff --git a/Mage.Sets/src/mage/cards/i/InterceptorShadowsHound.java b/Mage.Sets/src/mage/cards/i/InterceptorShadowsHound.java index d585ea6645d..cf9042d15d1 100644 --- a/Mage.Sets/src/mage/cards/i/InterceptorShadowsHound.java +++ b/Mage.Sets/src/mage/cards/i/InterceptorShadowsHound.java @@ -36,7 +36,7 @@ public final class InterceptorShadowsHound extends CardImpl { // Assassins you control have menace. this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( - new MenaceAbility(false), Duration.WhileControlled, filter + new MenaceAbility(false), Duration.WhileOnBattlefield, filter ))); // Whenever you attack with one or more legendary creatures, you may pay {2}{B}. If you do, return this card from your graveyard to the battlefield tapped and attacking. diff --git a/Mage.Sets/src/mage/cards/i/InterventionPact.java b/Mage.Sets/src/mage/cards/i/InterventionPact.java index e51c05f3e85..433aca7b3b3 100644 --- a/Mage.Sets/src/mage/cards/i/InterventionPact.java +++ b/Mage.Sets/src/mage/cards/i/InterventionPact.java @@ -1,28 +1,17 @@ package mage.cards.i; -import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.common.delayed.PactDelayedTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.PreventionEffectData; -import mage.abilities.effects.PreventionEffectImpl; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.players.Player; -import mage.target.Target; -import mage.target.TargetSource; -import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; /** - * * @author Plopman */ public final class InterventionPact extends CardImpl { @@ -33,7 +22,12 @@ public final class InterventionPact extends CardImpl { this.color.setWhite(true); // The next time a source of your choice would deal damage to you this turn, prevent that damage. You gain life equal to the damage prevented this way. - this.getSpellAbility().addEffect(new InterventionPactEffect()); + this.getSpellAbility().addEffect( + new PreventNextDamageFromChosenSourceEffect( + Duration.EndOfTurn, true, + PreventNextDamageFromChosenSourceEffect.ON_PREVENT_YOU_GAIN_THAT_MUCH_LIFE + ) + ); // At the beginning of your next upkeep, pay {1}{W}{W}. If you don't, you lose the game. this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new PactDelayedTriggeredAbility(new ManaCostsImpl<>("{1}{W}{W}")), false)); } @@ -46,78 +40,4 @@ public final class InterventionPact extends CardImpl { public InterventionPact copy() { return new InterventionPact(this); } -} - -class InterventionPactEffect extends OneShotEffect { - - InterventionPactEffect() { - super(Outcome.PreventDamage); - this.staticText = "The next time a source of your choice would deal damage to you this turn, prevent that damage. You gain life equal to the damage prevented this way"; - } - - private InterventionPactEffect(final InterventionPactEffect effect) { - super(effect); - } - - @Override - public InterventionPactEffect copy() { - return new InterventionPactEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Target target = new TargetSource(); - target.setRequired(true); - target.withNotTarget(true); - if (controller.chooseTarget(outcome, target, source, game)) { - ContinuousEffect continuousEffect = new InterventionPactPreventDamageEffect(); - continuousEffect.setTargetPointer(new FixedTarget(target.getFirstTarget(), game)); - game.addEffect(continuousEffect, source); - } - return true; - } - return false; - } -} - -class InterventionPactPreventDamageEffect extends PreventionEffectImpl { - - InterventionPactPreventDamageEffect() { - super(Duration.EndOfTurn, Integer.MAX_VALUE, false, false); - staticText = "The next time a source of your choice would deal damage to you this turn, prevent that damage. You gain life equal to the damage prevented this way"; - } - - private InterventionPactPreventDamageEffect(final InterventionPactPreventDamageEffect effect) { - super(effect); - } - - @Override - public InterventionPactPreventDamageEffect copy() { - return new InterventionPactPreventDamageEffect(this); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - PreventionEffectData preventEffectData = preventDamageAction(event, source, game); - if (preventEffectData.getPreventedDamage() > 0) { - used = true; - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - player.gainLife(preventEffectData.getPreventedDamage(), game, source); - } - } - return false; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (!this.used && super.applies(event, source, game) && event.getTargetId().equals(source.getControllerId())) { - if (event.getSourceId().equals(getTargetPointer().getFirst(game, source))) { - return true; - } - } - return false; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/i/IntoTheFloodMaw.java b/Mage.Sets/src/mage/cards/i/IntoTheFloodMaw.java index c0a5d0adb09..af078458bd5 100644 --- a/Mage.Sets/src/mage/cards/i/IntoTheFloodMaw.java +++ b/Mage.Sets/src/mage/cards/i/IntoTheFloodMaw.java @@ -39,7 +39,7 @@ public final class IntoTheFloodMaw extends CardImpl { // Return target creature an opponent controls to its owner's hand. If the gift was promise, instead return target nonland permanent an opponent controls to its owner's hand. this.getSpellAbility().addEffect(new ReturnToHandTargetEffect() - .setText("return target creature an opponent controls to its owner's hand. If the gift was promise, " + + .setText("return target creature an opponent controls to its owner's hand. If the gift was promised, " + "instead return target nonland permanent an opponent controls to its owner's hand")); this.getSpellAbility().addTarget(new TargetPermanent(playableFilter)); this.getSpellAbility().setTargetAdjuster(new ConditionalTargetAdjuster(GiftWasPromisedCondition.TRUE, diff --git a/Mage.Sets/src/mage/cards/i/InventiveWingsmith.java b/Mage.Sets/src/mage/cards/i/InventiveWingsmith.java index a1ede65f256..41e5960d038 100644 --- a/Mage.Sets/src/mage/cards/i/InventiveWingsmith.java +++ b/Mage.Sets/src/mage/cards/i/InventiveWingsmith.java @@ -1,15 +1,16 @@ package mage.cards.i; import mage.MageInt; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.abilities.condition.CompoundCondition; import mage.abilities.condition.Condition; import mage.abilities.condition.common.HaventCastSpellFromHandThisTurnCondition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.SubType; import mage.constants.TargetController; import mage.counters.CounterType; @@ -24,7 +25,7 @@ public final class InventiveWingsmith extends CardImpl { private static final Condition condition = new CompoundCondition( "if you haven't cast a spell from your hand this turn and {this} doesn't have a flying counter on it", HaventCastSpellFromHandThisTurnCondition.instance, - new SourceHasCounterCondition(CounterType.FLYING, 0, 0) + new SourceHasCounterCondition(CounterType.FLYING, ComparisonType.EQUAL_TO, 0) ); public InventiveWingsmith(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/i/Invulnerability.java b/Mage.Sets/src/mage/cards/i/Invulnerability.java index e1672b4dbb7..b3edd0d24e5 100644 --- a/Mage.Sets/src/mage/cards/i/Invulnerability.java +++ b/Mage.Sets/src/mage/cards/i/Invulnerability.java @@ -1,28 +1,28 @@ package mage.cards.i; -import java.util.UUID; -import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.abilities.keyword.BuybackAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; +import java.util.UUID; + /** - * * @author anonymous */ public final class Invulnerability extends CardImpl { public Invulnerability(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); // Buyback {3} this.addAbility(new BuybackAbility("{3}")); - + // The next time a source of your choice would deal damage to you this turn, prevent that damage. - this.getSpellAbility().addEffect(new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn)); + this.getSpellAbility().addEffect(new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, true)); } private Invulnerability(final Invulnerability card) { diff --git a/Mage.Sets/src/mage/cards/i/IridescentAngel.java b/Mage.Sets/src/mage/cards/i/IridescentAngel.java index 51cf0f3a4ee..e566264d531 100644 --- a/Mage.Sets/src/mage/cards/i/IridescentAngel.java +++ b/Mage.Sets/src/mage/cards/i/IridescentAngel.java @@ -20,7 +20,7 @@ import mage.filter.predicate.mageobject.ColorPredicate; */ public final class IridescentAngel extends CardImpl { - private static final FilterCard filter = new FilterCard("all colors"); + private static final FilterCard filter = new FilterCard("each color"); static { filter.add(Predicates.or( diff --git a/Mage.Sets/src/mage/cards/i/IronApprentice.java b/Mage.Sets/src/mage/cards/i/IronApprentice.java index 605488fb13e..0671b3432d1 100644 --- a/Mage.Sets/src/mage/cards/i/IronApprentice.java +++ b/Mage.Sets/src/mage/cards/i/IronApprentice.java @@ -5,7 +5,6 @@ import mage.abilities.Ability; import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.condition.common.SourceHasCountersCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; @@ -38,10 +37,9 @@ public final class IronApprentice extends CardImpl { ), "with a +1/+1 counter on it")); // When Iron Apprentice dies, if it had counters on it, put those counters on target creature you control. - Ability ability = new ConditionalTriggeredAbility( - new DiesSourceTriggeredAbility(new IronApprenticeEffect()), SourceHasCountersCondition.instance, - "When {this} dies, if it had counters on it, put those counters on target creature you control." - ); + Ability ability = new DiesSourceTriggeredAbility(new IronApprenticeEffect()) + .withTriggerCondition(SourceHasCountersCondition.instance) + .setTriggerPhrase("When {this} dies, if it had counters on it, "); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/i/IronGiant.java b/Mage.Sets/src/mage/cards/i/IronGiant.java new file mode 100644 index 00000000000..32f64b08617 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IronGiant.java @@ -0,0 +1,44 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.keyword.ReachAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class IronGiant extends CardImpl { + + public IronGiant(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{7}"); + + this.subtype.add(SubType.DEMON); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + } + + private IronGiant(final IronGiant card) { + super(card); + } + + @Override + public IronGiant copy() { + return new IronGiant(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/IrreverentGremlin.java b/Mage.Sets/src/mage/cards/i/IrreverentGremlin.java index 8c96753fd03..00b7a9d84db 100644 --- a/Mage.Sets/src/mage/cards/i/IrreverentGremlin.java +++ b/Mage.Sets/src/mage/cards/i/IrreverentGremlin.java @@ -1,7 +1,5 @@ package mage.cards.i; -import java.util.UUID; - import mage.MageInt; import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; import mage.abilities.costs.common.DiscardCardCost; @@ -18,6 +16,8 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.mageobject.PowerPredicate; +import java.util.UUID; + /** * @author paasar */ @@ -41,15 +41,9 @@ public final class IrreverentGremlin extends CardImpl { this.addAbility(new MenaceAbility()); // Whenever another creature you control with power 2 or less enters, you may discard a card. If you do, draw a card. Do this only once each turn. - this.addAbility( - new EntersBattlefieldAllTriggeredAbility( - new DoIfCostPaid( - new DrawCardSourceControllerEffect(1), - new DiscardCardCost(), - null, - false), // since triggered ability is optional (do only once), DoIfCostPaid must not be - filter) - .setDoOnlyOnceEachTurn(true)); + this.addAbility(new EntersBattlefieldAllTriggeredAbility( + new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new DiscardCardCost()), filter + ).setDoOnlyOnceEachTurn(true)); } private IrreverentGremlin(final IrreverentGremlin card) { diff --git a/Mage.Sets/src/mage/cards/i/IshiIshiAkkiCrackshot.java b/Mage.Sets/src/mage/cards/i/IshiIshiAkkiCrackshot.java index 127a351a1a7..6cbc709eefb 100644 --- a/Mage.Sets/src/mage/cards/i/IshiIshiAkkiCrackshot.java +++ b/Mage.Sets/src/mage/cards/i/IshiIshiAkkiCrackshot.java @@ -1,14 +1,15 @@ package mage.cards.i; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SpellCastOpponentTriggeredAbility; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterSpiritOrArcaneCard; +import mage.filter.StaticFilters; + +import java.util.UUID; /** * @@ -16,12 +17,6 @@ import mage.filter.common.FilterSpiritOrArcaneCard; */ public final class IshiIshiAkkiCrackshot extends CardImpl { - private static final FilterSpiritOrArcaneCard filter = new FilterSpiritOrArcaneCard(); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public IshiIshiAkkiCrackshot(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}"); this.supertype.add(SuperType.LEGENDARY); @@ -32,7 +27,9 @@ public final class IshiIshiAkkiCrackshot extends CardImpl { this.toughness = new MageInt(1); // Whenever an opponent casts a Spirit or Arcane spell, Ishi-Ishi, Akki Crackshot deals 2 damage to that player. - this.addAbility(new SpellCastOpponentTriggeredAbility(Zone.BATTLEFIELD, new DamageTargetEffect(2, true, "that player"), filter, false, SetTargetPointer.PLAYER)); + this.addAbility(new SpellCastOpponentTriggeredAbility(Zone.BATTLEFIELD, + new DamageTargetEffect(2, true, "that player"), + StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, false, SetTargetPointer.PLAYER)); } private IshiIshiAkkiCrackshot(final IshiIshiAkkiCrackshot card) { diff --git a/Mage.Sets/src/mage/cards/i/ItDoesntAddUp.java b/Mage.Sets/src/mage/cards/i/ItDoesntAddUp.java index 5ed94ee01fc..582ddae3de7 100644 --- a/Mage.Sets/src/mage/cards/i/ItDoesntAddUp.java +++ b/Mage.Sets/src/mage/cards/i/ItDoesntAddUp.java @@ -12,6 +12,7 @@ import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; +import mage.util.CardUtil; import java.util.Optional; import java.util.UUID; @@ -63,7 +64,7 @@ class ItDoesntAddUpEffect extends OneShotEffect { return false; } player.moveCards(card, Zone.BATTLEFIELD, source, game); - Optional.ofNullable(game.getPermanent(card.getId())) + Optional.ofNullable(CardUtil.getPermanentFromCardPutToBattlefield(card, game)) .ifPresent(permanent -> permanent.setSuspected(true, game, source)); return true; } diff --git a/Mage.Sets/src/mage/cards/j/JadeIdol.java b/Mage.Sets/src/mage/cards/j/JadeIdol.java index c4d4978df9f..d978bf4e6de 100644 --- a/Mage.Sets/src/mage/cards/j/JadeIdol.java +++ b/Mage.Sets/src/mage/cards/j/JadeIdol.java @@ -21,7 +21,7 @@ public final class JadeIdol extends CardImpl { public JadeIdol(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); - this.addAbility(new SpellCastControllerTriggeredAbility(new BecomesCreatureSourceEffect(new JadeIdolToken(), CardType.ARTIFACT, Duration.EndOfTurn), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new BecomesCreatureSourceEffect(new JadeIdolToken(), CardType.ARTIFACT, Duration.EndOfTurn), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, false)); } private JadeIdol(final JadeIdol card) { diff --git a/Mage.Sets/src/mage/cards/j/JadeMonolith.java b/Mage.Sets/src/mage/cards/j/JadeMonolith.java index 9812521fe1c..9d9bb4581e5 100644 --- a/Mage.Sets/src/mage/cards/j/JadeMonolith.java +++ b/Mage.Sets/src/mage/cards/j/JadeMonolith.java @@ -1,7 +1,6 @@ package mage.cards.j; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -12,8 +11,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.game.Game; import mage.game.events.DamageEvent; import mage.game.events.GameEvent; @@ -23,14 +21,15 @@ import mage.players.Player; import mage.target.TargetSource; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author Quercitron */ public final class JadeMonolith extends CardImpl { public JadeMonolith(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); // {1}: The next time a source of your choice would deal damage to target creature this turn, that source deals that damage to you instead. Ability ability = new SimpleActivatedAbility(new JadeMonolithRedirectionEffect(), new GenericManaCost(1)); @@ -51,13 +50,13 @@ public final class JadeMonolith extends CardImpl { class JadeMonolithRedirectionEffect extends ReplacementEffectImpl { private final TargetSource targetSource; - + public JadeMonolithRedirectionEffect() { super(Duration.OneUse, Outcome.RedirectDamage); this.staticText = "The next time a source of your choice would deal damage to target creature this turn, that source deals that damage to you instead"; - this.targetSource = new TargetSource(new FilterObject("source of your choice")); + this.targetSource = new TargetSource(new FilterSource("source of your choice")); } - + private JadeMonolithRedirectionEffect(final JadeMonolithRedirectionEffect effect) { super(effect); this.targetSource = effect.targetSource.copy(); @@ -96,11 +95,11 @@ class JadeMonolithRedirectionEffect extends ReplacementEffectImpl { public boolean checksEventType(GameEvent event, Game game) { return event.getType() == EventType.DAMAGE_PERMANENT; } - + @Override public boolean applies(GameEvent event, Ability source, Game game) { return event.getSourceId().equals(targetSource.getFirstTarget()) && event.getTargetId().equals(source.getFirstTarget()); } - + } diff --git a/Mage.Sets/src/mage/cards/j/JamieMcCrimmon.java b/Mage.Sets/src/mage/cards/j/JamieMcCrimmon.java index fccac5fa7f9..f33a8d92d67 100644 --- a/Mage.Sets/src/mage/cards/j/JamieMcCrimmon.java +++ b/Mage.Sets/src/mage/cards/j/JamieMcCrimmon.java @@ -14,8 +14,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.constants.SuperType; -import mage.filter.FilterSpell; -import mage.filter.common.FilterHistoricSpell; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.stack.Spell; @@ -28,8 +27,6 @@ import java.util.UUID; */ public final class JamieMcCrimmon extends CardImpl { - private static final FilterSpell filter = new FilterHistoricSpell(); - public JamieMcCrimmon(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); @@ -45,7 +42,7 @@ public final class JamieMcCrimmon extends CardImpl { // Whenever you cast a historic spell, Jamie McCrimmon gets +X/+X until end of turn, where X is that spell's mana value. this.addAbility(new SpellCastControllerTriggeredAbility(new BoostSourceEffect( JamieMcCrimmonValue.instance, JamieMcCrimmonValue.instance, Duration.EndOfTurn - ), filter, false)); + ), StaticFilters.FILTER_SPELL_HISTORIC, false)); // Doctor's companion this.addAbility(DoctorsCompanionAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/j/JediKnight.java b/Mage.Sets/src/mage/cards/j/JediKnight.java index 081ea96e0e0..731c15a167e 100644 --- a/Mage.Sets/src/mage/cards/j/JediKnight.java +++ b/Mage.Sets/src/mage/cards/j/JediKnight.java @@ -1,7 +1,5 @@ - package mage.cards.j; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; @@ -14,10 +12,11 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.common.FilterNonlandPermanent; -import mage.target.common.TargetNonlandPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author Styxo */ public final class JediKnight extends CardImpl { @@ -38,7 +37,7 @@ public final class JediKnight extends CardImpl { // When Jedi Knight leaves the battlefield, return target nonland permanent you don't control to its owner's hands. Ability ability = new LeavesBattlefieldTriggeredAbility(new ReturnToHandTargetEffect(), false); - ability.addTarget(new TargetNonlandPermanent(filter)); + ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); // Meditate {1}{U} diff --git a/Mage.Sets/src/mage/cards/j/JenovaAncientCalamity.java b/Mage.Sets/src/mage/cards/j/JenovaAncientCalamity.java new file mode 100644 index 00000000000..9a527d40923 --- /dev/null +++ b/Mage.Sets/src/mage/cards/j/JenovaAncientCalamity.java @@ -0,0 +1,98 @@ +package mage.cards.j; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.condition.common.MyTurnCondition; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.SourcePermanentPowerValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.AddCardSubTypeTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class JenovaAncientCalamity extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.MUTANT, "a Mutant you control"); + + public JenovaAncientCalamity(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{G}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.ALIEN); + this.power = new MageInt(1); + this.toughness = new MageInt(5); + + // At the beginning of combat on your turn, put a number of +1/+1 counters equal to Jenova's power onto up to one other target creature. That creature becomes a Mutant in addition to its other types. + Ability ability = new BeginningOfCombatTriggeredAbility(new AddCountersTargetEffect( + CounterType.P1P1.createInstance(), SourcePermanentPowerValue.NOT_NEGATIVE + ).setText("put a number of +1/+1 counters equal to {this}'s power on up to one other target creature")); + ability.addEffect(new AddCardSubTypeTargetEffect(SubType.MUTANT, Duration.Custom) + .setText("That creature becomes a Mutant in addition to its other types")); + ability.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_ANOTHER_TARGET_CREATURE)); + this.addAbility(ability); + + // Whenever a Mutant you control dies during your turn, draw cards equal to its power. + this.addAbility(new DiesCreatureTriggeredAbility( + new DrawCardSourceControllerEffect(JenovaAncientCalamityValue.instance) + .setText("you draw cards equal to its power"), false, filter + ).withTriggerCondition(MyTurnCondition.instance)); + } + + private JenovaAncientCalamity(final JenovaAncientCalamity card) { + super(card); + } + + @Override + public JenovaAncientCalamity copy() { + return new JenovaAncientCalamity(this); + } +} + +enum JenovaAncientCalamityValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return CardUtil.getEffectValueFromAbility(sourceAbility, "creatureDied", Permanent.class) + .map(MageObject::getPower) + .map(MageInt::getValue) + .orElse(0); + } + + @Override + public JenovaAncientCalamityValue copy() { + return this; + } + + @Override + public String getMessage() { + return ""; + } + + @Override + public String toString() { + return "1"; + } +} diff --git a/Mage.Sets/src/mage/cards/j/JensonCarthalionDruidExile.java b/Mage.Sets/src/mage/cards/j/JensonCarthalionDruidExile.java index f5da11bd7c5..c78364cd08b 100644 --- a/Mage.Sets/src/mage/cards/j/JensonCarthalionDruidExile.java +++ b/Mage.Sets/src/mage/cards/j/JensonCarthalionDruidExile.java @@ -2,6 +2,7 @@ package mage.cards.j; import mage.MageInt; import mage.Mana; +import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.condition.Condition; @@ -23,7 +24,6 @@ import mage.game.permanent.token.AngelVigilanceToken; import mage.game.stack.Spell; import mage.util.CardUtil; -import java.util.Objects; import java.util.UUID; /** @@ -72,15 +72,12 @@ enum JensonCarthalionDruidExileCondition implements Condition { @Override public boolean apply(Game game, Ability source) { - return CardUtil.castStream( - source.getEffects() - .stream() - .map(effect -> effect.getValue("spellCast")), - Spell.class - ).findAny() - .filter(Objects::nonNull) - .map(spell -> spell.getColor(game).getColorCount()) - .orElse(0) >= 5; + return CardUtil + .getEffectValueFromAbility(source, "spellCast", Spell.class) + .map(spell -> spell.getColor(game)) + .map(ObjectColor::getColorCount) + .filter(x -> x >= 5) + .isPresent(); } @Override diff --git a/Mage.Sets/src/mage/cards/j/JeweledAmulet.java b/Mage.Sets/src/mage/cards/j/JeweledAmulet.java index 3c00ce79b48..995ff03bb1a 100644 --- a/Mage.Sets/src/mage/cards/j/JeweledAmulet.java +++ b/Mage.Sets/src/mage/cards/j/JeweledAmulet.java @@ -2,18 +2,20 @@ package mage.cards.j; import mage.Mana; import mage.abilities.Ability; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.decorator.ConditionalActivatedAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.mana.ManaEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.mana.ManaEffect; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.Outcome; import mage.constants.Zone; import mage.counters.CounterType; @@ -29,14 +31,16 @@ import java.util.UUID; */ public final class JeweledAmulet extends CardImpl { - private static final String rule = "{1}, {T}: Put a charge counter on {this}. Note the type of mana spent to pay this activation cost. Activate only if there are no charge counters on {this}."; + private static final Condition condition = new SourceHasCounterCondition(CounterType.CHARGE, ComparisonType.EQUAL_TO, 0); public JeweledAmulet(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{0}"); // {1}, {tap}: Put a charge counter on Jeweled Amulet. Note the type of mana spent to pay this activation cost. Activate this ability only if there are no charge counters on Jeweled Amulet. - ConditionalActivatedAbility ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new JeweledAmuletAddCounterEffect(), new ManaCostsImpl<>("{1}"), new SourceHasCounterCondition(CounterType.CHARGE, 0, 0), rule); - ability.addEffect(new AddCountersSourceEffect(CounterType.CHARGE.createInstance(), true)); + ConditionalActivatedAbility ability = new ConditionalActivatedAbility( + new AddCountersSourceEffect(CounterType.CHARGE.createInstance(), true), new GenericManaCost(1), condition + ); + ability.addEffect(new JeweledAmuletAddCounterEffect()); ability.addCost(new TapSourceCost()); this.addAbility(ability); @@ -44,7 +48,6 @@ public final class JeweledAmulet extends CardImpl { Ability ability2 = new SimpleManaAbility(Zone.BATTLEFIELD, new JeweledAmuletAddManaEffect(), new TapSourceCost()); ability2.addCost(new RemoveCountersSourceCost(CounterType.CHARGE.createInstance())); this.addAbility(ability2); - } private JeweledAmulet(final JeweledAmulet card) { @@ -63,7 +66,7 @@ class JeweledAmuletAddCounterEffect extends OneShotEffect { public JeweledAmuletAddCounterEffect() { super(Outcome.Benefit); - this.staticText = "Note the type of mana spent to pay this activation cost. Activate only if there are no charge counters on {this}"; + this.staticText = "Note the type of mana spent to pay this activation cost"; } private JeweledAmuletAddCounterEffect(final JeweledAmuletAddCounterEffect effect) { diff --git a/Mage.Sets/src/mage/cards/j/JhoiraWeatherlightCaptain.java b/Mage.Sets/src/mage/cards/j/JhoiraWeatherlightCaptain.java index 2607fa6334d..b1290ef5049 100644 --- a/Mage.Sets/src/mage/cards/j/JhoiraWeatherlightCaptain.java +++ b/Mage.Sets/src/mage/cards/j/JhoiraWeatherlightCaptain.java @@ -8,15 +8,12 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.filter.FilterSpell; -import mage.filter.common.FilterHistoricSpell; +import mage.filter.StaticFilters; import java.util.UUID; public final class JhoiraWeatherlightCaptain extends CardImpl { - private static final FilterSpell filter = new FilterHistoricSpell(); - public JhoiraWeatherlightCaptain(UUID ownerId, CardSetInfo cardSetInfo) { super(ownerId, cardSetInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{R}"); this.supertype.add(SuperType.LEGENDARY); @@ -28,7 +25,7 @@ public final class JhoiraWeatherlightCaptain extends CardImpl { this.addAbility(new SpellCastControllerTriggeredAbility( new DrawCardSourceControllerEffect(1) .setText("draw a card. (Artifacts, legendaries, and Sagas are historic.)"), - filter, false + StaticFilters.FILTER_SPELL_HISTORIC, false )); } diff --git a/Mage.Sets/src/mage/cards/j/JhoirasFamiliar.java b/Mage.Sets/src/mage/cards/j/JhoirasFamiliar.java index 9d1c4a37870..c22a762c16e 100644 --- a/Mage.Sets/src/mage/cards/j/JhoirasFamiliar.java +++ b/Mage.Sets/src/mage/cards/j/JhoirasFamiliar.java @@ -1,7 +1,6 @@ package mage.cards.j; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; @@ -10,8 +9,10 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterHistoricCard; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.HistoricPredicate; + +import java.util.UUID; /** * @@ -19,6 +20,11 @@ import mage.filter.common.FilterHistoricCard; */ public final class JhoirasFamiliar extends CardImpl { + private static final FilterCard filter = new FilterCard("historic spells"); + static { + filter.add(HistoricPredicate.instance); + } + public JhoirasFamiliar(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}"); @@ -31,7 +37,7 @@ public final class JhoirasFamiliar extends CardImpl { // Historic spells you cast cost {1} less to cast. this.addAbility(new SimpleStaticAbility( - new SpellsCostReductionControllerEffect(new FilterHistoricCard(), 1) + new SpellsCostReductionControllerEffect(filter, 1) .setText("Historic spells you cast cost {1} less to cast. (Artifacts, legendaries, and Sagas are historic.)"))); } diff --git a/Mage.Sets/src/mage/cards/j/JinxedChoker.java b/Mage.Sets/src/mage/cards/j/JinxedChoker.java index 6d139469066..94cc4ea2104 100644 --- a/Mage.Sets/src/mage/cards/j/JinxedChoker.java +++ b/Mage.Sets/src/mage/cards/j/JinxedChoker.java @@ -1,23 +1,21 @@ package mage.cards.j; import mage.abilities.Ability; +import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; -import mage.abilities.common.OnEventTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageControllerEffect; import mage.abilities.effects.common.TargetPlayerGainControlSourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.counters.CounterType; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetOpponent; @@ -40,8 +38,7 @@ public final class JinxedChoker extends CardImpl { this.addAbility(endStepAbility); // At the beginning of your upkeep, Jinxed Choker deals damage to you equal to the number of charge counters on it. - Ability upkeepAbility = new OnEventTriggeredAbility(GameEvent.EventType.UPKEEP_STEP_PRE, "beginning of your upkeep", new DamageControllerEffect(new JinxedChokerDynamicValue()), false); - this.addAbility(upkeepAbility); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new DamageControllerEffect(new CountersSourceCount(CounterType.CHARGE)))); // {3}: Put a charge counter on Jinxed Choker or remove one from it. Ability ability = new SimpleActivatedAbility(new JinxedChokerCounterEffect(), new ManaCostsImpl<>("{3}")); @@ -84,35 +81,6 @@ class JinxedChokerAddCounterEffect extends OneShotEffect { } -class JinxedChokerDynamicValue implements DynamicValue { - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - Permanent permanent = game.getPermanent(sourceAbility.getSourceId()); - - int count = 0; - if (permanent != null) { - count = permanent.getCounters(game).getCount(CounterType.CHARGE); - } - return count; - } - - @Override - public JinxedChokerDynamicValue copy() { - return new JinxedChokerDynamicValue(); - } - - @Override - public String getMessage() { - return "charge counter on it"; - } - - @Override - public String toString() { - return "1"; - } -} - class JinxedChokerCounterEffect extends OneShotEffect { JinxedChokerCounterEffect() { diff --git a/Mage.Sets/src/mage/cards/j/JoGrant.java b/Mage.Sets/src/mage/cards/j/JoGrant.java index d6c4ef8593e..2fc65909432 100644 --- a/Mage.Sets/src/mage/cards/j/JoGrant.java +++ b/Mage.Sets/src/mage/cards/j/JoGrant.java @@ -15,7 +15,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.counters.CounterType; import mage.filter.FilterCard; -import mage.filter.common.FilterHistoricCard; +import mage.filter.predicate.mageobject.HistoricPredicate; import mage.game.Game; import mage.players.Player; @@ -59,7 +59,10 @@ public final class JoGrant extends CardImpl { class JoGrantEffect extends ContinuousEffectImpl { - private static final FilterCard filter = new FilterHistoricCard(); + private static final FilterCard filter = new FilterCard("historic card"); + static { + filter.add(HistoricPredicate.instance); + } JoGrantEffect() { super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); diff --git a/Mage.Sets/src/mage/cards/j/JoragaInvocation.java b/Mage.Sets/src/mage/cards/j/JoragaInvocation.java index 9c528fee3af..349ec87d4d2 100644 --- a/Mage.Sets/src/mage/cards/j/JoragaInvocation.java +++ b/Mage.Sets/src/mage/cards/j/JoragaInvocation.java @@ -1,7 +1,5 @@ - package mage.cards.j; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; @@ -18,8 +16,9 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class JoragaInvocation extends CardImpl { @@ -28,9 +27,9 @@ public final class JoragaInvocation extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{G}{G}"); // Each creature you control gets +3/+3 until end of turn and must be blocked this turn if able. - this.getSpellAbility().addEffect(new BoostControlledEffect(3, 3, Duration.EndOfTurn)); + this.getSpellAbility().addEffect(new BoostControlledEffect(3, 3, Duration.EndOfTurn) + .setText("each creature you control gets +3/+3 until end of turn")); this.getSpellAbility().addEffect(new JoragaInvocationEffect()); - } private JoragaInvocation(final JoragaInvocation card) { diff --git a/Mage.Sets/src/mage/cards/j/JoshuaPhoenixsDominant.java b/Mage.Sets/src/mage/cards/j/JoshuaPhoenixsDominant.java new file mode 100644 index 00000000000..bcc50be49c0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/j/JoshuaPhoenixsDominant.java @@ -0,0 +1,57 @@ +package mage.cards.j; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.ExileAndReturnSourceEffect; +import mage.abilities.effects.common.discard.DiscardAndDrawThatManyEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.PutCards; +import mage.constants.SubType; +import mage.constants.SuperType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class JoshuaPhoenixsDominant extends CardImpl { + + public JoshuaPhoenixsDominant(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.NOBLE); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + this.secondSideCardClazz = mage.cards.p.PhoenixWardenOfFire.class; + + // When Joshua enters, discard up to two cards, then draw that many cards. + this.addAbility(new EntersBattlefieldTriggeredAbility(new DiscardAndDrawThatManyEffect(2))); + + // {3}{R}{W}, {T}: Exile Joshua, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery. + this.addAbility(new TransformAbility()); + Ability ability = new ActivateAsSorceryActivatedAbility( + new ExileAndReturnSourceEffect(PutCards.BATTLEFIELD_TRANSFORMED), new ManaCostsImpl<>("{3}{R}{W}") + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private JoshuaPhoenixsDominant(final JoshuaPhoenixsDominant card) { + super(card); + } + + @Override + public JoshuaPhoenixsDominant copy() { + return new JoshuaPhoenixsDominant(this); + } +} diff --git a/Mage.Sets/src/mage/cards/j/JudgeMagisterGabranth.java b/Mage.Sets/src/mage/cards/j/JudgeMagisterGabranth.java new file mode 100644 index 00000000000..8c14f65cb8e --- /dev/null +++ b/Mage.Sets/src/mage/cards/j/JudgeMagisterGabranth.java @@ -0,0 +1,50 @@ +package mage.cards.j; + +import mage.MageInt; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class JudgeMagisterGabranth extends CardImpl { + + public JudgeMagisterGabranth(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ADVISOR); + this.subtype.add(SubType.KNIGHT); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Menace + this.addAbility(new MenaceAbility()); + + // Whenever another creature or artifact you control dies, put a +1/+1 counter on Judge Magister Gabranth. + this.addAbility(new DiesCreatureTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false, + StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE_OR_ARTIFACT + )); + } + + private JudgeMagisterGabranth(final JudgeMagisterGabranth card) { + super(card); + } + + @Override + public JudgeMagisterGabranth copy() { + return new JudgeMagisterGabranth(this); + } +} diff --git a/Mage.Sets/src/mage/cards/j/JunkWinder.java b/Mage.Sets/src/mage/cards/j/JunkWinder.java index 34865ac6ce8..70ea3f64e11 100644 --- a/Mage.Sets/src/mage/cards/j/JunkWinder.java +++ b/Mage.Sets/src/mage/cards/j/JunkWinder.java @@ -3,24 +3,15 @@ package mage.cards.j; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.common.AffinityEffect; import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; import mage.abilities.effects.common.TapTargetEffect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.AffinityAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AffinityType; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.common.FilterControlledPermanent; -import mage.filter.common.FilterNonlandPermanent; -import mage.filter.predicate.permanent.TokenPredicate; import mage.target.TargetPermanent; import java.util.UUID; @@ -30,16 +21,6 @@ import java.util.UUID; */ public final class JunkWinder extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("tokens"); - private static final FilterPermanent filter2 = new FilterNonlandPermanent("nonland permanent an opponent controls"); - - static { - filter.add(TokenPredicate.TRUE); - filter2.add(TargetController.OPPONENT.getControllerPredicate()); - } - - private static final Hint hint = new ValueHint("Tokens you control", new PermanentsOnBattlefieldCount(filter)); - public JunkWinder(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}{U}"); @@ -48,7 +29,7 @@ public final class JunkWinder extends CardImpl { this.toughness = new MageInt(6); // Affinity for tokens - this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filter)).addHint(hint)); + this.addAbility(new AffinityAbility(AffinityType.TOKENS)); // Whenever a token you control enters, tap target nonland permanent an opponent controls. It doesn't untap during its controller's next untap step. Ability ability = new EntersBattlefieldControlledTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/j/JurassicPark.java b/Mage.Sets/src/mage/cards/j/JurassicPark.java index b2581a4bede..7024769e637 100644 --- a/Mage.Sets/src/mage/cards/j/JurassicPark.java +++ b/Mage.Sets/src/mage/cards/j/JurassicPark.java @@ -90,7 +90,7 @@ class JurassicParkEffect extends ContinuousEffectImpl { .stream() .filter(Objects::nonNull) .filter(card -> !card.getManaCost().getText().isEmpty()) // card must have a mana cost - .filter(card -> card.getSubtype().contains(SubType.DINOSAUR)) + .filter(card -> card.hasSubtype(SubType.DINOSAUR, game)) .forEach(card -> { Ability ability = new EscapeAbility(card, card.getManaCost().getText(), 3); ability.setSourceId(card.getId()); diff --git a/Mage.Sets/src/mage/cards/j/JuvenileMistDragon.java b/Mage.Sets/src/mage/cards/j/JuvenileMistDragon.java index 3bee7fdc4fe..33e2c3f174a 100644 --- a/Mage.Sets/src/mage/cards/j/JuvenileMistDragon.java +++ b/Mage.Sets/src/mage/cards/j/JuvenileMistDragon.java @@ -11,7 +11,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; import java.util.UUID; @@ -43,7 +43,7 @@ public final class JuvenileMistDragon extends CardImpl { .setText("Each of those creatures doesn't untap during its controller's next untap step") ); ability.addTarget(new TargetCreaturePermanent(0,1)); - ability.setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); this.addAbility(ability.withFlavorWord("Confounding Clouds")); } diff --git a/Mage.Sets/src/mage/cards/k/KaaliaOfTheVast.java b/Mage.Sets/src/mage/cards/k/KaaliaOfTheVast.java index 1e007c1590c..626fb0be85a 100644 --- a/Mage.Sets/src/mage/cards/k/KaaliaOfTheVast.java +++ b/Mage.Sets/src/mage/cards/k/KaaliaOfTheVast.java @@ -16,6 +16,7 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInHand; +import mage.util.CardUtil; import java.util.UUID; @@ -127,7 +128,7 @@ class KaaliaOfTheVastEffect extends OneShotEffect { UUID defenderId = game.getCombat().getDefendingPlayerId(source.getSourceId(), game); if (defenderId != null) { controller.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null); - Permanent creature = game.getPermanent(cardId); + Permanent creature = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (creature != null) { game.getCombat().addAttackerToCombat(card.getId(), defenderId, game); return true; diff --git a/Mage.Sets/src/mage/cards/k/KainTraitorousDragoon.java b/Mage.Sets/src/mage/cards/k/KainTraitorousDragoon.java index e11d5e5f309..ab4cb5cd4d5 100644 --- a/Mage.Sets/src/mage/cards/k/KainTraitorousDragoon.java +++ b/Mage.Sets/src/mage/cards/k/KainTraitorousDragoon.java @@ -63,7 +63,7 @@ class KainTraitorousDragoonEffect extends OneShotEffect { KainTraitorousDragoonEffect() { super(Outcome.Benefit); - staticText = ", that player gains control of {this}. If they do, you draw that many cards, " + + staticText = "that player gains control of {this}. If they do, you draw that many cards, " + "create that many tapped Treasure tokens, then lose that much life"; } diff --git a/Mage.Sets/src/mage/cards/k/KaitoBaneOfNightmares.java b/Mage.Sets/src/mage/cards/k/KaitoBaneOfNightmares.java index f947283d7f0..af7f274d3cf 100644 --- a/Mage.Sets/src/mage/cards/k/KaitoBaneOfNightmares.java +++ b/Mage.Sets/src/mage/cards/k/KaitoBaneOfNightmares.java @@ -19,7 +19,10 @@ import mage.abilities.keyword.HexproofAbility; import mage.abilities.keyword.NinjutsuAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.counters.CounterType; import mage.game.Game; import mage.game.command.emblems.KaitoBaneOfNightmaresEmblem; @@ -31,7 +34,6 @@ import mage.watchers.common.PlayerLostLifeWatcher; import java.util.UUID; /** - * * @author jackd149 */ public final class KaitoBaneOfNightmares extends CardImpl { @@ -61,8 +63,8 @@ public final class KaitoBaneOfNightmares extends CardImpl { // 0: Surveil 2. Then draw a card for each opponent who lost life this turn. Ability ability = new LoyaltyAbility(new SurveilEffect(2), 0); - ability.addEffect(new DrawCardSourceControllerEffect(KaitoBaneOfNightmaresCount.instance)); - this.addAbility(ability, new PlayerLostLifeWatcher()); + ability.addEffect(new DrawCardSourceControllerEffect(KaitoBaneOfNightmaresCount.instance).concatBy("Then")); + this.addAbility(ability); // -2: Tap target creature. Put two stun counters on it. Ability minusTwoAbility = new LoyaltyAbility(new TapTargetEffect(), -2); @@ -87,10 +89,10 @@ enum KaitoBaneOfNightmaresCondition implements Condition { @Override public boolean apply(Game game, Ability source) { - if (!MyTurnCondition.instance.apply(game, source)){ + if (!MyTurnCondition.instance.apply(game, source)) { return false; } - + Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent == null) { diff --git a/Mage.Sets/src/mage/cards/k/KaitoShizuki.java b/Mage.Sets/src/mage/cards/k/KaitoShizuki.java index 83a867d4331..9103e9818e1 100644 --- a/Mage.Sets/src/mage/cards/k/KaitoShizuki.java +++ b/Mage.Sets/src/mage/cards/k/KaitoShizuki.java @@ -31,7 +31,7 @@ import java.util.UUID; public final class KaitoShizuki extends CardImpl { private static final Hint hint = new ConditionHint( - SourceEnteredThisTurnCondition.instance, "This permanent entered the battlefield this turn" + SourceEnteredThisTurnCondition.DID, "This permanent entered the battlefield this turn" ); private static final Condition condition = new InvertCondition(RaidCondition.instance); @@ -45,7 +45,7 @@ public final class KaitoShizuki extends CardImpl { // At the beginning of your end step, if Kaito Shizuki entered the battlefield this turn, he phases out. this.addAbility(new BeginningOfEndStepTriggeredAbility( TargetController.YOU, new PhaseOutSourceEffect().setText("he phases out"), - false, SourceEnteredThisTurnCondition.instance + false, SourceEnteredThisTurnCondition.DID ).addHint(hint)); // +1: Draw a card. Then discard a card unless you attacked this turn. diff --git a/Mage.Sets/src/mage/cards/k/KalamaxTheStormsire.java b/Mage.Sets/src/mage/cards/k/KalamaxTheStormsire.java index 6cec34514c2..6cffb041ca4 100644 --- a/Mage.Sets/src/mage/cards/k/KalamaxTheStormsire.java +++ b/Mage.Sets/src/mage/cards/k/KalamaxTheStormsire.java @@ -13,7 +13,7 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.constants.Zone; import mage.counters.CounterType; -import mage.filter.common.FilterInstantSpell; +import mage.filter.FilterSpell; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -55,8 +55,14 @@ public final class KalamaxTheStormsire extends CardImpl { } class KalamaxTheStormsireSpellCastAbility extends SpellCastControllerTriggeredAbility { + + private static final FilterSpell filterInstant = new FilterSpell(); + static { + filterInstant.add(CardType.INSTANT.getPredicate()); + } + KalamaxTheStormsireSpellCastAbility() { - super(new CopyTargetStackObjectEffect(true), new FilterInstantSpell(), false); + super(new CopyTargetStackObjectEffect(true), filterInstant, false); } private KalamaxTheStormsireSpellCastAbility(final KalamaxTheStormsireSpellCastAbility ability) { diff --git a/Mage.Sets/src/mage/cards/k/KamiOfFiresRoar.java b/Mage.Sets/src/mage/cards/k/KamiOfFiresRoar.java index c1786031541..98290b3d4b1 100644 --- a/Mage.Sets/src/mage/cards/k/KamiOfFiresRoar.java +++ b/Mage.Sets/src/mage/cards/k/KamiOfFiresRoar.java @@ -28,7 +28,7 @@ public final class KamiOfFiresRoar extends CardImpl { this.toughness = new MageInt(3); // Whenever you cast a Spirit or Arcane spell, target creature can't block this turn. - Ability ability = new SpellCastControllerTriggeredAbility(new CantBlockTargetEffect(Duration.EndOfTurn), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false); + Ability ability = new SpellCastControllerTriggeredAbility(new CantBlockTargetEffect(Duration.EndOfTurn), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, false); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/k/KamiOfIndustry.java b/Mage.Sets/src/mage/cards/k/KamiOfIndustry.java index 1aa23bac775..2c4cb8b4acb 100644 --- a/Mage.Sets/src/mage/cards/k/KamiOfIndustry.java +++ b/Mage.Sets/src/mage/cards/k/KamiOfIndustry.java @@ -20,6 +20,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -83,7 +84,7 @@ class KamiOfIndustryEffect extends OneShotEffect { return false; } player.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/k/KamiOfTatteredShoji.java b/Mage.Sets/src/mage/cards/k/KamiOfTatteredShoji.java index 8ef007fa0cc..800b79ba3b1 100644 --- a/Mage.Sets/src/mage/cards/k/KamiOfTatteredShoji.java +++ b/Mage.Sets/src/mage/cards/k/KamiOfTatteredShoji.java @@ -26,7 +26,7 @@ public final class KamiOfTatteredShoji extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(5); // Whenever you cast a Spirit or Arcane spell, Kami of Tattered Shoji gains flying until end of turn. - this.addAbility(new SpellCastControllerTriggeredAbility(new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, false)); } private KamiOfTatteredShoji(final KamiOfTatteredShoji card) { diff --git a/Mage.Sets/src/mage/cards/k/KamiOfTheHunt.java b/Mage.Sets/src/mage/cards/k/KamiOfTheHunt.java index 81d1d7666aa..cc7d3610941 100644 --- a/Mage.Sets/src/mage/cards/k/KamiOfTheHunt.java +++ b/Mage.Sets/src/mage/cards/k/KamiOfTheHunt.java @@ -24,7 +24,7 @@ public final class KamiOfTheHunt extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - this.addAbility(new SpellCastControllerTriggeredAbility(new BoostSourceEffect(1, 1, Duration.EndOfTurn), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new BoostSourceEffect(1, 1, Duration.EndOfTurn), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, false)); } private KamiOfTheHunt(final KamiOfTheHunt card) { diff --git a/Mage.Sets/src/mage/cards/k/KamiOfThePaintedRoad.java b/Mage.Sets/src/mage/cards/k/KamiOfThePaintedRoad.java index eb3e982a985..3eec67e8e27 100644 --- a/Mage.Sets/src/mage/cards/k/KamiOfThePaintedRoad.java +++ b/Mage.Sets/src/mage/cards/k/KamiOfThePaintedRoad.java @@ -26,7 +26,7 @@ public final class KamiOfThePaintedRoad extends CardImpl { this.toughness = new MageInt(3); // Whenever you cast a Spirit or Arcane spell, Kami of the Painted Road gains protection from the color of your choice until end of turn. - this.addAbility(new SpellCastControllerTriggeredAbility(new GainProtectionFromColorSourceEffect(Duration.EndOfTurn), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new GainProtectionFromColorSourceEffect(Duration.EndOfTurn), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, false)); } diff --git a/Mage.Sets/src/mage/cards/k/KamiOfTheWaningMoon.java b/Mage.Sets/src/mage/cards/k/KamiOfTheWaningMoon.java index 84a3858e174..a115270f7e0 100644 --- a/Mage.Sets/src/mage/cards/k/KamiOfTheWaningMoon.java +++ b/Mage.Sets/src/mage/cards/k/KamiOfTheWaningMoon.java @@ -29,7 +29,7 @@ public final class KamiOfTheWaningMoon extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); this.addAbility(FlyingAbility.getInstance()); - Ability ability = new SpellCastControllerTriggeredAbility(new GainAbilityTargetEffect(FearAbility.getInstance(), Duration.EndOfTurn), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false); + Ability ability = new SpellCastControllerTriggeredAbility(new GainAbilityTargetEffect(FearAbility.getInstance(), Duration.EndOfTurn), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, false); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/k/KaradorGhostChieftain.java b/Mage.Sets/src/mage/cards/k/KaradorGhostChieftain.java index 37455d3b7ab..470449a0195 100644 --- a/Mage.Sets/src/mage/cards/k/KaradorGhostChieftain.java +++ b/Mage.Sets/src/mage/cards/k/KaradorGhostChieftain.java @@ -3,14 +3,13 @@ package mage.cards.k; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.SpellAbility; -import mage.abilities.common.CastFromGraveyardOnceEachTurnAbility; +import mage.abilities.common.CastFromGraveyardOnceDuringEachOfYourTurnAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.cost.CostModificationEffectImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreatureCard; import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; @@ -18,13 +17,10 @@ import mage.util.CardUtil; import java.util.UUID; /** - * * @author emerald000 */ public final class KaradorGhostChieftain extends CardImpl { - private static final FilterCreatureCard filter = new FilterCreatureCard("a creature spell"); - public KaradorGhostChieftain(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}{B}{G}"); this.supertype.add(SuperType.LEGENDARY); @@ -39,7 +35,7 @@ public final class KaradorGhostChieftain extends CardImpl { new KaradorGhostChieftainCostReductionEffect())); // Once during each of your turns, you may cast a creature spell from your graveyard. - this.addAbility(new CastFromGraveyardOnceEachTurnAbility(filter)); + this.addAbility(new CastFromGraveyardOnceDuringEachOfYourTurnAbility(StaticFilters.FILTER_CARD_A_CREATURE_SPELL)); } private KaradorGhostChieftain(final KaradorGhostChieftain card) { diff --git a/Mage.Sets/src/mage/cards/k/KarakykGuardian.java b/Mage.Sets/src/mage/cards/k/KarakykGuardian.java index 6fd569725a6..01151692af5 100644 --- a/Mage.Sets/src/mage/cards/k/KarakykGuardian.java +++ b/Mage.Sets/src/mage/cards/k/KarakykGuardian.java @@ -1,10 +1,8 @@ package mage.cards.k; import mage.MageInt; -import mage.MageObjectReference; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.Condition; +import mage.abilities.condition.common.SourceHasntDealtDamageThisGameCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.keyword.FlyingAbility; @@ -15,14 +13,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.WatcherScope; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.watchers.Watcher; +import mage.watchers.common.DealtDamageThisGameWatcher; -import java.util.HashSet; -import java.util.Set; import java.util.UUID; /** @@ -49,8 +41,9 @@ public final class KarakykGuardian extends CardImpl { // This creature has hexproof if it hasn't dealt damage yet. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( new GainAbilitySourceEffect(HexproofAbility.getInstance()), - KarakykGuardianCondition.instance, "{this} has hexproof if it hasn't dealt damage yet" - )), new KarakykGuardianWatcher()); + SourceHasntDealtDamageThisGameCondition.instance, + "{this} has hexproof if it hasn't dealt damage yet" + )).addHint(SourceHasntDealtDamageThisGameCondition.getHint()), new DealtDamageThisGameWatcher()); } private KarakykGuardian(final KarakykGuardian card) { @@ -62,49 +55,3 @@ public final class KarakykGuardian extends CardImpl { return new KarakykGuardian(this); } } - -enum KarakykGuardianCondition implements Condition { - - instance; - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - KarakykGuardianWatcher watcher = game.getState().getWatcher(KarakykGuardianWatcher.class); - return permanent != null && !watcher.getDamagers().contains(new MageObjectReference(permanent, game)); - } - - @Override - public String toString() { - return "{this} hasn't dealt damage yet"; - } - -} - -class KarakykGuardianWatcher extends Watcher { - - private final Set damagers = new HashSet<>(); - - public KarakykGuardianWatcher() { - super(WatcherScope.GAME); - } - - @Override - public void watch(GameEvent event, Game game) { - switch (event.getType()) { - case DAMAGED_PERMANENT: - case DAMAGED_PLAYER: - break; - default: - return; - } - Permanent permanent = game.getPermanent(event.getSourceId()); - if (permanent != null) { - damagers.add(new MageObjectReference(permanent, game)); - } - } - - public Set getDamagers() { - return damagers; - } -} diff --git a/Mage.Sets/src/mage/cards/k/KarnLegacyReforged.java b/Mage.Sets/src/mage/cards/k/KarnLegacyReforged.java index ee1a49e88ca..eb6ac42523d 100644 --- a/Mage.Sets/src/mage/cards/k/KarnLegacyReforged.java +++ b/Mage.Sets/src/mage/cards/k/KarnLegacyReforged.java @@ -1,16 +1,14 @@ package mage.cards.k; import mage.MageInt; -import mage.MageObject; import mage.Mana; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.SetBasePowerToughnessSourceEffect; import mage.abilities.mana.builder.ConditionalManaBuilder; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -36,7 +34,7 @@ public final class KarnLegacyReforged extends CardImpl { // Karn, Legacy Reforged's power and toughness are each equal to the greatest mana value among artifacts you control. this.addAbility(new SimpleStaticAbility( - Zone.ALL, new SetBasePowerToughnessSourceEffect(KarnLegacyReforgedValue.instance) + Zone.ALL, new SetBasePowerToughnessSourceEffect(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_ARTIFACTS) .setText("{this}'s power and toughness are each equal to the greatest mana value among artifacts you control") )); @@ -56,39 +54,6 @@ public final class KarnLegacyReforged extends CardImpl { } } -enum KarnLegacyReforgedValue implements DynamicValue { - instance; - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - return game - .getBattlefield() - .getActivePermanents( - StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT, - sourceAbility.getControllerId(), sourceAbility, game - ) - .stream() - .mapToInt(MageObject::getManaValue) - .max() - .orElse(0); - } - - @Override - public KarnLegacyReforgedValue copy() { - return this; - } - - @Override - public String getMessage() { - return ""; - } - - @Override - public String toString() { - return "1"; - } -} - class KarnLegacyReforgedEffect extends OneShotEffect { KarnLegacyReforgedEffect() { diff --git a/Mage.Sets/src/mage/cards/k/KarnLiberated.java b/Mage.Sets/src/mage/cards/k/KarnLiberated.java index 8e46cccba02..8d77814e4c7 100644 --- a/Mage.Sets/src/mage/cards/k/KarnLiberated.java +++ b/Mage.Sets/src/mage/cards/k/KarnLiberated.java @@ -70,6 +70,7 @@ class KarnLiberatedEffect extends OneShotEffect { private KarnLiberatedEffect(final KarnLiberatedEffect effect) { super(effect); + this.exileId = effect.exileId; } @Override @@ -201,7 +202,7 @@ class KarnLiberatedDelayedEffect extends OneShotEffect { controller.moveCards(cards, Zone.BATTLEFIELD, source, game); for (Card card : cards.getCards(game)) { if (card != null) { - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { ((PermanentImpl) permanent).removeSummoningSickness(); } diff --git a/Mage.Sets/src/mage/cards/k/KazuulsTollCollector.java b/Mage.Sets/src/mage/cards/k/KazuulsTollCollector.java index 55b72dcbf2f..015c95a3a26 100644 --- a/Mage.Sets/src/mage/cards/k/KazuulsTollCollector.java +++ b/Mage.Sets/src/mage/cards/k/KazuulsTollCollector.java @@ -10,8 +10,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledPermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; @@ -23,9 +22,6 @@ import java.util.UUID; */ public final class KazuulsTollCollector extends CardImpl { - private static final FilterPermanent filter - = new FilterControlledPermanent(SubType.EQUIPMENT, "Equipment you control"); - public KazuulsTollCollector(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); this.subtype.add(SubType.OGRE); @@ -35,7 +31,7 @@ public final class KazuulsTollCollector extends CardImpl { // {0}: Attach target Equipment you control to Kazuul's Toll Collector. Activate this ability only any time you could cast a sorcery. Ability ability = new ActivateAsSorceryActivatedAbility(new KazuulsTollCollectorEffect(), new GenericManaCost(0)); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/k/KefkaCourtMage.java b/Mage.Sets/src/mage/cards/k/KefkaCourtMage.java new file mode 100644 index 00000000000..041856fd2c3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KefkaCourtMage.java @@ -0,0 +1,123 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.SacrificeOpponentsEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; +import mage.game.Controllable; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetDiscard; + +import java.util.*; + +/** + * @author TheElk801 + */ +public final class KefkaCourtMage extends CardImpl { + + public KefkaCourtMage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{B}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(4); + this.toughness = new MageInt(5); + this.secondSideCardClazz = mage.cards.k.KefkaRulerOfRuin.class; + + // Whenever Kefka enters or attacks, each player discards a card. Then you draw a card for each card type among cards discarded this way. + this.addAbility(new EntersBattlefieldOrAttacksSourceTriggeredAbility(new KefkaCourtMageEffect())); + + // {8}: Each opponent sacrifices a permanent of their choice. Transform Kefka. Activate only as a sorcery. + this.addAbility(new TransformAbility()); + Ability ability = new ActivateAsSorceryActivatedAbility( + new SacrificeOpponentsEffect(StaticFilters.FILTER_PERMANENT), new GenericManaCost(8) + ); + ability.addEffect(new TransformSourceEffect()); + this.addAbility(ability); + } + + private KefkaCourtMage(final KefkaCourtMage card) { + super(card); + } + + @Override + public KefkaCourtMage copy() { + return new KefkaCourtMage(this); + } +} + +class KefkaCourtMageEffect extends OneShotEffect { + + KefkaCourtMageEffect() { + super(Outcome.Benefit); + staticText = "each player discards a card. Then you draw a card " + + "for each card type among cards discarded this way"; + } + + private KefkaCourtMageEffect(final KefkaCourtMageEffect effect) { + super(effect); + } + + @Override + public KefkaCourtMageEffect copy() { + return new KefkaCourtMageEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Map map = new HashMap<>(); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + switch (player.getHand().size()) { + case 0: + continue; + case 1: + map.put(playerId, player.getHand().getRandom(game)); + continue; + default: + TargetDiscard target = new TargetDiscard(playerId); + player.choose(outcome, player.getHand(), target, source, game); + map.put(playerId, game.getCard(target.getFirstTarget())); + } + } + Cards cards = new CardsImpl(); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + Card card = map.getOrDefault(playerId, null); + if (player != null && card != null && player.discard(card, false, source, game)) { + cards.add(card); + } + } + if (cards.isEmpty()) { + return false; + } + int count = cards.getCards(game) + .stream() + .map(card -> card.getCardType(game)) + .flatMap(Collection::stream) + .distinct() + .mapToInt(x -> 1) + .sum(); + Optional.ofNullable(source) + .map(Controllable::getControllerId) + .map(game::getPlayer) + .ifPresent(player -> player.drawCards(count, source, game)); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/k/KefkaDancingMad.java b/Mage.Sets/src/mage/cards/k/KefkaDancingMad.java new file mode 100644 index 00000000000..c3ab872f77e --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KefkaDancingMad.java @@ -0,0 +1,128 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MyTurnCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.cards.*; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.util.CardUtil; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KefkaDancingMad extends CardImpl { + + public KefkaDancingMad(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{B}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // During your turn, Kefka has indestructible. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield), + MyTurnCondition.instance, "during your turn, {this} has indestructible" + ))); + + // At the beginning of your end step, exile a card at random from each opponent's graveyard. You may cast any number of spells from among cards exiled this way without paying their mana costs. Then each player who owns a spell you cast this way loses life equal to its mana value. + this.addAbility(new BeginningOfEndStepTriggeredAbility(new KefkaDancingMadEffect())); + } + + private KefkaDancingMad(final KefkaDancingMad card) { + super(card); + } + + @Override + public KefkaDancingMad copy() { + return new KefkaDancingMad(this); + } +} + +class KefkaDancingMadEffect extends OneShotEffect { + + private static final class KefkaDancingMadTracker implements CardUtil.SpellCastTracker { + + private final Map map = new HashMap<>(); + + @Override + public boolean checkCard(Card card, Game game) { + return true; + } + + @Override + public void addCard(Card card, Ability source, Game game) { + map.compute(card.getOwnerId(), (u, i) -> i == null ? card.getManaValue() : Integer.sum(i, card.getManaValue())); + } + + private void loseLife(UUID playerId, Game game, Ability source) { + int totalCost = map.getOrDefault(playerId, 0); + if (totalCost > 0) { + Optional.ofNullable(playerId) + .map(game::getPlayer) + .ifPresent(player -> player.loseLife(totalCost, game, source, false)); + } + } + } + + KefkaDancingMadEffect() { + super(Outcome.Benefit); + staticText = "exile a card at random from each opponent's graveyard. " + + "You may cast any number of spells from among cards exiled this way without paying their mana costs. " + + "Then each player who owns a spell you cast this way loses life equal to its mana value"; + } + + private KefkaDancingMadEffect(final KefkaDancingMadEffect effect) { + super(effect); + } + + @Override + public KefkaDancingMadEffect copy() { + return new KefkaDancingMadEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Cards cards = new CardsImpl(); + for (UUID playerId : game.getOpponents(source.getControllerId())) { + Player player = game.getPlayer(playerId); + if (player != null) { + cards.add(player.getGraveyard().getRandom(game)); + } + } + if (cards.isEmpty()) { + return false; + } + controller.moveCards(cards, Zone.EXILED, source, game); + cards.retainZone(Zone.EXILED, game); + KefkaDancingMadTracker tracker = new KefkaDancingMadTracker(); + CardUtil.castMultipleWithAttributeForFree( + controller, source, game, cards, StaticFilters.FILTER_CARD, + Integer.MAX_VALUE, tracker, false + ); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + tracker.loseLife(playerId, game, source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/k/KefkaRulerOfRuin.java b/Mage.Sets/src/mage/cards/k/KefkaRulerOfRuin.java new file mode 100644 index 00000000000..aa642b63b2b --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KefkaRulerOfRuin.java @@ -0,0 +1,54 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.common.LoseLifeTriggeredAbility; +import mage.abilities.condition.common.MyTurnCondition; +import mage.abilities.dynamicvalue.common.SavedLifeLossValue; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KefkaRulerOfRuin extends CardImpl { + + public KefkaRulerOfRuin(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.AVATAR); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(5); + this.toughness = new MageInt(7); + this.nightCard = true; + this.color.setBlue(true); + this.color.setBlack(true); + this.color.setRed(true); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever an opponent loses life during your turn, you draw that many cards. + this.addAbility(new LoseLifeTriggeredAbility( + new DrawCardSourceControllerEffect(SavedLifeLossValue.MANY, true), + TargetController.OPPONENT, false, false + ).withTriggerCondition(MyTurnCondition.instance)); + } + + private KefkaRulerOfRuin(final KefkaRulerOfRuin card) { + super(card); + } + + @Override + public KefkaRulerOfRuin copy() { + return new KefkaRulerOfRuin(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KefnetsMonument.java b/Mage.Sets/src/mage/cards/k/KefnetsMonument.java index 1350482e94f..87763e026e9 100644 --- a/Mage.Sets/src/mage/cards/k/KefnetsMonument.java +++ b/Mage.Sets/src/mage/cards/k/KefnetsMonument.java @@ -1,7 +1,6 @@ package mage.cards.k; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; @@ -12,26 +11,23 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SuperType; -import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.FilterSpell; import mage.filter.StaticFilters; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class KefnetsMonument extends CardImpl { private static final FilterCard filter = new FilterCard("Blue creature spells"); - private static final FilterSpell filter2 = new FilterSpell("a creature spell"); static { filter.add(Predicates.and(new ColorPredicate(ObjectColor.BLUE), CardType.CREATURE.getPredicate())); - filter2.add(CardType.CREATURE.getPredicate()); } public KefnetsMonument(UUID ownerId, CardSetInfo setInfo) { @@ -43,7 +39,10 @@ public final class KefnetsMonument extends CardImpl { this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 1))); // Whenever you cast a creature spell, target creature an opponent controls doesn't untap during its controller's next untap step. - Ability ability = new SpellCastControllerTriggeredAbility(new DontUntapInControllersNextUntapStepTargetEffect(), filter2, false); + Ability ability = new SpellCastControllerTriggeredAbility( + new DontUntapInControllersNextUntapStepTargetEffect(), + StaticFilters.FILTER_SPELL_A_CREATURE, false + ); ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/k/KeldonStrikeTeam.java b/Mage.Sets/src/mage/cards/k/KeldonStrikeTeam.java index 99490a7c82d..d718011d349 100644 --- a/Mage.Sets/src/mage/cards/k/KeldonStrikeTeam.java +++ b/Mage.Sets/src/mage/cards/k/KeldonStrikeTeam.java @@ -49,7 +49,7 @@ public final class KeldonStrikeTeam extends CardImpl { new GainAbilityControlledEffect( HasteAbility.getInstance(), Duration.WhileOnBattlefield, StaticFilters.FILTER_CONTROLLED_CREATURE - ), SourceEnteredThisTurnCondition.instance, "as long as {this} " + + ), SourceEnteredThisTurnCondition.DID, "as long as {this} " + "entered the battlefield this turn, creatures you control have haste" ))); } diff --git a/Mage.Sets/src/mage/cards/k/KembaKhaEnduring.java b/Mage.Sets/src/mage/cards/k/KembaKhaEnduring.java index 302e0deb7f4..f67c526b8d7 100644 --- a/Mage.Sets/src/mage/cards/k/KembaKhaEnduring.java +++ b/Mage.Sets/src/mage/cards/k/KembaKhaEnduring.java @@ -13,7 +13,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledPermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.permanent.EquippedPredicate; import mage.game.Game; @@ -29,11 +29,10 @@ import java.util.UUID; public final class KembaKhaEnduring extends CardImpl { private static final FilterPermanent filter = new FilterPermanent(SubType.CAT, "Cat"); - private static final FilterPermanent filter2 = new FilterControlledPermanent(SubType.EQUIPMENT); - private static final FilterCreaturePermanent filter3 = new FilterCreaturePermanent("equipped creatures"); + private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("equipped creatures"); static { - filter3.add(EquippedPredicate.instance); + filter2.add(EquippedPredicate.instance); } public KembaKhaEnduring(UUID ownerId, CardSetInfo setInfo) { @@ -49,12 +48,12 @@ public final class KembaKhaEnduring extends CardImpl { Ability ability = new EntersBattlefieldThisOrAnotherTriggeredAbility( new KembaKhaEnduringEffect(), filter, false, true ); - ability.addTarget(new TargetPermanent(0, 1, filter2)); + ability.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT)); this.addAbility(ability); // Equipped creatures you control get +1/+1. this.addAbility(new SimpleStaticAbility(new BoostControlledEffect( - 1, 1, Duration.WhileOnBattlefield, filter3 + 1, 1, Duration.WhileOnBattlefield, filter2 ))); // {3}{W}{W}: Create a 2/2 white Cat creature token. diff --git a/Mage.Sets/src/mage/cards/k/KemuriOnna.java b/Mage.Sets/src/mage/cards/k/KemuriOnna.java index ac916b867b4..96189a95f97 100644 --- a/Mage.Sets/src/mage/cards/k/KemuriOnna.java +++ b/Mage.Sets/src/mage/cards/k/KemuriOnna.java @@ -33,7 +33,7 @@ public final class KemuriOnna extends CardImpl { ability.addTarget(new TargetPlayer()); this.addAbility(ability); // Whenever you cast a Spirit or Arcane spell, you may return Kemuri-Onna to its owner's hand. - this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, true)); } private KemuriOnna(final KemuriOnna card) { diff --git a/Mage.Sets/src/mage/cards/k/KenessosPriestOfThassa.java b/Mage.Sets/src/mage/cards/k/KenessosPriestOfThassa.java index 5bb7751a0fb..4649890498c 100644 --- a/Mage.Sets/src/mage/cards/k/KenessosPriestOfThassa.java +++ b/Mage.Sets/src/mage/cards/k/KenessosPriestOfThassa.java @@ -131,8 +131,8 @@ class KenessosPriestOfThassaActivatedEffect extends OneShotEffect { return player.moveCards(card, Zone.BATTLEFIELD, source, game); } if (player.chooseUse(outcome, "Put " + card.getName() + " on the bottom of your library?", source, game)) { - return player.putCardsOnBottomOfLibrary(card, game, source, true); + return player.putCardsOnBottomOfLibrary(card, game, source); } return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/k/KheruLichLord.java b/Mage.Sets/src/mage/cards/k/KheruLichLord.java index 6079b97837e..ee72c41cc72 100644 --- a/Mage.Sets/src/mage/cards/k/KheruLichLord.java +++ b/Mage.Sets/src/mage/cards/k/KheruLichLord.java @@ -23,6 +23,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -79,7 +80,7 @@ class KheruLichLordEffect extends OneShotEffect { Card card = cards.getRandom(game); if (card != null) { controller.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { FixedTarget blueprintTarget = new FixedTarget(permanent, game); ContinuousEffect effect = new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn); diff --git a/Mage.Sets/src/mage/cards/k/KheruSpellsnatcher.java b/Mage.Sets/src/mage/cards/k/KheruSpellsnatcher.java index 2007ecb6737..02308a13a9f 100644 --- a/Mage.Sets/src/mage/cards/k/KheruSpellsnatcher.java +++ b/Mage.Sets/src/mage/cards/k/KheruSpellsnatcher.java @@ -56,7 +56,7 @@ class KheruSpellsnatcherEffect extends OneShotEffect { super(Outcome.Benefit); this.staticText = "counter target spell. If that spell is countered this way, " + "exile it instead of putting it into its owner's graveyard. " - + "You may cast that card without paying its mana cost as long as it remains exiled"; + + "You may cast that card without paying its mana cost for as long as it remains exiled"; } private KheruSpellsnatcherEffect(final KheruSpellsnatcherEffect effect) { diff --git a/Mage.Sets/src/mage/cards/k/KillerInstinct.java b/Mage.Sets/src/mage/cards/k/KillerInstinct.java index 877ac5baf9f..dfa17cdc4d9 100644 --- a/Mage.Sets/src/mage/cards/k/KillerInstinct.java +++ b/Mage.Sets/src/mage/cards/k/KillerInstinct.java @@ -25,6 +25,7 @@ import mage.game.permanent.Permanent; import mage.players.Library; import mage.players.Player; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @@ -82,7 +83,7 @@ class KillerInstinctEffect extends OneShotEffect { } player.revealCards(sourceObject.getIdName(), new CardsImpl(card), game); if (card.isCreature(game) && player.moveCards(card, Zone.BATTLEFIELD, source, game)) { - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { FixedTarget blueprintTarget = new FixedTarget(permanent, game); ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); diff --git a/Mage.Sets/src/mage/cards/k/KinTreeInvocation.java b/Mage.Sets/src/mage/cards/k/KinTreeInvocation.java index 4803e5b4672..7ac3c2e7e52 100644 --- a/Mage.Sets/src/mage/cards/k/KinTreeInvocation.java +++ b/Mage.Sets/src/mage/cards/k/KinTreeInvocation.java @@ -1,22 +1,20 @@ package mage.cards.k; -import java.util.UUID; - import mage.abilities.Ability; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.game.permanent.token.SpiritWarriorToken; -import mage.game.permanent.token.Token; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class KinTreeInvocation extends CardImpl { @@ -26,7 +24,7 @@ public final class KinTreeInvocation extends CardImpl { // Create an X/X black and green Spirit Warrior creature token, where X is the greatest toughness among creatures you control. this.getSpellAbility().addEffect(new KinTreeInvocationCreateTokenEffect()); - + this.getSpellAbility().addHint(GreatestAmongPermanentsValue.TOUGHNESS_CONTROLLED_CREATURES.getHint()); } private KinTreeInvocation(final KinTreeInvocation card) { @@ -57,16 +55,8 @@ class KinTreeInvocationCreateTokenEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - int value = Integer.MIN_VALUE; - for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game)) { - if (value < permanent.getToughness().getValue()) { - value = permanent.getToughness().getValue(); - } - } - Token token = new SpiritWarriorToken(value); - token.getAbilities().newId(); // neccessary if token has ability like DevourAbility() - token.putOntoBattlefield(1, game, source, source.getControllerId()); - return true; + int value = GreatestAmongPermanentsValue.TOUGHNESS_CONTROLLED_CREATURES.calculate(game, source, this); + return (new CreateTokenEffect(new SpiritWarriorToken(value))).apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/k/KiriOnna.java b/Mage.Sets/src/mage/cards/k/KiriOnna.java index 8328d3b120d..8a0a715f536 100644 --- a/Mage.Sets/src/mage/cards/k/KiriOnna.java +++ b/Mage.Sets/src/mage/cards/k/KiriOnna.java @@ -33,7 +33,7 @@ public final class KiriOnna extends CardImpl { ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); // Whenever you cast a Spirit or Arcane spell, you may return Kiri-Onna to its owner's hand. - this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, true)); } private KiriOnna(final KiriOnna card) { diff --git a/Mage.Sets/src/mage/cards/k/KirinTouchedOrochi.java b/Mage.Sets/src/mage/cards/k/KirinTouchedOrochi.java index 09d03710924..254d48fc2e5 100644 --- a/Mage.Sets/src/mage/cards/k/KirinTouchedOrochi.java +++ b/Mage.Sets/src/mage/cards/k/KirinTouchedOrochi.java @@ -17,9 +17,9 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; import mage.counters.CounterType; +import mage.filter.FilterCard; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreatureCard; -import mage.filter.common.FilterNoncreatureCard; +import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.permanent.token.SpiritToken; import mage.players.Player; @@ -32,7 +32,10 @@ import mage.target.common.TargetControlledCreaturePermanent; */ public final class KirinTouchedOrochi extends CardImpl { - private static final FilterNoncreatureCard filter2 = new FilterNoncreatureCard("noncreature card from a graveyard"); + private static final FilterCard filter = new FilterCard("noncreature card from a graveyard"); + static { + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); + } public KirinTouchedOrochi(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); @@ -51,7 +54,7 @@ public final class KirinTouchedOrochi extends CardImpl { // • Exile target noncreature card from a graveyard. When you do, put a +1/+1 counter on target creature you control. Mode mode = new Mode(new KirinTouchedOrochiCounterEffect()); - mode.addTarget(new TargetCardInGraveyard(filter2)); + mode.addTarget(new TargetCardInGraveyard(filter)); ability.addMode(mode); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/k/KitesailLarcenist.java b/Mage.Sets/src/mage/cards/k/KitesailLarcenist.java index 69eabbe7bb5..1a1e933b462 100644 --- a/Mage.Sets/src/mage/cards/k/KitesailLarcenist.java +++ b/Mage.Sets/src/mage/cards/k/KitesailLarcenist.java @@ -15,12 +15,9 @@ import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.AnotherPredicate; -import mage.filter.predicate.permanent.ControllerIdPredicate; -import mage.game.Game; import mage.game.permanent.token.TreasureToken; -import mage.players.Player; import mage.target.TargetPermanent; -import mage.target.targetadjustment.TargetAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; import java.util.UUID; @@ -30,6 +27,16 @@ import java.util.UUID; */ public final class KitesailLarcenist extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent("other target artifact or creature"); + + static { + filter.add(AnotherPredicate.instance); + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.CREATURE.getPredicate() + )); + } + public KitesailLarcenist(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); @@ -53,7 +60,9 @@ public final class KitesailLarcenist extends CardImpl { "target artifact or creature that player controls. For as long as {this} " + "remains on the battlefield, the chosen permanents become Treasure artifacts with " + "\"{T}, Sacrifice this artifact: Add one mana of any color\" and lose all other abilities")); - this.addAbility(ability.setTargetAdjuster(KitesailLarcenistAdjuster.instance)); + ability.addTarget(new TargetPermanent(0, 1, filter)); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, false)); + this.addAbility(ability); } private KitesailLarcenist(final KitesailLarcenist card) { @@ -65,28 +74,3 @@ public final class KitesailLarcenist extends CardImpl { return new KitesailLarcenist(this); } } - -enum KitesailLarcenistAdjuster implements TargetAdjuster { - instance; - - @Override - public void adjustTargets(Ability ability, Game game) { - ability.getTargets().clear(); - for (UUID playerId : game.getState().getPlayersInRange(ability.getControllerId(), game)) { - Player player = game.getPlayer(playerId); - if (player == null) { - continue; - } - FilterPermanent filter = new FilterPermanent( - "other artifact or creature " + (ability.isControlledBy(playerId) ? " you control" : player.getName() + " controls") - ); - filter.add(new ControllerIdPredicate(playerId)); - filter.add(AnotherPredicate.instance); - filter.add(Predicates.or( - CardType.ARTIFACT.getPredicate(), - CardType.CREATURE.getPredicate() - )); - ability.addTarget(new TargetPermanent(0, 1, filter)); - } - } -} diff --git a/Mage.Sets/src/mage/cards/k/KithkinArmor.java b/Mage.Sets/src/mage/cards/k/KithkinArmor.java index 0db15948f2e..e367ce69c25 100644 --- a/Mage.Sets/src/mage/cards/k/KithkinArmor.java +++ b/Mage.Sets/src/mage/cards/k/KithkinArmor.java @@ -1,9 +1,6 @@ package mage.cards.k; -import java.util.UUID; -import mage.constants.SubType; -import mage.game.events.DamageEvent; -import mage.target.common.TargetCreaturePermanent; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; @@ -12,18 +9,22 @@ import mage.abilities.costs.CostImpl; import mage.abilities.effects.PreventionEffectImpl; import mage.abilities.effects.RestrictionEffect; import mage.abilities.effects.common.AttachEffect; -import mage.constants.Outcome; -import mage.target.TargetPermanent; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.Outcome; +import mage.constants.SubType; import mage.game.Game; +import mage.game.events.DamageEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; import mage.target.TargetSource; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; /** * @@ -40,8 +41,7 @@ public final class KithkinArmor extends CardImpl { TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget); - this.addAbility(ability); + this.addAbility(new EnchantAbility(auraTarget)); // Enchanted creature can't be blocked by creatures with power 3 or greater. this.addAbility(new SimpleStaticAbility(new KithkinArmorRestrictionEffect())); @@ -50,7 +50,6 @@ public final class KithkinArmor extends CardImpl { Ability protectionAbility = new SimpleActivatedAbility( new KithkinArmorPreventionEffect(), new KithkinArmorCost()); - protectionAbility.addTarget(new TargetSource()); this.addAbility(protectionAbility); } @@ -137,6 +136,7 @@ class KithkinArmorRestrictionEffect extends RestrictionEffect { class KithkinArmorPreventionEffect extends PreventionEffectImpl { + private MageObjectReference mageObjectReference; KithkinArmorPreventionEffect() { super(Duration.EndOfTurn, Integer.MAX_VALUE, false); staticText = "The next time a source of your choice would deal damage to enchanted creature this turn, prevent that damage"; @@ -144,13 +144,20 @@ class KithkinArmorPreventionEffect extends PreventionEffectImpl { private KithkinArmorPreventionEffect(final KithkinArmorPreventionEffect effect) { super(effect); + mageObjectReference = effect.mageObjectReference; } @Override public KithkinArmorPreventionEffect copy() { return new KithkinArmorPreventionEffect(this); } - + @Override + public void init(Ability source, Game game) { + super.init(source, game); + TargetSource target = new TargetSource(); + target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); + mageObjectReference = new MageObjectReference(target.getFirstTarget(), game); + } @Override public boolean applies(GameEvent event, Ability source, Game game) { if (super.applies(event, source, game) diff --git a/Mage.Sets/src/mage/cards/k/Knightfisher.java b/Mage.Sets/src/mage/cards/k/Knightfisher.java index 35291fcb2e9..3f54f354aa4 100644 --- a/Mage.Sets/src/mage/cards/k/Knightfisher.java +++ b/Mage.Sets/src/mage/cards/k/Knightfisher.java @@ -1,7 +1,7 @@ package mage.cards.k; import mage.MageInt; -import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -9,6 +9,8 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.permanent.token.FishNoAbilityToken; @@ -19,9 +21,10 @@ import java.util.UUID; */ public final class Knightfisher extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent(SubType.BIRD, "nontoken Bird"); + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.BIRD, "another nontoken Bird you control"); static { + filter.add(AnotherPredicate.instance); filter.add(TokenPredicate.FALSE); } @@ -37,9 +40,7 @@ public final class Knightfisher extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever another nontoken Bird you control enters, create a 1/1 blue Fish creature token. - this.addAbility(new EntersBattlefieldControlledTriggeredAbility( - new CreateTokenEffect(new FishNoAbilityToken()), filter - )); + this.addAbility(new EntersBattlefieldAllTriggeredAbility(new CreateTokenEffect(new FishNoAbilityToken()), filter)); } private Knightfisher(final Knightfisher card) { diff --git a/Mage.Sets/src/mage/cards/k/KodamaOfTheEastTree.java b/Mage.Sets/src/mage/cards/k/KodamaOfTheEastTree.java index 0d07e5dca56..a012f1f02d9 100644 --- a/Mage.Sets/src/mage/cards/k/KodamaOfTheEastTree.java +++ b/Mage.Sets/src/mage/cards/k/KodamaOfTheEastTree.java @@ -25,6 +25,7 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInHand; +import mage.util.CardUtil; import mage.watchers.Watcher; import java.util.*; @@ -134,7 +135,7 @@ class KodamaOfTheEastTreeEffect extends OneShotEffect { return false; } player.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent otherPermanent = game.getPermanent(card.getId()); + Permanent otherPermanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (otherPermanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/k/KodamaOfTheSouthTree.java b/Mage.Sets/src/mage/cards/k/KodamaOfTheSouthTree.java index b0562ec0d0d..8c00fb6b64c 100644 --- a/Mage.Sets/src/mage/cards/k/KodamaOfTheSouthTree.java +++ b/Mage.Sets/src/mage/cards/k/KodamaOfTheSouthTree.java @@ -29,7 +29,7 @@ public final class KodamaOfTheSouthTree extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - Ability ability = new SpellCastControllerTriggeredAbility(new BoostControlledEffect(1, 1, Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURE, true), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false); + Ability ability = new SpellCastControllerTriggeredAbility(new BoostControlledEffect(1, 1, Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURE, true), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, false); ability.addEffect(new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURE, true)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/k/KorOutfitter.java b/Mage.Sets/src/mage/cards/k/KorOutfitter.java index 66cb79241d4..51cf26ad6f5 100644 --- a/Mage.Sets/src/mage/cards/k/KorOutfitter.java +++ b/Mage.Sets/src/mage/cards/k/KorOutfitter.java @@ -1,36 +1,26 @@ - package mage.cards.k; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachTargetToTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.filter.common.FilterControlledPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; -import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; /** - * * @author North */ public final class KorOutfitter extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("Equipment you control"); - - static { - filter.add(SubType.EQUIPMENT.getPredicate()); - } - public KorOutfitter(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{W}"); this.subtype.add(SubType.KOR); this.subtype.add(SubType.SOLDIER); @@ -38,8 +28,8 @@ public final class KorOutfitter extends CardImpl { this.toughness = new MageInt(2); // When Kor Outfitter enters the battlefield, you may attach target Equipment you control to target creature you control. - Ability ability = new EntersBattlefieldTriggeredAbility(new EquipEffect(), true); - ability.addTarget(new TargetControlledPermanent(filter)); + Ability ability = new EntersBattlefieldTriggeredAbility(new AttachTargetToTargetEffect(), true); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT)); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); } @@ -53,30 +43,3 @@ public final class KorOutfitter extends CardImpl { return new KorOutfitter(this); } } - -class EquipEffect extends OneShotEffect { - - EquipEffect() { - super(Outcome.BoostCreature); - staticText = "attach target Equipment you control to target creature you control"; - } - - private EquipEffect(final EquipEffect effect) { - super(effect); - } - - @Override - public EquipEffect copy() { - return new EquipEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent equipment = game.getPermanent(source.getFirstTarget()); - Permanent creature = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if (creature != null && equipment != null) { - return creature.addAttachment(equipment.getId(), source, game); - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/k/KothOfTheHammer.java b/Mage.Sets/src/mage/cards/k/KothOfTheHammer.java index 006aaafbfa1..14ee8e04dd6 100644 --- a/Mage.Sets/src/mage/cards/k/KothOfTheHammer.java +++ b/Mage.Sets/src/mage/cards/k/KothOfTheHammer.java @@ -1,41 +1,36 @@ package mage.cards.k; -import java.util.UUID; import mage.MageInt; import mage.Mana; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.mana.DynamicManaEffect; import mage.abilities.effects.common.GetEmblemEffect; import mage.abilities.effects.common.UntapTargetEffect; import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect; +import mage.abilities.effects.mana.DynamicManaEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.TargetController; -import mage.filter.common.FilterLandPermanent; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.game.command.emblems.KothOfTheHammerEmblem; import mage.game.permanent.token.TokenImpl; -import mage.target.common.TargetLandPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author Loki, North */ public final class KothOfTheHammer extends CardImpl { - static final FilterLandPermanent filter = new FilterLandPermanent(SubType.MOUNTAIN, "Mountain"); - static final FilterLandPermanent filterCount = new FilterLandPermanent("Mountain you control"); - - static { - filterCount.add(SubType.MOUNTAIN.getPredicate()); - filterCount.add(TargetController.YOU.getControllerPredicate()); - } + static final FilterPermanent filter = new FilterPermanent(SubType.MOUNTAIN, "Mountain"); + static final FilterPermanent filterCount = new FilterControlledPermanent("Mountain you control"); public KothOfTheHammer(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{R}{R}"); @@ -47,7 +42,7 @@ public final class KothOfTheHammer extends CardImpl { // +1: Untap target Mountain. It becomes a 4/4 red Elemental creature until end of turn. It's still a land. Ability ability = new LoyaltyAbility(new UntapTargetEffect(), 1); ability.addEffect(new BecomesCreatureTargetEffect(new KothOfTheHammerToken(), false, true, Duration.EndOfTurn)); - ability.addTarget(new TargetLandPermanent(filter)); + ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); // -2: Add {R} for each Mountain you control. @@ -78,6 +73,7 @@ class KothOfTheHammerToken extends TokenImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); } + private KothOfTheHammerToken(final KothOfTheHammerToken token) { super(token); } diff --git a/Mage.Sets/src/mage/cards/k/KotisSibsigChampion.java b/Mage.Sets/src/mage/cards/k/KotisSibsigChampion.java index 2a5ea82c714..49fc09d007a 100644 --- a/Mage.Sets/src/mage/cards/k/KotisSibsigChampion.java +++ b/Mage.Sets/src/mage/cards/k/KotisSibsigChampion.java @@ -1,7 +1,7 @@ package mage.cards.k; import mage.MageInt; -import mage.abilities.common.CastFromGraveyardOnceEachTurnAbility; +import mage.abilities.common.CastFromGraveyardOnceDuringEachOfYourTurnAbility; import mage.abilities.common.EntersBattlefieldOneOrMoreTriggeredAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.ExileFromGraveCost; @@ -12,7 +12,6 @@ import mage.constants.*; import mage.counters.CounterType; import mage.filter.FilterCard; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.events.ZoneChangeEvent; @@ -23,21 +22,19 @@ import mage.target.common.TargetCardInYourGraveyard; import java.util.UUID; /** - * * @author Jmlundeen */ public final class KotisSibsigChampion extends CardImpl { - private static final FilterCreatureCard filter = new FilterCreatureCard("a creature spell"); - private static final FilterCard filter2 = new FilterCard("other cards"); + private static final FilterCard filter = new FilterCard("other cards"); static { - filter2.add(AnotherPredicate.instance); + filter.add(AnotherPredicate.instance); } public KotisSibsigChampion(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{G}{U}"); - + this.supertype.add(SuperType.LEGENDARY); this.subtype.add(SubType.ZOMBIE); this.subtype.add(SubType.WARRIOR); @@ -45,9 +42,9 @@ public final class KotisSibsigChampion extends CardImpl { this.toughness = new MageInt(3); // Once during each of your turns, you may cast a creature spell from your graveyard by exiling three other cards from your graveyard in addition to paying its other costs. - Cost cost = new ExileFromGraveCost(new TargetCardInYourGraveyard(3, filter2)); + Cost cost = new ExileFromGraveCost(new TargetCardInYourGraveyard(3, filter)); cost.setText(cost.getText().replace("exile", "exiling")); - this.addAbility(new CastFromGraveyardOnceEachTurnAbility(filter, cost)); + this.addAbility(new CastFromGraveyardOnceDuringEachOfYourTurnAbility(StaticFilters.FILTER_CARD_A_CREATURE_SPELL, cost)); // Whenever one or more creatures you control enter, if one or more of them entered from a graveyard or was cast from a graveyard, put two +1/+1 counters on Kotis. this.addAbility(new KotisSibsigTriggeredAbility()); diff --git a/Mage.Sets/src/mage/cards/k/KukemssaPirates.java b/Mage.Sets/src/mage/cards/k/KukemssaPirates.java index 6e063d28208..b493a7546d6 100644 --- a/Mage.Sets/src/mage/cards/k/KukemssaPirates.java +++ b/Mage.Sets/src/mage/cards/k/KukemssaPirates.java @@ -13,7 +13,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.common.FilterArtifactPermanent; import mage.filter.predicate.permanent.DefendingPlayerControlsSourceAttackingPredicate; -import mage.target.common.TargetArtifactPermanent; +import mage.target.TargetPermanent; import java.util.UUID; @@ -39,7 +39,7 @@ public final class KukemssaPirates extends CardImpl { // Whenever Kukemssa Pirates attacks and isn't blocked, you may gain control of target artifact defending player controls. If you do, Kukemssa Pirates assigns no combat damage this turn. Ability ability = new AttacksAndIsNotBlockedTriggeredAbility(new GainControlTargetEffect(Duration.Custom), true); ability.addEffect(new AssignNoCombatDamageSourceEffect(Duration.EndOfTurn, true)); - ability.addTarget(new TargetArtifactPermanent(filter)); + ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/k/KukemssaSerpent.java b/Mage.Sets/src/mage/cards/k/KukemssaSerpent.java index 55ec3a99f98..d60ca7f2fe7 100644 --- a/Mage.Sets/src/mage/cards/k/KukemssaSerpent.java +++ b/Mage.Sets/src/mage/cards/k/KukemssaSerpent.java @@ -14,21 +14,20 @@ import mage.abilities.effects.common.continuous.BecomesBasicLandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledLandPermanent; import mage.filter.common.FilterLandPermanent; -import mage.target.common.TargetControlledPermanent; -import mage.target.common.TargetLandPermanent; +import mage.target.TargetPermanent; import java.util.UUID; /** - * * @author fireshoes */ public final class KukemssaSerpent extends CardImpl { - private static final FilterLandPermanent filterOpponentLand = new FilterLandPermanent("land an opponent controls"); - private static final FilterControlledLandPermanent filterControlledLand = new FilterControlledLandPermanent("an Island"); + private static final FilterPermanent filterOpponentLand = new FilterLandPermanent("land an opponent controls"); + private static final FilterPermanent filterControlledLand = new FilterControlledLandPermanent("an Island"); static { filterOpponentLand.add(TargetController.OPPONENT.getControllerPredicate()); @@ -36,7 +35,7 @@ public final class KukemssaSerpent extends CardImpl { } public KukemssaSerpent(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); this.subtype.add(SubType.SERPENT); this.power = new MageInt(4); this.toughness = new MageInt(3); @@ -47,7 +46,7 @@ public final class KukemssaSerpent extends CardImpl { // {U}, Sacrifice an Island: Target land an opponent controls becomes an Island until end of turn. Ability ability = new SimpleActivatedAbility(new BecomesBasicLandTargetEffect(Duration.EndOfTurn, SubType.ISLAND), new ManaCostsImpl<>("{U}")); ability.addCost(new SacrificeTargetCost(filterControlledLand)); - ability.addTarget(new TargetLandPermanent(filterOpponentLand)); + ability.addTarget(new TargetPermanent(filterOpponentLand)); this.addAbility(ability); // When you control no Islands, sacrifice Kukemssa Serpent. diff --git a/Mage.Sets/src/mage/cards/k/KumanoFacesKakkazan.java b/Mage.Sets/src/mage/cards/k/KumanoFacesKakkazan.java index ae6783b04a5..b5008af8aa6 100644 --- a/Mage.Sets/src/mage/cards/k/KumanoFacesKakkazan.java +++ b/Mage.Sets/src/mage/cards/k/KumanoFacesKakkazan.java @@ -1,9 +1,7 @@ package mage.cards.k; -import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.SagaAbility; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.common.delayed.AddCounterNextSpellDelayedTriggeredAbility; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.DamageAllEffect; import mage.abilities.effects.common.DamagePlayersEffect; @@ -11,24 +9,22 @@ import mage.abilities.effects.common.ExileSagaAndReturnTransformedEffect; import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.counters.CounterType; +import mage.constants.CardType; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; import mage.filter.common.FilterPlaneswalkerPermanent; -import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.game.stack.Spell; import java.util.UUID; /** - * * @author weirddan455 */ public final class KumanoFacesKakkazan extends CardImpl { - private static final FilterPlaneswalkerPermanent filter = new FilterPlaneswalkerPermanent(); + private static final FilterPermanent filter = new FilterPlaneswalkerPermanent(); + static { filter.add(TargetController.OPPONENT.getControllerPredicate()); } @@ -49,7 +45,10 @@ public final class KumanoFacesKakkazan extends CardImpl { ); // II — When you cast your next creature spell this turn, that creature enters the battlefield with an additional +1/+1 counter on it. - sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II, new CreateDelayedTriggeredAbilityEffect(new KumanoFacesKakkazanTriggeredAbility())); + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_II, + new CreateDelayedTriggeredAbilityEffect(new AddCounterNextSpellDelayedTriggeredAbility()) + ); // III — Exile this Saga, then return it to the battlefield transformed under your control. this.addAbility(new TransformAbility()); @@ -67,81 +66,3 @@ public final class KumanoFacesKakkazan extends CardImpl { return new KumanoFacesKakkazan(this); } } - -class KumanoFacesKakkazanTriggeredAbility extends DelayedTriggeredAbility { - - KumanoFacesKakkazanTriggeredAbility() { - super(null, Duration.EndOfTurn); - } - - private KumanoFacesKakkazanTriggeredAbility(final KumanoFacesKakkazanTriggeredAbility ability) { - super(ability); - } - - @Override - public KumanoFacesKakkazanTriggeredAbility copy() { - return new KumanoFacesKakkazanTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.SPELL_CAST; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (this.isControlledBy(event.getPlayerId())) { - Spell spell = game.getSpell(event.getTargetId()); - if (spell != null && spell.isCreature(game)) { - this.getEffects().clear(); - this.getEffects().add(new KumanoFacesKakkazanCounterEffect(spell.getSourceId())); - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "When you next cast a creature spell this turn, that creature enters the battlefield with an additional +1/+1 counter on it."; - } -} - -class KumanoFacesKakkazanCounterEffect extends ReplacementEffectImpl { - - private final UUID spellCastId; - - KumanoFacesKakkazanCounterEffect(UUID spellCastId) { - super(Duration.EndOfTurn, Outcome.BoostCreature); - this.spellCastId = spellCastId; - } - - private KumanoFacesKakkazanCounterEffect(final KumanoFacesKakkazanCounterEffect effect) { - super(effect); - this.spellCastId = effect.spellCastId; - } - - @Override - public KumanoFacesKakkazanCounterEffect copy() { - return new KumanoFacesKakkazanCounterEffect(this); - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - return spellCastId.equals(event.getTargetId()); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget(); - if (creature != null) { - creature.addCounters(CounterType.P1P1.createInstance(), source.getControllerId(), source, game, event.getAppliedEffects()); - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/k/KyloxsVoltstrider.java b/Mage.Sets/src/mage/cards/k/KyloxsVoltstrider.java index 81d48b65a17..e0c8cf7ee23 100644 --- a/Mage.Sets/src/mage/cards/k/KyloxsVoltstrider.java +++ b/Mage.Sets/src/mage/cards/k/KyloxsVoltstrider.java @@ -157,7 +157,7 @@ class KyloxsVoltstriderReplacementEffect extends ReplacementEffectImpl { return false; } - controller.putCardsOnBottomOfLibrary(card, game, source, false); + controller.putCardsOnBottomOfLibrary(card, game, source); return true; } } diff --git a/Mage.Sets/src/mage/cards/k/KyokiSanitysEclipse.java b/Mage.Sets/src/mage/cards/k/KyokiSanitysEclipse.java index 80ad8e4e972..1395961e371 100644 --- a/Mage.Sets/src/mage/cards/k/KyokiSanitysEclipse.java +++ b/Mage.Sets/src/mage/cards/k/KyokiSanitysEclipse.java @@ -32,7 +32,7 @@ public final class KyokiSanitysEclipse extends CardImpl { // Whenever you cast a Spirit or Arcane spell, target opponent exiles a card from their hand. Ability ability = new SpellCastControllerTriggeredAbility( new ExileFromZoneTargetEffect(Zone.HAND, false), - StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false + StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, false ); ability.addTarget(new TargetOpponent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/l/LandCap.java b/Mage.Sets/src/mage/cards/l/LandCap.java index b0e41ca6f37..d2d40f20d1b 100644 --- a/Mage.Sets/src/mage/cards/l/LandCap.java +++ b/Mage.Sets/src/mage/cards/l/LandCap.java @@ -1,50 +1,50 @@ - package mage.cards.l; -import java.util.UUID; -import mage.Mana; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; -import mage.abilities.costs.common.TapSourceCost; import mage.abilities.decorator.ConditionalContinuousRuleModifyingEffect; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DontUntapInControllersUntapStepSourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; -import mage.abilities.mana.SimpleManaAbility; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; import mage.counters.CounterType; +import java.util.UUID; + /** - * * @author Luna Skyrise */ public final class LandCap extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.DEPLETION); + public LandCap(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // Land Cap doesn't untap during your untap step if it has a depletion counter on it. - Effect effect = new ConditionalContinuousRuleModifyingEffect(new DontUntapInControllersUntapStepSourceEffect(false, true), - new SourceHasCounterCondition(CounterType.DEPLETION, 1, Integer.MAX_VALUE)); - effect.setText("{this} doesn't untap during your untap step if it has a depletion counter on it"); - Ability ability = new SimpleStaticAbility(effect); - this.addAbility(ability); + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousRuleModifyingEffect( + new DontUntapInControllersUntapStepSourceEffect(false, true), condition + ).setText("{this} doesn't untap during your untap step if it has a depletion counter on it"))); + // At the beginning of your upkeep, remove a depletion counter from Land Cap. - Ability ability2 = new BeginningOfUpkeepTriggeredAbility(new RemoveCounterSourceEffect(CounterType.DEPLETION.createInstance())); - this.addAbility(ability2); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new RemoveCounterSourceEffect(CounterType.DEPLETION.createInstance()) + )); + // {T}: Add {W} or {U}. Put a depletion counter on Land Cap. - Ability ability3 = new SimpleManaAbility(Zone.BATTLEFIELD, Mana.WhiteMana(1), new TapSourceCost()); - ability3.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance())); - this.addAbility(ability3); - Ability ability4 = new SimpleManaAbility(Zone.BATTLEFIELD, Mana.BlueMana(1), new TapSourceCost()); - ability4.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance())); - this.addAbility(ability4); + Ability ability = new WhiteManaAbility(); + ability.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance())); + this.addAbility(ability); + Ability ability2 = new BlueManaAbility(); + ability2.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance())); + this.addAbility(ability2); } private LandCap(final LandCap card) { diff --git a/Mage.Sets/src/mage/cards/l/LanternOfRevealing.java b/Mage.Sets/src/mage/cards/l/LanternOfRevealing.java index d026d3a9cdd..ae02ff500d3 100644 --- a/Mage.Sets/src/mage/cards/l/LanternOfRevealing.java +++ b/Mage.Sets/src/mage/cards/l/LanternOfRevealing.java @@ -81,7 +81,7 @@ class LanternOfRevealingEffect extends OneShotEffect { if (game.getState().getZone(card.getId()) == Zone.LIBRARY && player.chooseUse( outcome, "Put " + card.getName() + " on the bottom of your library?", source, game )) { - player.putCardsOnBottomOfLibrary(card, game, source, false); + player.putCardsOnBottomOfLibrary(card, game, source); } return true; } diff --git a/Mage.Sets/src/mage/cards/l/LastLaugh.java b/Mage.Sets/src/mage/cards/l/LastLaugh.java index 89a2c391a8c..929369f04a7 100644 --- a/Mage.Sets/src/mage/cards/l/LastLaugh.java +++ b/Mage.Sets/src/mage/cards/l/LastLaugh.java @@ -22,7 +22,7 @@ import mage.game.events.GameEvent; */ public final class LastLaugh extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("a permanent other than Last Laugh"); + private static final FilterPermanent filter = new FilterPermanent("a permanent other than {this}"); static { filter.add(AnotherPredicate.instance); } diff --git a/Mage.Sets/src/mage/cards/l/LastMarchOfTheEnts.java b/Mage.Sets/src/mage/cards/l/LastMarchOfTheEnts.java index 95efa9a8b04..81d3d757f55 100644 --- a/Mage.Sets/src/mage/cards/l/LastMarchOfTheEnts.java +++ b/Mage.Sets/src/mage/cards/l/LastMarchOfTheEnts.java @@ -2,7 +2,7 @@ package mage.cards.l; import mage.abilities.Ability; import mage.abilities.common.CantBeCounteredSourceAbility; -import mage.abilities.dynamicvalue.common.GreatestToughnessAmongControlledCreaturesValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; @@ -32,10 +32,10 @@ public final class LastMarchOfTheEnts extends CardImpl { // Draw cards equal to the greatest toughness among creatures you control, then put any number of creature cards from your hand onto the battlefield. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect( - GreatestToughnessAmongControlledCreaturesValue.ALL + GreatestAmongPermanentsValue.TOUGHNESS_CONTROLLED_CREATURES ).setText("draw cards equal to the greatest toughness among creatures you control")); this.getSpellAbility().addEffect(new LastMarchOfTheEntsEffect()); - this.getSpellAbility().addHint(GreatestToughnessAmongControlledCreaturesValue.ALL.getHint()); + this.getSpellAbility().addHint(GreatestAmongPermanentsValue.TOUGHNESS_CONTROLLED_CREATURES.getHint()); } private LastMarchOfTheEnts(final LastMarchOfTheEnts card) { diff --git a/Mage.Sets/src/mage/cards/l/LatullasOrders.java b/Mage.Sets/src/mage/cards/l/LatullasOrders.java index c3ca0307682..3e274506c02 100644 --- a/Mage.Sets/src/mage/cards/l/LatullasOrders.java +++ b/Mage.Sets/src/mage/cards/l/LatullasOrders.java @@ -15,7 +15,7 @@ import mage.constants.SubType; import mage.filter.common.FilterArtifactPermanent; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -45,7 +45,7 @@ public final class LatullasOrders extends CardImpl { new DestroyTargetEffect(), "enchanted creature", true, true ).setTriggerPhrase("Whenever enchanted creature deals combat damage to defending player, "); ability.addTarget(new TargetPermanent(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/l/LavaTubes.java b/Mage.Sets/src/mage/cards/l/LavaTubes.java index 78de147fb20..23c02380bd9 100644 --- a/Mage.Sets/src/mage/cards/l/LavaTubes.java +++ b/Mage.Sets/src/mage/cards/l/LavaTubes.java @@ -1,50 +1,51 @@ package mage.cards.l; -import java.util.UUID; -import mage.Mana; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; -import mage.abilities.costs.common.TapSourceCost; import mage.abilities.decorator.ConditionalContinuousRuleModifyingEffect; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DontUntapInControllersUntapStepSourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; -import mage.abilities.mana.SimpleManaAbility; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.RedManaAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; import mage.counters.CounterType; +import java.util.UUID; + /** - * * @author Luna Skyrise */ public final class LavaTubes extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.DEPLETION); + public LavaTubes(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // Lava Tubes doesn't untap during your untap step if it has a depletion counter on it. - Effect effect = new ConditionalContinuousRuleModifyingEffect(new DontUntapInControllersUntapStepSourceEffect(false, true), - new SourceHasCounterCondition(CounterType.DEPLETION, 1, Integer.MAX_VALUE)); - effect.setText("{this} doesn't untap during your untap step if it has a depletion counter on it"); - Ability ability = new SimpleStaticAbility(effect); - this.addAbility(ability); + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousRuleModifyingEffect( + new DontUntapInControllersUntapStepSourceEffect(false, true), condition + ).setText("{this} doesn't untap during your untap step if it has a depletion counter on it"))); + // At the beginning of your upkeep, remove a depletion counter from Lava Tubes. - Ability ability2 = new BeginningOfUpkeepTriggeredAbility(new RemoveCounterSourceEffect(CounterType.DEPLETION.createInstance())); - this.addAbility(ability2); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new RemoveCounterSourceEffect(CounterType.DEPLETION.createInstance()) + )); + // {tap}: Add {B} or {R}. Put a depletion counter on Lava Tubes. - Ability ability3 = new SimpleManaAbility(Zone.BATTLEFIELD, Mana.BlackMana(1), new TapSourceCost()); - ability3.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance())); - this.addAbility(ability3); - Ability ability4 = new SimpleManaAbility(Zone.BATTLEFIELD, Mana.RedMana(1), new TapSourceCost()); - ability4.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance())); - this.addAbility(ability4); + Ability ability = new BlackManaAbility(); + ability.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance())); + this.addAbility(ability); + Ability ability2 = new RedManaAbility(); + ability2.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance())); + this.addAbility(ability2); } private LavaTubes(final LavaTubes card) { diff --git a/Mage.Sets/src/mage/cards/l/LaviniaAzoriusRenegade.java b/Mage.Sets/src/mage/cards/l/LaviniaAzoriusRenegade.java index 5dd5ac6e8d5..534cbbc9dd3 100644 --- a/Mage.Sets/src/mage/cards/l/LaviniaAzoriusRenegade.java +++ b/Mage.Sets/src/mage/cards/l/LaviniaAzoriusRenegade.java @@ -42,7 +42,7 @@ public final class LaviniaAzoriusRenegade extends CardImpl { // Whenever an opponent casts a spell, if no mana was spent to cast it, counter that spell. this.addAbility(new SpellCastOpponentTriggeredAbility( Zone.BATTLEFIELD, new CounterTargetEffect(), - StaticFilters.FILTER_SPELL_NO_MANA_SPENT, false, true + StaticFilters.FILTER_SPELL_NO_MANA_SPENT, false )); } diff --git a/Mage.Sets/src/mage/cards/l/LaylaHassan.java b/Mage.Sets/src/mage/cards/l/LaylaHassan.java index b3eaf7c2a2e..d18f44ca3b6 100644 --- a/Mage.Sets/src/mage/cards/l/LaylaHassan.java +++ b/Mage.Sets/src/mage/cards/l/LaylaHassan.java @@ -1,19 +1,7 @@ package mage.cards.l; -import java.util.UUID; import mage.MageInt; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.common.FilterHistoricCard; -import mage.game.Game; -import mage.game.events.DamagedBatchForOnePlayerEvent; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.common.TargetCardInYourGraveyard; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.OneOrMoreDamagePlayerTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; @@ -21,13 +9,26 @@ import mage.abilities.keyword.FirstStrikeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.HistoricPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; /** * @author balazskristof */ public final class LaylaHassan extends CardImpl { - private static final FilterHistoricCard filter = new FilterHistoricCard("historic card from your graveyard"); + private static final FilterCard filter = new FilterCard("historic card from your graveyard"); + static { + filter.add(HistoricPredicate.instance); + } public LaylaHassan(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); diff --git a/Mage.Sets/src/mage/cards/l/LeechBonder.java b/Mage.Sets/src/mage/cards/l/LeechBonder.java index 07759436689..42db886683d 100644 --- a/Mage.Sets/src/mage/cards/l/LeechBonder.java +++ b/Mage.Sets/src/mage/cards/l/LeechBonder.java @@ -6,28 +6,19 @@ import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.UntapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.common.counter.MoveCounterTargetsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.choices.Choice; -import mage.choices.ChoiceImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; -import mage.counters.Counter; import mage.counters.CounterType; +import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.other.AnotherTargetPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Set; import java.util.UUID; /** @@ -35,6 +26,12 @@ import java.util.UUID; */ public final class LeechBonder extends CardImpl { + private static final FilterPermanent filter = new FilterCreaturePermanent("a second target creature"); + + static { + filter.add(new AnotherTargetPredicate(2)); + } + public LeechBonder(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); this.subtype.add(SubType.MERFOLK); @@ -47,21 +44,11 @@ public final class LeechBonder extends CardImpl { this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.M1M1.createInstance(2)), "with two -1/-1 counters on it")); // {U}, {untap}: Move a counter from target creature onto another target creature. - Ability ability = new SimpleActivatedAbility(new LeechBonderEffect(), new ManaCostsImpl<>("{U}")); + Ability ability = new SimpleActivatedAbility(new MoveCounterTargetsEffect(), new ManaCostsImpl<>("{U}")); ability.addCost(new UntapSourceCost()); - // target 1 - TargetCreaturePermanent target1 = new TargetCreaturePermanent(new FilterCreaturePermanent("creature to remove counter from")); - target1.setTargetTag(1); - ability.addTarget(target1); - // target 2 - FilterCreaturePermanent filter = new FilterCreaturePermanent("creature to put counter on"); - filter.add(new AnotherTargetPredicate(2)); - TargetCreaturePermanent target2 = new TargetCreaturePermanent(filter); - target2.setTargetTag(2); - ability.addTarget(target2); - + ability.addTarget(new TargetCreaturePermanent().withChooseHint("to remove a counter from").setTargetTag(1)); + ability.addTarget(new TargetPermanent(filter).withChooseHint("to move a counter to").setTargetTag(2)); this.addAbility(ability); - } private LeechBonder(final LeechBonder card) { @@ -73,53 +60,3 @@ public final class LeechBonder extends CardImpl { return new LeechBonder(this); } } - -class LeechBonderEffect extends OneShotEffect { - - LeechBonderEffect() { - super(Outcome.AIDontUseIt); - this.staticText = "Move a counter from target creature onto a second target creature"; - } - - private LeechBonderEffect(final LeechBonderEffect effect) { - super(effect); - } - - @Override - public LeechBonderEffect copy() { - return new LeechBonderEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent fromPermanent = game.getPermanent(source.getFirstTarget()); - Permanent toPermanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if (fromPermanent == null - || toPermanent == null - || controller == null) { - return false; - } - - Set possibleChoices = new LinkedHashSet<>(fromPermanent.getCounters(game).keySet()); - if (possibleChoices.isEmpty()) { - return false; - } - - Choice choice = new ChoiceImpl(false); - choice.setChoices(possibleChoices); - if (controller.choose(outcome, choice, game)) { - String chosen = choice.getChoice(); - if (fromPermanent.getCounters(game).containsKey(chosen)) { - CounterType counterType = CounterType.findByName(chosen); - if (counterType != null) { - Counter counter = counterType.createInstance(); - fromPermanent.removeCounters(counterType.getName(), 1, source, game); - toPermanent.addCounters(counter, source.getControllerId(), source, game); - return true; - } - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/l/LegionsToAshes.java b/Mage.Sets/src/mage/cards/l/LegionsToAshes.java index eb7be9f735c..4ab8eb5cbbe 100644 --- a/Mage.Sets/src/mage/cards/l/LegionsToAshes.java +++ b/Mage.Sets/src/mage/cards/l/LegionsToAshes.java @@ -1,8 +1,5 @@ package mage.cards.l; -import java.util.HashSet; -import java.util.UUID; - import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; @@ -18,10 +15,12 @@ import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.common.TargetNonlandPermanent; +import mage.target.TargetPermanent; + +import java.util.HashSet; +import java.util.UUID; /** - * * @author weirddan455 */ public final class LegionsToAshes extends CardImpl { @@ -31,7 +30,7 @@ public final class LegionsToAshes extends CardImpl { // Exile target nonland permanent an opponent controls and all tokens that player controls with the same name as that permanent. this.getSpellAbility().addEffect(new LegionsToAshesEffect()); - this.getSpellAbility().addTarget(new TargetNonlandPermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_NON_LAND)); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_NON_LAND)); } private LegionsToAshes(final LegionsToAshes card) { diff --git a/Mage.Sets/src/mage/cards/l/LifeOfTheParty.java b/Mage.Sets/src/mage/cards/l/LifeOfTheParty.java index 0c7345f43cb..86c13e6ed56 100644 --- a/Mage.Sets/src/mage/cards/l/LifeOfTheParty.java +++ b/Mage.Sets/src/mage/cards/l/LifeOfTheParty.java @@ -4,14 +4,15 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.SourceMatchesFilterCondition; import mage.abilities.dynamicvalue.common.CreaturesYouControlCount; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenCopyTargetEffect; import mage.abilities.effects.common.combat.GoadTargetEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; -import mage.abilities.hint.ValueHint; +import mage.abilities.hint.common.CreaturesYouControlHint; import mage.abilities.keyword.FirstStrikeAbility; import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.TrampleAbility; @@ -21,13 +22,14 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.permanent.TokenPredicate; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.PermanentToken; import mage.target.targetpointer.FixedTargets; -import java.util.ArrayList; -import java.util.List; +import java.util.HashSet; +import java.util.Set; import java.util.UUID; /** @@ -35,6 +37,14 @@ import java.util.UUID; */ public final class LifeOfTheParty extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent("it's not a token"); + + static { + filter.add(TokenPredicate.FALSE); + } + + private static final Condition condition = new SourceMatchesFilterCondition(filter); + public LifeOfTheParty(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); @@ -55,15 +65,11 @@ public final class LifeOfTheParty extends CardImpl { this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect( CreaturesYouControlCount.instance, StaticValue.get(0), Duration.EndOfTurn, "it" - ).setText("it gets +X/+0 until end of turn, where X is the number of creatures you control") - ).addHint(new ValueHint("Creatures you control", CreaturesYouControlCount.instance))); + ).setText("it gets +X/+0 until end of turn, where X is the number of creatures you control")) + .addHint(CreaturesYouControlHint.instance)); // When Life of the Party enters the battlefield, if it's not a token, each opponent creates a token that's a copy of it. The tokens are goaded for the rest of the game. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new LifeOfThePartyEffect()), LifeOfTheParty::checkSource, - "When {this} enters, if it's not a token, each opponent creates a " + - "token that's a copy of it. The tokens are goaded for the rest of the game." - )); + this.addAbility(new EntersBattlefieldTriggeredAbility(new LifeOfThePartyEffect()).withInterveningIf(condition)); } private LifeOfTheParty(final LifeOfTheParty card) { @@ -74,16 +80,13 @@ public final class LifeOfTheParty extends CardImpl { public LifeOfTheParty copy() { return new LifeOfTheParty(this); } - - static boolean checkSource(Game game, Ability source) { - return !(source.getSourcePermanentOrLKI(game) instanceof PermanentToken); - } } class LifeOfThePartyEffect extends OneShotEffect { LifeOfThePartyEffect() { super(Outcome.Benefit); + staticText = "each opponent creates a token that's a copy of it. The tokens are goaded for the rest of the game"; } private LifeOfThePartyEffect(final LifeOfThePartyEffect effect) { @@ -101,7 +104,7 @@ class LifeOfThePartyEffect extends OneShotEffect { if (permanent == null) { return false; } - List permanents = new ArrayList<>(); + Set permanents = new HashSet<>(); for (UUID playerId : game.getOpponents(source.getControllerId())) { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(playerId); effect.setSavedPermanent(permanent); @@ -111,12 +114,8 @@ class LifeOfThePartyEffect extends OneShotEffect { if (permanents.isEmpty()) { return false; } - game.addEffect( - new GoadTargetEffect() - .setDuration(Duration.EndOfGame) - .setTargetPointer(new FixedTargets(permanents, game)), - source - ); + game.addEffect(new GoadTargetEffect(Duration.EndOfGame) + .setTargetPointer(new FixedTargets(permanents, game)), source); return true; } } diff --git a/Mage.Sets/src/mage/cards/l/LifecraftAwakening.java b/Mage.Sets/src/mage/cards/l/LifecraftAwakening.java index 14b6adf5ddb..90f7b7c73de 100644 --- a/Mage.Sets/src/mage/cards/l/LifecraftAwakening.java +++ b/Mage.Sets/src/mage/cards/l/LifecraftAwakening.java @@ -16,13 +16,12 @@ import mage.filter.common.FilterArtifactPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.TokenImpl; -import mage.target.common.TargetArtifactPermanent; +import mage.target.TargetPermanent; import mage.target.targetpointer.FixedTarget; import java.util.UUID; /** - * * @author fireshoes */ public final class LifecraftAwakening extends CardImpl { @@ -40,7 +39,7 @@ public final class LifecraftAwakening extends CardImpl { getSpellAbility().addEffect(new AddCountersTargetEffect( CounterType.P1P1.createInstance(), GetXValue.instance ).setText("put X +1/+1 counters on target artifact you control")); - getSpellAbility().addTarget(new TargetArtifactPermanent(filter)); + getSpellAbility().addTarget(new TargetPermanent(filter)); getSpellAbility().addEffect(new LifecraftAwakeningEffect()); } @@ -94,6 +93,7 @@ class LifecraftAwakeningToken extends TokenImpl { this.power = new MageInt(0); this.toughness = new MageInt(0); } + private LifecraftAwakeningToken(final LifecraftAwakeningToken token) { super(token); } diff --git a/Mage.Sets/src/mage/cards/l/LifecraftEngine.java b/Mage.Sets/src/mage/cards/l/LifecraftEngine.java index dae3206bb16..f7d3493c669 100644 --- a/Mage.Sets/src/mage/cards/l/LifecraftEngine.java +++ b/Mage.Sets/src/mage/cards/l/LifecraftEngine.java @@ -68,7 +68,7 @@ class LifecraftEngineAddSubTypeAllEffect extends ContinuousEffectImpl { public LifecraftEngineAddSubTypeAllEffect() { super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit); - staticText = "Vehicle creatures you control are the chosen type in addition to their other types."; + staticText = "Vehicle creatures you control are the chosen creature type in addition to their other types."; } private LifecraftEngineAddSubTypeAllEffect(final LifecraftEngineAddSubTypeAllEffect effect) { diff --git a/Mage.Sets/src/mage/cards/l/LifestreamsBlessing.java b/Mage.Sets/src/mage/cards/l/LifestreamsBlessing.java new file mode 100644 index 00000000000..450b5f036bf --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LifestreamsBlessing.java @@ -0,0 +1,141 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.MageObject; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.keyword.ForetellAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.WatcherScope; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LifestreamsBlessing extends CardImpl { + + public LifestreamsBlessing(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{G}{G}"); + + // Draw X cards, where X is the greatest power among creatures you controlled as you cast this spell. If this spell was cast from exile, you gain twice X life. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(LifestreamsBlessingValue.ONCE)); + this.getSpellAbility().addEffect(new GainLifeEffect(LifestreamsBlessingValue.TWICE) + .setText("if this spell was cast from exile, you gain twice X life")); + this.getSpellAbility().addWatcher(new LifestreamsBlessingWatcher()); + + // Foretell {4}{G} + this.addAbility(new ForetellAbility(this, "{4}{G}")); + } + + private LifestreamsBlessing(final LifestreamsBlessing card) { + super(card); + } + + @Override + public LifestreamsBlessing copy() { + return new LifestreamsBlessing(this); + } +} + +enum LifestreamsBlessingValue implements DynamicValue { + ONCE(false), + TWICE(true); + private final boolean flag; + + LifestreamsBlessingValue(boolean flag) { + this.flag = flag; + } + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + if (flag && !Optional + .ofNullable(sourceAbility) + .map(Ability::getSourceId) + .map(game::getSpell) + .map(Spell::getFromZone) + .map(Zone.EXILED::match) + .orElse(false)) { + return 0; + } + return (flag ? 2 : 1) * LifestreamsBlessingWatcher.getValue(game, sourceAbility); + } + + @Override + public LifestreamsBlessingValue copy() { + return this; + } + + @Override + public String getMessage() { + return "the greatest power among creatures you controlled as you cast this spell"; + } + + @Override + public String toString() { + return "X"; + } +} + +class LifestreamsBlessingWatcher extends Watcher { + + private final Map map = new HashMap<>(); + + LifestreamsBlessingWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.SPELL_CAST) { + return; + } + Spell spell = game.getSpell(event.getTargetId()); + if (spell == null) { + return; + } + map.put( + new MageObjectReference(spell.getSpellAbility().getSourceId(), game), + game.getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_CONTROLLED_CREATURE, spell.getControllerId(), game + ) + .stream() + .map(MageObject::getPower) + .mapToInt(MageInt::getValue) + .max() + .orElse(0) + ); + } + + @Override + public void reset() { + super.reset(); + map.clear(); + } + + static int getValue(Game game, Ability source) { + return game + .getState() + .getWatcher(LifestreamsBlessingWatcher.class) + .map + .getOrDefault(new MageObjectReference( + source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game + ), 0); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LightOfJudgment.java b/Mage.Sets/src/mage/cards/l/LightOfJudgment.java new file mode 100644 index 00000000000..9b8410a1aeb --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LightOfJudgment.java @@ -0,0 +1,89 @@ +package mage.cards.l; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.PermanentIdPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetImpl; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class LightOfJudgment extends CardImpl { + + public LightOfJudgment(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{R}"); + + // Light of Judgment deals 6 damage to target creature. Destroy up to one Equipment attached to that creature. + this.getSpellAbility().addEffect(new DamageTargetEffect(6)); + this.getSpellAbility().addEffect(new LightOfJudgmentEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private LightOfJudgment(final LightOfJudgment card) { + super(card); + } + + @Override + public LightOfJudgment copy() { + return new LightOfJudgment(this); + } +} + +class LightOfJudgmentEffect extends OneShotEffect { + + LightOfJudgmentEffect() { + super(Outcome.Benefit); + staticText = "Destroy up to one Equipment attached to that creature"; + } + + private LightOfJudgmentEffect(final LightOfJudgmentEffect effect) { + super(effect); + } + + @Override + public LightOfJudgmentEffect copy() { + return new LightOfJudgmentEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (player == null || permanent == null || permanent.getAttachments().isEmpty()) { + return false; + } + FilterPermanent filter = new FilterPermanent(SubType.EQUIPMENT, "Equipment attached to " + permanent.getIdName()); + filter.add(Predicates.or( + permanent + .getAttachments() + .stream() + .map(PermanentIdPredicate::new) + .collect(Collectors.toSet()) + )); + TargetPermanent target = new TargetPermanent(0, 1, filter, true); + player.choose(outcome, target, source, game); + return Optional + .ofNullable(target) + .map(TargetImpl::getFirstTarget) + .map(game::getPermanent) + .map(p -> p.destroy(source, game)) + .orElse(false); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LightningArmyOfOne.java b/Mage.Sets/src/mage/cards/l/LightningArmyOfOne.java new file mode 100644 index 00000000000..835a197cd11 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LightningArmyOfOne.java @@ -0,0 +1,107 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LightningArmyOfOne extends CardImpl { + + public LightningArmyOfOne(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Stagger -- Whenever Lightning deals combat damage to a player, 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. + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( + new LightningArmyOfOneEffect(), false, true + ).withFlavorWord("Stagger")); + } + + private LightningArmyOfOne(final LightningArmyOfOne card) { + super(card); + } + + @Override + public LightningArmyOfOne copy() { + return new LightningArmyOfOne(this); + } +} + +class LightningArmyOfOneEffect extends ReplacementEffectImpl { + + LightningArmyOfOneEffect() { + super(Duration.UntilYourNextTurn, Outcome.Benefit); + staticText = "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"; + } + + private LightningArmyOfOneEffect(final LightningArmyOfOneEffect effect) { + super(effect); + } + + @Override + public LightningArmyOfOneEffect copy() { + return new LightningArmyOfOneEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + switch (event.getType()) { + case DAMAGE_PLAYER: + case DAMAGE_PERMANENT: + return true; + default: + return false; + } + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + UUID targetId = this.getTargetPointer().getFirst(game, source); + if (targetId == null) { + return false; + } + switch (event.getType()) { + case DAMAGE_PLAYER: + return targetId.equals(event.getPlayerId()); + case DAMAGE_PERMANENT: + return targetId.equals(game.getControllerId(event.getTargetId())); + default: + return false; + } + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + event.setAmount(CardUtil.overflowMultiply(event.getAmount(), 2)); + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/l/Lightwalker.java b/Mage.Sets/src/mage/cards/l/Lightwalker.java index 3ddc8f25aef..c6877f09737 100644 --- a/Mage.Sets/src/mage/cards/l/Lightwalker.java +++ b/Mage.Sets/src/mage/cards/l/Lightwalker.java @@ -1,9 +1,8 @@ - package mage.cards.l; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; @@ -12,26 +11,29 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; import mage.counters.CounterType; +import java.util.UUID; + /** - * * @author fireshoes */ public final class Lightwalker extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.P1P1); + public Lightwalker(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WARRIOR); this.power = new MageInt(2); this.toughness = new MageInt(1); - // Lightwalker has flying as long as it has a +1/+1 counter on it. - this.addAbility(new SimpleStaticAbility( - new ConditionalContinuousEffect(new GainAbilitySourceEffect(FlyingAbility.getInstance()), - new SourceHasCounterCondition(CounterType.P1P1),"Lightwalker has flying as long as it has a +1/+1 counter on it"))); + // Lightwalker has flying as long as it has a +1/+1 counter on it. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(FlyingAbility.getInstance()), condition, + "{this} has flying as long as it has a +1/+1 counter on it" + ))); } private Lightwalker(final Lightwalker card) { diff --git a/Mage.Sets/src/mage/cards/l/LightwielderPaladin.java b/Mage.Sets/src/mage/cards/l/LightwielderPaladin.java index 00d395d0cbd..3203a1ebd15 100644 --- a/Mage.Sets/src/mage/cards/l/LightwielderPaladin.java +++ b/Mage.Sets/src/mage/cards/l/LightwielderPaladin.java @@ -15,7 +15,7 @@ import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; import mage.target.TargetPermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -46,7 +46,7 @@ public final class LightwielderPaladin extends CardImpl { // Whenever Lightwielder Paladin deals combat damage to a player, you may exile target black or red permanent that player controls. Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new ExileTargetEffect(), true, true); ability.addTarget(new TargetPermanent(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/l/LimDulTheNecromancer.java b/Mage.Sets/src/mage/cards/l/LimDulTheNecromancer.java index 96cae253c0d..ad25e1f1b72 100644 --- a/Mage.Sets/src/mage/cards/l/LimDulTheNecromancer.java +++ b/Mage.Sets/src/mage/cards/l/LimDulTheNecromancer.java @@ -16,12 +16,12 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -88,7 +88,7 @@ class LimDulTheNecromancerEffect extends OneShotEffect { if (card != null) { if (controller.moveCards(card, Zone.BATTLEFIELD, source, game) && card.isCreature(game)) { - Permanent creature = game.getPermanent(card.getId()); + Permanent creature = CardUtil.getPermanentFromCardPutToBattlefield(card, game); ContinuousEffect effect = new AddCardSubTypeTargetEffect(SubType.ZOMBIE, Duration.WhileOnBattlefield); effect.setTargetPointer(new FixedTarget(creature.getId(), game)); game.addEffect(effect, source); diff --git a/Mage.Sets/src/mage/cards/l/LindblumIndustrialRegency.java b/Mage.Sets/src/mage/cards/l/LindblumIndustrialRegency.java new file mode 100644 index 00000000000..b1d08de83f6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LindblumIndustrialRegency.java @@ -0,0 +1,44 @@ +package mage.cards.l; + +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.mana.RedManaAbility; +import mage.cards.AdventureCard; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.BlackWizardToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LindblumIndustrialRegency extends AdventureCard { + + public LindblumIndustrialRegency(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, new CardType[]{CardType.INSTANT}, "", "Mage Siege", "{2}{R}"); + + this.subtype.add(SubType.TOWN); + + // This land enters tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {R}. + this.addAbility(new RedManaAbility()); + + // Mage Siege + // Create a 0/1 black Wizard creature token with "Whenever you cast a noncreature spell, this token deals 1 damage to each opponent." + this.getSpellCard().getSpellAbility().addEffect(new CreateTokenEffect(new BlackWizardToken())); + this.finalizeAdventure(); + } + + private LindblumIndustrialRegency(final LindblumIndustrialRegency card) { + super(card); + } + + @Override + public LindblumIndustrialRegency copy() { + return new LindblumIndustrialRegency(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LionHeart.java b/Mage.Sets/src/mage/cards/l/LionHeart.java new file mode 100644 index 00000000000..715fada643a --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LionHeart.java @@ -0,0 +1,47 @@ +package mage.cards.l; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LionHeart extends CardImpl { + + public LionHeart(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); + + this.subtype.add(SubType.EQUIPMENT); + + // When this Equipment enters, it deals 2 damage to any target. + Ability ability = new EntersBattlefieldTriggeredAbility(new DamageTargetEffect(2, "it")); + ability.addTarget(new TargetAnyTarget()); + this.addAbility(ability); + + // Equipped creature gets +2/+1. + this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(2, 1))); + + // Equip {2} + this.addAbility(new EquipAbility(2)); + } + + private LionHeart(final LionHeart card) { + super(card); + } + + @Override + public LionHeart copy() { + return new LionHeart(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LlawanCephalidEmpress.java b/Mage.Sets/src/mage/cards/l/LlawanCephalidEmpress.java index fb1032599f5..10978d95a8d 100644 --- a/Mage.Sets/src/mage/cards/l/LlawanCephalidEmpress.java +++ b/Mage.Sets/src/mage/cards/l/LlawanCephalidEmpress.java @@ -103,9 +103,7 @@ class LlawanCephalidRuleModifyingEffect extends ContinuousRuleModifyingEffectImp Player controller = game.getPlayer(source.getControllerId()); if (controller != null && game.isOpponent(controller, event.getPlayerId())) { Card card = game.getCard(event.getSourceId()); - if (card != null && filter.match(card, source.getControllerId(), game)) { - return true; - } + return card != null && filter.match(card, source.getControllerId(), source, game); } return false; } diff --git a/Mage.Sets/src/mage/cards/l/LoamDweller.java b/Mage.Sets/src/mage/cards/l/LoamDweller.java index 989ea0a8066..6aaad407200 100644 --- a/Mage.Sets/src/mage/cards/l/LoamDweller.java +++ b/Mage.Sets/src/mage/cards/l/LoamDweller.java @@ -27,7 +27,7 @@ public final class LoamDweller extends CardImpl { // Whenever you cast a Spirit or Arcane spell, you may put a land card from your hand onto the battlefield tapped. this.addAbility(new SpellCastControllerTriggeredAbility( new PutCardFromHandOntoBattlefieldEffect(StaticFilters.FILTER_CARD_LAND_A, false, true), - StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); + StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, true)); } private LoamDweller(final LoamDweller card) { diff --git a/Mage.Sets/src/mage/cards/l/LockeCole.java b/Mage.Sets/src/mage/cards/l/LockeCole.java new file mode 100644 index 00000000000..8f62ecb8b55 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LockeCole.java @@ -0,0 +1,48 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.effects.common.DrawDiscardControllerEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LockeCole extends CardImpl { + + public LockeCole(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Whenever Locke Cole deals combat damage to a player, draw a card, then discard a card. + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new DrawDiscardControllerEffect(1, 1))); + } + + private LockeCole(final LockeCole card) { + super(card); + } + + @Override + public LockeCole copy() { + return new LockeCole(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LockeTreasureHunter.java b/Mage.Sets/src/mage/cards/l/LockeTreasureHunter.java new file mode 100644 index 00000000000..844a2f5e185 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LockeTreasureHunter.java @@ -0,0 +1,205 @@ +package mage.cards.l; + +import mage.MageIdentifier; +import mage.MageInt; +import mage.MageObject; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesSourceEffect; +import mage.cards.*; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.TreasureToken; +import mage.players.Player; +import mage.util.CardUtil; +import mage.watchers.Watcher; + +import java.util.*; + +/** + * @author TheElk801 + */ +public final class LockeTreasureHunter extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures with greater power"); + + static { + filter.add(LockeTreasureHunterPredicate.instance); + } + + public LockeTreasureHunter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Locke can't be blocked by creatures with greater power. + this.addAbility(new SimpleStaticAbility(new CantBeBlockedByCreaturesSourceEffect(filter, Duration.WhileOnBattlefield))); + + // Mug -- Whenever Locke attacks, each player mills a card. If a land card was milled this way, create a Treasure token. Until end of turn, you may cast a spell from among those cards. + this.addAbility(new AttacksTriggeredAbility(new LockeTreasureHunterEffect()) + .withFlavorWord("Mug") + .setIdentifier(MageIdentifier.LockeTreasureHunterWatcher), new LockeTreasureHunterWatcher()); + } + + private LockeTreasureHunter(final LockeTreasureHunter card) { + super(card); + } + + @Override + public LockeTreasureHunter copy() { + return new LockeTreasureHunter(this); + } + + public static Ability makeTestAbility() { + Ability ability = new SimpleActivatedAbility( + new LockeTreasureHunterEffect(), new GenericManaCost(0) + ).setIdentifier(MageIdentifier.LockeTreasureHunterWatcher); + ability.addWatcher(new LockeTreasureHunterWatcher()); + return ability; + } +} + +enum LockeTreasureHunterPredicate implements ObjectSourcePlayerPredicate { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + return Optional + .ofNullable(input.getSource().getSourcePermanentIfItStillExists(game)) + .map(MageObject::getPower) + .map(MageInt::getValue) + .map(x -> x < input.getObject().getPower().getValue()) + .orElse(false); + } +} + +class LockeTreasureHunterEffect extends OneShotEffect { + + LockeTreasureHunterEffect() { + super(Outcome.Benefit); + staticText = "each player mills a card. If a land card was milled this way, " + + "create a Treasure token. Until end of turn, you may cast a spell from among those cards"; + } + + private LockeTreasureHunterEffect(final LockeTreasureHunterEffect effect) { + super(effect); + } + + @Override + public LockeTreasureHunterEffect copy() { + return new LockeTreasureHunterEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Cards cards = new CardsImpl(); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + cards.addAllCards(player.millCards(1, source, game).getCards(game)); + } + } + if (cards.isEmpty()) { + return false; + } + if (cards.count(StaticFilters.FILTER_CARD_LAND, game) > 0) { + new TreasureToken().putOntoBattlefield(1, game, source); + } + LockeTreasureHunterWatcher.saveCards(cards, game, source); + return true; + } +} + +class LockeTreasureHunterWatcher extends Watcher { + + private static class LockeTreasureHunterCondition implements Condition { + private final UUID permissionId; + + LockeTreasureHunterCondition(UUID permissionId) { + this.permissionId = permissionId; + } + + @Override + public boolean apply(Game game, Ability source) { + return game + .getState() + .getWatcher(LockeTreasureHunterWatcher.class) + .checkPermission(permissionId, source, game); + } + } + + // Maps cards to the specific instance that gave them permission to be cast + private final Map morMap = new HashMap<>(); + + // Maps permissions to the players who can use them + private static final Map playerPermissionMap = new HashMap<>(); + + // Tracks permissions which have already been used + private final Set usedSet = new HashSet<>(); + + LockeTreasureHunterWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.SPELL_CAST + || !event.hasApprovingIdentifier(MageIdentifier.LockeTreasureHunterWatcher)) { + return; + } + Optional.ofNullable(event) + .map(GameEvent::getTargetId) + .map(game::getSpell) + .map(spell -> new MageObjectReference(spell.getMainCard(), game, -1)) + .map(mor -> morMap.getOrDefault(mor, null)) + .ifPresent(usedSet::add); + } + + @Override + public void reset() { + super.reset(); + morMap.clear(); + playerPermissionMap.clear(); + usedSet.clear(); + } + + static void saveCards(Cards cards, Game game, Ability source) { + game.getState() + .getWatcher(LockeTreasureHunterWatcher.class) + .handleSaveCards(cards, game, source); + } + + private void handleSaveCards(Cards cards, Game game, Ability source) { + UUID permissionId = UUID.randomUUID(); + playerPermissionMap.put(permissionId, source.getControllerId()); + Condition condition = new LockeTreasureHunterCondition(permissionId); + for (Card card : cards.getCards(game)) { + morMap.put(new MageObjectReference(card, game), permissionId); + CardUtil.makeCardPlayable( + game, source, card, true, Duration.EndOfTurn, + false, source.getControllerId(), condition + ); + } + } + + private boolean checkPermission(UUID permissionId, Ability source, Game game) { + return !usedSet.contains(permissionId) + && source.isControlledBy(playerPermissionMap.getOrDefault(permissionId, null)); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LoneWolfOfTheNatterknolls.java b/Mage.Sets/src/mage/cards/l/LoneWolfOfTheNatterknolls.java index 886d757a064..76c89b0d922 100644 --- a/Mage.Sets/src/mage/cards/l/LoneWolfOfTheNatterknolls.java +++ b/Mage.Sets/src/mage/cards/l/LoneWolfOfTheNatterknolls.java @@ -4,9 +4,7 @@ import mage.MageInt; import mage.abilities.common.SpellCastOpponentTriggeredAbility; import mage.abilities.common.WerewolfBackTriggeredAbility; import mage.abilities.condition.common.MyTurnCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.abilities.hint.common.MyTurnHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -30,11 +28,9 @@ public final class LoneWolfOfTheNatterknolls extends CardImpl { this.nightCard = true; // Whenever an opponent cast a spell during your turn, draw two cards. - this.addAbility(new ConditionalTriggeredAbility( - new SpellCastOpponentTriggeredAbility(new DrawCardSourceControllerEffect(2), StaticFilters.FILTER_SPELL_A, false), - MyTurnCondition.instance, - "Whenever an opponent casts a spell during your turn, draw two cards." - ).addHint(MyTurnHint.instance)); + this.addAbility(new SpellCastOpponentTriggeredAbility( + new DrawCardSourceControllerEffect(2), StaticFilters.FILTER_SPELL_A, false + ).withTriggerCondition(MyTurnCondition.instance)); // At the beginning of each upkeep, if a player cast two or more spells last turn, transform Lone Wolf of the Natterknolls. this.addAbility(new WerewolfBackTriggeredAbility()); diff --git a/Mage.Sets/src/mage/cards/l/LongListOfTheEnts.java b/Mage.Sets/src/mage/cards/l/LongListOfTheEnts.java index aa9b9f02c9e..95abf6d63d5 100644 --- a/Mage.Sets/src/mage/cards/l/LongListOfTheEnts.java +++ b/Mage.Sets/src/mage/cards/l/LongListOfTheEnts.java @@ -1,21 +1,20 @@ package mage.cards.l; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.SagaAbility; +import mage.abilities.common.delayed.AddCounterNextSpellDelayedTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.hint.Hint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.choices.ChoiceCreatureType; -import mage.constants.*; -import mage.counters.CounterType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.filter.FilterSpell; +import mage.filter.common.FilterCreatureSpell; import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.game.stack.Spell; import mage.players.Player; import mage.util.CardUtil; @@ -134,91 +133,9 @@ class LongListOfTheEntsEffect extends OneShotEffect { game.informPlayers(player.getLogName() + " notes the creature type " + subType); newEntList.add(subType); game.getState().setValue(LongListOfTheEnts.getKey(game, source, offset), newEntList); - game.addDelayedTriggeredAbility(new LongListOfTheEntsTriggeredAbility(subType), source); + FilterSpell filter = new FilterCreatureSpell("a creature spell of that type"); + filter.add(subType.getPredicate()); + game.addDelayedTriggeredAbility(new AddCounterNextSpellDelayedTriggeredAbility(filter), source); return true; } - -} - -class LongListOfTheEntsTriggeredAbility extends DelayedTriggeredAbility { - - private final SubType subType; - - LongListOfTheEntsTriggeredAbility(SubType subType) { - super(new LongListOfTheEntsCounterEffect(), Duration.EndOfTurn, true, false); - this.subType = subType; - } - - private LongListOfTheEntsTriggeredAbility(final LongListOfTheEntsTriggeredAbility ability) { - super(ability); - this.subType = ability.subType; - } - - @Override - public LongListOfTheEntsTriggeredAbility copy() { - return new LongListOfTheEntsTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.SPELL_CAST; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (!isControlledBy(event.getPlayerId())) { - return false; - } - Spell spell = game.getSpell(event.getTargetId()); - if (spell == null || !spell.isCreature(game) || !spell.hasSubtype(subType, game)) { - return false; - } - this.getEffects().setValue("spellCast", spell); - return true; - } - - @Override - public String getRule() { - return "When you cast your next creature spell of that type this turn, that creature enters the battlefield with an additional +1/+1 counter on it."; - } -} - -class LongListOfTheEntsCounterEffect extends ReplacementEffectImpl { - - LongListOfTheEntsCounterEffect() { - super(Duration.EndOfStep, Outcome.BoostCreature); - } - - private LongListOfTheEntsCounterEffect(LongListOfTheEntsCounterEffect effect) { - super(effect); - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - Spell spell = (Spell) getValue("spellCast"); - return spell != null && event.getTargetId().equals(spell.getCard().getId()); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget(); - if (creature != null) { - creature.addCounters( - CounterType.P1P1.createInstance(), source.getControllerId(), - source, game, event.getAppliedEffects() - ); - discard(); - } - return false; - } - - @Override - public LongListOfTheEntsCounterEffect copy() { - return new LongListOfTheEntsCounterEffect(this); - } } diff --git a/Mage.Sets/src/mage/cards/l/LongstalkBrawl.java b/Mage.Sets/src/mage/cards/l/LongstalkBrawl.java index db52e9e26b8..e6032504f7e 100644 --- a/Mage.Sets/src/mage/cards/l/LongstalkBrawl.java +++ b/Mage.Sets/src/mage/cards/l/LongstalkBrawl.java @@ -33,7 +33,7 @@ public final class LongstalkBrawl extends CardImpl { GiftWasPromisedCondition.TRUE, "choose target creature you control and target creature " + "you don't control. Put a +1/+1 counter on the creature you control if the gift was promised" )); - this.getSpellAbility().addEffect(new FightTargetsEffect().setText("then those creatures fight each other")); + this.getSpellAbility().addEffect(new FightTargetsEffect().setText("Then those creatures fight each other")); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); } diff --git a/Mage.Sets/src/mage/cards/l/LootExuberantExplorer.java b/Mage.Sets/src/mage/cards/l/LootExuberantExplorer.java index feae082642f..346a2f6b8e7 100644 --- a/Mage.Sets/src/mage/cards/l/LootExuberantExplorer.java +++ b/Mage.Sets/src/mage/cards/l/LootExuberantExplorer.java @@ -13,7 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.card.ManaValueLessThanControlledLandCountPredicate; +import mage.filter.predicate.mageobject.ManaValueLessThanControlledLandCountPredicate; import java.util.UUID; diff --git a/Mage.Sets/src/mage/cards/l/LoporritScout.java b/Mage.Sets/src/mage/cards/l/LoporritScout.java new file mode 100644 index 00000000000..daf628ece9f --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LoporritScout.java @@ -0,0 +1,43 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LoporritScout extends CardImpl { + + public LoporritScout(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.RABBIT); + this.subtype.add(SubType.SCOUT); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Whenever another creature you control enters, this creature gets +1/+1 until end of turn. + this.addAbility(new EntersBattlefieldAllTriggeredAbility( + new BoostSourceEffect(1, 1, Duration.EndOfTurn), + StaticFilters.FILTER_ANOTHER_CREATURE_YOU_CONTROL + )); + } + + private LoporritScout(final LoporritScout card) { + super(card); + } + + @Override + public LoporritScout copy() { + return new LoporritScout(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LordJyscalGuado.java b/Mage.Sets/src/mage/cards/l/LordJyscalGuado.java index 4303d1247ca..f3705c78c4b 100644 --- a/Mage.Sets/src/mage/cards/l/LordJyscalGuado.java +++ b/Mage.Sets/src/mage/cards/l/LordJyscalGuado.java @@ -10,10 +10,7 @@ import mage.abilities.keyword.FlyingAbility; import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.constants.WatcherScope; +import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -41,7 +38,7 @@ public final class LordJyscalGuado extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // At the beginning of each end step, if you put a counter on a creature this turn, investigate. - this.addAbility(new BeginningOfEndStepTriggeredAbility(new InvestigateEffect()) + this.addAbility(new BeginningOfEndStepTriggeredAbility(TargetController.ANY, new InvestigateEffect(), false) .withInterveningIf(LordJyscalGuadoCondition.instance) .addHint(LordJyscalGuadoCondition.getHint()), new LordJyscalGuadoWatcher() diff --git a/Mage.Sets/src/mage/cards/l/LordWindgrace.java b/Mage.Sets/src/mage/cards/l/LordWindgrace.java index 9ecd26cdd61..c9c0828850c 100644 --- a/Mage.Sets/src/mage/cards/l/LordWindgrace.java +++ b/Mage.Sets/src/mage/cards/l/LordWindgrace.java @@ -1,6 +1,5 @@ package mage.cards.l; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.CanBeYourCommanderAbility; @@ -11,21 +10,18 @@ import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffec import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.constants.TargetController; +import mage.constants.*; import mage.filter.StaticFilters; import mage.filter.common.FilterLandCard; import mage.game.Game; import mage.game.permanent.token.CatWarriorToken; import mage.players.Player; +import mage.target.TargetPermanent; import mage.target.common.TargetCardInYourGraveyard; -import mage.target.common.TargetNonlandPermanent; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class LordWindgrace extends CardImpl { @@ -60,7 +56,7 @@ public final class LordWindgrace extends CardImpl { .setText(", then create six 2/2 green Cat Warrior " + "creature tokens with forestwalk") ); - ability.addTarget(new TargetNonlandPermanent(0, 6, StaticFilters.FILTER_PERMANENTS_NON_LAND, false)); + ability.addTarget(new TargetPermanent(0, 6, StaticFilters.FILTER_PERMANENTS_NON_LAND)); this.addAbility(ability); // Lord Windgrace can be your commander. diff --git a/Mage.Sets/src/mage/cards/l/LouisoixsSacrifice.java b/Mage.Sets/src/mage/cards/l/LouisoixsSacrifice.java new file mode 100644 index 00000000000..1e01f063bad --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LouisoixsSacrifice.java @@ -0,0 +1,63 @@ +package mage.cards.l; + +import mage.abilities.costs.CompositeCost; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterStackObject; +import mage.filter.StaticFilters; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.game.stack.StackObject; +import mage.target.TargetStackObject; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LouisoixsSacrifice extends CardImpl { + + private static final FilterStackObject filter + = new FilterStackObject("activated ability, triggered ability, or noncreature spell"); + + static { + filter.add(LouisoixsSacrificePredicate.instance); + } + + public LouisoixsSacrifice(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}"); + + // As an additional cost to cast this spell, sacrifice a legendary creature or pay {2}. + this.getSpellAbility().addCost(new CompositeCost( + new SacrificeTargetCost(StaticFilters.FILTER_CREATURE_LEGENDARY), + new GenericManaCost(2), "sacrifice a legendary creature or pay {2}" + )); + + // Counter target activated ability, triggered ability, or noncreature spell. + this.getSpellAbility().addEffect(new CounterTargetEffect()); + this.getSpellAbility().addTarget(new TargetStackObject(filter)); + } + + private LouisoixsSacrifice(final LouisoixsSacrifice card) { + super(card); + } + + @Override + public LouisoixsSacrifice copy() { + return new LouisoixsSacrifice(this); + } +} + +enum LouisoixsSacrificePredicate implements Predicate { + instance; + + @Override + public boolean apply(StackObject input, Game game) { + return !(input instanceof Spell) || !input.isCreature(game); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LukkaBoundToRuin.java b/Mage.Sets/src/mage/cards/l/LukkaBoundToRuin.java index 267548e8f33..de53a52db18 100644 --- a/Mage.Sets/src/mage/cards/l/LukkaBoundToRuin.java +++ b/Mage.Sets/src/mage/cards/l/LukkaBoundToRuin.java @@ -6,7 +6,7 @@ import mage.Mana; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.condition.Condition; -import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DamageMultiEffect; @@ -56,7 +56,7 @@ public class LukkaBoundToRuin extends CardImpl { "where X is the greatest power among creatures you control as you activate this ability."); ability = new LoyaltyAbility(damageMultiEffect, -4); ability.setTargetAdjuster(LukkaBoundToRuinAdjuster.instance); - ability.addHint(GreatestPowerAmongControlledCreaturesValue.getHint()); + ability.addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint()); this.addAbility(ability); } @@ -133,7 +133,7 @@ enum LukkaBoundToRuinAdjuster implements TargetAdjuster { public void adjustTargets(Ability ability, Game game) { // Maximum targets is equal to the damage - as each target need to be assigned at least 1 damage ability.getTargets().clear(); - int xValue = GreatestPowerAmongControlledCreaturesValue.instance.calculate(game, ability, null); + int xValue = GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.calculate(game, ability, null); ability.addTarget(new TargetCreatureOrPlaneswalkerAmount(xValue, 0, xValue)); } } diff --git a/Mage.Sets/src/mage/cards/l/LuminatePrimordial.java b/Mage.Sets/src/mage/cards/l/LuminatePrimordial.java index 33a07ca6a3f..aa4b6e3cfdd 100644 --- a/Mage.Sets/src/mage/cards/l/LuminatePrimordial.java +++ b/Mage.Sets/src/mage/cards/l/LuminatePrimordial.java @@ -17,7 +17,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import java.util.*; import java.util.stream.Collectors; @@ -41,7 +41,7 @@ public final class LuminatePrimordial extends CardImpl { // that player controls and that player gains life equal to its power. Ability ability = new EntersBattlefieldTriggeredAbility(new LuminatePrimordialEffect(), false); ability.addTarget(new TargetCreaturePermanent(0,1)); - ability.setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/l/LunaticPandora.java b/Mage.Sets/src/mage/cards/l/LunaticPandora.java new file mode 100644 index 00000000000..99c7665003f --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LunaticPandora.java @@ -0,0 +1,49 @@ +package mage.cards.l; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.keyword.SurveilEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SuperType; +import mage.target.common.TargetNonlandPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LunaticPandora extends CardImpl { + + public LunaticPandora(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); + + this.supertype.add(SuperType.LEGENDARY); + + // {2}, {T}: Surveil 1. + Ability ability = new SimpleActivatedAbility(new SurveilEffect(1), new GenericManaCost(2)); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + + // {6}, {T}, Sacrifice Lunatic Pandora: Destroy target nonland permanent. + ability = new SimpleActivatedAbility(new DestroyTargetEffect(), new GenericManaCost(6)); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new TargetNonlandPermanent()); + this.addAbility(ability); + } + + private LunaticPandora(final LunaticPandora card) { + super(card); + } + + @Override + public LunaticPandora copy() { + return new LunaticPandora(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LurkingDeadeye.java b/Mage.Sets/src/mage/cards/l/LurkingDeadeye.java index 3c2c3cdb556..77952066422 100644 --- a/Mage.Sets/src/mage/cards/l/LurkingDeadeye.java +++ b/Mage.Sets/src/mage/cards/l/LurkingDeadeye.java @@ -9,9 +9,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.WasDealtDamageThisTurnPredicate; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import java.util.UUID; @@ -21,13 +19,6 @@ import java.util.UUID; */ public final class LurkingDeadeye extends CardImpl { - private static final FilterPermanent filter - = new FilterCreaturePermanent("creature that was dealt damage this turn"); - - static { - filter.add(WasDealtDamageThisTurnPredicate.instance); - } - public LurkingDeadeye(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); @@ -41,7 +32,7 @@ public final class LurkingDeadeye extends CardImpl { // When Lurking Deadeye enters the battlefield, destroy target creature that was dealt damage this turn. Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect()); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_DAMAGED_THIS_TURN)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/l/LurrusOfTheDreamDen.java b/Mage.Sets/src/mage/cards/l/LurrusOfTheDreamDen.java index dece80f0725..bf7372a09d2 100644 --- a/Mage.Sets/src/mage/cards/l/LurrusOfTheDreamDen.java +++ b/Mage.Sets/src/mage/cards/l/LurrusOfTheDreamDen.java @@ -2,7 +2,7 @@ package mage.cards.l; import mage.MageInt; import mage.MageObject; -import mage.abilities.common.CastFromGraveyardOnceEachTurnAbility; +import mage.abilities.common.CastFromGraveyardOnceDuringEachOfYourTurnAbility; import mage.abilities.keyword.CompanionAbility; import mage.abilities.keyword.CompanionCondition; import mage.abilities.keyword.LifelinkAbility; @@ -13,8 +13,10 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.constants.SubType; import mage.constants.SuperType; +import mage.filter.FilterCard; import mage.filter.common.FilterPermanentCard; import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.filter.predicate.mageobject.PermanentPredicate; import java.util.Set; import java.util.UUID; @@ -24,9 +26,10 @@ import java.util.UUID; */ public final class LurrusOfTheDreamDen extends CardImpl { - private static final FilterPermanentCard filter = new FilterPermanentCard("a permanent spell with mana value 2 or less"); + private static final FilterCard filter = new FilterPermanentCard("a permanent spell with mana value 2 or less"); static { + filter.add(PermanentPredicate.instance); filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); } @@ -46,7 +49,7 @@ public final class LurrusOfTheDreamDen extends CardImpl { this.addAbility(LifelinkAbility.getInstance()); // During each of your turns, you may cast one permanent spell with converted mana cost 2 or less from your graveyard. - this.addAbility(new CastFromGraveyardOnceEachTurnAbility(filter)); + this.addAbility(new CastFromGraveyardOnceDuringEachOfYourTurnAbility(filter)); } private LurrusOfTheDreamDen(final LurrusOfTheDreamDen card) { diff --git a/Mage.Sets/src/mage/cards/l/LuxaRiverShrine.java b/Mage.Sets/src/mage/cards/l/LuxaRiverShrine.java index 4bac79d4cd7..44b5a6d7b5f 100644 --- a/Mage.Sets/src/mage/cards/l/LuxaRiverShrine.java +++ b/Mage.Sets/src/mage/cards/l/LuxaRiverShrine.java @@ -1,43 +1,39 @@ - package mage.cards.l; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.decorator.ConditionalActivatedAbility; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; import mage.counters.CounterType; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class LuxaRiverShrine extends CardImpl { - private static final String rule = "{T}: You gain 2 life. Activate only if there are three or more brick counters on {this}."; + private static final Condition condition = new SourceHasCounterCondition(CounterType.BRICK, 3); public LuxaRiverShrine(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); // {1}, {T}: You gain 1 life. Put a brick counter on Luxa River Shrine. - Ability ability = new SimpleActivatedAbility(new GainLifeEffect(1), new ManaCostsImpl<>("{1}")); + Ability ability = new SimpleActivatedAbility(new GainLifeEffect(1), new GenericManaCost(1)); ability.addCost(new TapSourceCost()); ability.addEffect(new AddCountersSourceEffect(CounterType.BRICK.createInstance())); this.addAbility(ability); // {T}: You gain 2 life. Activate this ability only if there are three or more brick counters on Luxa River Shrine. - Condition condition = new SourceHasCounterCondition(CounterType.BRICK, 3, Integer.MAX_VALUE); - this.addAbility(new ConditionalActivatedAbility(Zone.BATTLEFIELD, new GainLifeEffect(2), new TapSourceCost(), condition, rule)); - + this.addAbility(new ConditionalActivatedAbility(new GainLifeEffect(2), new TapSourceCost(), condition)); } private LuxaRiverShrine(final LuxaRiverShrine card) { diff --git a/Mage.Sets/src/mage/cards/l/LyevSkyknight.java b/Mage.Sets/src/mage/cards/l/LyevSkyknight.java index cc3d341e79b..49aa3faa84f 100644 --- a/Mage.Sets/src/mage/cards/l/LyevSkyknight.java +++ b/Mage.Sets/src/mage/cards/l/LyevSkyknight.java @@ -1,7 +1,6 @@ package mage.cards.l; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -11,40 +10,31 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.TargetController; -import mage.filter.common.FilterNonlandPermanent; -import mage.target.common.TargetNonlandPermanent; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class LyevSkyknight extends CardImpl { - private static final FilterNonlandPermanent filter = new FilterNonlandPermanent("nonland permanent an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public LyevSkyknight(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{W}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{U}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.KNIGHT); - - this.power = new MageInt(3); this.toughness = new MageInt(1); // Flying this.addAbility(FlyingAbility.getInstance()); - + // When Lyev Skyknight enters the battlefield, detain target nonland permanent an opponent controls. // (Until your next turn, that permanent can't attack or block and its activated abilities can't be activated.) Ability ability = new EntersBattlefieldTriggeredAbility(new DetainTargetEffect(), false); - TargetNonlandPermanent target = new TargetNonlandPermanent(filter); - ability.addTarget(target); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_NON_LAND)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/l/LyseHext.java b/Mage.Sets/src/mage/cards/l/LyseHext.java new file mode 100644 index 00000000000..a32865238c9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LyseHext.java @@ -0,0 +1,116 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.abilities.keyword.ProwessAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.watchers.common.SpellsCastWatcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class LyseHext extends CardImpl { + + private static final FilterCard filter = new FilterCard("noncreature spells"); + static { + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); + } + + public LyseHext(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.REBEL); + this.subtype.add(SubType.MONK); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Prowess + this.addAbility(new ProwessAbility()); + + // Noncreature spells you cast cost {1} less to cast. + this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 1))); + + // As long as you've cast two or more noncreature spells this turn, Lyse Hext has double strike. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect( + DoubleStrikeAbility.getInstance(), Duration.WhileOnBattlefield + ), LyseHextCondition.instance, "as long as you've cast two or more " + + "noncreature spells this turn, {this} has double strike" + )).addHint(LyseHextValue.getHint())); + } + + private LyseHext(final LyseHext card) { + super(card); + } + + @Override + public LyseHext copy() { + return new LyseHext(this); + } +} + +enum LyseHextCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + return LyseHextValue.instance.calculate(game, source, null) >= 2; + } +} + +enum LyseHextValue implements DynamicValue { + instance; + private static final Hint hint = new ValueHint("Noncreature spells you've cast this turn", instance); + + public static Hint getHint() { + return hint; + } + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return game + .getState() + .getWatcher(SpellsCastWatcher.class) + .getSpellsCastThisTurn(sourceAbility.getControllerId()) + .stream() + .mapToInt(spell -> spell.isCreature(game) ? 0 : 1) + .sum(); + } + + @Override + public LyseHextValue copy() { + return this; + } + + @Override + public String getMessage() { + return ""; + } + + @Override + public String toString() { + return "1"; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MacabreMockery.java b/Mage.Sets/src/mage/cards/m/MacabreMockery.java index d33b140b12d..76e1b60ab6c 100644 --- a/Mage.Sets/src/mage/cards/m/MacabreMockery.java +++ b/Mage.Sets/src/mage/cards/m/MacabreMockery.java @@ -22,6 +22,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInOpponentsGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -75,7 +76,7 @@ class MacabreMockeryEffect extends OneShotEffect { if (controller == null || !controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { return false; } - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/m/MachinistsArsenal.java b/Mage.Sets/src/mage/cards/m/MachinistsArsenal.java index 56df78e2728..e730429dd30 100644 --- a/Mage.Sets/src/mage/cards/m/MachinistsArsenal.java +++ b/Mage.Sets/src/mage/cards/m/MachinistsArsenal.java @@ -38,7 +38,7 @@ public final class MachinistsArsenal extends CardImpl { .setText("equipped creature gets +2/+2 for each artifact you control")); ability.addEffect(new AddCardSubtypeAttachedEffect( SubType.ARTIFICER, AttachmentType.EQUIPMENT - ).setText(", and is a Artificer in addition to its other types")); + ).setText("and is an Artificer in addition to its other types")); this.addAbility(ability.addHint(ArtifactYouControlHint.instance)); // Machina -- Equip {4} diff --git a/Mage.Sets/src/mage/cards/m/MaesterSeymour.java b/Mage.Sets/src/mage/cards/m/MaesterSeymour.java new file mode 100644 index 00000000000..a30bb7635a1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MaesterSeymour.java @@ -0,0 +1,102 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.SourcePermanentPowerValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.MonstrosityAbility; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.counters.Counters; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MaesterSeymour extends CardImpl { + + public MaesterSeymour(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ELF); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // At the beginning of combat on your turn, put a number of +1/+1 counters equal to Maester Seymour's power on another target creature you control. + Ability ability = new BeginningOfCombatTriggeredAbility(new AddCountersTargetEffect( + CounterType.P1P1.createInstance(), SourcePermanentPowerValue.NOT_NEGATIVE + ).setText("put a number of +1/+1 counters equal to {this}'s power on another target creature you control")); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_ANOTHER_TARGET_CREATURE_YOU_CONTROL)); + this.addAbility(ability); + + // {3}{G}{G}: Monstrosity X, where X is the number of counters among creatures you control. + this.addAbility(new MonstrosityAbility( + "{3}{G}{G}", MaesterSeymourValue.instance, null, "" + ).addHint(MaesterSeymourValue.getHint())); + } + + private MaesterSeymour(final MaesterSeymour card) { + super(card); + } + + @Override + public MaesterSeymour copy() { + return new MaesterSeymour(this); + } +} + +enum MaesterSeymourValue implements DynamicValue { + instance; + private static final Hint hint = new ValueHint( + "The number of counters among creatures you control", instance + ); + + public static Hint getHint() { + return hint; + } + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return game + .getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_CONTROLLED_CREATURE, + sourceAbility.getControllerId(), sourceAbility, game + ) + .stream() + .map(permanent -> permanent.getCounters(game)) + .mapToInt(Counters::getTotalCount) + .sum(); + } + + @Override + public MaesterSeymourValue copy() { + return this; + } + + @Override + public String getMessage() { + return "the number of counters among creatures you control"; + } + + @Override + public String toString() { + return "X"; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MagicDamper.java b/Mage.Sets/src/mage/cards/m/MagicDamper.java new file mode 100644 index 00000000000..acdbd8b4268 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MagicDamper.java @@ -0,0 +1,42 @@ +package mage.cards.m; + +import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.HexproofAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MagicDamper extends CardImpl { + + public MagicDamper(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}"); + + // Target creature you control gets +1/+1 and gains hexproof until end of turn. Untap it. + this.getSpellAbility().addEffect(new BoostTargetEffect( + 1, 1, Duration.EndOfTurn + ).setText("target creature you control gets +1/+1")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + HexproofAbility.getInstance(), Duration.EndOfTurn + ).setText("and gains hexproof until end of turn")); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + this.getSpellAbility().addEffect(new UntapTargetEffect("Untap it")); + } + + private MagicDamper(final MagicDamper card) { + super(card); + } + + @Override + public MagicDamper copy() { + return new MagicDamper(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MagicPot.java b/Mage.Sets/src/mage/cards/m/MagicPot.java new file mode 100644 index 00000000000..b59460a2677 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MagicPot.java @@ -0,0 +1,51 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.TreasureToken; +import mage.target.common.TargetCardInGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MagicPot extends CardImpl { + + public MagicPot(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); + + this.subtype.add(SubType.GOBLIN); + this.subtype.add(SubType.CONSTRUCT); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // When this creature dies, create a Treasure token. + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new TreasureToken()))); + + // {2}, {T}: Exile target card from a graveyard. + Ability ability = new SimpleActivatedAbility(new ExileTargetEffect(), new GenericManaCost(2)); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetCardInGraveyard()); + this.addAbility(ability); + } + + private MagicPot(final MagicPot card) { + super(card); + } + + @Override + public MagicPot copy() { + return new MagicPot(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MagickedCard.java b/Mage.Sets/src/mage/cards/m/MagickedCard.java new file mode 100644 index 00000000000..08ee1d907fb --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MagickedCard.java @@ -0,0 +1,42 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.keyword.CrewAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MagickedCard extends CardImpl { + + public MagickedCard(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, ""); + + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + this.nightCard = true; + this.color.setBlue(true); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Crew 1 + this.addAbility(new CrewAbility(1)); + } + + private MagickedCard(final MagickedCard card) { + super(card); + } + + @Override + public MagickedCard copy() { + return new MagickedCard(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MagitekArmor.java b/Mage.Sets/src/mage/cards/m/MagitekArmor.java new file mode 100644 index 00000000000..b398bbadd3d --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MagitekArmor.java @@ -0,0 +1,42 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.CrewAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.HeroToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MagitekArmor extends CardImpl { + + public MagitekArmor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}{W}"); + + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // When this Vehicle enters, create a 1/1 colorless Hero creature token. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new HeroToken()))); + + // Crew 1 + this.addAbility(new CrewAbility(1)); + } + + private MagitekArmor(final MagitekArmor card) { + super(card); + } + + @Override + public MagitekArmor copy() { + return new MagitekArmor(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MagitekInfantry.java b/Mage.Sets/src/mage/cards/m/MagitekInfantry.java new file mode 100644 index 00000000000..ffdc4f6a749 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MagitekInfantry.java @@ -0,0 +1,70 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MagitekInfantry extends CardImpl { + + private static final Condition condition + = new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_CONTROLLED_ANOTHER_ARTIFACT); + private static final Hint hint = new ConditionHint(condition, "You control another artifact"); + private static final FilterCard filter = new FilterCard("card named Magitek Infantry"); + + static { + filter.add(new NamePredicate("Magitek Infantry")); + } + + public MagitekInfantry(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{W}"); + + this.subtype.add(SubType.ROBOT); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // This creature gets +1/+0 as long as you control another artifact. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostSourceEffect(1, 0, Duration.WhileOnBattlefield), + condition, "{this} gets +1/+0 as long as you control another artifact" + )).addHint(hint)); + + // {2}{W}: Search your library for a card named Magitek Infantry, put it onto the battlefield tapped, then shuffle. + this.addAbility(new SimpleActivatedAbility( + new SearchLibraryPutInPlayEffect( + new TargetCardInLibrary(filter), true + ), new ManaCostsImpl<>("{2}{W}") + )); + } + + private MagitekInfantry(final MagitekInfantry card) { + super(card); + } + + @Override + public MagitekInfantry copy() { + return new MagitekInfantry(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MagneticMine.java b/Mage.Sets/src/mage/cards/m/MagneticMine.java index 5446f865141..8922a1c138c 100644 --- a/Mage.Sets/src/mage/cards/m/MagneticMine.java +++ b/Mage.Sets/src/mage/cards/m/MagneticMine.java @@ -1,33 +1,32 @@ package mage.cards.m; -import java.util.Objects; -import java.util.UUID; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.common.PutIntoGraveFromBattlefieldAllTriggeredAbility; +import mage.abilities.effects.common.DamageTargetControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; -import mage.target.TargetPlayer; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterArtifactPermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; + +import java.util.UUID; /** * * @author North */ public final class MagneticMine extends CardImpl { + private static final FilterPermanent filter = new FilterArtifactPermanent("another artifact"); + static { + filter.add(AnotherPredicate.instance); + } public MagneticMine(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); - // Whenever another artifact is put into a graveyard from the battlefield, Magnetic Mine deals 2 damage to that artifact’s controller. - MagneticMineTriggeredAbility ability = new MagneticMineTriggeredAbility(new DamageTargetEffect(2)); - ability.addTarget(new TargetPlayer().withNotTarget(true)); - this.addAbility(ability); + this.addAbility(new PutIntoGraveFromBattlefieldAllTriggeredAbility( + new DamageTargetControllerEffect(2, "artifact"), false, filter, true)); } private MagneticMine(final MagneticMine card) { @@ -39,41 +38,3 @@ public final class MagneticMine extends CardImpl { return new MagneticMine(this); } } - -class MagneticMineTriggeredAbility extends TriggeredAbilityImpl { - - public MagneticMineTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect, false); - } - - private MagneticMineTriggeredAbility(final MagneticMineTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ZONE_CHANGE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.isDiesEvent() - && zEvent.getTarget().isArtifact(game) - && !Objects.equals(zEvent.getTarget().getId(), this.getSourceId())) { - this.getTargets().get(0).add(zEvent.getTarget().getControllerId(), game); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever another artifact is put into a graveyard from the battlefield, {this} deals 2 damage to that artifact's controller"; - } - - @Override - public MagneticMineTriggeredAbility copy() { - return new MagneticMineTriggeredAbility(this); - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/m/MagneticTheft.java b/Mage.Sets/src/mage/cards/m/MagneticTheft.java index 11fd36897b6..23203ef05c3 100644 --- a/Mage.Sets/src/mage/cards/m/MagneticTheft.java +++ b/Mage.Sets/src/mage/cards/m/MagneticTheft.java @@ -1,39 +1,26 @@ - package mage.cards.m; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.AttachTargetToTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.filter.FilterPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author Plopman */ public final class MagneticTheft extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("Equipment"); - - static { - filter.add(SubType.EQUIPMENT.getPredicate()); - } - public MagneticTheft(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{R}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}"); // Attach target Equipment to target creature. - this.getSpellAbility().addEffect(new EquipEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addEffect(new AttachTargetToTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_EQUIPMENT)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } @@ -46,30 +33,3 @@ public final class MagneticTheft extends CardImpl { return new MagneticTheft(this); } } - -class EquipEffect extends OneShotEffect { - - EquipEffect() { - super(Outcome.BoostCreature); - staticText = "Attach target Equipment to target creature"; - } - - private EquipEffect(final EquipEffect effect) { - super(effect); - } - - @Override - public EquipEffect copy() { - return new EquipEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent equipment = game.getPermanent(source.getFirstTarget()); - Permanent creature = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if (creature != null && equipment != null) { - return creature.addAttachment(equipment.getId(), source, game); - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/m/MagusOfTheUnseen.java b/Mage.Sets/src/mage/cards/m/MagusOfTheUnseen.java index 54aeccaada4..585af1075b4 100644 --- a/Mage.Sets/src/mage/cards/m/MagusOfTheUnseen.java +++ b/Mage.Sets/src/mage/cards/m/MagusOfTheUnseen.java @@ -1,7 +1,6 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; @@ -18,29 +17,29 @@ import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.TargetController; -import mage.constants.Zone; import mage.filter.common.FilterArtifactPermanent; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.target.common.TargetArtifactPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author emerald000 */ public final class MagusOfTheUnseen extends CardImpl { - + private static final FilterArtifactPermanent filter = new FilterArtifactPermanent("artifact an opponent controls"); + static { filter.add(TargetController.OPPONENT.getControllerPredicate()); } public MagusOfTheUnseen(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WIZARD); @@ -50,7 +49,7 @@ public final class MagusOfTheUnseen extends CardImpl { // {1}{U}, {tap}: Untap target artifact an opponent controls and gain control of it until end of turn. It gains haste until end of turn. When you lose control of the artifact, tap it. Ability ability = new SimpleActivatedAbility(new UntapTargetEffect(), new ManaCostsImpl<>("{1}{U}")); ability.addCost(new TapSourceCost()); - ability.addTarget(new TargetArtifactPermanent(filter)); + ability.addTarget(new TargetPermanent(filter)); Effect effect = new GainControlTargetEffect(Duration.EndOfTurn); effect.setText("and gain control of it until end of turn. "); ability.addEffect(effect); @@ -88,15 +87,15 @@ class MagusOfTheUnseenDelayedTriggeredAbility extends DelayedTriggeredAbility { @Override public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(controllerId) + return event.getPlayerId().equals(controllerId) && event.getTargetId().equals(this.getEffects().get(0).getTargetPointer().getFirst(game, this)); } - + @Override public MagusOfTheUnseenDelayedTriggeredAbility copy() { return new MagusOfTheUnseenDelayedTriggeredAbility(this); } - + @Override public String getRule() { return "When you lose control of the artifact, tap it"; diff --git a/Mage.Sets/src/mage/cards/m/MajesticGenesis.java b/Mage.Sets/src/mage/cards/m/MajesticGenesis.java index ed6effe3c5a..7ee351f9249 100644 --- a/Mage.Sets/src/mage/cards/m/MajesticGenesis.java +++ b/Mage.Sets/src/mage/cards/m/MajesticGenesis.java @@ -26,7 +26,7 @@ public final class MajesticGenesis extends CardImpl { public MajesticGenesis(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{6}{G}{G}"); - // Reveal the top X cards of your library, where X is the highest mana value of a commander you own on the battlefield or in the command zone. You may put any number of a permanent cards from among them onto the battlefield. Put the rest on the bottom of your library in a random order. + // Reveal the top X cards of your library, where X is the greatest mana value of a commander you own on the battlefield or in the command zone. You may put any number of a permanent cards from among them onto the battlefield. Put the rest on the bottom of your library in a random order. this.getSpellAbility().addEffect(new MajesticGenesisEffect()); this.getSpellAbility().addHint(GreatestCommanderManaValue.getHint()); } diff --git a/Mage.Sets/src/mage/cards/m/MakeshiftMannequin.java b/Mage.Sets/src/mage/cards/m/MakeshiftMannequin.java index a075c356e47..09a468d465e 100644 --- a/Mage.Sets/src/mage/cards/m/MakeshiftMannequin.java +++ b/Mage.Sets/src/mage/cards/m/MakeshiftMannequin.java @@ -24,6 +24,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; +import mage.util.CardUtil; /** * @@ -84,7 +85,7 @@ class MakeshiftMannequinEffect extends OneShotEffect { counters.addCounter(CounterType.MANNEQUIN.createInstance()); game.setEnterWithCounters(cardId, counters); if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { - Permanent permanent = game.getPermanent(cardId); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { ContinuousEffect gainedEffect = new MakeshiftMannequinGainAbilityEffect(); // Bug #6885 Fixed when owner/controller leaves the game the effect still applies diff --git a/Mage.Sets/src/mage/cards/m/Malfegor.java b/Mage.Sets/src/mage/cards/m/Malfegor.java index 350a076c26b..8b81dc09beb 100644 --- a/Mage.Sets/src/mage/cards/m/Malfegor.java +++ b/Mage.Sets/src/mage/cards/m/Malfegor.java @@ -1,25 +1,26 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.SacrificeOpponentsEffect; -import mage.abilities.effects.common.discard.DiscardHandControllerEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.StaticFilters; +import mage.game.Controllable; import mage.game.Game; -import mage.players.Player; + +import java.util.Optional; +import java.util.Set; +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class Malfegor extends CardImpl { @@ -38,7 +39,6 @@ public final class Malfegor extends CardImpl { // When Malfegor enters the battlefield, discard your hand. Each opponent sacrifices a creature for each card discarded this way. this.addAbility(new EntersBattlefieldTriggeredAbility(new MalfegorEffect(), false)); - } private Malfegor(final Malfegor card) { @@ -64,16 +64,17 @@ class MalfegorEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { - return false; - } - int sacrificeNumber = controller.getHand().size(); - if (sacrificeNumber == 0) { - return true; - } - new DiscardHandControllerEffect().apply(game, source); - return new SacrificeOpponentsEffect(sacrificeNumber, StaticFilters.FILTER_CONTROLLED_CREATURE).apply(game, source); + return Optional + .ofNullable(source) + .map(Controllable::getControllerId) + .map(game::getPlayer) + .filter(player -> !player.getHand().isEmpty()) + .map(player -> player.discard(player.getHand(), false, source, game)) + .map(Set::size) + .filter(amount -> amount > 0 && new SacrificeOpponentsEffect( + amount, StaticFilters.FILTER_CONTROLLED_CREATURE + ).apply(game, source)) + .isPresent(); } @Override diff --git a/Mage.Sets/src/mage/cards/m/ManaBloom.java b/Mage.Sets/src/mage/cards/m/ManaBloom.java index b3653beb35c..e2aad460227 100644 --- a/Mage.Sets/src/mage/cards/m/ManaBloom.java +++ b/Mage.Sets/src/mage/cards/m/ManaBloom.java @@ -1,32 +1,29 @@ - package mage.cards.m; -import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.RemoveCountersSourceCost; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.EntersBattlefieldWithXCountersEffect; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.abilities.effects.mana.AddManaOfAnyColorEffect; import mage.abilities.mana.LimitedTimesPerTurnActivatedManaAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.Zone; import mage.counters.CounterType; import java.util.UUID; /** - * * @author LevelX2 */ public final class ManaBloom extends CardImpl { - static final String rule = "with X charge counters on it"; + private static final Condition condition = new SourceHasCounterCondition(CounterType.CHARGE, ComparisonType.EQUAL_TO, 0); public ManaBloom(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{X}{G}"); @@ -35,16 +32,15 @@ public final class ManaBloom extends CardImpl { this.addAbility(new EntersBattlefieldAbility(new EntersBattlefieldWithXCountersEffect(CounterType.CHARGE.createInstance()))); // Remove a charge counter from Mana Bloom: Add one mana of any color. Activate this ability only once each turn. - Ability ability = new LimitedTimesPerTurnActivatedManaAbility( + this.addAbility(new LimitedTimesPerTurnActivatedManaAbility( Zone.BATTLEFIELD, new AddManaOfAnyColorEffect(), new RemoveCountersSourceCost(CounterType.CHARGE.createInstance()) - ); - this.addAbility(ability); + )); // At the beginning of your upkeep, if Mana Bloom has no charge counters on it, return it to its owner's hand. - TriggeredAbility triggeredAbility = new BeginningOfUpkeepTriggeredAbility(new ReturnToHandSourceEffect(true)); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(triggeredAbility, new SourceHasCounterCondition(CounterType.CHARGE, 0, 0), "At the beginning of your upkeep, if Mana Bloom has no charge counters on it, return it to its owner's hand.")); - + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new ReturnToHandSourceEffect(true).setText("return it to its owner's hand") + ).withInterveningIf(condition)); } private ManaBloom(final ManaBloom card) { diff --git a/Mage.Sets/src/mage/cards/m/MangarasBlessing.java b/Mage.Sets/src/mage/cards/m/MangarasBlessing.java index 9fc0f218eaf..9ea2945fb29 100644 --- a/Mage.Sets/src/mage/cards/m/MangarasBlessing.java +++ b/Mage.Sets/src/mage/cards/m/MangarasBlessing.java @@ -26,7 +26,7 @@ public final class MangarasBlessing extends CardImpl { // When a spell or ability an opponent controls causes you to discard Mangara's Blessing, // you gain 2 life, and you return Mangara's Blessing from your graveyard to your hand at the beginning of the next end step. - Ability ability = new DiscardedByOpponentTriggeredAbility(new GainLifeEffect(2), true); + Ability ability = new DiscardedByOpponentTriggeredAbility(new GainLifeEffect(2)); Effect graveyardReturnNextEndEffect = new CreateDelayedTriggeredAbilityEffect( new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnSourceFromGraveyardToHandEffect())); diff --git a/Mage.Sets/src/mage/cards/m/ManrikiGusari.java b/Mage.Sets/src/mage/cards/m/ManrikiGusari.java index b22bc820adc..8cf47fcc006 100644 --- a/Mage.Sets/src/mage/cards/m/ManrikiGusari.java +++ b/Mage.Sets/src/mage/cards/m/ManrikiGusari.java @@ -1,7 +1,5 @@ - package mage.cards.m; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; @@ -13,29 +11,27 @@ import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EquipAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.FilterPermanent; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author Loki */ public final class ManrikiGusari extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("Equipment"); - - static { - filter.add(SubType.EQUIPMENT.getPredicate()); - } - public ManrikiGusari(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); this.subtype.add(SubType.EQUIPMENT); // Equipped creature gets +1/+2 and has "{tap}: Destroy target Equipment." this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(1, 2))); Ability gainedAbility = new SimpleActivatedAbility(new DestroyTargetEffect(), new TapSourceCost()); - gainedAbility.addTarget(new TargetPermanent(filter)); + gainedAbility.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_EQUIPMENT)); this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect(gainedAbility, AttachmentType.EQUIPMENT))); // Equip {1} this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(1))); diff --git a/Mage.Sets/src/mage/cards/m/Manticore.java b/Mage.Sets/src/mage/cards/m/Manticore.java index ea26f11881f..6c730c9c237 100644 --- a/Mage.Sets/src/mage/cards/m/Manticore.java +++ b/Mage.Sets/src/mage/cards/m/Manticore.java @@ -10,9 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterOpponentsCreaturePermanent; -import mage.filter.predicate.permanent.WasDealtDamageThisTurnPredicate; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import java.util.UUID; @@ -22,14 +20,6 @@ import java.util.UUID; */ public final class Manticore extends CardImpl { - private static final FilterPermanent filter = new FilterOpponentsCreaturePermanent( - "creature an opponent controls that was dealt damage this turn" - ); - - static { - filter.add(WasDealtDamageThisTurnPredicate.instance); - } - public Manticore(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); @@ -45,7 +35,7 @@ public final class Manticore extends CardImpl { // Tail Spikes — When Manticore enters the battlefield, destroy target creature an opponent controls that was dealt damage this turn. Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect()); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_OPPONENTS_CREATURE_DAMAGED_THIS_TURN)); this.addAbility(ability.withFlavorWord("Tail Spikes")); } diff --git a/Mage.Sets/src/mage/cards/m/MarchOfTheWorldOoze.java b/Mage.Sets/src/mage/cards/m/MarchOfTheWorldOoze.java index 44328e1b21f..2385f48705f 100644 --- a/Mage.Sets/src/mage/cards/m/MarchOfTheWorldOoze.java +++ b/Mage.Sets/src/mage/cards/m/MarchOfTheWorldOoze.java @@ -34,11 +34,11 @@ public final class MarchOfTheWorldOoze extends CardImpl { // Creatures you control have base power and toughness 6/6 and are Oozes in addition to their other types. Ability ability = new SimpleStaticAbility(new SetBasePowerToughnessAllEffect( - 6, 6, Duration.WhileOnBattlefield, StaticFilters.FILTER_CONTROLLED_CREATURE + 6, 6, Duration.WhileOnBattlefield, StaticFilters.FILTER_CONTROLLED_CREATURES )); ability.addEffect(new AddCardSubtypeAllEffect( - StaticFilters.FILTER_CONTROLLED_CREATURE, SubType.OOZE, null - ).concatBy("and")); + StaticFilters.FILTER_CONTROLLED_CREATURES, SubType.OOZE, null + ).setText("and are Oozes in addition to their other types")); this.addAbility(ability); // Whenever an opponent casts a spell, if it's not their turn, you create a 3/3 green Elephant creature token. diff --git a/Mage.Sets/src/mage/cards/m/MarketbackWalker.java b/Mage.Sets/src/mage/cards/m/MarketbackWalker.java index fcca439cd66..79ae7f2372e 100644 --- a/Mage.Sets/src/mage/cards/m/MarketbackWalker.java +++ b/Mage.Sets/src/mage/cards/m/MarketbackWalker.java @@ -41,7 +41,7 @@ public final class MarketbackWalker extends CardImpl { )); // When this creature dies, draw a card for each +1/+1 counter on it. - this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(xValue))); + this.addAbility(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(xValue).setText("draw a card for each +1/+1 counter on it"))); } private MarketbackWalker(final MarketbackWalker card) { diff --git a/Mage.Sets/src/mage/cards/m/MarshalsPathcruiser.java b/Mage.Sets/src/mage/cards/m/MarshalsPathcruiser.java index 86b53670b0d..16a2732dc8c 100644 --- a/Mage.Sets/src/mage/cards/m/MarshalsPathcruiser.java +++ b/Mage.Sets/src/mage/cards/m/MarshalsPathcruiser.java @@ -42,7 +42,7 @@ public final class MarshalsPathcruiser extends CardImpl { new AddCardTypeSourceEffect(Duration.Custom, CardType.ARTIFACT, CardType.CREATURE), new ManaCostsImpl<>("{W}{U}{B}{R}{G}") ); - ability.addEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance(2))); + ability.addEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance(2)).setText("Put two +1/+1 counters on it")); this.addAbility(ability); // Crew 5 diff --git a/Mage.Sets/src/mage/cards/m/MarshdrinkerGiant.java b/Mage.Sets/src/mage/cards/m/MarshdrinkerGiant.java index 548d41f1fcf..eefeb409125 100644 --- a/Mage.Sets/src/mage/cards/m/MarshdrinkerGiant.java +++ b/Mage.Sets/src/mage/cards/m/MarshdrinkerGiant.java @@ -1,7 +1,6 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -11,27 +10,29 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.TargetController; -import mage.filter.common.FilterLandPermanent; +import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; -import mage.target.common.TargetLandPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class MarshdrinkerGiant extends CardImpl { - - private static final FilterLandPermanent filter = new FilterLandPermanent("Island or Swamp an opponent controls"); - + + private static final FilterPermanent filter = new FilterPermanent("Island or Swamp an opponent controls"); + static { filter.add(Predicates.or( SubType.ISLAND.getPredicate(), - SubType.SWAMP.getPredicate())); + SubType.SWAMP.getPredicate() + )); filter.add(TargetController.OPPONENT.getControllerPredicate()); } public MarshdrinkerGiant(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{G}"); this.subtype.add(SubType.GIANT); this.subtype.add(SubType.WARRIOR); @@ -40,9 +41,8 @@ public final class MarshdrinkerGiant extends CardImpl { // When Marshdrinker Giant enters the battlefield, destroy target Island or Swamp an opponent controls. Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), false); - ability.addTarget(new TargetLandPermanent(filter)); + ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); - } private MarshdrinkerGiant(final MarshdrinkerGiant card) { diff --git a/Mage.Sets/src/mage/cards/m/MassMutiny.java b/Mage.Sets/src/mage/cards/m/MassMutiny.java index 642cda34302..9335db63e0d 100644 --- a/Mage.Sets/src/mage/cards/m/MassMutiny.java +++ b/Mage.Sets/src/mage/cards/m/MassMutiny.java @@ -15,7 +15,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.FixedTarget; import java.util.UUID; @@ -32,7 +32,7 @@ public final class MassMutiny extends CardImpl { // For each opponent, gain control of up to one target creature that player controls until end of turn. Untap those creatures. They gain haste until end of turn. this.getSpellAbility().addEffect(new MassMutinyEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(0,1)); - this.getSpellAbility().setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + this.getSpellAbility().setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); } private MassMutiny(final MassMutiny card) { diff --git a/Mage.Sets/src/mage/cards/m/MastersRebuke.java b/Mage.Sets/src/mage/cards/m/MastersRebuke.java index 399b4227601..30b79c0ca82 100644 --- a/Mage.Sets/src/mage/cards/m/MastersRebuke.java +++ b/Mage.Sets/src/mage/cards/m/MastersRebuke.java @@ -29,13 +29,11 @@ public class MastersRebuke extends CardImpl { // Target creature you control deals damage equal to its power to target creature or planeswalker you don’t control. this.getSpellAbility().addEffect(new DamageWithPowerFromOneToAnotherTargetEffect()); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - TargetPermanent targetForDamage = new TargetPermanent(filter); - targetForDamage.setTargetTag(2); - this.getSpellAbility().addTarget(targetForDamage); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); } private MastersRebuke(final MastersRebuke card) { super(card); } @Override public MastersRebuke copy() { return new MastersRebuke(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/m/MemoriesReturning.java b/Mage.Sets/src/mage/cards/m/MemoriesReturning.java new file mode 100644 index 00000000000..a5b7c0e15b0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MemoriesReturning.java @@ -0,0 +1,129 @@ +package mage.cards.m; + +import mage.abilities.Ability; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.FlashbackAbility; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.TargetPlayer; +import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MemoriesReturning extends CardImpl { + + public MemoriesReturning(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}{U}"); + + // Reveal the top five cards of your library. Put one of them into your hand. Then choose an opponent. They put one on the bottom of your library. Then you put one into your hand. Then they put one on the bottom of your library. Put the other into your hand. + this.getSpellAbility().addEffect(new MemoriesReturningEffect()); + + // Flashback {7}{U}{U}. + this.addAbility(new FlashbackAbility(this, new ManaCostsImpl<>("{7}{U}{U}"))); + } + + private MemoriesReturning(final MemoriesReturning card) { + super(card); + } + + @Override + public MemoriesReturning copy() { + return new MemoriesReturning(this); + } +} + +class MemoriesReturningEffect extends OneShotEffect { + + MemoriesReturningEffect() { + super(Outcome.Benefit); + staticText = "reveal the top five cards of your library. Put one of them into your hand. " + + "Then choose an opponent. They put one on the bottom of your library. " + + "Then you put one into your hand. Then they put one on the bottom of your library. " + + "Put the other into your hand"; + } + + private MemoriesReturningEffect(final MemoriesReturningEffect effect) { + super(effect); + } + + @Override + public MemoriesReturningEffect copy() { + return new MemoriesReturningEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, 5)); + controller.revealCards(source, cards, game); + if (putCardInHand(controller, cards, source, game)) { + return true; + } + TargetPlayer target = new TargetOpponent(); + target.withNotTarget(true); + Player opponent = game.getPlayer(target.getFirstTarget()); + if (putCardOnBottom(controller, opponent, cards, source, game)) { + return true; + } + if (putCardInHand(controller, cards, source, game)) { + return true; + } + if (putCardOnBottom(controller, opponent, cards, source, game)) { + return true; + } + putCardInHand(controller, cards, source, game); + return true; + } + + private static boolean putCardInHand(Player controller, Cards cards, Ability source, Game game) { + switch (cards.size()) { + case 0: + return true; + case 1: + controller.moveCards(cards, Zone.HAND, source, game); + return true; + } + TargetCard target = new TargetCardInLibrary(); + target.withChooseHint("to put in your hand"); + controller.choose(Outcome.DrawCard, cards, target, source, game); + controller.moveCards(game.getCard(target.getFirstTarget()), Zone.HAND, source, game); + cards.retainZone(Zone.LIBRARY, game); + return false; + } + + private static boolean putCardOnBottom(Player controller, Player opponent, Cards cards, Ability source, Game game) { + switch (cards.size()) { + case 0: + return true; + case 1: + controller.putCardsOnBottomOfLibrary(cards, game, source, true); + return true; + } + if (opponent == null) { + return false; + } + TargetCard target = new TargetCardInLibrary(); + target.withChooseHint("to put on the bottom of " + controller.getName() + "'s library"); + opponent.choose(Outcome.Discard, cards, target, source, game); + Card card = game.getCard(target.getFirstTarget()); + if (card == null) { + return false; + } + controller.putCardsOnBottomOfLibrary(card, game, source); + cards.remove(card); + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MemoryVampire.java b/Mage.Sets/src/mage/cards/m/MemoryVampire.java new file mode 100644 index 00000000000..d1925c1c47f --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MemoryVampire.java @@ -0,0 +1,108 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.CollectEvidenceCost; +import mage.abilities.dynamicvalue.common.SavedDamageValue; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.MayCastTargetCardEffect; +import mage.abilities.effects.common.MillCardsTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.CastManaAdjustment; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.common.FilterNonlandCard; +import mage.filter.predicate.card.OwnerIdPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetPlayer; +import mage.target.common.TargetCardInGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MemoryVampire extends CardImpl { + + public MemoryVampire(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{B}"); + + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.DETECTIVE); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever Memory Vampire deals combat damage to a player, any number of target players each mill that many cards. Then you may collect evidence 9. When you do, you may cast target nonland card from defending player's graveyard without paying its mana cost. + Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility( + new MillCardsTargetEffect(SavedDamageValue.MANY) + .setText("any number of target players each mill that many cards") + ); + ability.addEffect(new MemoryVampireEffect()); + ability.addTarget(new TargetPlayer(0, Integer.MAX_VALUE, false)); + this.addAbility(ability); + } + + private MemoryVampire(final MemoryVampire card) { + super(card); + } + + @Override + public MemoryVampire copy() { + return new MemoryVampire(this); + } +} + +class MemoryVampireEffect extends OneShotEffect { + + MemoryVampireEffect() { + super(Outcome.Benefit); + staticText = "Then you may collect evidence 9. When you do, you may cast target " + + "nonland card from defending player's graveyard without paying its mana cost"; + } + + private MemoryVampireEffect(final MemoryVampireEffect effect) { + super(effect); + } + + @Override + public MemoryVampireEffect copy() { + return new MemoryVampireEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cost cost = new CollectEvidenceCost(9); + if (!cost.canPay(source, source, source.getControllerId(), game) + || !player.chooseUse(outcome, "Collect evidence 9?", source, game) + || !cost.pay(source, game, source, source.getControllerId(), true)) { + return false; + } + UUID defenderId = (UUID) getValue("damagedPlayer"); + if (defenderId == null) { + return true; + } + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new MayCastTargetCardEffect(CastManaAdjustment.WITHOUT_PAYING_MANA_COST), false + ); + FilterCard filter = new FilterNonlandCard("nonland card from defending player's graveyard"); + filter.add(new OwnerIdPredicate(defenderId)); + ability.addTarget(new TargetCardInGraveyard(filter)); + game.fireReflexiveTriggeredAbility(ability, source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MendicantCoreGuidelight.java b/Mage.Sets/src/mage/cards/m/MendicantCoreGuidelight.java index 8825fd6713b..452594e3917 100644 --- a/Mage.Sets/src/mage/cards/m/MendicantCoreGuidelight.java +++ b/Mage.Sets/src/mage/cards/m/MendicantCoreGuidelight.java @@ -1,45 +1,35 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.MaxSpeedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; -import mage.abilities.common.delayed.CopyNextSpellDelayedTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.CopyStackObjectEffect; import mage.abilities.effects.common.CopyTargetStackObjectEffect; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.continuous.SetBasePowerSourceEffect; -import mage.constants.*; import mage.abilities.keyword.StartYourEnginesAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.filter.FilterPermanent; -import mage.filter.FilterSpell; +import mage.constants.*; import mage.filter.StaticFilters; -import mage.filter.common.FilterControlledPermanent; + +import java.util.UUID; /** - * * @author Jmlundeen */ public final class MendicantCoreGuidelight extends CardImpl { - private static final FilterSpell filter = new FilterSpell("an artifact spell"); private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACTS); - static { - filter.add(CardType.ARTIFACT.getPredicate()); - } - public MendicantCoreGuidelight(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{W}{U}"); - + this.supertype.add(SuperType.LEGENDARY); this.subtype.add(SubType.ROBOT); this.power = new MageInt(0); @@ -53,8 +43,8 @@ public final class MendicantCoreGuidelight extends CardImpl { // Max speed -- Whenever you cast an artifact spell, you may pay {1}. If you do, copy it. Effect copyEffect = new CopyTargetStackObjectEffect(true) .setText("copy it. (The copy becomes a token.)"); - Effect doIfEffect = new DoIfCostPaid(copyEffect,new ManaCostsImpl<>("{1}")); - Ability ability = new SpellCastControllerTriggeredAbility(doIfEffect, filter, false, SetTargetPointer.SPELL); + Effect doIfEffect = new DoIfCostPaid(copyEffect, new ManaCostsImpl<>("{1}")); + Ability ability = new SpellCastControllerTriggeredAbility(doIfEffect, StaticFilters.FILTER_SPELL_AN_ARTIFACT, false, SetTargetPointer.SPELL); this.addAbility(new MaxSpeedAbility(ability)); } diff --git a/Mage.Sets/src/mage/cards/m/Merseine.java b/Mage.Sets/src/mage/cards/m/Merseine.java index 504e36fc3f1..7dc44d7a816 100644 --- a/Mage.Sets/src/mage/cards/m/Merseine.java +++ b/Mage.Sets/src/mage/cards/m/Merseine.java @@ -48,7 +48,7 @@ public final class Merseine extends CardImpl { // Enchanted creature doesn't untap during its controller's untap step if Merseine has a net counter on it. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousRuleModifyingEffect(new DontUntapInControllersUntapStepEnchantedEffect(), - new SourceHasCounterCondition(CounterType.NET)).setText("Enchanted creature doesn't untap during its controller's untap step if Merseine has a net counter on it"))); + new SourceHasCounterCondition(CounterType.NET)).setText("Enchanted creature doesn't untap during its controller's untap step if {this} has a net counter on it"))); // Pay enchanted creature's mana cost: Remove a net counter from Merseine. Any player may activate this ability, but only if they control the enchanted creature. SimpleActivatedAbility ability = new MerseineActivatedAbility(); @@ -95,7 +95,7 @@ class MerseineActivatedAbility extends SimpleActivatedAbility { @Override public String getRule() { - return "Pay enchanted creature's mana cost: Remove a net counter from Merseine. Any player may activate this ability, but only if they control the enchanted creature."; + return "Pay enchanted creature's mana cost: Remove a net counter from {this}. Only the controller of the enchanted creature may activate this ability."; } } diff --git a/Mage.Sets/src/mage/cards/m/Metrognome.java b/Mage.Sets/src/mage/cards/m/Metrognome.java index 012cb2271c0..4c11c7b397c 100644 --- a/Mage.Sets/src/mage/cards/m/Metrognome.java +++ b/Mage.Sets/src/mage/cards/m/Metrognome.java @@ -10,7 +10,6 @@ import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; import mage.game.permanent.token.GnomeToken; /** @@ -23,7 +22,7 @@ public final class Metrognome extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); // When a spell or ability an opponent controls causes you to discard Metrognome, create four 1/1 colorless Gnome artifact creature tokens. - this.addAbility(new DiscardedByOpponentTriggeredAbility(new CreateTokenEffect(new GnomeToken(), 4), true)); + this.addAbility(new DiscardedByOpponentTriggeredAbility(new CreateTokenEffect(new GnomeToken(), 4))); // {4}, {tap}: Create a 1/1 colorless Gnome artifact creature token. Ability ability = new SimpleActivatedAbility(new CreateTokenEffect(new GnomeToken()), new ManaCostsImpl<>("{4}")); diff --git a/Mage.Sets/src/mage/cards/m/MidgarCityOfMako.java b/Mage.Sets/src/mage/cards/m/MidgarCityOfMako.java new file mode 100644 index 00000000000..d7ee7dadd91 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MidgarCityOfMako.java @@ -0,0 +1,49 @@ +package mage.cards.m; + +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.mana.BlackManaAbility; +import mage.cards.AdventureCard; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MidgarCityOfMako extends AdventureCard { + + public MidgarCityOfMako(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, new CardType[]{CardType.SORCERY}, "", "Reactor Raid", "{2}{B}"); + + this.subtype.add(SubType.TOWN); + + // This land enters tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {B}. + this.addAbility(new BlackManaAbility()); + + // Reactor Raid + // You may sacrifice an artifact or creature. If you do, draw two cards. + this.getSpellCard().getSpellAbility().addEffect(new DoIfCostPaid( + new DrawCardSourceControllerEffect(2), + new SacrificeTargetCost(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_CREATURE) + )); + this.finalizeAdventure(); + } + + private MidgarCityOfMako(final MidgarCityOfMako card) { + super(card); + } + + @Override + public MidgarCityOfMako copy() { + return new MidgarCityOfMako(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MiglozMazeCrusher.java b/Mage.Sets/src/mage/cards/m/MiglozMazeCrusher.java index 26c9844dd52..4a3e9f702f9 100644 --- a/Mage.Sets/src/mage/cards/m/MiglozMazeCrusher.java +++ b/Mage.Sets/src/mage/cards/m/MiglozMazeCrusher.java @@ -1,7 +1,5 @@ package mage.cards.m; -import java.util.UUID; - import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; @@ -14,16 +12,18 @@ import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.keyword.MenaceAbility; import mage.abilities.keyword.VigilanceAbility; -import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.SuperType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.target.TargetPermanent; +import java.util.UUID; + /** * @author TheElk801 */ @@ -49,7 +49,7 @@ public final class MiglozMazeCrusher extends CardImpl { VigilanceAbility.getInstance(), Duration.EndOfTurn ).setText("it gains vigilance"), new GenericManaCost(1)); ability.addCost(new RemoveCountersSourceCost(CounterType.OIL.createInstance())); - ability.addEffect(new GainAbilitySourceEffect(new MenaceAbility(false)) + ability.addEffect(new GainAbilitySourceEffect(new MenaceAbility(false), Duration.EndOfTurn) .setText("and menace until end of turn")); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/m/MilaCraftyCompanion.java b/Mage.Sets/src/mage/cards/m/MilaCraftyCompanion.java index 162e0b54cc6..1ebe00d09ab 100644 --- a/Mage.Sets/src/mage/cards/m/MilaCraftyCompanion.java +++ b/Mage.Sets/src/mage/cards/m/MilaCraftyCompanion.java @@ -26,6 +26,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.Objects; import java.util.UUID; @@ -185,7 +186,7 @@ class LukkaWaywardBonderReturnEffect extends OneShotEffect { return false; } player.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/m/MilitantInquisitor.java b/Mage.Sets/src/mage/cards/m/MilitantInquisitor.java index 3a342dad7b4..37de9a9c551 100644 --- a/Mage.Sets/src/mage/cards/m/MilitantInquisitor.java +++ b/Mage.Sets/src/mage/cards/m/MilitantInquisitor.java @@ -1,7 +1,5 @@ - package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; @@ -12,31 +10,24 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterControlledPermanent; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ public final class MilitantInquisitor extends CardImpl { - - private static final FilterControlledPermanent filter = new FilterControlledPermanent("Equipment you control"); - - static { - filter.add(CardType.ARTIFACT.getPredicate()); - filter.add(SubType.EQUIPMENT.getPredicate()); - } public MilitantInquisitor(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.CLERIC); this.power = new MageInt(2); this.toughness = new MageInt(3); // Miltant Inquisitor gets +1/+0 for each Equipment you control. - this.addAbility(new SimpleStaticAbility(new BoostSourceEffect(new PermanentsOnBattlefieldCount(filter), StaticValue.get(0), Duration.WhileOnBattlefield))); + this.addAbility(new SimpleStaticAbility(new BoostSourceEffect(new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT), StaticValue.get(0), Duration.WhileOnBattlefield))); } private MilitantInquisitor(final MilitantInquisitor card) { diff --git a/Mage.Sets/src/mage/cards/m/MillicentRestlessRevenant.java b/Mage.Sets/src/mage/cards/m/MillicentRestlessRevenant.java index 7d46e8984dc..39cca31950d 100644 --- a/Mage.Sets/src/mage/cards/m/MillicentRestlessRevenant.java +++ b/Mage.Sets/src/mage/cards/m/MillicentRestlessRevenant.java @@ -3,19 +3,12 @@ package mage.cards.m; import mage.MageInt; import mage.MageObject; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.common.AffinityEffect; import mage.abilities.effects.common.CreateTokenEffect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.AffinityAbility; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.permanent.TokenPredicate; @@ -33,9 +26,6 @@ import java.util.UUID; */ public final class MillicentRestlessRevenant extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.SPIRIT, "Spirits"); - private static final Hint hint = new ValueHint("Spirits you control", new PermanentsOnBattlefieldCount(filter)); - public MillicentRestlessRevenant(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}{U}"); @@ -45,8 +35,8 @@ public final class MillicentRestlessRevenant extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - // This spell costs {1} less to cast for each Spirit you control. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filter)).addHint(hint)); + // Affinity for Spirits + this.addAbility(new AffinityAbility(AffinityType.SPIRITS)); // Flying this.addAbility(FlyingAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/m/MimingSlime.java b/Mage.Sets/src/mage/cards/m/MimingSlime.java index 34b3dc27aef..ea4d4bc7311 100644 --- a/Mage.Sets/src/mage/cards/m/MimingSlime.java +++ b/Mage.Sets/src/mage/cards/m/MimingSlime.java @@ -1,31 +1,30 @@ package mage.cards.m; -import java.util.List; -import java.util.UUID; import mage.abilities.Ability; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.game.permanent.token.OozeToken; -import mage.players.Player; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class MimingSlime extends CardImpl { public MimingSlime(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{G}"); // Create an X/X green Ooze creature token, where X is the greatest power among creatures you control. - this.getSpellAbility().addEffect(new MimingSlimeEffect()); + this.getSpellAbility().addEffect(new MimingSlimeCreateTokenEffect()); + this.getSpellAbility().addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint()); } private MimingSlime(final MimingSlime card) { @@ -38,40 +37,25 @@ public final class MimingSlime extends CardImpl { } } -class MimingSlimeEffect extends OneShotEffect { +class MimingSlimeCreateTokenEffect extends OneShotEffect { - MimingSlimeEffect() { + MimingSlimeCreateTokenEffect() { super(Outcome.PutCreatureInPlay); staticText = "Create an X/X green Ooze creature token, where X is the greatest power among creatures you control"; } - private MimingSlimeEffect(final MimingSlimeEffect effect) { + private MimingSlimeCreateTokenEffect(final MimingSlimeCreateTokenEffect effect) { super(effect); } @Override - public MimingSlimeEffect copy() { - return new MimingSlimeEffect(this); + public MimingSlimeCreateTokenEffect copy() { + return new MimingSlimeCreateTokenEffect(this); } @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - List creatures = game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURES, player.getId(), game); - int amount = 0; - for (Permanent creature : creatures) { - int power = creature.getPower().getValue(); - if (amount < power) { - amount = power; - } - } - OozeToken oozeToken = new OozeToken(); - oozeToken.setPower(amount); - oozeToken.setToughness(amount); - oozeToken.putOntoBattlefield(1, game, source, source.getControllerId()); - return true; - } - return false; + int value = GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.calculate(game, source, this); + return (new CreateTokenEffect(new OozeToken(value, value))).apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/m/MinionOfTheMighty.java b/Mage.Sets/src/mage/cards/m/MinionOfTheMighty.java index 6420b9064e4..da230db6b23 100644 --- a/Mage.Sets/src/mage/cards/m/MinionOfTheMighty.java +++ b/Mage.Sets/src/mage/cards/m/MinionOfTheMighty.java @@ -18,6 +18,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInHand; +import mage.util.CardUtil; import java.util.UUID; @@ -88,7 +89,7 @@ class MinionOfTheMightyEffect extends OneShotEffect { card, Zone.BATTLEFIELD, source, game, true, false, true, null ); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { game.getCombat().addAttackingCreature(permanent.getId(), game); } diff --git a/Mage.Sets/src/mage/cards/m/MinwuWhiteMage.java b/Mage.Sets/src/mage/cards/m/MinwuWhiteMage.java new file mode 100644 index 00000000000..705e3b7291e --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MinwuWhiteMage.java @@ -0,0 +1,55 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.common.GainLifeControllerTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MinwuWhiteMage extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.CLERIC); + + public MinwuWhiteMage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Whenever you gain life, put a +1/+1 counter on each Cleric you control. + this.addAbility(new GainLifeControllerTriggeredAbility( + new AddCountersAllEffect(CounterType.P1P1.createInstance(), filter) + )); + } + + private MinwuWhiteMage(final MinwuWhiteMage card) { + super(card); + } + + @Override + public MinwuWhiteMage copy() { + return new MinwuWhiteMage(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/Mirrex.java b/Mage.Sets/src/mage/cards/m/Mirrex.java index e7e737a999e..d9b718fcaec 100644 --- a/Mage.Sets/src/mage/cards/m/Mirrex.java +++ b/Mage.Sets/src/mage/cards/m/Mirrex.java @@ -34,7 +34,7 @@ public final class Mirrex extends CardImpl { // {T}: Add one mana of any color. Activate only if Mirrex entered the battlefield this turn. this.addAbility(new ActivateIfConditionManaAbility( Zone.BATTLEFIELD, new AddManaOfAnyColorEffect(), - new TapSourceCost(), SourceEnteredThisTurnCondition.instance + new TapSourceCost(), SourceEnteredThisTurnCondition.DID )); // {3}, {T}: Create a 1/1 colorless Phyrexian Mite artifact creature token with toxic 1 and "This creature can't block." diff --git a/Mage.Sets/src/mage/cards/m/MirrodinAvenged.java b/Mage.Sets/src/mage/cards/m/MirrodinAvenged.java index d7cd3f0a408..2c12846d728 100644 --- a/Mage.Sets/src/mage/cards/m/MirrodinAvenged.java +++ b/Mage.Sets/src/mage/cards/m/MirrodinAvenged.java @@ -5,9 +5,7 @@ import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.WasDealtDamageThisTurnPredicate; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import java.util.UUID; @@ -17,19 +15,12 @@ import java.util.UUID; */ public final class MirrodinAvenged extends CardImpl { - private static final FilterPermanent filter - = new FilterCreaturePermanent("creature that was dealt damage this turn"); - - static { - filter.add(WasDealtDamageThisTurnPredicate.instance); - } - public MirrodinAvenged(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}"); // Destroy target creature that was dealt damage this turn. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_DAMAGED_THIS_TURN)); // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
    ")); diff --git a/Mage.Sets/src/mage/cards/m/MirrorOfFate.java b/Mage.Sets/src/mage/cards/m/MirrorOfFate.java index 93b35e1a10f..728f9cff566 100644 --- a/Mage.Sets/src/mage/cards/m/MirrorOfFate.java +++ b/Mage.Sets/src/mage/cards/m/MirrorOfFate.java @@ -116,7 +116,7 @@ class MirrorOfFateTarget extends TargetCardInExile { && game.getState().getZone(card.getId()) == Zone.EXILED) { for (ExileZone exile : game.getExile().getExileZones()) { if (exile != null && exile.contains(id)) { - return filter.match(card, source.getControllerId(), game); + return filter.match(card, source.getControllerId(), source, game); } } } diff --git a/Mage.Sets/src/mage/cards/m/MishrasWorkshop.java b/Mage.Sets/src/mage/cards/m/MishrasWorkshop.java index 907f02c9c13..4ab48ee0610 100644 --- a/Mage.Sets/src/mage/cards/m/MishrasWorkshop.java +++ b/Mage.Sets/src/mage/cards/m/MishrasWorkshop.java @@ -1,4 +1,3 @@ - package mage.cards.m; import java.util.UUID; @@ -8,7 +7,7 @@ import mage.abilities.mana.conditional.ConditionalSpellManaBuilder; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterArtifactSpell; +import mage.filter.FilterSpell; /** * @@ -16,12 +15,17 @@ import mage.filter.common.FilterArtifactSpell; */ public final class MishrasWorkshop extends CardImpl { + private static final FilterSpell filter = new FilterSpell("artifact spells"); + static { + filter.add(CardType.ARTIFACT.getPredicate()); + } + public MishrasWorkshop(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.LAND},""); // {tap}: Add {C}{C}{C}. Spend this mana only to cast artifact spells. this.addAbility(new ConditionalColorlessManaAbility(new TapSourceCost(), 3, - new ConditionalSpellManaBuilder(new FilterArtifactSpell("artifact spells")))); + new ConditionalSpellManaBuilder(filter))); } diff --git a/Mage.Sets/src/mage/cards/m/MisleadingSignpost.java b/Mage.Sets/src/mage/cards/m/MisleadingSignpost.java index a4d6c6dcdb3..9f8ed245c9d 100644 --- a/Mage.Sets/src/mage/cards/m/MisleadingSignpost.java +++ b/Mage.Sets/src/mage/cards/m/MisleadingSignpost.java @@ -1,11 +1,9 @@ package mage.cards.m; -import java.util.UUID; - import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.IsStepCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.combat.ReselectDefenderAttackedByTargetEffect; import mage.abilities.keyword.FlashAbility; import mage.abilities.mana.BlueManaAbility; @@ -15,26 +13,25 @@ import mage.constants.CardType; import mage.constants.PhaseStep; import mage.target.common.TargetAttackingCreature; +import java.util.UUID; /** - * * @author Xanderhall */ public final class MisleadingSignpost extends CardImpl { + private static final Condition condition = new IsStepCondition(PhaseStep.DECLARE_ATTACKERS, false); + public MisleadingSignpost(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{U}"); - // Flash this.addAbility(FlashAbility.getInstance()); // When Misleading Signpost enters the battlefield during the declare attackers step, you may reselect which player or permanent target attacking creature is attacking. - Ability ability = new ConditionalTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new ReselectDefenderAttackedByTargetEffect(true), true), - new IsStepCondition(PhaseStep.DECLARE_ATTACKERS, false), - "When {this} enters during the declare attackers step, you may reselect which player or permanent target attacking creature is attacking. " - + "(It can't attack its controller or their permanents)"); + Ability ability = new EntersBattlefieldTriggeredAbility( + new ReselectDefenderAttackedByTargetEffect(true), true + ).withTriggerCondition(condition); ability.addTarget(new TargetAttackingCreature()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/m/MistbladeShinobi.java b/Mage.Sets/src/mage/cards/m/MistbladeShinobi.java index 304042e182e..9959e14301a 100644 --- a/Mage.Sets/src/mage/cards/m/MistbladeShinobi.java +++ b/Mage.Sets/src/mage/cards/m/MistbladeShinobi.java @@ -12,7 +12,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -38,7 +38,7 @@ public final class MistbladeShinobi extends CardImpl { // Whenever Mistblade Shinobi deals combat damage to a player, you may return target creature that player controls to its owner's hand. Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new ReturnToHandTargetEffect(), true, true); ability.addTarget(new TargetCreaturePermanent(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/MistveilPlains.java b/Mage.Sets/src/mage/cards/m/MistveilPlains.java index 65f9b15cabb..09cb3dea2eb 100644 --- a/Mage.Sets/src/mage/cards/m/MistveilPlains.java +++ b/Mage.Sets/src/mage/cards/m/MistveilPlains.java @@ -88,6 +88,6 @@ class MistveilPlainsGraveyardToLibraryEffect extends OneShotEffect { || game.getState().getZone(card.getId()) != Zone.GRAVEYARD) { return false; } - return player.putCardsOnBottomOfLibrary(card, game, source, false); + return player.putCardsOnBottomOfLibrary(card, game, source); } } diff --git a/Mage.Sets/src/mage/cards/m/MogMoogleWarrior.java b/Mage.Sets/src/mage/cards/m/MogMoogleWarrior.java new file mode 100644 index 00000000000..d13b54582ae --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MogMoogleWarrior.java @@ -0,0 +1,116 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.MoogleToken; +import mage.players.Player; +import mage.target.common.TargetDiscard; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MogMoogleWarrior extends CardImpl { + + public MogMoogleWarrior(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.MOOGLE); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Dance -- At the beginning of your end step, each player may discard a card. Each player who discarded a card this way draws a card. If a creature card was discarded this way, you create a 1/2 white Moogle creature token with lifelink. Then if a noncreature card was discarded this way, put a +1/+1 counter on each Moogle you control. + this.addAbility(new BeginningOfEndStepTriggeredAbility(new MogMoogleWarriorEffect()).withFlavorWord("Dance")); + } + + private MogMoogleWarrior(final MogMoogleWarrior card) { + super(card); + } + + @Override + public MogMoogleWarrior copy() { + return new MogMoogleWarrior(this); + } +} + +class MogMoogleWarriorEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.MOOGLE); + + MogMoogleWarriorEffect() { + super(Outcome.Benefit); + staticText = "each player may discard a card. Each player who discarded a card this way draws a card. " + + "If a creature card was discarded this way, you create a 1/2 white Moogle creature token with lifelink. " + + "Then if a noncreature card was discarded this way, put a +1/+1 counter on each Moogle you control"; + } + + private MogMoogleWarriorEffect(final MogMoogleWarriorEffect effect) { + super(effect); + } + + @Override + public MogMoogleWarriorEffect copy() { + return new MogMoogleWarriorEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Map map = new HashMap<>(); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + TargetDiscard target = new TargetDiscard(0, 1, StaticFilters.FILTER_CARD, playerId); + player.choose(outcome, player.getHand(), target, source, game); + Card card = game.getCard(target.getFirstTarget()); + if (card != null) { + map.put(playerId, card); + } + } + Cards cards = new CardsImpl(); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + Card card = map.getOrDefault(playerId, null); + if (player != null && card != null && player.discard(card, false, source, game)) { + cards.add(card); + player.drawCards(1, source, game); + } + } + if (cards.count(StaticFilters.FILTER_CARD_CREATURE, game) > 0) { + new MoogleToken().putOntoBattlefield(1, game, source); + } + if (cards.count(StaticFilters.FILTER_CARD_NON_CREATURE, game) < 1) { + return true; + } + game.processAction(); + for (Permanent permanent : game + .getBattlefield() + .getActivePermanents(filter, source.getControllerId(), source, game)) { + permanent.addCounters(CounterType.P1P1.createInstance(), source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MogisGodOfSlaughter.java b/Mage.Sets/src/mage/cards/m/MogisGodOfSlaughter.java index 38cf90d99a6..b35b152259b 100644 --- a/Mage.Sets/src/mage/cards/m/MogisGodOfSlaughter.java +++ b/Mage.Sets/src/mage/cards/m/MogisGodOfSlaughter.java @@ -61,7 +61,7 @@ class MogisGodOfSlaughterEffect extends OneShotEffect { MogisGodOfSlaughterEffect() { super(Outcome.Damage); - staticText = "{this} deals 2 damage to that player unless they sacrifice a creature"; + staticText = "{this} deals 2 damage to that player unless they sacrifice a creature of their choice"; } private MogisGodOfSlaughterEffect(final MogisGodOfSlaughterEffect effect) { diff --git a/Mage.Sets/src/mage/cards/m/MoiraAndTeshar.java b/Mage.Sets/src/mage/cards/m/MoiraAndTeshar.java index 94122d6d173..7af8132d513 100644 --- a/Mage.Sets/src/mage/cards/m/MoiraAndTeshar.java +++ b/Mage.Sets/src/mage/cards/m/MoiraAndTeshar.java @@ -16,20 +16,19 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.FilterSpell; -import mage.filter.common.FilterHistoricSpell; +import mage.filter.StaticFilters; import mage.filter.common.FilterPermanentCard; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; public final class MoiraAndTeshar extends CardImpl { - private static final FilterSpell filter = new FilterHistoricSpell(); private static final FilterPermanentCard targetFilter = new FilterPermanentCard( "nonland permanent card from your graveyard"); @@ -50,7 +49,7 @@ public final class MoiraAndTeshar extends CardImpl { // Whenever you cast a historic spell, return target nonland permanent card from // your graveyard to the battlefield. It gains haste. Exile it at the beginning // of the next end step. - Ability ability = new SpellCastControllerTriggeredAbility(new MoiraAndTesharEffect(), filter, false); + Ability ability = new SpellCastControllerTriggeredAbility(new MoiraAndTesharEffect(), StaticFilters.FILTER_SPELL_HISTORIC, false); ability.addTarget(new TargetCardInYourGraveyard(targetFilter)); // If it would leave the battlefield, exile it instead of putting it anywhere @@ -91,7 +90,7 @@ class MoiraAndTesharEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null && card != null) { if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { // It gains haste ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); diff --git a/Mage.Sets/src/mage/cards/m/MoltenMonstrosity.java b/Mage.Sets/src/mage/cards/m/MoltenMonstrosity.java index 62642fade32..cc506b91734 100644 --- a/Mage.Sets/src/mage/cards/m/MoltenMonstrosity.java +++ b/Mage.Sets/src/mage/cards/m/MoltenMonstrosity.java @@ -2,7 +2,7 @@ package mage.cards.m; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; @@ -27,8 +27,8 @@ public final class MoltenMonstrosity extends CardImpl { // This spell costs {X} less to cast, where X is the greatest power among creatures you control. this.addAbility(new SimpleStaticAbility( - Zone.ALL, new SpellCostReductionSourceEffect(GreatestPowerAmongControlledCreaturesValue.instance) - ).setRuleAtTheTop(true).addHint(GreatestPowerAmongControlledCreaturesValue.getHint())); + Zone.ALL, new SpellCostReductionSourceEffect(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES) + ).setRuleAtTheTop(true).addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint())); // Trample this.addAbility(TrampleAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/m/MoltenPrimordial.java b/Mage.Sets/src/mage/cards/m/MoltenPrimordial.java index 8a72d610c63..b3d43d553a1 100644 --- a/Mage.Sets/src/mage/cards/m/MoltenPrimordial.java +++ b/Mage.Sets/src/mage/cards/m/MoltenPrimordial.java @@ -19,7 +19,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.FixedTarget; import java.util.UUID; @@ -42,7 +42,7 @@ public final class MoltenPrimordial extends CardImpl { // When Molten Primordial enters the battlefield, for each opponent, take control of up to one target creature that player controls until end of turn. Untap those creatures. They have haste until end of turn. Ability ability = new EntersBattlefieldTriggeredAbility(new MoltenPrimordialEffect(), false); ability.addTarget(new TargetCreaturePermanent(0,1)); - ability.setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/MoltenRebuke.java b/Mage.Sets/src/mage/cards/m/MoltenRebuke.java index b5becec8997..1d8dda8cd18 100644 --- a/Mage.Sets/src/mage/cards/m/MoltenRebuke.java +++ b/Mage.Sets/src/mage/cards/m/MoltenRebuke.java @@ -6,8 +6,9 @@ import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; import mage.target.common.TargetCreatureOrPlaneswalker; -import mage.target.common.TargetEquipmentPermanent; import java.util.UUID; @@ -28,7 +29,7 @@ public final class MoltenRebuke extends CardImpl { this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); // * Destroy target Equipment. - this.getSpellAbility().addMode(new Mode(new DestroyTargetEffect()).addTarget(new TargetEquipmentPermanent())); + this.getSpellAbility().addMode(new Mode(new DestroyTargetEffect()).addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_EQUIPMENT))); } private MoltenRebuke(final MoltenRebuke card) { diff --git a/Mage.Sets/src/mage/cards/m/MomentOfTruth.java b/Mage.Sets/src/mage/cards/m/MomentOfTruth.java index 374cd179626..94a300ede78 100644 --- a/Mage.Sets/src/mage/cards/m/MomentOfTruth.java +++ b/Mage.Sets/src/mage/cards/m/MomentOfTruth.java @@ -93,7 +93,7 @@ class MomentOfTruthEffect extends OneShotEffect { if (card == null) { return true; } - player.putCardsOnBottomOfLibrary(card, game, source, false); + player.putCardsOnBottomOfLibrary(card, game, source); return true; } } diff --git a/Mage.Sets/src/mage/cards/m/MonasteryMessenger.java b/Mage.Sets/src/mage/cards/m/MonasteryMessenger.java index cf0872e16b9..f4fecac108f 100644 --- a/Mage.Sets/src/mage/cards/m/MonasteryMessenger.java +++ b/Mage.Sets/src/mage/cards/m/MonasteryMessenger.java @@ -11,7 +11,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.filter.FilterCard; -import mage.filter.common.FilterNoncreatureCard; +import mage.filter.common.FilterNonlandCard; import mage.filter.predicate.Predicates; import mage.target.common.TargetCardInYourGraveyard; @@ -22,10 +22,10 @@ import java.util.UUID; */ public final class MonasteryMessenger extends CardImpl { - private static final FilterCard filter = new FilterNoncreatureCard("noncreature, nonland card from your graveyard"); + private static final FilterCard filter = new FilterNonlandCard("noncreature, nonland card from your graveyard"); static { - filter.add(Predicates.not(CardType.LAND.getPredicate())); + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); } public MonasteryMessenger(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/m/MonksFist.java b/Mage.Sets/src/mage/cards/m/MonksFist.java new file mode 100644 index 00000000000..6ca0bd1e6d3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MonksFist.java @@ -0,0 +1,49 @@ +package mage.cards.m; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.AddCardSubtypeAttachedEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.abilities.keyword.JobSelectAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MonksFist extends CardImpl { + + public MonksFist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Job select + this.addAbility(new JobSelectAbility()); + + // Equipped creature gets +1/+0 and is a Monk in addition to its other types. + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(1, 0)); + ability.addEffect(new AddCardSubtypeAttachedEffect( + SubType.MONK, AttachmentType.EQUIPMENT + ).setText("and is a Monk in addition to its other types")); + this.addAbility(ability); + + // Equip {2} + this.addAbility(new EquipAbility(2)); + } + + private MonksFist(final MonksFist card) { + super(card); + } + + @Override + public MonksFist copy() { + return new MonksFist(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MonstrousOnslaught.java b/Mage.Sets/src/mage/cards/m/MonstrousOnslaught.java index 4669d980755..077958bb41b 100644 --- a/Mage.Sets/src/mage/cards/m/MonstrousOnslaught.java +++ b/Mage.Sets/src/mage/cards/m/MonstrousOnslaught.java @@ -1,9 +1,7 @@ package mage.cards.m; -import java.util.UUID; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageMultiEffect; import mage.cards.CardImpl; @@ -11,8 +9,9 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.target.common.TargetCreaturePermanentAmount; +import java.util.UUID; + /** - * * @author Styxo */ public final class MonstrousOnslaught extends CardImpl { @@ -21,12 +20,11 @@ public final class MonstrousOnslaught extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{G}{G}"); // Monstrous Onslaught deals X damage divided as you choose among any number of target creatures, where X is the greatest power among creatures you control as you cast Monstrous Onslaught. - DynamicValue xValue = GreatestPowerAmongControlledCreaturesValue.instance; Effect effect = new DamageMultiEffect(); effect.setText("{this} deals X damage divided as you choose among any number of target creatures, where X is the greatest power among creatures you control as you cast this spell"); this.getSpellAbility().addEffect(effect); - this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(xValue)); - this.getSpellAbility().addHint(GreatestPowerAmongControlledCreaturesValue.getHint()); + this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES)); + this.getSpellAbility().addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint()); } private MonstrousOnslaught(final MonstrousOnslaught card) { diff --git a/Mage.Sets/src/mage/cards/m/MoonCircuitHacker.java b/Mage.Sets/src/mage/cards/m/MoonCircuitHacker.java index 797138e2087..295048463f3 100644 --- a/Mage.Sets/src/mage/cards/m/MoonCircuitHacker.java +++ b/Mage.Sets/src/mage/cards/m/MoonCircuitHacker.java @@ -22,7 +22,7 @@ import java.util.UUID; */ public final class MoonCircuitHacker extends CardImpl { - private static final Condition condition = new InvertCondition(SourceEnteredThisTurnCondition.instance); + private static final Condition condition = new InvertCondition(SourceEnteredThisTurnCondition.DID); public MoonCircuitHacker(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{1}{U}"); diff --git a/Mage.Sets/src/mage/cards/m/MordantDragon.java b/Mage.Sets/src/mage/cards/m/MordantDragon.java index d336d6a8a2f..634fc2d91ec 100644 --- a/Mage.Sets/src/mage/cards/m/MordantDragon.java +++ b/Mage.Sets/src/mage/cards/m/MordantDragon.java @@ -15,9 +15,8 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -45,7 +44,7 @@ public final class MordantDragon extends CardImpl { effect.setText("have it deal that much damage to target creature that player controls."); Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(effect, true, true); ability.addTarget(new TargetCreaturePermanent()); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/m/MotivatedPony.java b/Mage.Sets/src/mage/cards/m/MotivatedPony.java index 041329efe47..3c74d4df841 100644 --- a/Mage.Sets/src/mage/cards/m/MotivatedPony.java +++ b/Mage.Sets/src/mage/cards/m/MotivatedPony.java @@ -69,7 +69,7 @@ class MotivatedPonyWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD - && ((EntersTheBattlefieldEvent) event).getTarget().getSubtype(game).contains(SubType.FOOD)) { + && ((EntersTheBattlefieldEvent) event).getTarget().hasSubtype(SubType.FOOD, game)) { players.add(event.getPlayerId()); } } @@ -129,4 +129,4 @@ class MotivatedPonyEffect extends OneShotEffect { return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/m/MournersShield.java b/Mage.Sets/src/mage/cards/m/MournersShield.java index 1362b980ab7..3c190addd62 100644 --- a/Mage.Sets/src/mage/cards/m/MournersShield.java +++ b/Mage.Sets/src/mage/cards/m/MournersShield.java @@ -1,7 +1,5 @@ package mage.cards.m; -import java.util.UUID; - import mage.MageObject; import mage.MageObjectReference; import mage.ObjectColor; @@ -15,8 +13,11 @@ import mage.abilities.effects.PreventionEffectImpl; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.FilterObject; +import mage.constants.AbilityWord; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.SharesColorPredicate; import mage.game.ExileZone; import mage.game.Game; @@ -27,15 +28,16 @@ import mage.target.TargetSource; import mage.target.common.TargetCardInGraveyard; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author noahg */ public final class MournersShield extends CardImpl { public MournersShield(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); - + // Imprint - When Mourner's Shield enters the battlefield, you may exile target card from a graveyard. Ability ability = new EntersBattlefieldTriggeredAbility(new MournersShieldImprintEffect(), true); @@ -93,6 +95,10 @@ class MournersShieldImprintEffect extends OneShotEffect { } } +/** + * TODO: custom effect should not be needed with the properly set up {@link FilterSource} + * and {@link mage.abilities.effects.common.PreventDamageByChosenSourceEffect} + */ class MournersShieldEffect extends PreventionEffectImpl { private TargetSource target; @@ -131,17 +137,17 @@ class MournersShieldEffect extends PreventionEffectImpl { noneExiled = true; return; } - for (UUID imprinted : sourceObject.getImprinted()){ - if (imprinted != null && exileZone.contains(imprinted)){ + for (UUID imprinted : sourceObject.getImprinted()) { + if (imprinted != null && exileZone.contains(imprinted)) { Card card = game.getCard(imprinted); if (card != null) { colorsAmongImprinted = colorsAmongImprinted.union(card.getColor(game)); } } } - FilterObject filterObject = new FilterObject("a source of your choice that shares a color with the exiled card"); - filterObject.add(new SharesColorPredicate(colorsAmongImprinted)); - this.target = new TargetSource(filterObject); + FilterSource filterSource = new FilterSource("a source of your choice that shares a color with the exiled card"); + filterSource.add(new SharesColorPredicate(colorsAmongImprinted)); + this.target = new TargetSource(filterSource); this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); if (target.getFirstTarget() != null) { mageObjectReference = new MageObjectReference(target.getFirstTarget(), game); @@ -152,7 +158,7 @@ class MournersShieldEffect extends PreventionEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (noneExiled || mageObjectReference == null){ + if (noneExiled || mageObjectReference == null) { return false; } if (super.applies(event, source, game)) { diff --git a/Mage.Sets/src/mage/cards/m/MuscleBurst.java b/Mage.Sets/src/mage/cards/m/MuscleBurst.java index be0b8904a56..a5d94e6c15e 100644 --- a/Mage.Sets/src/mage/cards/m/MuscleBurst.java +++ b/Mage.Sets/src/mage/cards/m/MuscleBurst.java @@ -67,7 +67,7 @@ public final class MuscleBurst extends CardImpl { class CountAsMuscleBurstAbility extends SimpleStaticAbility { public CountAsMuscleBurstAbility() { - super(Zone.GRAVEYARD, new InfoEffect("If {this} is in a graveyard, effects from spells named Muscle Burst count it as a card named Muscle Burst")); + super(Zone.GRAVEYARD, new InfoEffect("If this card is in a graveyard, effects from spells named Muscle Burst count it as a card named Muscle Burst")); } private CountAsMuscleBurstAbility(CountAsMuscleBurstAbility ability) { diff --git a/Mage.Sets/src/mage/cards/m/MuseVessel.java b/Mage.Sets/src/mage/cards/m/MuseVessel.java index 5107bc656c9..04a1518e5e6 100644 --- a/Mage.Sets/src/mage/cards/m/MuseVessel.java +++ b/Mage.Sets/src/mage/cards/m/MuseVessel.java @@ -154,9 +154,7 @@ class TargetCardInMuseVesselExile extends TargetCardInExile { if (sourceCard != null) { UUID exileId = CardUtil.getCardExileZoneId(game, source.getSourceId()); ExileZone exile = game.getExile().getExileZone(exileId); - if (exile != null && !exile.isEmpty()) { - return true; - } + return exile != null && !exile.isEmpty(); } return false; } @@ -172,7 +170,7 @@ class TargetCardInMuseVesselExile extends TargetCardInExile { exile = game.getExile().getExileZone(exileId); } if (exile != null && exile.contains(id)) { - return filter.match(card, source.getControllerId(), game); + return filter.match(card, source.getControllerId(), source, game); } } return false; diff --git a/Mage.Sets/src/mage/cards/m/MuzzioVisionaryArchitect.java b/Mage.Sets/src/mage/cards/m/MuzzioVisionaryArchitect.java index 9bb7daa6e4c..381244a4efa 100644 --- a/Mage.Sets/src/mage/cards/m/MuzzioVisionaryArchitect.java +++ b/Mage.Sets/src/mage/cards/m/MuzzioVisionaryArchitect.java @@ -1,29 +1,23 @@ package mage.cards.m; -import java.util.List; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.OneShotEffect; import mage.cards.*; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.common.FilterArtifactCard; -import mage.filter.common.FilterArtifactPermanent; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetCard; +import java.util.UUID; + /** - * * @author fireshoes */ public final class MuzzioVisionaryArchitect extends CardImpl { @@ -36,9 +30,10 @@ public final class MuzzioVisionaryArchitect extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(3); - // {3}{U}, {tap}: Look at the top X cards of your library, where X is the highest converted mana cost among artifacts you control. You may reveal an artifact card from among them and put it onto the battlefield. Put the rest on the bottom of your library in any order. + // {3}{U}, {tap}: Look at the top X cards of your library, where X is the greatest mana value among artifacts you control. You may reveal an artifact card from among them and put it onto the battlefield. Put the rest on the bottom of your library in any order. Ability ability = new SimpleActivatedAbility(new MuzzioVisionaryArchitectEffect(), new ManaCostsImpl<>("{3}{U}")); ability.addCost(new TapSourceCost()); + ability.addHint(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_ARTIFACTS.getHint()); this.addAbility(ability); } @@ -56,7 +51,7 @@ class MuzzioVisionaryArchitectEffect extends OneShotEffect { MuzzioVisionaryArchitectEffect() { super(Outcome.Benefit); - this.staticText = "look at the top X cards of your library, where X is the highest mana value among artifacts you control. You may put an artifact card from among them onto the battlefield. Put the rest on the bottom of your library in any order"; + this.staticText = "look at the top X cards of your library, where X is the greatest mana value among artifacts you control. You may put an artifact card from among them onto the battlefield. Put the rest on the bottom of your library in any order"; } private MuzzioVisionaryArchitectEffect(final MuzzioVisionaryArchitectEffect effect) { @@ -74,22 +69,11 @@ class MuzzioVisionaryArchitectEffect extends OneShotEffect { if (controller == null) { return false; } - - int highCMC = 0; - List controlledArtifacts = game.getBattlefield().getAllActivePermanents(new FilterArtifactPermanent(), controller.getId(), game); - for (Permanent permanent : controlledArtifacts) { - if (permanent.getSpellAbility() != null) { - int cmc = permanent.getSpellAbility().getManaCosts().manaValue(); - if (cmc > highCMC) { - highCMC = cmc; - } - } - } - - Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, highCMC)); + int amount = GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_ARTIFACTS.calculate(game, source, this); + Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, amount)); controller.lookAtCards(source, null, cards, game); if (!cards.isEmpty()) { - TargetCard target = new TargetCard(Zone.LIBRARY, new FilterArtifactCard("artifact card to put onto the battlefield")); + TargetCard target = new TargetCard(0, 1, Zone.LIBRARY, new FilterArtifactCard("artifact card to put onto the battlefield")); if (target.canChoose(controller.getId(), source, game) && controller.choose(Outcome.Benefit, cards, target, source, game)) { Card card = cards.get(target.getFirstTarget(), game); if (card != null) { diff --git a/Mage.Sets/src/mage/cards/m/MysidianElder.java b/Mage.Sets/src/mage/cards/m/MysidianElder.java new file mode 100644 index 00000000000..2b8a6cf55f6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MysidianElder.java @@ -0,0 +1,39 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.BlackWizardToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MysidianElder extends CardImpl { + + public MysidianElder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // When this creature enters, create a 0/1 black Wizard creature token with "Whenever you cast a noncreature spell, this token deals 1 damage to each opponent." + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new BlackWizardToken()))); + } + + private MysidianElder(final MysidianElder card) { + super(card); + } + + @Override + public MysidianElder copy() { + return new MysidianElder(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MysticEnforcer.java b/Mage.Sets/src/mage/cards/m/MysticEnforcer.java index cb8df497a06..8e4f79a485d 100644 --- a/Mage.Sets/src/mage/cards/m/MysticEnforcer.java +++ b/Mage.Sets/src/mage/cards/m/MysticEnforcer.java @@ -39,7 +39,7 @@ public final class MysticEnforcer extends CardImpl { // Threshold - As long as seven or more cards are in your graveyard, Mystic Enforcer gets +3/+3 and has flying. Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( new BoostSourceEffect(3, 3, Duration.WhileOnBattlefield), ThresholdCondition.instance, - " as long as seven or more cards are in your graveyard, {this} gets +3/+3" + "as long as seven or more cards are in your graveyard, {this} gets +3/+3" )); ability.addEffect(new ConditionalContinuousEffect( new GainAbilitySourceEffect(FlyingAbility.getInstance()), diff --git a/Mage.Sets/src/mage/cards/n/NabanDeanOfIteration.java b/Mage.Sets/src/mage/cards/n/NabanDeanOfIteration.java index 4f2707720d2..8061511e12c 100644 --- a/Mage.Sets/src/mage/cards/n/NabanDeanOfIteration.java +++ b/Mage.Sets/src/mage/cards/n/NabanDeanOfIteration.java @@ -52,7 +52,7 @@ class NabanDeanOfIterationEffect extends ReplacementEffectImpl { NabanDeanOfIterationEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "If a Wizard entering under your control causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time"; + staticText = "if a Wizard you control entering causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time"; } private NabanDeanOfIterationEffect(final NabanDeanOfIterationEffect effect) { diff --git a/Mage.Sets/src/mage/cards/n/NahiriForgedInFury.java b/Mage.Sets/src/mage/cards/n/NahiriForgedInFury.java index 81767b74734..291781b2532 100644 --- a/Mage.Sets/src/mage/cards/n/NahiriForgedInFury.java +++ b/Mage.Sets/src/mage/cards/n/NahiriForgedInFury.java @@ -3,20 +3,14 @@ package mage.cards.n; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksCreatureYouControlTriggeredAbility; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.AffinityEffect; import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.AffinityAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.permanent.EquippedPredicate; import mage.game.Game; import mage.players.Player; @@ -26,19 +20,12 @@ import java.util.UUID; * @author correl */ public final class NahiriForgedInFury extends CardImpl { - private static final FilterControlledPermanent equipmentFilter = new FilterControlledPermanent(SubType.EQUIPMENT, - "Equipment"); + private static final FilterControlledCreaturePermanent equippedFilter = new FilterControlledCreaturePermanent( "an equipped creature you control"); - private static final Hint hint = new ValueHint( - "Equipment you control", new PermanentsOnBattlefieldCount(equipmentFilter)); - - static { - equippedFilter.add(EquippedPredicate.instance); - } public NahiriForgedInFury(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[] { CardType.CREATURE }, "{4}{R}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{W}"); this.supertype.add(SuperType.LEGENDARY); this.subtype.add(SubType.KOR); @@ -47,7 +34,8 @@ public final class NahiriForgedInFury extends CardImpl { this.toughness = new MageInt(4); // Affinity for Equipment - this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(equipmentFilter)).addHint(hint)); + this.addAbility(new AffinityAbility(AffinityType.EQUIPMENT)); + // Whenever an equipped creature you control attacks, exile the top card // of your library. You may play that card this turn. You may cast // Equipment spells this way without paying their mana costs. diff --git a/Mage.Sets/src/mage/cards/n/NahiriTheHarbinger.java b/Mage.Sets/src/mage/cards/n/NahiriTheHarbinger.java index d8e5e460afa..cad767499d9 100644 --- a/Mage.Sets/src/mage/cards/n/NahiriTheHarbinger.java +++ b/Mage.Sets/src/mage/cards/n/NahiriTheHarbinger.java @@ -34,6 +34,7 @@ import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCardInLibrary; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @@ -113,7 +114,7 @@ class NahiriTheHarbingerEffect extends SearchEffect { Card card = controller.getLibrary().getCard(target.getFirstTarget(), game); if (card != null) { controller.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); effect.setTargetPointer(new FixedTarget(permanent.getId(), permanent.getZoneChangeCounter(game))); diff --git a/Mage.Sets/src/mage/cards/n/NahiriTheLithomancer.java b/Mage.Sets/src/mage/cards/n/NahiriTheLithomancer.java index 725c151e8f3..6f17a3f311a 100644 --- a/Mage.Sets/src/mage/cards/n/NahiriTheLithomancer.java +++ b/Mage.Sets/src/mage/cards/n/NahiriTheLithomancer.java @@ -1,7 +1,6 @@ package mage.cards.n; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.CanBeYourCommanderAbility; @@ -16,7 +15,7 @@ import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.FilterCard; -import mage.filter.common.FilterControlledPermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.KorSoldierToken; @@ -24,10 +23,11 @@ import mage.game.permanent.token.NahiriTheLithomancerEquipmentToken; import mage.game.permanent.token.Token; import mage.players.Player; import mage.target.Target; -import mage.target.common.TargetControlledPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author emerald000 */ public final class NahiriTheLithomancer extends CardImpl { @@ -72,12 +72,6 @@ public final class NahiriTheLithomancer extends CardImpl { class NahiriTheLithomancerFirstAbilityEffect extends OneShotEffect { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("an Equipment you control"); - - static { - filter.add(SubType.EQUIPMENT.getPredicate()); - } - NahiriTheLithomancerFirstAbilityEffect() { super(Outcome.PutCreatureInPlay); this.staticText = "Create a 1/1 white Kor Soldier creature token. You may attach an Equipment you control to it"; @@ -102,7 +96,7 @@ class NahiriTheLithomancerFirstAbilityEffect extends OneShotEffect { Permanent tokenPermanent = game.getPermanent(tokenId); if (tokenPermanent != null) { //TODO: Make sure the Equipment can legally enchant the token, preferably on targetting. - Target target = new TargetControlledPermanent(0, 1, filter, true); + Target target = new TargetPermanent(0, 1, StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT, true); if (target.canChoose(controller.getId(), source, game) && controller.chooseUse(outcome, "Attach an Equipment you control to the created " + tokenPermanent.getIdName() + '?', source, game)) { if (target.choose(Outcome.Neutral, source.getControllerId(), source.getSourceId(), source, game)) { diff --git a/Mage.Sets/src/mage/cards/n/NecromanticSelection.java b/Mage.Sets/src/mage/cards/n/NecromanticSelection.java index bcbe5455933..5ae14f15495 100644 --- a/Mage.Sets/src/mage/cards/n/NecromanticSelection.java +++ b/Mage.Sets/src/mage/cards/n/NecromanticSelection.java @@ -23,6 +23,7 @@ import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCardInGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.ArrayList; import java.util.List; @@ -95,10 +96,13 @@ class NecromanticSelectionEffect extends OneShotEffect { Card card = game.getCard(target.getFirstTarget()); if (card != null) { controller.moveCards(card, Zone.BATTLEFIELD, source, game); - ContinuousEffect effect = new AddCreatureTypeAdditionEffect(SubType.ZOMBIE, true); - effect.setText("It's a black Zombie in addition to its other colors and types"); - effect.setTargetPointer(new FixedTarget(card.getId(), game)); - game.addEffect(effect, source); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); + if (permanent != null) { + ContinuousEffect effect = new AddCreatureTypeAdditionEffect(SubType.ZOMBIE, true); + effect.setText("It's a black Zombie in addition to its other colors and types"); + effect.setTargetPointer(new FixedTarget(permanent.getId(), game)); + game.addEffect(effect, source); + } } } return true; diff --git a/Mage.Sets/src/mage/cards/n/Necroplasm.java b/Mage.Sets/src/mage/cards/n/Necroplasm.java index fe130d75dc9..1d9d686046a 100644 --- a/Mage.Sets/src/mage/cards/n/Necroplasm.java +++ b/Mage.Sets/src/mage/cards/n/Necroplasm.java @@ -1,26 +1,21 @@ - package mage.cards.n; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyAllEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.keyword.DredgeAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.ComparisonType; -import mage.constants.Outcome; import mage.counters.CounterType; +import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ManaValuePredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.filter.predicate.mageobject.ManaValueEqualToCountersSourceCountPredicate; + +import java.util.UUID; /** * @@ -28,6 +23,13 @@ import mage.players.Player; */ public final class Necroplasm extends CardImpl { + private static final FilterPermanent filter = new FilterCreaturePermanent( + "each creature with mana value equal to the number of +1/+1 counters on {this}" + ); + static { + filter.add(new ManaValueEqualToCountersSourceCountPredicate(CounterType.P1P1)); + } + public Necroplasm(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}{B}"); this.subtype.add(SubType.OOZE); @@ -39,7 +41,7 @@ public final class Necroplasm extends CardImpl { this.addAbility(new BeginningOfUpkeepTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()))); // At the beginning of your end step, destroy each creature with converted mana cost equal to the number of +1/+1 counters on Necroplasm. - this.addAbility(new BeginningOfEndStepTriggeredAbility(new NecroplasmEffect())); + this.addAbility(new BeginningOfEndStepTriggeredAbility(new DestroyAllEffect(filter))); // Dredge 2 this.addAbility(new DredgeAbility(2)); @@ -54,38 +56,3 @@ public final class Necroplasm extends CardImpl { return new Necroplasm(this); } } - -class NecroplasmEffect extends OneShotEffect { - - NecroplasmEffect() { - super(Outcome.DestroyPermanent); - this.staticText = "destroy each creature with mana value equal to the number of +1/+1 counters on {this}."; - } - - private NecroplasmEffect(final NecroplasmEffect effect) { - super(effect); - } - - @Override - public NecroplasmEffect copy() { - return new NecroplasmEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (player != null && sourcePermanent != null) { - int numCounters = sourcePermanent.getCounters(game).getCount(CounterType.P1P1); - FilterCreaturePermanent filter = new FilterCreaturePermanent(); - filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, numCounters)); - for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source, game)) { - if(permanent != null) { - permanent.destroy(source, game, false); - } - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/n/NeeraWildMage.java b/Mage.Sets/src/mage/cards/n/NeeraWildMage.java index d3f43d2d202..6762de326e9 100644 --- a/Mage.Sets/src/mage/cards/n/NeeraWildMage.java +++ b/Mage.Sets/src/mage/cards/n/NeeraWildMage.java @@ -75,7 +75,7 @@ class NeeraWildMageEffect extends OneShotEffect { return false; } - if (!spellController.putCardsOnBottomOfLibrary(spell, game, source, true)) { + if (!spellController.putCardsOnBottomOfLibrary(spell, game, source)) { return false; } diff --git a/Mage.Sets/src/mage/cards/n/Nefashu.java b/Mage.Sets/src/mage/cards/n/Nefashu.java index cacd0053c4e..7556c4eaacb 100644 --- a/Mage.Sets/src/mage/cards/n/Nefashu.java +++ b/Mage.Sets/src/mage/cards/n/Nefashu.java @@ -1,4 +1,3 @@ - package mage.cards.n; import java.util.UUID; @@ -18,8 +17,6 @@ import mage.target.common.TargetCreaturePermanent; * @author jeffwadsworth */ public final class Nefashu extends CardImpl { - - static final String rule = "Whenever Nefashu attacks, up to five target creatures each get -1/-1 until end of turn."; public Nefashu(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{B}{B}"); @@ -30,7 +27,7 @@ public final class Nefashu extends CardImpl { this.toughness = new MageInt(3); // Whenever Nefashu attacks, up to five target creatures each get -1/-1 until end of turn. - Ability ability = new AttacksTriggeredAbility(new BoostTargetEffect(-1, -1, Duration.EndOfTurn), false, rule); + Ability ability = new AttacksTriggeredAbility(new BoostTargetEffect(-1, -1, Duration.EndOfTurn), false); ability.addTarget(new TargetCreaturePermanent(0, 5)); this.addAbility(ability); } @@ -43,4 +40,4 @@ public final class Nefashu extends CardImpl { public Nefashu copy() { return new Nefashu(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/n/NeglectedHeirloom.java b/Mage.Sets/src/mage/cards/n/NeglectedHeirloom.java index 695aa58165a..a4db3dfc9be 100644 --- a/Mage.Sets/src/mage/cards/n/NeglectedHeirloom.java +++ b/Mage.Sets/src/mage/cards/n/NeglectedHeirloom.java @@ -1,8 +1,5 @@ - package mage.cards.n; -import java.util.UUID; - import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.GenericManaCost; @@ -13,13 +10,15 @@ import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.target.common.TargetControlledCreaturePermanent; +import java.util.UUID; + /** * @author halljared */ @@ -85,6 +84,6 @@ class NeglectedHeirloomTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "When equipped creature transforms, transform Neglected Heirloom."; + return "When equipped creature transforms, transform {this}."; } } diff --git a/Mage.Sets/src/mage/cards/n/NeoExdeathDimensionsEnd.java b/Mage.Sets/src/mage/cards/n/NeoExdeathDimensionsEnd.java new file mode 100644 index 00000000000..d2602d2d66b --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NeoExdeathDimensionsEnd.java @@ -0,0 +1,52 @@ +package mage.cards.n; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.common.continuous.SetBasePowerSourceEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NeoExdeathDimensionsEnd extends CardImpl { + + private static final DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_PERMANENTS); + + public NeoExdeathDimensionsEnd(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.AVATAR); + this.power = new MageInt(0); + this.toughness = new MageInt(3); + this.nightCard = true; + this.color.setBlack(true); + this.color.setGreen(true); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Neo Exdeath's power is equal to the number of permanent cards in your graveyard. + this.addAbility(new SimpleStaticAbility(new SetBasePowerSourceEffect(xValue))); + } + + private NeoExdeathDimensionsEnd(final NeoExdeathDimensionsEnd card) { + super(card); + } + + @Override + public NeoExdeathDimensionsEnd copy() { + return new NeoExdeathDimensionsEnd(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/Neoform.java b/Mage.Sets/src/mage/cards/n/Neoform.java index aa083b38db2..96be2209ceb 100644 --- a/Mage.Sets/src/mage/cards/n/Neoform.java +++ b/Mage.Sets/src/mage/cards/n/Neoform.java @@ -3,25 +3,20 @@ package mage.cards.n; import mage.abilities.Ability; import mage.abilities.costs.Cost; import mage.abilities.costs.common.SacrificeTargetCost; -import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.ReplacementEffectImpl; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.counters.CounterType; +import mage.counters.Counters; import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInLibrary; -import mage.target.common.TargetControlledCreaturePermanent; -import mage.target.targetpointer.FixedTarget; import java.util.UUID; @@ -88,12 +83,8 @@ class NeoformEffect extends OneShotEffect { if (controller.searchLibrary(target, source, game)) { Card card = controller.getLibrary().getCard(target.getFirstTarget(), game); if (card != null) { - ContinuousEffectImpl effect = new NeoformReplacementEffect(); - effect.setTargetPointer(new FixedTarget(card, game)); - game.addEffect(effect, source); - if (!controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { - effect.discard(); - } + game.setEnterWithCounters(card.getId(), new Counters(CounterType.P1P1.createInstance())); + controller.moveCards(card, Zone.BATTLEFIELD, source, game); } } controller.shuffleLibrary(source, game); @@ -105,39 +96,3 @@ class NeoformEffect extends OneShotEffect { return new NeoformEffect(this); } } - -class NeoformReplacementEffect extends ReplacementEffectImpl { - - NeoformReplacementEffect() { - super(Duration.EndOfStep, Outcome.BoostCreature); - } - - private NeoformReplacementEffect(NeoformReplacementEffect effect) { - super(effect); - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - return event.getTargetId().equals(getTargetPointer().getFirst(game, source)); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget(); - if (creature != null) { - creature.addCounters(CounterType.P1P1.createInstance(), source.getControllerId(), source, game, event.getAppliedEffects()); - } - discard(); - return false; - } - - @Override - public NeoformReplacementEffect copy() { - return new NeoformReplacementEffect(this); - } -} diff --git a/Mage.Sets/src/mage/cards/n/NestingGrounds.java b/Mage.Sets/src/mage/cards/n/NestingGrounds.java index 8fb8fcf5775..7de92d27804 100644 --- a/Mage.Sets/src/mage/cards/n/NestingGrounds.java +++ b/Mage.Sets/src/mage/cards/n/NestingGrounds.java @@ -4,29 +4,16 @@ import mage.abilities.Ability; import mage.abilities.common.ActivateAsSorceryActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.MoveCounterTargetsEffect; import mage.abilities.mana.ColorlessManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.choices.Choice; -import mage.choices.ChoiceImpl; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.counters.Counter; -import mage.counters.CounterType; import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.other.AnotherTargetPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetControlledPermanent; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Set; import java.util.UUID; /** @@ -34,6 +21,12 @@ import java.util.UUID; */ public final class NestingGrounds extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent("another target permanent"); + + static { + filter.add(new AnotherTargetPredicate(2)); + } + public NestingGrounds(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); @@ -41,19 +34,10 @@ public final class NestingGrounds extends CardImpl { this.addAbility(new ColorlessManaAbility()); // {1}, {T}: Move a counter from target permanent you control onto another target permanent. Activate this ability only any time you could cast a sorcery. - Ability ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new NestingGroundsEffect(), new GenericManaCost(1)); + Ability ability = new ActivateAsSorceryActivatedAbility(new MoveCounterTargetsEffect(), new GenericManaCost(1)); ability.addCost(new TapSourceCost()); - // target 1 - TargetControlledPermanent target1 = new TargetControlledPermanent(new FilterControlledPermanent("permanent to remove counter from")); - target1.setTargetTag(1); - ability.addTarget(target1); - // target 2 - FilterPermanent filter = new FilterPermanent("permanent to put counter on"); - filter.add(new AnotherTargetPredicate(2)); - TargetPermanent target2 = new TargetPermanent(filter); - target2.setTargetTag(2); - ability.addTarget(target2); - + ability.addTarget(new TargetControlledPermanent().withChooseHint("to remove a counter from").setTargetTag(1)); + ability.addTarget(new TargetPermanent(filter).withChooseHint("to move a counter to").setTargetTag(2)); this.addAbility(ability); } @@ -66,59 +50,3 @@ public final class NestingGrounds extends CardImpl { return new NestingGrounds(this); } } - -class NestingGroundsEffect extends OneShotEffect { - - NestingGroundsEffect() { - super(Outcome.AIDontUseIt); - this.staticText = "Move a counter from target permanent you control onto another target permanent"; - } - - private NestingGroundsEffect(final NestingGroundsEffect effect) { - super(effect); - } - - @Override - public NestingGroundsEffect copy() { - return new NestingGroundsEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent fromPermanent = game.getPermanent(source.getFirstTarget()); - Permanent toPermanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if (fromPermanent == null - || toPermanent == null - || controller == null - || fromPermanent.getCounters(game).size() == 0) { - return false; - } - - if (fromPermanent.getCounters(game).size() == 1) { - for (Counter counter : fromPermanent.getCounters(game).values()) { - fromPermanent.removeCounters(counter.getName(), 1, source, game); - toPermanent.addCounters(new Counter(counter.getName()), source.getControllerId(), source, game); - } - return true; - } - - Choice choice = new ChoiceImpl(false); - Set possibleChoices = new LinkedHashSet<>(fromPermanent.getCounters(game).keySet()); - choice.setChoices(possibleChoices); - choice.setMessage("Choose a counter"); - if (controller.choose(outcome, choice, game)) { - String chosen = choice.getChoice(); - if (fromPermanent.getCounters(game).containsKey(chosen)) { - CounterType counterType = CounterType.findByName(chosen); - if (counterType != null) { - Counter counter = counterType.createInstance(); - fromPermanent.removeCounters(counterType.getName(), 1, source, game); - toPermanent.addCounters(counter, source.getControllerId(), source, game); - return true; - } - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/n/NetheresePuzzleWard.java b/Mage.Sets/src/mage/cards/n/NetheresePuzzleWard.java index 7943a43dfb6..350c48aadf3 100644 --- a/Mage.Sets/src/mage/cards/n/NetheresePuzzleWard.java +++ b/Mage.Sets/src/mage/cards/n/NetheresePuzzleWard.java @@ -2,9 +2,10 @@ package mage.cards.n; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -14,7 +15,6 @@ import mage.game.Game; import mage.game.events.DieRolledEvent; import mage.game.events.GameEvent; import mage.players.Player; -import mage.util.CardUtil; import java.util.UUID; @@ -67,7 +67,9 @@ class NetheresePuzzleWardEffect extends OneShotEffect { if (player == null) { return false; } - return player.scry(player.rollDice(outcome, source, game, 4), source, game); + int roll = player.rollDice(outcome, source, game, 4); + new ScryEffect(roll).apply(game, source); + return true; } } @@ -76,6 +78,7 @@ class NetheresePuzzleWardTriggeredAbility extends TriggeredAbilityImpl { NetheresePuzzleWardTriggeredAbility() { super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1)); this.withFlavorWord("Perfect Illumination"); + this.setTriggerPhrase("Whenever you roll a die's highest natural result, "); } private NetheresePuzzleWardTriggeredAbility(final NetheresePuzzleWardTriggeredAbility ability) { @@ -98,10 +101,4 @@ class NetheresePuzzleWardTriggeredAbility extends TriggeredAbilityImpl { public NetheresePuzzleWardTriggeredAbility copy() { return new NetheresePuzzleWardTriggeredAbility(this); } - - @Override - public String getRule() { - return CardUtil.italicizeWithEmDash(flavorWord) - + "Whenever you roll a die's highest natural result, draw a card."; - } } diff --git a/Mage.Sets/src/mage/cards/n/NewPrahvGuildmage.java b/Mage.Sets/src/mage/cards/n/NewPrahvGuildmage.java index 30cd3aa9a26..15bac3da56e 100644 --- a/Mage.Sets/src/mage/cards/n/NewPrahvGuildmage.java +++ b/Mage.Sets/src/mage/cards/n/NewPrahvGuildmage.java @@ -1,7 +1,5 @@ - package mage.cards.n; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -12,32 +10,23 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.filter.common.FilterNonlandPermanent; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; -import mage.target.common.TargetNonlandPermanent; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class NewPrahvGuildmage extends CardImpl { - private static final FilterNonlandPermanent filter = new FilterNonlandPermanent("nonland permanent an opponent controls"); - - static { - filter.add(TargetController.OPPONENT.getControllerPredicate()); - } - public NewPrahvGuildmage(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{U}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WIZARD); - - this.power = new MageInt(2); this.toughness = new MageInt(2); @@ -46,12 +35,11 @@ public final class NewPrahvGuildmage extends CardImpl { Ability ability = new SimpleActivatedAbility(new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), new ManaCostsImpl<>("{W}{U}")); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); - + // {3}{W}{U}: Detain target nonland permanent an opponent controls. // (Until your next turn, that permanent can't attack or block and its activated abilities can't be activated.) ability = new SimpleActivatedAbility(new DetainTargetEffect(), new ManaCostsImpl<>("{3}{W}{U}")); - TargetNonlandPermanent target = new TargetNonlandPermanent(filter); - ability.addTarget(target); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_NON_LAND)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/n/NewWayForward.java b/Mage.Sets/src/mage/cards/n/NewWayForward.java index 7b359e2b9ea..5da9902ad17 100644 --- a/Mage.Sets/src/mage/cards/n/NewWayForward.java +++ b/Mage.Sets/src/mage/cards/n/NewWayForward.java @@ -3,17 +3,16 @@ package mage.cards.n; import mage.abilities.Ability; import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.effects.PreventionEffectData; -import mage.abilities.effects.PreventionEffectImpl; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.game.Game; import mage.game.events.GameEvent; -import mage.target.TargetSource; +import mage.target.Target; import mage.target.targetpointer.FixedTarget; import java.util.UUID; @@ -27,7 +26,12 @@ public final class NewWayForward extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}{R}{W}"); // The next time a source of your choice would deal damage to you this turn, prevent that damage. When damage is prevented this way, New Way Forward deals that much damage to that source's controller and you draw that many cards. - this.getSpellAbility().addEffect(new NewWayForwardEffect()); + this.getSpellAbility().addEffect( + new PreventNextDamageFromChosenSourceEffect( + Duration.EndOfTurn, true, + NewWayForwardPreventionApplier.instance + ) + ); } private NewWayForward(final NewWayForward card) { @@ -40,58 +44,26 @@ public final class NewWayForward extends CardImpl { } } -class NewWayForwardEffect extends PreventionEffectImpl { +enum NewWayForwardPreventionApplier implements PreventNextDamageFromChosenSourceEffect.ApplierOnPrevention { + instance; - private final TargetSource target; - - NewWayForwardEffect() { - super(Duration.EndOfTurn, Integer.MAX_VALUE, false, false); - this.staticText = "the next time a source of your choice would deal damage to you this turn, " + - "prevent that damage. When damage is prevented this way, " + - "{this} deals that much damage to that source's controller and you draw that many cards"; - this.target = new TargetSource(); - } - - private NewWayForwardEffect(final NewWayForwardEffect effect) { - super(effect); - this.target = effect.target.copy(); - } - - @Override - public NewWayForwardEffect copy() { - return new NewWayForwardEffect(this); - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - PreventionEffectData preventionData = preventDamageAction(event, source, game); - this.used = true; - this.discard(); // only one use - if (preventionData.getPreventedDamage() < 1) { - return true; + public boolean apply(PreventionEffectData data, Target target, GameEvent event, Ability source, Game game) { + if (data == null || data.getPreventedDamage() <= 0) { + return false; } + int prevented = data.getPreventedDamage(); UUID objectControllerId = game.getControllerId(target.getFirstTarget()); ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( - new DamageTargetEffect(preventionData.getPreventedDamage()) + new DamageTargetEffect(prevented) .setTargetPointer(new FixedTarget(objectControllerId)), false ); - ability.addEffect(new DrawCardSourceControllerEffect(preventionData.getPreventedDamage())); + ability.addEffect(new DrawCardSourceControllerEffect(prevented)); game.fireReflexiveTriggeredAbility(ability, source); return true; } - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - return !this.used - && super.applies(event, source, game) - && event.getTargetId().equals(source.getControllerId()) - && event.getSourceId().equals(target.getFirstTarget()); + public String getText() { + return "When damage is prevented this way, {this} deals that much damage to that source's controller and you draw that many cards"; } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/n/NextOfKin.java b/Mage.Sets/src/mage/cards/n/NextOfKin.java index 3e2fce5d95a..a8847d11381 100644 --- a/Mage.Sets/src/mage/cards/n/NextOfKin.java +++ b/Mage.Sets/src/mage/cards/n/NextOfKin.java @@ -19,6 +19,7 @@ import mage.target.TargetCard; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -92,9 +93,12 @@ class NextOfKinDiesEffect extends OneShotEffect { Card card = game.getCard(target.getFirstTarget()); if (card != null) { controller.moveCards(card, Zone.BATTLEFIELD, source, game); - Effect returnToBattlefieldAttachedEffect = new ReturnToBattlefieldAttachedEffect(); - returnToBattlefieldAttachedEffect.setTargetPointer(new FixedTarget(card, game)); - game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(returnToBattlefieldAttachedEffect), source); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); + if (permanent != null) { + Effect returnToBattlefieldAttachedEffect = new ReturnToBattlefieldAttachedEffect(); + returnToBattlefieldAttachedEffect.setTargetPointer(new FixedTarget(permanent, game)); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(returnToBattlefieldAttachedEffect), source); + } } return true; } diff --git a/Mage.Sets/src/mage/cards/n/NikkoOnna.java b/Mage.Sets/src/mage/cards/n/NikkoOnna.java index 0db75a430b1..67a6bbf9035 100644 --- a/Mage.Sets/src/mage/cards/n/NikkoOnna.java +++ b/Mage.Sets/src/mage/cards/n/NikkoOnna.java @@ -33,7 +33,7 @@ public final class NikkoOnna extends CardImpl { this.addAbility(ability); // Whenever you cast a Spirit or Arcane spell, you may return Nikko-Onna to its owner's hand. - this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, true)); } private NikkoOnna(final NikkoOnna card) { diff --git a/Mage.Sets/src/mage/cards/n/NikoLightOfHope.java b/Mage.Sets/src/mage/cards/n/NikoLightOfHope.java index 931831c102e..3422ccb86fc 100644 --- a/Mage.Sets/src/mage/cards/n/NikoLightOfHope.java +++ b/Mage.Sets/src/mage/cards/n/NikoLightOfHope.java @@ -74,7 +74,7 @@ class NikoLightOfHopeEffect extends OneShotEffect { NikoLightOfHopeEffect() { super(Outcome.Benefit); - staticText = "Exile target nonlegendary creature you control. Shards you control become copies of it until the beginning of the next end step. Return it to the battlefield under its owner's control at the beginning of the next end step."; + staticText = "Exile target nonlegendary creature you control. Shards you control become copies of it until the next end step. Return it to the battlefield under its owner's control at the beginning of the next end step."; } private NikoLightOfHopeEffect(final NikoLightOfHopeEffect effect) { @@ -112,4 +112,4 @@ class NikoLightOfHopeEffect extends OneShotEffect { return false; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/n/NilsDisciplineEnforcer.java b/Mage.Sets/src/mage/cards/n/NilsDisciplineEnforcer.java index 0f94f592c7c..5c0e9fa4f76 100644 --- a/Mage.Sets/src/mage/cards/n/NilsDisciplineEnforcer.java +++ b/Mage.Sets/src/mage/cards/n/NilsDisciplineEnforcer.java @@ -2,25 +2,21 @@ package mage.cards.n; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCosts; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.combat.CantAttackYouUnlessPayAllEffect; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.counters.CounterType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.TargetPermanent; -import mage.target.targetadjustment.TargetAdjuster; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import java.util.UUID; @@ -42,7 +38,8 @@ public final class NilsDisciplineEnforcer extends CardImpl { Ability ability = new BeginningOfEndStepTriggeredAbility( new NilsDisciplineEnforcerCountersEffect() ); - ability.setTargetAdjuster(NilsDisciplineEnforcerAdjuster.instance); + ability.addTarget(new TargetCreaturePermanent(0, 1)); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, false)); this.addAbility(ability); // Each creature with one or more counters on it can't attack you or planeswalkers you control unless its controller pays {X}, where X is the number of counters on that creature. @@ -59,23 +56,6 @@ public final class NilsDisciplineEnforcer extends CardImpl { } } -enum NilsDisciplineEnforcerAdjuster implements TargetAdjuster { - instance; - - @Override - public void adjustTargets(Ability ability, Game game) { - ability.getTargets().clear(); - game.getState().getPlayersInRange(ability.getControllerId(), game).forEach(playerId -> { - Player player = game.getPlayer(playerId); - if (!(player == null)) { - FilterPermanent filter = new FilterCreaturePermanent("creature controlled by " + player.getName()); - filter.add(new ControllerIdPredicate(playerId)); - ability.addTarget(new TargetPermanent(0, 1, filter)); - } - }); - } -} - class NilsDisciplineEnforcerCountersEffect extends OneShotEffect { NilsDisciplineEnforcerCountersEffect() { diff --git a/Mage.Sets/src/mage/cards/n/NimDeathmantle.java b/Mage.Sets/src/mage/cards/n/NimDeathmantle.java index 3dca6eb6eeb..0d54b48cc5d 100644 --- a/Mage.Sets/src/mage/cards/n/NimDeathmantle.java +++ b/Mage.Sets/src/mage/cards/n/NimDeathmantle.java @@ -26,6 +26,7 @@ import mage.game.permanent.PermanentToken; import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import mage.util.ManaUtil; import java.util.UUID; @@ -144,7 +145,7 @@ class NimDeathmantleEffect extends OneShotEffect { // check if it's still in graveyard if (card != null && game.getState().getZone(card.getId()) == Zone.GRAVEYARD) { if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { permanent.addAttachment(equipment.getId(), source, game); } diff --git a/Mage.Sets/src/mage/cards/n/NinjasBlades.java b/Mage.Sets/src/mage/cards/n/NinjasBlades.java new file mode 100644 index 00000000000..4e7aa868922 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NinjasBlades.java @@ -0,0 +1,98 @@ +package mage.cards.n; + +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.AddCardSubtypeAttachedEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.abilities.keyword.JobSelectAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.players.Player; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NinjasBlades extends CardImpl { + + public NinjasBlades(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{B}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Job select + this.addAbility(new JobSelectAbility()); + + // Equipped creature gets +1/+1, is a Ninja in addition to its other types, and has "Whenever this creature deals combat damage to a player, draw a card, then discard a card. That player loses life equal to the discarded card's mana value." + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(1, 1)); + ability.addEffect(new AddCardSubtypeAttachedEffect( + SubType.NINJA, AttachmentType.EQUIPMENT + ).setText(", is a Ninja in addition to its other types")); + ability.addEffect(new GainAbilityAttachedEffect( + new DealsCombatDamageToAPlayerTriggeredAbility( + new NinjasBladesEffect(), false, true + ), AttachmentType.EQUIPMENT + ).setText(", and has \"Whenever this creature deals combat damage to a player, draw a card, " + + "then discard a card. That player loses life equal to the discarded card's mana value.\"")); + this.addAbility(ability); + + // Mutsunokami -- Equip {2} + this.addAbility(new EquipAbility(2).withFlavorWord("Mutsunokami")); + } + + private NinjasBlades(final NinjasBlades card) { + super(card); + } + + @Override + public NinjasBlades copy() { + return new NinjasBlades(this); + } +} + +class NinjasBladesEffect extends OneShotEffect { + + NinjasBladesEffect() { + super(Outcome.Benefit); + staticText = "draw a card, then discard a card. " + + "That player loses life equal to the discarded card's mana value"; + } + + private NinjasBladesEffect(final NinjasBladesEffect effect) { + super(effect); + } + + @Override + public NinjasBladesEffect copy() { + return new NinjasBladesEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + controller.drawCards(1, source, game); + Card card = controller.discardOne(false, false, source, game); + if (card == null || card.getManaValue() < 1) { + return true; + } + Optional.ofNullable(getTargetPointer().getFirst(game, source)) + .map(game::getPlayer) + .ifPresent(player -> player.loseLife(card.getManaValue(), game, source, false)); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/n/NissaOfShadowedBoughs.java b/Mage.Sets/src/mage/cards/n/NissaOfShadowedBoughs.java index 652760017a8..9b5cde1e639 100644 --- a/Mage.Sets/src/mage/cards/n/NissaOfShadowedBoughs.java +++ b/Mage.Sets/src/mage/cards/n/NissaOfShadowedBoughs.java @@ -15,7 +15,7 @@ import mage.counters.CounterType; import mage.counters.Counters; import mage.filter.StaticFilters; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.card.ManaValueLessThanControlledLandCountPredicate; +import mage.filter.predicate.mageobject.ManaValueLessThanControlledLandCountPredicate; import mage.game.Game; import mage.game.permanent.token.custom.CreatureToken; import mage.players.Player; diff --git a/Mage.Sets/src/mage/cards/n/NissaVitalForce.java b/Mage.Sets/src/mage/cards/n/NissaVitalForce.java index dd69a674abd..9ba2e994bc4 100644 --- a/Mage.Sets/src/mage/cards/n/NissaVitalForce.java +++ b/Mage.Sets/src/mage/cards/n/NissaVitalForce.java @@ -1,4 +1,3 @@ - package mage.cards.n; import mage.MageInt; @@ -10,14 +9,17 @@ import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.filter.FilterCard; -import mage.filter.common.FilterLandPermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterPermanentCard; import mage.game.command.emblems.NissaVitalForceEmblem; import mage.game.permanent.token.TokenImpl; +import mage.target.TargetPermanent; import mage.target.common.TargetCardInYourGraveyard; -import mage.target.common.TargetLandPermanent; import java.util.UUID; @@ -26,12 +28,7 @@ import java.util.UUID; */ public final class NissaVitalForce extends CardImpl { - private static final FilterLandPermanent filter = new FilterLandPermanent("land you control"); - private static final FilterCard filter2 = new FilterPermanentCard("permanent card from your graveyard"); - - static { - filter.add(TargetController.YOU.getControllerPredicate()); - } + private static final FilterCard filter = new FilterPermanentCard("permanent card from your graveyard"); public NissaVitalForce(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{3}{G}{G}"); @@ -45,12 +42,12 @@ public final class NissaVitalForce extends CardImpl { ability.addEffect(new BecomesCreatureTargetEffect( new NissaVitalForceToken(), false, true, Duration.UntilYourNextTurn ).withDurationRuleAtStart(true).setText("Until your next turn, it becomes a 5/5 Elemental creature with haste. It's still a land")); - ability.addTarget(new TargetLandPermanent(filter)); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND)); this.addAbility(ability); // -3: Return target permanent card from your graveyard to your hand. ability = new LoyaltyAbility(new ReturnFromGraveyardToHandTargetEffect(), -3); - ability.addTarget(new TargetCardInYourGraveyard(filter2)); + ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); // -6: You get an emblem with "Whenever a land you control enters, you may draw a card." diff --git a/Mage.Sets/src/mage/cards/n/NissaWorldwaker.java b/Mage.Sets/src/mage/cards/n/NissaWorldwaker.java index f5b4ec8d455..f399233fcb9 100644 --- a/Mage.Sets/src/mage/cards/n/NissaWorldwaker.java +++ b/Mage.Sets/src/mage/cards/n/NissaWorldwaker.java @@ -22,6 +22,7 @@ import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCardInLibrary; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -93,7 +94,7 @@ class NissaWorldwakerSearchEffect extends OneShotEffect { Card card = controller.getLibrary().getCard(cardId, game); if (card != null) { if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { - Permanent land = game.getPermanent(card.getId()); + Permanent land = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (land != null) { ContinuousEffect effect = new BecomesCreatureTargetEffect(new NissaWorldwakerToken(), false, true, Duration.Custom); effect.setTargetPointer(new FixedTarget(land, game)); diff --git a/Mage.Sets/src/mage/cards/n/NissasEncouragement.java b/Mage.Sets/src/mage/cards/n/NissasEncouragement.java index 62083b226ff..a442e7c1431 100644 --- a/Mage.Sets/src/mage/cards/n/NissasEncouragement.java +++ b/Mage.Sets/src/mage/cards/n/NissasEncouragement.java @@ -74,11 +74,7 @@ class NissasEncouragementEffect extends OneShotEffect { NissasEncouragementTarget target = new NissasEncouragementTarget(filter); if (player.searchLibrary(target, source, game)) { - boolean searchGY = false; - - if (target.getTargets().size() < 3) { - searchGY = true; - } + boolean searchGY = target.getTargets().size() < 3; Map foundCards = new HashMap<>(); foundCards.put("Forest", 0); @@ -154,7 +150,7 @@ class NissasEncouragementTarget extends TargetCardInLibrary { return false; } } - return filter.match(card, playerId, game); + return filter.match(card, playerId, source, game); } return false; } diff --git a/Mage.Sets/src/mage/cards/n/NoctisPrinceOfLucis.java b/Mage.Sets/src/mage/cards/n/NoctisPrinceOfLucis.java new file mode 100644 index 00000000000..14ad8788e14 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NoctisPrinceOfLucis.java @@ -0,0 +1,126 @@ +package mage.cards.n; + +import mage.MageIdentifier; +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.Costs; +import mage.abilities.costs.CostsImpl; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.common.counter.AddCounterEnteringCreatureEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.watchers.Watcher; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NoctisPrinceOfLucis extends CardImpl { + + public NoctisPrinceOfLucis(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{U}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.NOBLE); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // You may cast artifact spells from your graveyard by paying 3 life in addition to paying their other costs. If you cast a spell this way, that artifact enters with a finality counter on it. + this.addAbility(new SimpleStaticAbility(new NoctisPrinceOfLucisEffect()), new NoctisPrinceOfLucisWatcher()); + } + + private NoctisPrinceOfLucis(final NoctisPrinceOfLucis card) { + super(card); + } + + @Override + public NoctisPrinceOfLucis copy() { + return new NoctisPrinceOfLucis(this); + } +} + +class NoctisPrinceOfLucisEffect extends AsThoughEffectImpl { + + NoctisPrinceOfLucisEffect() { + super(AsThoughEffectType.CAST_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.AIDontUseIt); + staticText = "you may cast artifact spells from your graveyard by paying 3 life in addition to paying their " + + "other costs. If you cast a spell this way, that artifact enters with a finality counter on it"; + } + + private NoctisPrinceOfLucisEffect(final NoctisPrinceOfLucisEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public NoctisPrinceOfLucisEffect copy() { + return new NoctisPrinceOfLucisEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + if (!source.isControlledBy(affectedControllerId)) { + return false; + } + Card card = game.getCard(objectId); + Player player = game.getPlayer(affectedControllerId); + if (card == null + || player == null + || !card.isOwnedBy(affectedControllerId) + || !card.isArtifact(game) + || !game.getState().getZone(objectId).match(Zone.GRAVEYARD)) { + return false; + } + Costs newCosts = new CostsImpl<>(); + newCosts.addAll(card.getSpellAbility().getCosts()); + newCosts.add(new PayLifeCost(3)); + player.setCastSourceIdWithAlternateMana( + card.getId(), card.getManaCost(), newCosts, + MageIdentifier.NoctisPrinceOfLucisAlternateCast + ); + return true; + } +} + +class NoctisPrinceOfLucisWatcher extends Watcher { + + NoctisPrinceOfLucisWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (!GameEvent.EventType.SPELL_CAST.equals(event.getType()) + || !event.hasApprovingIdentifier(MageIdentifier.NoctisPrinceOfLucisAlternateCast)) { + return; + } + Spell target = game.getSpell(event.getTargetId()); + if (target != null) { + game.getState().addEffect(new AddCounterEnteringCreatureEffect( + new MageObjectReference(target.getCard(), game), + CounterType.FINALITY.createInstance(), Outcome.UnboostCreature + ), target.getSpellAbility()); + } + } +} diff --git a/Mage.Sets/src/mage/cards/n/NotDeadAfterAll.java b/Mage.Sets/src/mage/cards/n/NotDeadAfterAll.java index 9d58d02de3a..ef5e9f47054 100644 --- a/Mage.Sets/src/mage/cards/n/NotDeadAfterAll.java +++ b/Mage.Sets/src/mage/cards/n/NotDeadAfterAll.java @@ -14,6 +14,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -76,7 +77,7 @@ class NotDeadAfterAllEffect extends OneShotEffect { } player.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, true, null); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } @@ -86,4 +87,4 @@ class NotDeadAfterAllEffect extends OneShotEffect { .apply(game, source); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/n/NowIKnowMyABCs.java b/Mage.Sets/src/mage/cards/n/NowIKnowMyABCs.java index ef6074fa4a0..bf6728ce0db 100644 --- a/Mage.Sets/src/mage/cards/n/NowIKnowMyABCs.java +++ b/Mage.Sets/src/mage/cards/n/NowIKnowMyABCs.java @@ -1,34 +1,29 @@ - package mage.cards.n; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.WinGameSourceControllerEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author L_J */ public final class NowIKnowMyABCs extends CardImpl { public NowIKnowMyABCs(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}{U}"); // At the beginning of your upkeep, if you control permanents with names that include all twenty-six letters of the English alphabet, you win the game. - this.addAbility(new ConditionalTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility(new WinGameSourceControllerEffect()), - new NowIKnowMyABCsCondition(), - "At the beginning of your upkeep, if you control permanents with names that include all twenty-six letters of the English alphabet, you win the game.")); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new WinGameSourceControllerEffect()).withInterveningIf(NowIKnowMyABCsCondition.instance)); } private NowIKnowMyABCs(final NowIKnowMyABCs card) { @@ -41,10 +36,8 @@ public final class NowIKnowMyABCs extends CardImpl { } } -class NowIKnowMyABCsCondition implements Condition { - - public NowIKnowMyABCsCondition() { - } +enum NowIKnowMyABCsCondition implements Condition { + instance; @Override public boolean apply(Game game, Ability source) { diff --git a/Mage.Sets/src/mage/cards/n/NutCollector.java b/Mage.Sets/src/mage/cards/n/NutCollector.java index f524b4ec945..adbf617ce71 100644 --- a/Mage.Sets/src/mage/cards/n/NutCollector.java +++ b/Mage.Sets/src/mage/cards/n/NutCollector.java @@ -43,7 +43,7 @@ public final class NutCollector extends CardImpl { // Threshold - Squirrel creatures get +2/+2 as long as seven or more cards are in your graveyard. Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( new BoostAllEffect(2, 2, Duration.WhileOnBattlefield, filter, false), - ThresholdCondition.instance, "Squirrel creatures get +2/+2 as long as seven or more cards are in your graveyard" + ThresholdCondition.instance, "All Squirrels get +2/+2 as long as seven or more cards are in your graveyard" )); ability.setAbilityWord(AbilityWord.THRESHOLD); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/o/OathOfJace.java b/Mage.Sets/src/mage/cards/o/OathOfJace.java index dbfcd69cfa2..92471f6bd54 100644 --- a/Mage.Sets/src/mage/cards/o/OathOfJace.java +++ b/Mage.Sets/src/mage/cards/o/OathOfJace.java @@ -1,27 +1,32 @@ package mage.cards.o; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.DrawDiscardControllerEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SuperType; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.players.Player; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPlaneswalkerPermanent; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class OathOfJace extends CardImpl { + private static final FilterPermanent filter = new FilterControlledPlaneswalkerPermanent("planeswalkers you control"); + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, null); + private static final Hint hint = new ValueHint("Planeswalkers you control", xValue); + public OathOfJace(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); this.supertype.add(SuperType.LEGENDARY); @@ -30,8 +35,7 @@ public final class OathOfJace extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawDiscardControllerEffect(3, 2), false)); // At the beginning of your upkeep, scry X, where X is the number of planeswalkers you control. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new OathOfJaceEffect())); - + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new ScryEffect(xValue)).addHint(hint)); } private OathOfJace(final OathOfJace card) { @@ -42,34 +46,4 @@ public final class OathOfJace extends CardImpl { public OathOfJace copy() { return new OathOfJace(this); } -} - -class OathOfJaceEffect extends OneShotEffect { - - OathOfJaceEffect() { - super(Outcome.DrawCard); - this.staticText = "scry X, where X is the number of planeswalkers you control"; - } - - private OathOfJaceEffect(final OathOfJaceEffect effect) { - super(effect); - } - - @Override - public OathOfJaceEffect copy() { - return new OathOfJaceEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - int planeswalker = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_PLANESWALKER, source.getControllerId(), game); - if (planeswalker > 0) { - controller.scry(planeswalker, source, game); - } - return true; - } - return false; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/o/ObsidianFireheart.java b/Mage.Sets/src/mage/cards/o/ObsidianFireheart.java index 9575ce891a9..84f8fc0cc08 100644 --- a/Mage.Sets/src/mage/cards/o/ObsidianFireheart.java +++ b/Mage.Sets/src/mage/cards/o/ObsidianFireheart.java @@ -1,9 +1,7 @@ package mage.cards.o; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -12,23 +10,21 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageControllerEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.CounterType; +import mage.filter.FilterPermanent; import mage.filter.common.FilterLandPermanent; import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.target.common.TargetLandPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class ObsidianFireheart extends CardImpl { @@ -36,7 +32,7 @@ public final class ObsidianFireheart extends CardImpl { private static final String rule = "For as long as that land has a blaze counter " + "on it, it has \"At the beginning of your upkeep, this land deals 1 damage " + "to you.\" (The land continues to burn after Obsidian Fireheart has left the battlefield.)"; - private static final FilterLandPermanent filter = new FilterLandPermanent("land without a blaze counter on it"); + private static final FilterPermanent filter = new FilterLandPermanent("land without a blaze counter on it"); static { filter.add(Predicates.not(CounterType.BLAZE.getPredicate())); @@ -56,7 +52,7 @@ public final class ObsidianFireheart extends CardImpl { Ability ability = new SimpleActivatedAbility( new AddCountersTargetEffect(CounterType.BLAZE.createInstance()), new ManaCostsImpl<>("{1}{R}{R}")); - ability.addTarget(new TargetLandPermanent(filter)); + ability.addTarget(new TargetPermanent(filter)); OneShotEffect effect = new ObsidianFireheartOneShotEffect(); effect.setText(rule); ability.addEffect(effect); @@ -134,7 +130,7 @@ class ObsidianFireheartGainAbilityEffect extends GainAbilityTargetEffect { @Override public boolean isInactive(Ability source, Game game) { Permanent targetLand = game.getPermanent(this.getTargetPointer().getFirst(game, source)); - if (targetLand != null + if (targetLand != null && targetLand.getCounters(game).getCount(CounterType.BLAZE) < 1) { return true; } diff --git a/Mage.Sets/src/mage/cards/o/OgreSiegebreaker.java b/Mage.Sets/src/mage/cards/o/OgreSiegebreaker.java index 977fa7c5a32..e0e6d58c2d3 100644 --- a/Mage.Sets/src/mage/cards/o/OgreSiegebreaker.java +++ b/Mage.Sets/src/mage/cards/o/OgreSiegebreaker.java @@ -9,9 +9,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.WasDealtDamageThisTurnPredicate; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import java.util.UUID; @@ -21,13 +19,6 @@ import java.util.UUID; */ public final class OgreSiegebreaker extends CardImpl { - private static final FilterPermanent filter - = new FilterCreaturePermanent("creature that was dealt damage this turn"); - - static { - filter.add(WasDealtDamageThisTurnPredicate.instance); - } - public OgreSiegebreaker(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{R}"); @@ -38,7 +29,7 @@ public final class OgreSiegebreaker extends CardImpl { // {2}{B}{R}: Destroy target creature that was dealt damage this turn. Ability ability = new SimpleActivatedAbility(new DestroyTargetEffect(), new ManaCostsImpl<>("{2}{B}{R}")); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_DAMAGED_THIS_TURN)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/o/OketrasMonument.java b/Mage.Sets/src/mage/cards/o/OketrasMonument.java index 5a78c71b598..cd1a3cef338 100644 --- a/Mage.Sets/src/mage/cards/o/OketrasMonument.java +++ b/Mage.Sets/src/mage/cards/o/OketrasMonument.java @@ -1,7 +1,6 @@ package mage.cards.o; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; @@ -11,28 +10,24 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SuperType; -import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.permanent.token.WarriorVigilantToken; +import java.util.UUID; + /** - * * @author fireshoes */ public final class OketrasMonument extends CardImpl { private static final FilterCard filter = new FilterCard("White creature spells"); - private static final FilterSpell filter2 = new FilterSpell("a creature spell"); static { filter.add(Predicates.and(new ColorPredicate(ObjectColor.WHITE), CardType.CREATURE.getPredicate())); } - static { - filter2.add(CardType.CREATURE.getPredicate()); - } public OketrasMonument(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); @@ -43,7 +38,10 @@ public final class OketrasMonument extends CardImpl { this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 1))); // Whenever you cast a creature spell, create a 1/1 white Warrior creature token with vigilance. - this.addAbility(new SpellCastControllerTriggeredAbility(new CreateTokenEffect(new WarriorVigilantToken()), filter2, false)); + this.addAbility(new SpellCastControllerTriggeredAbility( + new CreateTokenEffect(new WarriorVigilantToken()), + StaticFilters.FILTER_SPELL_A_CREATURE, false + )); } private OketrasMonument(final OketrasMonument card) { diff --git a/Mage.Sets/src/mage/cards/o/OkibaSalvage.java b/Mage.Sets/src/mage/cards/o/OkibaSalvage.java index 7fc74ead283..747c8ecff5d 100644 --- a/Mage.Sets/src/mage/cards/o/OkibaSalvage.java +++ b/Mage.Sets/src/mage/cards/o/OkibaSalvage.java @@ -18,6 +18,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; +import mage.util.CardUtil; import java.util.UUID; @@ -79,7 +80,7 @@ class OkibaSalvageEffect extends OneShotEffect { return false; } player.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null && ControlArtifactAndEnchantmentCondition.instance.apply(game, source)) { permanent.addCounters(CounterType.P1P1.createInstance(2), source, game); } diff --git a/Mage.Sets/src/mage/cards/o/OldGrowthTroll.java b/Mage.Sets/src/mage/cards/o/OldGrowthTroll.java index 912bdff5417..acf6b65c22b 100644 --- a/Mage.Sets/src/mage/cards/o/OldGrowthTroll.java +++ b/Mage.Sets/src/mage/cards/o/OldGrowthTroll.java @@ -32,6 +32,7 @@ import mage.game.permanent.Permanent; import mage.game.permanent.token.TrollWarriorToken; import mage.players.Player; import mage.target.TargetPermanent; +import mage.util.CardUtil; import java.util.UUID; @@ -117,7 +118,7 @@ class OldGrowthTrollReturnEffect extends OneShotEffect { } game.addEffect(new OldGrowthTrollContinuousEffect(game.getState().getZoneChangeCounter(source.getSourceId()) + 1), source); controller.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent aura = game.getPermanent(card.getId()); + Permanent aura = CardUtil.getPermanentFromCardPutToBattlefield(card, game); Permanent creature = game.getPermanent(target.getFirstTarget()); if (aura == null || creature == null) { return true; @@ -198,7 +199,7 @@ class OldGrowthTrollContinuousEffect extends ContinuousEffectImpl { return new OldGrowthTrollContinuousEffect(this); } - private static final Ability makeAbility() { + private static Ability makeAbility() { Ability activatedAbility = new SimpleActivatedAbility( new CreateTokenEffect(new TrollWarriorToken(), 1, true, false), new GenericManaCost(1) ); diff --git a/Mage.Sets/src/mage/cards/o/OliviaCrimsonBride.java b/Mage.Sets/src/mage/cards/o/OliviaCrimsonBride.java index ee3d654841e..a13aef8b44d 100644 --- a/Mage.Sets/src/mage/cards/o/OliviaCrimsonBride.java +++ b/Mage.Sets/src/mage/cards/o/OliviaCrimsonBride.java @@ -22,6 +22,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -89,7 +90,7 @@ class OliviaCrimsonBrideEffect extends OneShotEffect { return false; } controller.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/o/OmegaHeartlessEvolution.java b/Mage.Sets/src/mage/cards/o/OmegaHeartlessEvolution.java new file mode 100644 index 00000000000..1fde5fdf4fc --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OmegaHeartlessEvolution.java @@ -0,0 +1,68 @@ +package mage.cards.o; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledLandPermanent; +import mage.filter.predicate.Predicates; +import mage.target.TargetPermanent; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; +import mage.target.targetpointer.EachTargetPointer; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OmegaHeartlessEvolution extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledLandPermanent("nonbasic lands you control"); + + static { + filter.add(Predicates.not(SuperType.BASIC.getPredicate())); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, null); + + public OmegaHeartlessEvolution(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}{G}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.ROBOT); + this.power = new MageInt(8); + this.toughness = new MageInt(8); + + // Wave Cannon -- When Omega enters, for each opponent, tap up to one target nonland permanent that opponent controls. Put X stun counters on each of those permanents and you gain X life, where X is the number of nonbasic lands you control. + Ability ability = new EntersBattlefieldTriggeredAbility(new TapTargetEffect() + .setTargetPointer(new EachTargetPointer()) + .setText("for each opponent, tap up to one target nonland permanent that opponent controls")); + ability.addEffect(new AddCountersTargetEffect(CounterType.STUN.createInstance(), xValue) + .setTargetPointer(new EachTargetPointer()) + .setText("Put X stun counters on each of those permanents")); + ability.addEffect(new GainLifeEffect(xValue).setText("and you gain X life, where X is the number of nonbasic lands you control")); + ability.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_PERMANENT_NON_LAND)); + this.addAbility(ability.withFlavorWord("Wave Cannon").setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true))); + } + + private OmegaHeartlessEvolution(final OmegaHeartlessEvolution card) { + super(card); + } + + @Override + public OmegaHeartlessEvolution copy() { + return new OmegaHeartlessEvolution(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/OneWithTheMachine.java b/Mage.Sets/src/mage/cards/o/OneWithTheMachine.java index ab33ed55038..652332212c0 100644 --- a/Mage.Sets/src/mage/cards/o/OneWithTheMachine.java +++ b/Mage.Sets/src/mage/cards/o/OneWithTheMachine.java @@ -1,15 +1,14 @@ package mage.cards.o; -import java.util.UUID; -import mage.abilities.dynamicvalue.common.HighestManaValueCount; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class OneWithTheMachine extends CardImpl { @@ -17,10 +16,12 @@ public final class OneWithTheMachine extends CardImpl { public OneWithTheMachine(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}"); - // Draw cards equal to the highest converted mana cost among artifacts you control. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect( - new HighestManaValueCount(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT) - ).setText("Draw cards equal to the highest mana value among artifacts you control")); + // Draw cards equal to the greatest mana value among artifacts you control. + this.getSpellAbility().addEffect( + new DrawCardSourceControllerEffect(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_ARTIFACTS) + .setText("Draw cards equal to the greatest mana value among artifacts you control") + ); + this.getSpellAbility().addHint(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_ARTIFACTS.getHint()); } private OneWithTheMachine(final OneWithTheMachine card) { diff --git a/Mage.Sets/src/mage/cards/o/OpenSeason.java b/Mage.Sets/src/mage/cards/o/OpenSeason.java index e9dc1a2d342..0e7ea3f3047 100644 --- a/Mage.Sets/src/mage/cards/o/OpenSeason.java +++ b/Mage.Sets/src/mage/cards/o/OpenSeason.java @@ -13,13 +13,12 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import java.util.UUID; @@ -36,7 +35,7 @@ public final class OpenSeason extends CardImpl { effect.setText("for each opponent, put a bounty counter on target creature that player controls"); Ability ability = new EntersBattlefieldTriggeredAbility(effect); ability.addTarget(new TargetCreaturePermanent()); - ability.setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); this.addAbility(ability); // Creatures your opponent control with bounty counters on them can't activate abilities diff --git a/Mage.Sets/src/mage/cards/o/OperaLoveSong.java b/Mage.Sets/src/mage/cards/o/OperaLoveSong.java new file mode 100644 index 00000000000..5e272054087 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OperaLoveSong.java @@ -0,0 +1,38 @@ +package mage.cards.o; + +import mage.abilities.Mode; +import mage.abilities.effects.common.ExileTopXMayPlayUntilEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OperaLoveSong extends CardImpl { + + public OperaLoveSong(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); + + // Choose one -- + // * Exile the top two cards of your library. You may play those cards until your next end step. + this.getSpellAbility().addEffect(new ExileTopXMayPlayUntilEffect(2, Duration.UntilYourNextEndStep)); + + // * One or two target creatures each get +2/+0 until end of turn. + this.getSpellAbility().addMode(new Mode(new BoostTargetEffect(2, 0)).addTarget(new TargetCreaturePermanent(1, 2))); + } + + private OperaLoveSong(final OperaLoveSong card) { + super(card); + } + + @Override + public OperaLoveSong copy() { + return new OperaLoveSong(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/Opportunist.java b/Mage.Sets/src/mage/cards/o/Opportunist.java index 91e118c0703..4bb6ddc3296 100644 --- a/Mage.Sets/src/mage/cards/o/Opportunist.java +++ b/Mage.Sets/src/mage/cards/o/Opportunist.java @@ -1,8 +1,5 @@ - - package mage.cards.o; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -12,24 +9,18 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.WasDealtDamageThisTurnPredicate; -import mage.target.common.TargetCreaturePermanent; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; /** * @author LevelX */ public final class Opportunist extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature that was dealt damage this turn"); - - static { - filter.add(WasDealtDamageThisTurnPredicate.instance); - } - public Opportunist(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SOLDIER); @@ -38,7 +29,7 @@ public final class Opportunist extends CardImpl { // {T}: Opportunist deals 1 damage to target creature that was dealt damage this turn. Ability ability = new SimpleActivatedAbility(new DamageTargetEffect(1), new TapSourceCost()); - ability.addTarget(new TargetCreaturePermanent(filter)); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_DAMAGED_THIS_TURN)); this.addAbility(ability); } @@ -50,5 +41,4 @@ public final class Opportunist extends CardImpl { public Opportunist copy() { return new Opportunist(this); } - } diff --git a/Mage.Sets/src/mage/cards/o/OraclesVault.java b/Mage.Sets/src/mage/cards/o/OraclesVault.java index 94b7f095643..9ddbc215f7c 100644 --- a/Mage.Sets/src/mage/cards/o/OraclesVault.java +++ b/Mage.Sets/src/mage/cards/o/OraclesVault.java @@ -2,18 +2,21 @@ package mage.cards.o; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.decorator.ConditionalActivatedAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileTopXMayPlayUntilEffect; import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.TargetController; import mage.counters.CounterType; import mage.game.Game; import mage.players.Player; @@ -25,25 +28,21 @@ import java.util.UUID; */ public final class OraclesVault extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.BRICK, 3); + public OraclesVault(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); - // {2}, {T}: Exile the top card of your library. Until end of turn, you may play that card. + // {2}, {T}: Exile the top card of your library. Until end of turn, you may play that card. Put a brick counter on Oracle's Vault. Ability ability = new SimpleActivatedAbility(new ExileTopXMayPlayUntilEffect(1, Duration.EndOfTurn) .withTextOptions("that card", false), new GenericManaCost(2)); ability.addCost(new TapSourceCost()); - - // Put a brick counter on Oracle's Vault. - Effect effect2 = new AddCountersSourceEffect(CounterType.BRICK.createInstance()); - ability.addEffect(effect2); + ability.addEffect(new AddCountersSourceEffect(CounterType.BRICK.createInstance())); this.addAbility(ability); // {T}: Exile the top card of your library. Until end of turn, you may play that card without paying its mana cost. // Activate this ability only if there are three or more brick counters on Oracle's Vault. - this.addAbility(new ConditionalActivatedAbility(Zone.BATTLEFIELD, - new OraclesVaultFreeEffect(), new TapSourceCost(), new SourceHasCounterCondition(CounterType.BRICK, 3, Integer.MAX_VALUE), - "{T}: Exile the top card of your library. Until end of turn, you may play that card without paying its mana cost. " - + "Activate only if there are three or more brick counters on {this}.")); + this.addAbility(new ConditionalActivatedAbility(new OraclesVaultFreeEffect(), new TapSourceCost(), condition)); } private OraclesVault(final OraclesVault card) { @@ -60,6 +59,7 @@ class OraclesVaultFreeEffect extends OneShotEffect { OraclesVaultFreeEffect() { super(Outcome.Benefit); + staticText = "exile the top card of your library. Until end of turn, you may play that card without paying its mana cost"; } private OraclesVaultFreeEffect(final OraclesVaultFreeEffect effect) { diff --git a/Mage.Sets/src/mage/cards/o/OranRiefTheVastwood.java b/Mage.Sets/src/mage/cards/o/OranRiefTheVastwood.java index b86bbdcebf4..c2f65f93180 100644 --- a/Mage.Sets/src/mage/cards/o/OranRiefTheVastwood.java +++ b/Mage.Sets/src/mage/cards/o/OranRiefTheVastwood.java @@ -1,37 +1,40 @@ - - package mage.cards.o; -import java.util.UUID; import mage.ObjectColor; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTappedAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCountersAllEffect; import mage.abilities.mana.GreenManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.filter.predicate.permanent.EnteredThisTurnPredicate; + +import java.util.UUID; /** - * * @author BetaSteward_at_googlemail.com */ public final class OranRiefTheVastwood extends CardImpl { + private static final FilterPermanent filter = new FilterCreaturePermanent("green creature that entered this turn"); + + static { + filter.add(new ColorPredicate(ObjectColor.GREEN)); + filter.add(EnteredThisTurnPredicate.instance); + } + public OranRiefTheVastwood(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},null); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, null); + this.addAbility(new EntersBattlefieldTappedAbility()); this.addAbility(new GreenManaAbility()); - this.addAbility(new SimpleActivatedAbility(new OranRiefTheVastwoodEffect(), new TapSourceCost())); + this.addAbility(new SimpleActivatedAbility(new AddCountersAllEffect(CounterType.P1P1.createInstance(), filter), new TapSourceCost())); } private OranRiefTheVastwood(final OranRiefTheVastwood card) { @@ -42,36 +45,4 @@ public final class OranRiefTheVastwood extends CardImpl { public OranRiefTheVastwood copy() { return new OranRiefTheVastwood(this); } - -} - -class OranRiefTheVastwoodEffect extends OneShotEffect { - - OranRiefTheVastwoodEffect() { - super(Outcome.BoostCreature); - staticText = "Put a +1/+1 counter on each green creature that entered the battlefield this turn"; - } - - private OranRiefTheVastwoodEffect(final OranRiefTheVastwoodEffect effect) { - super(effect); - } - - @Override - public OranRiefTheVastwoodEffect copy() { - return new OranRiefTheVastwoodEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - FilterPermanent filter = new FilterPermanent(); - filter.add(CardType.CREATURE.getPredicate()); - filter.add(new ColorPredicate(ObjectColor.GREEN)); - for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { - if (permanent.getTurnsOnBattlefield() == 0) { - permanent.addCounters(CounterType.P1P1.createInstance(), source.getControllerId(), source, game); - } - } - return true; - } - } diff --git a/Mage.Sets/src/mage/cards/o/OrbweaverKumo.java b/Mage.Sets/src/mage/cards/o/OrbweaverKumo.java index 0fbaddcd627..5a700426460 100644 --- a/Mage.Sets/src/mage/cards/o/OrbweaverKumo.java +++ b/Mage.Sets/src/mage/cards/o/OrbweaverKumo.java @@ -27,7 +27,7 @@ public final class OrbweaverKumo extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(4); this.addAbility(ReachAbility.getInstance()); - this.addAbility(new SpellCastControllerTriggeredAbility(new GainAbilitySourceEffect(new ForestwalkAbility(), Duration.EndOfTurn), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new GainAbilitySourceEffect(new ForestwalkAbility(), Duration.EndOfTurn), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, false)); } private OrbweaverKumo(final OrbweaverKumo card) { diff --git a/Mage.Sets/src/mage/cards/o/OrcishSiegemaster.java b/Mage.Sets/src/mage/cards/o/OrcishSiegemaster.java index 536782d61f0..306ccbce5cd 100644 --- a/Mage.Sets/src/mage/cards/o/OrcishSiegemaster.java +++ b/Mage.Sets/src/mage/cards/o/OrcishSiegemaster.java @@ -3,7 +3,7 @@ package mage.cards.o; import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; @@ -13,7 +13,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; @@ -60,10 +59,10 @@ public final class OrcishSiegemaster extends CardImpl { // Whenever Orcish Siegemaster attacks, it gets +X/+0 until end of turn, where X is the greatest power among creatures you control. this.addAbility(new AttacksTriggeredAbility( new BoostSourceEffect( - GreatestPowerAmongControlledCreaturesValue.instance, + GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES, StaticValue.get(0), Duration.EndOfTurn, "it" ) - ).addHint(GreatestPowerAmongControlledCreaturesValue.getHint())); + ).addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint())); } private OrcishSiegemaster(final OrcishSiegemaster card) { diff --git a/Mage.Sets/src/mage/cards/o/OrdruunMentor.java b/Mage.Sets/src/mage/cards/o/OrdruunMentor.java new file mode 100644 index 00000000000..a42c25b507a --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OrdruunMentor.java @@ -0,0 +1,104 @@ +package mage.cards.o; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.keyword.MentorAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OrdruunMentor extends CardImpl { + + public OrdruunMentor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R/W}"); + + this.subtype.add(SubType.MINOTAUR); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Mentor + this.addAbility(new MentorAbility()); + + // Whenever you attack a player, target creature that's attacking that player gains first strike until end of turn. + this.addAbility(new OrdruunMentorTriggeredAbility()); + } + + private OrdruunMentor(final OrdruunMentor card) { + super(card); + } + + @Override + public OrdruunMentor copy() { + return new OrdruunMentor(this); + } +} + +class OrdruunMentorTriggeredAbility extends TriggeredAbilityImpl { + + private enum OrdruunMentorPredicate implements ObjectSourcePlayerPredicate { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + return CardUtil.getEffectValueFromAbility( + input.getSource(), "playerAttacked", UUID.class + ) + .filter(uuid -> uuid.equals(game.getCombat().getDefenderId(input.getObject().getId()))) + .isPresent(); + } + } + + private static final FilterPermanent filter = new FilterCreaturePermanent("creature that's attacking that player"); + + static { + filter.add(OrdruunMentorPredicate.instance); + } + + OrdruunMentorTriggeredAbility() { + super(Zone.BATTLEFIELD, new GainAbilityTargetEffect(FirstStrikeAbility.getInstance()).setText("")); + this.setTriggerPhrase("Whenever you attack a player, "); + this.addTarget(new TargetPermanent(filter)); + } + + private OrdruunMentorTriggeredAbility(final OrdruunMentorTriggeredAbility ability) { + super(ability); + } + + @Override + public OrdruunMentorTriggeredAbility copy() { + return new OrdruunMentorTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DEFENDER_ATTACKED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!isControlledBy(event.getPlayerId()) || game.getPlayer(event.getTargetId()) == null) { + return false; + } + this.getEffects().setValue("playerAttacked", event.getTargetId()); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/o/OreGorger.java b/Mage.Sets/src/mage/cards/o/OreGorger.java index 19a5014d5df..b593719d0ff 100644 --- a/Mage.Sets/src/mage/cards/o/OreGorger.java +++ b/Mage.Sets/src/mage/cards/o/OreGorger.java @@ -25,7 +25,7 @@ public final class OreGorger extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(1); - Ability ability = new SpellCastControllerTriggeredAbility(new DestroyTargetEffect(), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true); + Ability ability = new SpellCastControllerTriggeredAbility(new DestroyTargetEffect(), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, true); ability.addTarget(new TargetNonBasicLandPermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/o/OrzhovEuthanist.java b/Mage.Sets/src/mage/cards/o/OrzhovEuthanist.java index 9a8361faa96..a649b3ff144 100644 --- a/Mage.Sets/src/mage/cards/o/OrzhovEuthanist.java +++ b/Mage.Sets/src/mage/cards/o/OrzhovEuthanist.java @@ -1,7 +1,5 @@ - package mage.cards.o; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.effects.common.DestroyTargetEffect; @@ -10,24 +8,18 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.WasDealtDamageThisTurnPredicate; -import mage.target.Target; -import mage.target.common.TargetCreaturePermanent; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class OrzhovEuthanist extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature that was dealt damage this turn"); - static { - filter.add(WasDealtDamageThisTurnPredicate.instance); - } - public OrzhovEuthanist(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.ASSASSIN); @@ -37,8 +29,7 @@ public final class OrzhovEuthanist extends CardImpl { // Haunt // When Orzhov Euthanist enters the battlefield or the creature it haunts dies, destroy target creature that was dealt damage this turn. Ability ability = new HauntAbility(this, new DestroyTargetEffect()); - Target target = new TargetCreaturePermanent(filter); - ability.addTarget(target); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_DAMAGED_THIS_TURN)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/o/Overkill.java b/Mage.Sets/src/mage/cards/o/Overkill.java new file mode 100644 index 00000000000..47623faeaad --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/Overkill.java @@ -0,0 +1,32 @@ +package mage.cards.o; + +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Overkill extends CardImpl { + + public Overkill(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}"); + + // Target creature gets -0/-9999 until end of turn. + this.getSpellAbility().addEffect(new BoostTargetEffect(0, -9999)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private Overkill(final Overkill card) { + super(card); + } + + @Override + public Overkill copy() { + return new Overkill(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/OverwhelmingStampede.java b/Mage.Sets/src/mage/cards/o/OverwhelmingStampede.java index ce5311a6dcf..74b783216d2 100644 --- a/Mage.Sets/src/mage/cards/o/OverwhelmingStampede.java +++ b/Mage.Sets/src/mage/cards/o/OverwhelmingStampede.java @@ -1,10 +1,7 @@ package mage.cards.o; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.TrampleAbility; @@ -12,14 +9,11 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; + +import java.util.UUID; /** - * * @author BetaSteward_at_googlemail.com */ public final class OverwhelmingStampede extends CardImpl { @@ -28,7 +22,14 @@ public final class OverwhelmingStampede extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{G}{G}"); // Until end of turn, creatures you control gain trample and get +X/+X, where X is the greatest power among creatures you control. - this.getSpellAbility().addEffect(new OverwhelmingStampedeInitEffect()); + this.getSpellAbility().addEffect(new GainAbilityControlledEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES + ).setText("Until end of turn, creatures you control gain trample")); + this.getSpellAbility().addEffect(new BoostControlledEffect( + GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES, GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES, + Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES, false + ).setText("and get +X/+X, where X is the greatest power among creatures you control")); + this.getSpellAbility().addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint()); } private OverwhelmingStampede(final OverwhelmingStampede card) { @@ -39,38 +40,4 @@ public final class OverwhelmingStampede extends CardImpl { public OverwhelmingStampede copy() { return new OverwhelmingStampede(this); } -} - -class OverwhelmingStampedeInitEffect extends OneShotEffect { - - OverwhelmingStampedeInitEffect() { - super(Outcome.BoostCreature); - this.staticText = "Until end of turn, creatures you control gain trample and get +X/+X, where X is the greatest power among creatures you control"; - } - - private OverwhelmingStampedeInitEffect(final OverwhelmingStampedeInitEffect effect) { - super(effect); - } - - @Override - public OverwhelmingStampedeInitEffect copy() { - return new OverwhelmingStampedeInitEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - int maxPower = 0; - for (Permanent perm : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game)) { - if (perm.getPower().getValue() > maxPower) { - maxPower = perm.getPower().getValue(); - } - } - ContinuousEffect effect = new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, new FilterCreaturePermanent()); - game.addEffect(effect, source); - if (maxPower != 0) { - effect = new BoostControlledEffect(maxPower, maxPower, Duration.EndOfTurn); - game.addEffect(effect, source); - } - return true; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/o/OviyaAutomechArtisan.java b/Mage.Sets/src/mage/cards/o/OviyaAutomechArtisan.java index 14865f03ca6..7d4882b2ba5 100644 --- a/Mage.Sets/src/mage/cards/o/OviyaAutomechArtisan.java +++ b/Mage.Sets/src/mage/cards/o/OviyaAutomechArtisan.java @@ -25,6 +25,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetCardInHand; +import mage.util.CardUtil; import java.util.UUID; @@ -120,7 +121,7 @@ class OviyaAutomechArtisanEffect extends OneShotEffect { return false; } player.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null && permanent.isArtifact(game)) { permanent.addCounters(CounterType.P1P1.createInstance(2), source, game); } diff --git a/Mage.Sets/src/mage/cards/o/OwlbearCub.java b/Mage.Sets/src/mage/cards/o/OwlbearCub.java index eeb98af5694..10f8af835bd 100644 --- a/Mage.Sets/src/mage/cards/o/OwlbearCub.java +++ b/Mage.Sets/src/mage/cards/o/OwlbearCub.java @@ -17,6 +17,7 @@ import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetCardInLibrary; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -116,7 +117,7 @@ class OwlbearCubEffect extends OneShotEffect { Card card = game.getCard(target.getFirstTarget()); if (card != null) { player.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { game.getCombat().addAttackerToCombat(permanent.getId(), getTargetPointer().getFirst(game, source), game); } diff --git a/Mage.Sets/src/mage/cards/o/OxiddaFinisher.java b/Mage.Sets/src/mage/cards/o/OxiddaFinisher.java index 395ca9c0410..3df56a7b716 100644 --- a/Mage.Sets/src/mage/cards/o/OxiddaFinisher.java +++ b/Mage.Sets/src/mage/cards/o/OxiddaFinisher.java @@ -1,32 +1,21 @@ package mage.cards.o; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.common.AffinityEffect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; -import mage.constants.SubType; +import mage.abilities.keyword.AffinityAbility; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AffinityType; import mage.constants.CardType; -import mage.constants.Zone; -import mage.filter.common.FilterControlledPermanent; +import mage.constants.SubType; + +import java.util.UUID; /** * @author TheElk801 */ public final class OxiddaFinisher extends CardImpl { - private static final FilterControlledPermanent filter - = new FilterControlledPermanent(SubType.EQUIPMENT, "Equipment"); - private static final Hint hint = new ValueHint( - "Equipment you control", new PermanentsOnBattlefieldCount(filter) - ); - public OxiddaFinisher(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{R}{R}"); @@ -36,7 +25,7 @@ public final class OxiddaFinisher extends CardImpl { this.toughness = new MageInt(5); // Affinity for Equipment - this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filter)).addHint(hint)); + this.addAbility(new AffinityAbility(AffinityType.EQUIPMENT)); // Trample this.addAbility(TrampleAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/o/OxiddaGolem.java b/Mage.Sets/src/mage/cards/o/OxiddaGolem.java index dbb370c8b48..d7b91f346f5 100644 --- a/Mage.Sets/src/mage/cards/o/OxiddaGolem.java +++ b/Mage.Sets/src/mage/cards/o/OxiddaGolem.java @@ -1,30 +1,30 @@ - package mage.cards.o; -import java.util.UUID; import mage.MageInt; -import mage.abilities.keyword.AffinityForLandTypeAbility; +import mage.abilities.keyword.AffinityAbility; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AffinityType; import mage.constants.CardType; import mage.constants.SubType; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class OxiddaGolem extends CardImpl { public OxiddaGolem(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{6}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{6}"); this.subtype.add(SubType.GOLEM); this.power = new MageInt(3); this.toughness = new MageInt(2); // Affinity for Mountains - this.addAbility(new AffinityForLandTypeAbility(SubType.MOUNTAIN, "Mountains")); - + this.addAbility(new AffinityAbility(AffinityType.MOUNTAINS)); + // Haste this.addAbility(HasteAbility.getInstance()); } diff --git a/Mage.Sets/src/mage/cards/o/OyobiWhoSplitTheHeavens.java b/Mage.Sets/src/mage/cards/o/OyobiWhoSplitTheHeavens.java index ddfb7efc877..2840bfb6b6f 100644 --- a/Mage.Sets/src/mage/cards/o/OyobiWhoSplitTheHeavens.java +++ b/Mage.Sets/src/mage/cards/o/OyobiWhoSplitTheHeavens.java @@ -29,7 +29,7 @@ public final class OyobiWhoSplitTheHeavens extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever you cast a Spirit or Arcane spell, create a 3/3 white Spirit creature token with flying. - this.addAbility(new SpellCastControllerTriggeredAbility(new CreateTokenEffect(new AnotherSpiritToken()), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new CreateTokenEffect(new AnotherSpiritToken()), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, false)); } private OyobiWhoSplitTheHeavens(final OyobiWhoSplitTheHeavens card) { diff --git a/Mage.Sets/src/mage/cards/p/PadeemConsulOfInnovation.java b/Mage.Sets/src/mage/cards/p/PadeemConsulOfInnovation.java index e9e39dc8b95..d7b4ed5f466 100644 --- a/Mage.Sets/src/mage/cards/p/PadeemConsulOfInnovation.java +++ b/Mage.Sets/src/mage/cards/p/PadeemConsulOfInnovation.java @@ -1,18 +1,28 @@ package mage.cards.p; import mage.MageInt; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; +import mage.MageObject; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; -import mage.abilities.condition.common.ControlsPermanentGreatestCMCCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; import mage.abilities.keyword.HexproofAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.permanent.Permanent; import java.util.UUID; @@ -21,8 +31,16 @@ import java.util.UUID; */ public final class PadeemConsulOfInnovation extends CardImpl { - private static final Condition condition - = new ControlsPermanentGreatestCMCCondition(StaticFilters.FILTER_PERMANENT_ARTIFACT); + private static final FilterPermanent filter = new FilterControlledArtifactPermanent( + "you control the artifact with the greatest mana value or tied for the greatest mana value" + ); + + static { + filter.add(PadeemConsulOfInnovationPredicate.instance); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + private static final Hint hint = new ConditionHint(condition); public PadeemConsulOfInnovation(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); @@ -39,12 +57,7 @@ public final class PadeemConsulOfInnovation extends CardImpl { ))); // At the beginning of your upkeep, if you control the artifact with the highest converted mana cost or tied for the highest converted mana cost, draw a card. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility( - new DrawCardSourceControllerEffect(1), false - ), condition, "At the beginning of your upkeep, if you control the artifact " + - "with the highest mana value or tied for the highest mana value, draw a card." - )); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new DrawCardSourceControllerEffect(1)).withInterveningIf(condition).addHint(hint)); } private PadeemConsulOfInnovation(final PadeemConsulOfInnovation card) { @@ -56,3 +69,18 @@ public final class PadeemConsulOfInnovation extends CardImpl { return new PadeemConsulOfInnovation(this); } } + +enum PadeemConsulOfInnovationPredicate implements Predicate { + instance; + + @Override + public boolean apply(Permanent input, Game game) { + return input.getManaValue() >= game + .getBattlefield() + .getActivePermanents(StaticFilters.FILTER_PERMANENT_ARTIFACT, input.getControllerId(), game) + .stream() + .mapToInt(MageObject::getManaValue) + .max() + .orElse(-1); + } +} diff --git a/Mage.Sets/src/mage/cards/p/Painbringer.java b/Mage.Sets/src/mage/cards/p/Painbringer.java index 1ad9beda52c..c5057018317 100644 --- a/Mage.Sets/src/mage/cards/p/Painbringer.java +++ b/Mage.Sets/src/mage/cards/p/Painbringer.java @@ -38,7 +38,8 @@ public final class Painbringer extends CardImpl { Effect effect = new BoostTargetEffect(X, X, Duration.EndOfTurn); effect.setText("Target creature gets -X/-X until end of turn, where X is the number of cards exiled this way"); Ability ability = new SimpleActivatedAbility(effect, new TapSourceCost()); - ability.addCost(new ExileXFromYourGraveCost(new FilterCard("any number of cards from your graveyard"))); + ability.addCost(new ExileXFromYourGraveCost(new FilterCard("any number of cards from your graveyard")) + .setText("Exile any number of cards from your graveyard")); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/p/PaladinElizabethTaggerdy.java b/Mage.Sets/src/mage/cards/p/PaladinElizabethTaggerdy.java index 44f5e78d200..9548a0a9b8b 100644 --- a/Mage.Sets/src/mage/cards/p/PaladinElizabethTaggerdy.java +++ b/Mage.Sets/src/mage/cards/p/PaladinElizabethTaggerdy.java @@ -16,6 +16,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInHand; +import mage.util.CardUtil; import java.util.UUID; @@ -79,7 +80,7 @@ class PaladinElizabethTaggerdyEffect extends OneShotEffect { Card card = controller.getHand().get(cardId, game); if (card != null) { if (controller.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, true, null)) { - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { game.getCombat().addAttackingCreature(permanent.getId(), game); } diff --git a/Mage.Sets/src/mage/cards/p/PalladiaMorsTheRuiner.java b/Mage.Sets/src/mage/cards/p/PalladiaMorsTheRuiner.java index 52a3f52255f..5a650a1ec43 100644 --- a/Mage.Sets/src/mage/cards/p/PalladiaMorsTheRuiner.java +++ b/Mage.Sets/src/mage/cards/p/PalladiaMorsTheRuiner.java @@ -1,13 +1,8 @@ package mage.cards.p; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.MageInt; -import mage.MageObjectReference; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.Condition; +import mage.abilities.condition.common.SourceHasntDealtDamageThisGameCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.keyword.FlyingAbility; @@ -19,15 +14,11 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.WatcherScope; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.watchers.Watcher; +import mage.watchers.common.DealtDamageThisGameWatcher; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class PalladiaMorsTheRuiner extends CardImpl { @@ -51,13 +42,11 @@ public final class PalladiaMorsTheRuiner extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // Palladia-Mors, the Ruiner has hexproof if it hasn't dealt damage yet. - this.addAbility(new SimpleStaticAbility( - new ConditionalContinuousEffect( - new GainAbilitySourceEffect(HexproofAbility.getInstance()), - PalladiaMorsTheRuinerCondition.instance, - "{this} has hexproof if it hasn't dealt damage yet" - ) - ), new PalladiaMorsTheRuinerWatcher()); + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(HexproofAbility.getInstance()), + SourceHasntDealtDamageThisGameCondition.instance, + "{this} has hexproof if it hasn't dealt damage yet" + )).addHint(SourceHasntDealtDamageThisGameCondition.getHint()), new DealtDamageThisGameWatcher()); } private PalladiaMorsTheRuiner(final PalladiaMorsTheRuiner card) { @@ -69,49 +58,3 @@ public final class PalladiaMorsTheRuiner extends CardImpl { return new PalladiaMorsTheRuiner(this); } } - -enum PalladiaMorsTheRuinerCondition implements Condition { - - instance; - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - PalladiaMorsTheRuinerWatcher watcher = game.getState().getWatcher(PalladiaMorsTheRuinerWatcher.class); - return permanent != null && !watcher.getDamagers().contains(new MageObjectReference(permanent, game)); - } - - @Override - public String toString() { - return "{this} hasn't dealt damage yet"; - } - -} - -class PalladiaMorsTheRuinerWatcher extends Watcher { - - private final Set damagers = new HashSet<>(); - - public PalladiaMorsTheRuinerWatcher() { - super(WatcherScope.GAME); - } - - @Override - public void watch(GameEvent event, Game game) { - switch (event.getType()) { - case DAMAGED_PERMANENT: - case DAMAGED_PLAYER: - break; - default: - return; - } - Permanent permanent = game.getPermanent(event.getSourceId()); - if (permanent != null) { - damagers.add(new MageObjectReference(permanent, game)); - } - } - - public Set getDamagers() { - return damagers; - } -} diff --git a/Mage.Sets/src/mage/cards/p/Panharmonicon.java b/Mage.Sets/src/mage/cards/p/Panharmonicon.java index 239825fbfaa..5d9f0a9aebd 100644 --- a/Mage.Sets/src/mage/cards/p/Panharmonicon.java +++ b/Mage.Sets/src/mage/cards/p/Panharmonicon.java @@ -43,7 +43,7 @@ class PanharmoniconEffect extends ReplacementEffectImpl { PanharmoniconEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "If an artifact or creature entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time"; + staticText = "If an artifact or creature entering causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time"; } private PanharmoniconEffect(final PanharmoniconEffect effect) { diff --git a/Mage.Sets/src/mage/cards/p/PapalymoTotolymo.java b/Mage.Sets/src/mage/cards/p/PapalymoTotolymo.java new file mode 100644 index 00000000000..c707d7a82cc --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PapalymoTotolymo.java @@ -0,0 +1,120 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.CanBeSacrificedPredicate; +import mage.filter.predicate.permanent.GreatestPowerControlledPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.watchers.common.PlayerLostLifeWatcher; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PapalymoTotolymo extends CardImpl { + + public PapalymoTotolymo(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.DWARF); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // Whenever you cast a noncreature spell, Papalymo Totolymo deals 1 damage to each opponent and you gain 1 life. + Ability ability = new SpellCastControllerTriggeredAbility( + new DamagePlayersEffect(1, TargetController.OPPONENT), + StaticFilters.FILTER_SPELL_A_NON_CREATURE, false + ); + ability.addEffect(new GainLifeEffect(1).concatBy("and")); + this.addAbility(ability); + + // {4}, {T}, Sacrifice Papalymo Totolymo: Each opponent who lost life this turn sacrifices a creature with the greatest power among creatures they control. + ability = new SimpleActivatedAbility(new PapalymoTotolymoEffect(), new GenericManaCost(4)); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeSourceCost()); + this.addAbility(ability); + } + + private PapalymoTotolymo(final PapalymoTotolymo card) { + super(card); + } + + @Override + public PapalymoTotolymo copy() { + return new PapalymoTotolymo(this); + } +} + +class PapalymoTotolymoEffect extends OneShotEffect { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("a creature you control with the greatest power"); + + static { + filter.add(GreatestPowerControlledPredicate.instance); + filter.add(CanBeSacrificedPredicate.instance); + } + + PapalymoTotolymoEffect() { + super(Outcome.Benefit); + staticText = "each opponent who lost life this turn sacrifices a creature " + + "with the greatest power among creatures they control"; + } + + private PapalymoTotolymoEffect(final PapalymoTotolymoEffect effect) { + super(effect); + } + + @Override + public PapalymoTotolymoEffect copy() { + return new PapalymoTotolymoEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + List permanents = new ArrayList<>(); + PlayerLostLifeWatcher watcher = game.getState().getWatcher(PlayerLostLifeWatcher.class); + for (UUID opponentId : game.getOpponents(source.getControllerId())) { + if (watcher.getLifeLost(opponentId) < 1) { + continue; + } + Player opponent = game.getPlayer(opponentId); + if (opponent == null || !game.getBattlefield().contains(filter, opponentId, source, game, 1)) { + continue; + } + TargetPermanent target = new TargetPermanent(filter); + target.withNotTarget(true); + target.withChooseHint("to sacrifice"); + opponent.choose(outcome, target, source, game); + permanents.add(game.getPermanent(target.getFirstTarget())); + } + permanents.removeIf(Objects::isNull); + for (Permanent permanent : permanents) { + permanent.sacrifice(source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/p/ParallelThoughts.java b/Mage.Sets/src/mage/cards/p/ParallelThoughts.java index 8d8e5c29245..e7cd28480ab 100644 --- a/Mage.Sets/src/mage/cards/p/ParallelThoughts.java +++ b/Mage.Sets/src/mage/cards/p/ParallelThoughts.java @@ -114,7 +114,7 @@ class ParallelThoughtsReplacementEffect extends ReplacementEffectImpl { ParallelThoughtsReplacementEffect() { super(Duration.WhileOnBattlefield, Outcome.DrawCard); - staticText = "If you would draw a card, you may instead put the top card of the pile you exiled with {this} into your hand"; + staticText = "If you would draw a card, you may instead put the top card of the pile you exiled into your hand"; } private ParallelThoughtsReplacementEffect(final ParallelThoughtsReplacementEffect effect) { diff --git a/Mage.Sets/src/mage/cards/p/ParapetThrasher.java b/Mage.Sets/src/mage/cards/p/ParapetThrasher.java index 8fe9ba4b29e..48725cea523 100644 --- a/Mage.Sets/src/mage/cards/p/ParapetThrasher.java +++ b/Mage.Sets/src/mage/cards/p/ParapetThrasher.java @@ -22,7 +22,7 @@ import mage.filter.common.FilterControlledPermanent; import mage.game.Game; import mage.players.Player; import mage.target.TargetPermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; /** * @@ -49,7 +49,7 @@ public final class ParapetThrasher extends CardImpl { filter, true, true, SetTargetPointer.PLAYER, false) .setTriggerPhrase("Whenever one or more Dragons you control deal combat damage to an opponent, "); ability.addTarget(new TargetPermanent(artifactFilter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); ability.setModeTag("destroy artifact"); ability.getModes().setLimitUsageByOnce(true); diff --git a/Mage.Sets/src/mage/cards/p/PardicArsonist.java b/Mage.Sets/src/mage/cards/p/PardicArsonist.java index ca73e9a1fe3..0af67015c66 100644 --- a/Mage.Sets/src/mage/cards/p/PardicArsonist.java +++ b/Mage.Sets/src/mage/cards/p/PardicArsonist.java @@ -35,7 +35,7 @@ public final class PardicArsonist extends CardImpl { this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( new GainAbilitySourceEffect(ability), ThresholdCondition.instance, "As long as " + "seven or more cards are in your graveyard, {this} has \"When {this} " + - "enters the battlefield, it deals 3 damage to any target.\"" + "enters, it deals 3 damage to any target.\"" )).setAbilityWord(AbilityWord.THRESHOLD)); } diff --git a/Mage.Sets/src/mage/cards/p/PartingGust.java b/Mage.Sets/src/mage/cards/p/PartingGust.java index 6dc25b06a9e..4d7fbfeb5d7 100644 --- a/Mage.Sets/src/mage/cards/p/PartingGust.java +++ b/Mage.Sets/src/mage/cards/p/PartingGust.java @@ -36,6 +36,7 @@ import java.util.stream.Collectors; public final class PartingGust extends CardImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nontoken creature"); + static { filter.add(TokenPredicate.FALSE); } @@ -52,7 +53,7 @@ public final class PartingGust extends CardImpl { new ExileTargetEffect(), new PartingGustExileReturnEffect(), GiftWasPromisedCondition.TRUE, - "Exile target nontoken creature. If the gift wasn't promised, return that creature to the " + + "Exile target nontoken creature. If the gift wasn't promised, return that card to the " + "battlefield under its owner's control with a +1/+1 counter on it at the beginning of the next end step." )); } diff --git a/Mage.Sets/src/mage/cards/p/PathbreakerIbex.java b/Mage.Sets/src/mage/cards/p/PathbreakerIbex.java index 2d27c01050d..1177aac355d 100644 --- a/Mage.Sets/src/mage/cards/p/PathbreakerIbex.java +++ b/Mage.Sets/src/mage/cards/p/PathbreakerIbex.java @@ -1,27 +1,23 @@ package mage.cards.p; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Outcome; +import mage.constants.SubType; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.permanent.Permanent; + +import java.util.UUID; /** - * * @author fireshoes */ public final class PathbreakerIbex extends CardImpl { @@ -33,7 +29,15 @@ public final class PathbreakerIbex extends CardImpl { this.toughness = new MageInt(3); // Whenever Pathbreaker Ibex attacks, creatures you control gain trample and get +X/+X until end of turn, where X is the greatest power among creatures you control. - this.addAbility(new AttacksTriggeredAbility(new PathbreakerIbexEffect(), false)); + Ability ability = new AttacksTriggeredAbility(new GainAbilityControlledEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES + ).setText("creatures you control gain trample"), false); + ability.addEffect(new BoostControlledEffect( + GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES, GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES, + Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES, false + ).setText("and get +X/+X until end of turn, where X is the greatest power among creatures you control")); + ability.addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint()); + this.addAbility(ability); } private PathbreakerIbex(final PathbreakerIbex card) { @@ -44,38 +48,4 @@ public final class PathbreakerIbex extends CardImpl { public PathbreakerIbex copy() { return new PathbreakerIbex(this); } -} - -class PathbreakerIbexEffect extends OneShotEffect { - - PathbreakerIbexEffect() { - super(Outcome.BoostCreature); - this.staticText = "creatures you control gain trample and get +X/+X until end of turn, where X is the greatest power among creatures you control"; - } - - private PathbreakerIbexEffect(final PathbreakerIbexEffect effect) { - super(effect); - } - - @Override - public PathbreakerIbexEffect copy() { - return new PathbreakerIbexEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - int maxPower = 0; - for (Permanent perm : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), game)) { - if (perm.getPower().getValue() > maxPower) { - maxPower = perm.getPower().getValue(); - } - } - ContinuousEffect effect = new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURES); - game.addEffect(effect, source); - if (maxPower != 0) { - effect = new BoostControlledEffect(maxPower, maxPower, Duration.EndOfTurn); - game.addEffect(effect, source); - } - return true; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/p/PayNoHeed.java b/Mage.Sets/src/mage/cards/p/PayNoHeed.java index ee3f33992cc..0b6b008e369 100644 --- a/Mage.Sets/src/mage/cards/p/PayNoHeed.java +++ b/Mage.Sets/src/mage/cards/p/PayNoHeed.java @@ -2,24 +2,24 @@ package mage.cards.p; import java.util.UUID; -import mage.abilities.effects.common.PreventDamageBySourceEffect; + +import mage.abilities.effects.common.PreventDamageByChosenSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; /** - * * @author jeffwadsworth */ public final class PayNoHeed extends CardImpl { public PayNoHeed(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{W}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}"); // Prevent all damage a source of your choice would deal this turn. - this.getSpellAbility().addEffect(new PreventDamageBySourceEffect()); - + this.getSpellAbility().addEffect(new PreventDamageByChosenSourceEffect()); + } private PayNoHeed(final PayNoHeed card) { diff --git a/Mage.Sets/src/mage/cards/p/PeakEruption.java b/Mage.Sets/src/mage/cards/p/PeakEruption.java index 3ccfbe7fba2..1ff4a31225f 100644 --- a/Mage.Sets/src/mage/cards/p/PeakEruption.java +++ b/Mage.Sets/src/mage/cards/p/PeakEruption.java @@ -7,30 +7,25 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.common.FilterLandPermanent; -import mage.target.common.TargetLandPermanent; +import mage.filter.FilterPermanent; +import mage.target.TargetPermanent; import java.util.UUID; /** - * * @author Plopman */ public final class PeakEruption extends CardImpl { - private static final FilterLandPermanent filter = new FilterLandPermanent("Mountain"); - - static{ - filter.add(SubType.MOUNTAIN.getPredicate()); - } + private static final FilterPermanent filter = new FilterPermanent(SubType.MOUNTAIN, "Mountain"); public PeakEruption(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}"); // Destroy target Mountain. Peak Eruption deals 3 damage to that land's controller. this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addEffect(new DamageTargetControllerEffect(3, "land")); - this.getSpellAbility().addTarget(new TargetLandPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); } private PeakEruption(final PeakEruption card) { diff --git a/Mage.Sets/src/mage/cards/p/PearlEarImperialAdvisor.java b/Mage.Sets/src/mage/cards/p/PearlEarImperialAdvisor.java index 96a69d75506..39222e48050 100644 --- a/Mage.Sets/src/mage/cards/p/PearlEarImperialAdvisor.java +++ b/Mage.Sets/src/mage/cards/p/PearlEarImperialAdvisor.java @@ -3,19 +3,16 @@ package mage.cards.p; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.common.AffinityEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledSpellsEffect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.AffinityAbility; import mage.abilities.keyword.LifelinkAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AffinityType; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.FilterSpell; import mage.filter.common.FilterControlledPermanent; @@ -37,11 +34,6 @@ public final class PearlEarImperialAdvisor extends CardImpl { filter.add(CardType.ENCHANTMENT.getPredicate()); } - private static final FilterControlledPermanent filterPermanentAura = new FilterControlledPermanent(SubType.AURA, "Auras"); - private static final Hint hint = new ValueHint( - "Auras you control", new PermanentsOnBattlefieldCount(filterPermanentAura) - ); - private static final FilterPermanent filterModified = new FilterControlledPermanent(); private static final FilterSpell filterAura = new FilterSpell("an Aura spell that targets a modified permanent you control"); @@ -64,12 +56,7 @@ public final class PearlEarImperialAdvisor extends CardImpl { this.addAbility(LifelinkAbility.getInstance()); // Enchantment spells you cast have affinity for Auras. - this.addAbility(new SimpleStaticAbility( - new GainAbilityControlledSpellsEffect( - new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filterPermanentAura)).addHint(hint), - filter - ) - )); + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledSpellsEffect(new AffinityAbility(AffinityType.AURAS), filter))); // Whenever you cast an Aura spell that targets a modified permanent you control, draw a card. this.addAbility(new SpellCastControllerTriggeredAbility(new DrawCardSourceControllerEffect(1), filterAura, false)); diff --git a/Mage.Sets/src/mage/cards/p/PeatBog.java b/Mage.Sets/src/mage/cards/p/PeatBog.java index efcfde01a27..7607eef1477 100644 --- a/Mage.Sets/src/mage/cards/p/PeatBog.java +++ b/Mage.Sets/src/mage/cards/p/PeatBog.java @@ -1,11 +1,10 @@ package mage.cards.p; -import java.util.UUID; import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; -import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.costs.common.TapSourceCost; @@ -17,17 +16,21 @@ import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.Zone; import mage.counters.CounterType; +import java.util.UUID; + /** - * * @author Plopman */ public final class PeatBog extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.DEPLETION, ComparisonType.EQUAL_TO, 0); + public PeatBog(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // Peat Bog enters the battlefield tapped with two depletion counters on it. Ability etbAbility = new EntersBattlefieldAbility( @@ -35,11 +38,15 @@ public final class PeatBog extends CardImpl { ); etbAbility.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance(2))); this.addAbility(etbAbility); + // {T}, Remove a depletion counter from Peat Bog: Add {B}{B}. If there are no depletion counters on Peat Bog, sacrifice it. Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, Mana.BlackMana(2), new TapSourceCost()); ability.addCost(new RemoveCountersSourceCost(CounterType.DEPLETION.createInstance(1))); - ability.addEffect(new ConditionalOneShotEffect(new SacrificeSourceEffect(), new SourceHasCounterCondition(CounterType.DEPLETION, 0,0), "If there are no depletion counters on {this}, sacrifice it")); - this.addAbility(ability); + ability.addEffect(new ConditionalOneShotEffect( + new SacrificeSourceEffect(), condition, + "If there are no depletion counters on {this}, sacrifice it" + )); + this.addAbility(ability); } private PeatBog(final PeatBog card) { diff --git a/Mage.Sets/src/mage/cards/p/PeemaAetherSeer.java b/Mage.Sets/src/mage/cards/p/PeemaAetherSeer.java index 1b38e871a9c..ad550e666f4 100644 --- a/Mage.Sets/src/mage/cards/p/PeemaAetherSeer.java +++ b/Mage.Sets/src/mage/cards/p/PeemaAetherSeer.java @@ -1,26 +1,25 @@ package mage.cards.p; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.PayEnergyCost; -import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.combat.BlocksIfAbleTargetEffect; import mage.abilities.effects.common.counter.GetEnergyCountersControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class PeemaAetherSeer extends CardImpl { @@ -34,9 +33,9 @@ public final class PeemaAetherSeer extends CardImpl { this.toughness = new MageInt(2); // When Peema Aether-Seer enters the battlefield, you get an amount of {E} equal to the greatest power among creatures you control. - Effect effect = new GetEnergyCountersControllerEffect(GreatestPowerAmongControlledCreaturesValue.instance); + Effect effect = new GetEnergyCountersControllerEffect(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES); effect.setText("you get an amount of {E} equal to the greatest power among creatures you control"); - this.addAbility(new EntersBattlefieldTriggeredAbility(effect).addHint(GreatestPowerAmongControlledCreaturesValue.getHint())); + this.addAbility(new EntersBattlefieldTriggeredAbility(effect).addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint())); // Pay {E}{E}{E}: Target creature blocks this turn if able. Ability ability = new SimpleActivatedAbility(new BlocksIfAbleTargetEffect(Duration.EndOfTurn), new PayEnergyCost(3)); diff --git a/Mage.Sets/src/mage/cards/p/PeemaTrailblazer.java b/Mage.Sets/src/mage/cards/p/PeemaTrailblazer.java index eb050ad05f8..efec92c480e 100644 --- a/Mage.Sets/src/mage/cards/p/PeemaTrailblazer.java +++ b/Mage.Sets/src/mage/cards/p/PeemaTrailblazer.java @@ -4,7 +4,7 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.costs.common.PayEnergyCost; -import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.dynamicvalue.common.SavedDamageValue; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; @@ -42,11 +42,10 @@ public final class PeemaTrailblazer extends CardImpl { final Ability ability = new ExhaustAbility( new AddCountersSourceEffect(CounterType.P1P1.createInstance(2)), new PayEnergyCost(6) ); - ability.addEffect(new DrawCardSourceControllerEffect(GreatestPowerAmongControlledCreaturesValue.instance)); - ability.addHint(GreatestPowerAmongControlledCreaturesValue.getHint()); + ability.addEffect(new DrawCardSourceControllerEffect(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES) + .setText("Then draw cards equal to the greatest power among creatures you control")); + ability.addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint()); this.addAbility(ability); - - } private PeemaTrailblazer(final PeemaTrailblazer card) { diff --git a/Mage.Sets/src/mage/cards/p/PeerlessRecycling.java b/Mage.Sets/src/mage/cards/p/PeerlessRecycling.java index 55f023fd46f..bdc60ef9894 100644 --- a/Mage.Sets/src/mage/cards/p/PeerlessRecycling.java +++ b/Mage.Sets/src/mage/cards/p/PeerlessRecycling.java @@ -26,7 +26,7 @@ public final class PeerlessRecycling extends CardImpl { // Return target permanent from your graveyard to your hand. If the gift was promised, instead return two target permanent cards from your graveyard to your hand. this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect() - .setText("return target permanent from your graveyard to your hand. If the gift was promised, " + + .setText("return target permanent card from your graveyard to your hand. If the gift was promised, " + "instead return two target permanent cards from your graveyard to your hand")); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_PERMANENT)); this.getSpellAbility().setTargetAdjuster(new ConditionalTargetAdjuster(GiftWasPromisedCondition.TRUE, diff --git a/Mage.Sets/src/mage/cards/p/PeltCollector.java b/Mage.Sets/src/mage/cards/p/PeltCollector.java index 848dd821458..86a8f70d3ea 100644 --- a/Mage.Sets/src/mage/cards/p/PeltCollector.java +++ b/Mage.Sets/src/mage/cards/p/PeltCollector.java @@ -111,7 +111,7 @@ class PeltCollectorTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever another creature you control enters the battlefield " + return "Whenever another creature you control enters " + "or dies, if that creature's power is greater than {this}'s, " + "put a +1/+1 counter on {this}."; } diff --git a/Mage.Sets/src/mage/cards/p/Penance.java b/Mage.Sets/src/mage/cards/p/Penance.java index db6296fa3b1..194760b22ca 100644 --- a/Mage.Sets/src/mage/cards/p/Penance.java +++ b/Mage.Sets/src/mage/cards/p/Penance.java @@ -1,34 +1,39 @@ package mage.cards.p; -import java.util.UUID; import mage.ObjectColor; -import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.PutCardFromHandOnTopOfLibraryCost; -import mage.abilities.effects.PreventionEffectImpl; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.target.TargetSource; +import mage.filter.FilterSource; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorPredicate; + +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class Penance extends CardImpl { + private static final FilterSource filter = new FilterSource("black or red source"); + + static { + filter.add(Predicates.or(new ColorPredicate(ObjectColor.BLACK), new ColorPredicate(ObjectColor.RED))); + } + public Penance(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); // Put a card from your hand on top of your library: The next time a black or red source of your choice would deal damage this turn, prevent that damage. - this.addAbility(new SimpleActivatedAbility(new PenanceEffect(), new PutCardFromHandOnTopOfLibraryCost())); - + this.addAbility(new SimpleActivatedAbility( + new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, false, filter), + new PutCardFromHandOnTopOfLibraryCost() + )); } private Penance(final Penance card) { @@ -39,49 +44,4 @@ public final class Penance extends CardImpl { public Penance copy() { return new Penance(this); } -} - -class PenanceEffect extends PreventionEffectImpl { - - private final TargetSource target; - - public PenanceEffect() { - super(Duration.EndOfTurn, Integer.MAX_VALUE, false, false); - this.staticText = "The next time a black or red source of your choice would deal damage this turn, prevent that damage."; - this.target = new TargetSource(); - } - - private PenanceEffect(final PenanceEffect effect) { - super(effect); - this.target = effect.target.copy(); - } - - @Override - public PenanceEffect copy() { - return new PenanceEffect(this); - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - this.used = true; - this.discard(); // only one use - return true; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (!this.used && super.applies(event, source, game)) { - if (event.getSourceId().equals(target.getFirstTarget())) { - return (game.getObject(target.getFirstTarget()).getColor(game).contains(ObjectColor.BLACK) - || game.getObject(target.getFirstTarget()).getColor(game).contains(ObjectColor.RED)); - } - } - return false; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/p/PentagramOfTheAges.java b/Mage.Sets/src/mage/cards/p/PentagramOfTheAges.java index 5c3c1130f7d..11b08998a58 100644 --- a/Mage.Sets/src/mage/cards/p/PentagramOfTheAges.java +++ b/Mage.Sets/src/mage/cards/p/PentagramOfTheAges.java @@ -1,29 +1,28 @@ package mage.cards.p; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; + +import java.util.UUID; /** - * * @author Quercitron */ public final class PentagramOfTheAges extends CardImpl { public PentagramOfTheAges(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); - // {4}, {tap}: The next time a source of your choice would deal damage to you this turn, prevent that damage. - Ability ability = new SimpleActivatedAbility(new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn), new GenericManaCost(4)); + // {4}, {T}: The next time a source of your choice would deal damage to you this turn, prevent that damage. + Ability ability = new SimpleActivatedAbility(new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, true), new GenericManaCost(4)); ability.addCost(new TapSourceCost()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/p/PetalmaneBaku.java b/Mage.Sets/src/mage/cards/p/PetalmaneBaku.java index 4f75542a530..456de66e613 100644 --- a/Mage.Sets/src/mage/cards/p/PetalmaneBaku.java +++ b/Mage.Sets/src/mage/cards/p/PetalmaneBaku.java @@ -31,7 +31,7 @@ public final class PetalmaneBaku extends CardImpl { this.toughness = new MageInt(2); // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Petalmane Baku. - this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, true)); // {1}, Remove X ki counters from Petalmane Baku: Add X mana of any one color. Ability ability = new DynamicManaAbility( diff --git a/Mage.Sets/src/mage/cards/p/PhantomTrain.java b/Mage.Sets/src/mage/cards/p/PhantomTrain.java index 2e7f7480b82..64d7541a0ce 100644 --- a/Mage.Sets/src/mage/cards/p/PhantomTrain.java +++ b/Mage.Sets/src/mage/cards/p/PhantomTrain.java @@ -37,13 +37,14 @@ public final class PhantomTrain extends CardImpl { Ability ability = new SimpleActivatedAbility( new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new SacrificeTargetCost(StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE_OR_ARTIFACT) + .setText("sacrifice another artifact or creature") ); ability.addEffect(new AddCardTypeSourceEffect( Duration.EndOfTurn, CardType.ARTIFACT, CardType.CREATURE ).setText("it becomes a")); ability.addEffect(new AddCardSubTypeSourceEffect( Duration.EndOfTurn, true, SubType.SPIRIT - ).setText("Spirit artifact creature in addition to its other types until end of turn")); + ).setText(" Spirit artifact creature in addition to its other types until end of turn")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/p/PheliaExuberantShepherd.java b/Mage.Sets/src/mage/cards/p/PheliaExuberantShepherd.java index 70c0fa47b49..851b8af4e35 100644 --- a/Mage.Sets/src/mage/cards/p/PheliaExuberantShepherd.java +++ b/Mage.Sets/src/mage/cards/p/PheliaExuberantShepherd.java @@ -1,6 +1,7 @@ package mage.cards.p; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; @@ -49,10 +50,7 @@ public final class PheliaExuberantShepherd extends CardImpl { this.addAbility(FlashAbility.getInstance()); // Whenever Phelia, Exuberant Shepherd attacks, exile up to one other target nonland permanent. At the beginning of the next end step, return that card to the battlefield under its owner's control. If it entered under your control, put a +1/+1 counter on Phelia. - Ability ability = new AttacksTriggeredAbility(new ExileTargetEffect().setToSourceExileZone(true)); - ability.addEffect(new CreateDelayedTriggeredAbilityEffect( - new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new PheliaExuberantShepherdEffect()), false - )); + Ability ability = new AttacksTriggeredAbility(new PheliaExuberantShepherdExileEffect()); ability.addTarget(new TargetPermanent(0, 1, filter)); this.addAbility(ability); } @@ -67,16 +65,55 @@ public final class PheliaExuberantShepherd extends CardImpl { } } +class PheliaExuberantShepherdExileEffect extends ExileTargetEffect { + + PheliaExuberantShepherdExileEffect() { + super(); + outcome = Outcome.Neutral; // quite contextual outcome. + staticText = "exile up to one other target nonland permanent. At the beginning of the next end step, " + + "return that card to the battlefield under its owner's control. If it entered under your control, " + + "put a +1/+1 counter on {this}."; + } + + private PheliaExuberantShepherdExileEffect(final PheliaExuberantShepherdExileEffect effect) { + super(effect); + } + + @Override + public PheliaExuberantShepherdExileEffect copy() { + return new PheliaExuberantShepherdExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + MageObject sourceObject = source.getSourceObject(game); + this.exileId = UUID.randomUUID(); + this.exileZone = sourceObject == null ? null : sourceObject.getIdName(); + // attempting to exile to the fresh exileId + boolean didSomething = super.apply(game, source); + // delayed trigger is created even if exiling failed. + didSomething |= new CreateDelayedTriggeredAbilityEffect( + new AtTheBeginOfNextEndStepDelayedTriggeredAbility( + new PheliaExuberantShepherdEffect(this.exileId)), false + ).apply(game, source); + return didSomething; + } +} + class PheliaExuberantShepherdEffect extends OneShotEffect { - PheliaExuberantShepherdEffect() { + private final UUID zoneId; // the exile zone's id for cards to return. + + PheliaExuberantShepherdEffect(UUID zoneId) { super(Outcome.Benefit); staticText = "return that card to the battlefield under its owner's control. " + "If it entered under your control, put a +1/+1 counter on {this}"; + this.zoneId = zoneId; } private PheliaExuberantShepherdEffect(final PheliaExuberantShepherdEffect effect) { super(effect); + this.zoneId = effect.zoneId; } @Override @@ -90,7 +127,6 @@ class PheliaExuberantShepherdEffect extends OneShotEffect { if (player == null) { return false; } - UUID zoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); ExileZone exileZone = game.getExile().getExileZone(zoneId); if (exileZone == null || exileZone.isEmpty()) { return false; @@ -106,7 +142,7 @@ class PheliaExuberantShepherdEffect extends OneShotEffect { boolean enteredUnderYourControl = false; for (Card card : cards) { // Try to find the permanent that card became - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null && permanent.getControllerId().equals(source.getControllerId())) { enteredUnderYourControl = true; break; diff --git a/Mage.Sets/src/mage/cards/p/PhialOfGaladriel.java b/Mage.Sets/src/mage/cards/p/PhialOfGaladriel.java index c91fa0fdc2f..bb42297564b 100644 --- a/Mage.Sets/src/mage/cards/p/PhialOfGaladriel.java +++ b/Mage.Sets/src/mage/cards/p/PhialOfGaladriel.java @@ -2,33 +2,40 @@ package mage.cards.p; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.FatefulHourCondition; +import mage.abilities.decorator.ConditionalReplacementEffect; import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.replacement.GainDoubleLifeReplacementEffect; import mage.abilities.mana.AnyColorManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SuperType; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; -import mage.util.CardUtil; import java.util.UUID; /** - * * @author Susucr */ public final class PhialOfGaladriel extends CardImpl { public PhialOfGaladriel(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); - + this.supertype.add(SuperType.LEGENDARY); // If you would draw a card while you have no cards in hand, draw two cards instead. this.addAbility(new SimpleStaticAbility(new PhialOfGaladrielDrawEffect())); + // If you would gain life while you have 5 or less life, you gain twice that much life instead. - this.addAbility(new SimpleStaticAbility(new PhialOfGaladrielLifeEffect())); + this.addAbility(new SimpleStaticAbility(new ConditionalReplacementEffect( + new GainDoubleLifeReplacementEffect(), FatefulHourCondition.instance + ).setText("if you would gain life while you have 5 or less life, you gain twice that much life instead"))); // {T}: Add one mana of any color. this.addAbility(new AnyColorManaAbility()); @@ -44,50 +51,6 @@ public final class PhialOfGaladriel extends CardImpl { } } -class PhialOfGaladrielLifeEffect extends ReplacementEffectImpl { - - PhialOfGaladrielLifeEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "If you would gain life while you have 5 or less life, " + - "you gain twice that much life instead"; - } - - private PhialOfGaladrielLifeEffect(final PhialOfGaladrielLifeEffect effect) { - super(effect); - } - - @Override - public PhialOfGaladrielLifeEffect copy() { - return new PhialOfGaladrielLifeEffect(this); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - event.setAmount(CardUtil.overflowMultiply(event.getAmount(), 2)); - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.GAIN_LIFE; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - UUID playerId = source.getControllerId(); - if(playerId == null || !event.getPlayerId().equals(playerId)){ - return false; - } - - Player player = game.getPlayer(playerId); - if(player == null){ - return false; - } - - return player.getLife() <= 5; - } -} - class PhialOfGaladrielDrawEffect extends ReplacementEffectImpl { PhialOfGaladrielDrawEffect() { @@ -121,12 +84,12 @@ class PhialOfGaladrielDrawEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { UUID playerId = source.getControllerId(); - if(playerId == null || !event.getPlayerId().equals(playerId)){ + if (playerId == null || !event.getPlayerId().equals(playerId)) { return false; } Player player = game.getPlayer(playerId); - if(player == null){ + if (player == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/p/PhoenixWardenOfFire.java b/Mage.Sets/src/mage/cards/p/PhoenixWardenOfFire.java new file mode 100644 index 00000000000..f95a9a46b15 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PhoenixWardenOfFire.java @@ -0,0 +1,125 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.effects.common.ExileSourceAndReturnFaceUpEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; +import mage.game.Game; +import mage.target.common.TargetCardInYourGraveyard; +import mage.util.CardUtil; + +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PhoenixWardenOfFire extends CardImpl { + + public PhoenixWardenOfFire(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SAGA); + this.subtype.add(SubType.PHOENIX); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + this.nightCard = true; + this.color.setRed(true); + this.color.setWhite(true); + + // (As this Saga enters and after your draw step, add a lore counter.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I, II -- Rising Flames -- Phoenix deals 2 damage to each opponent. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, ability -> { + ability.addEffect(new DamagePlayersEffect(2, TargetController.OPPONENT)); + ability.withFlavorWord("Rising Flames"); + }); + + // III -- Flames of Rebirth -- Return any number of target creature cards with total mana value 6 or less from your graveyard to the battlefield. Exile Phoenix, then return it to the battlefield. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, ability -> { + ability.addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); + ability.addEffect(new ExileSourceAndReturnFaceUpEffect()); + ability.addTarget(new PhoenixWardenOfFireTarget()); + ability.withFlavorWord("Flames of Rebirth"); + }); + this.addAbility(sagaAbility); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + } + + private PhoenixWardenOfFire(final PhoenixWardenOfFire card) { + super(card); + } + + @Override + public PhoenixWardenOfFire copy() { + return new PhoenixWardenOfFire(this); + } +} + +class PhoenixWardenOfFireTarget extends TargetCardInYourGraveyard { + + private static final FilterCard filterStatic = new FilterCreatureCard( + "creature cards with total mana value 6 or less from your graveyard" + ); + + PhoenixWardenOfFireTarget() { + super(0, Integer.MAX_VALUE, filterStatic, false); + } + + private PhoenixWardenOfFireTarget(final PhoenixWardenOfFireTarget target) { + super(target); + } + + @Override + public PhoenixWardenOfFireTarget copy() { + return new PhoenixWardenOfFireTarget(this); + } + + @Override + public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) { + return super.canTarget(controllerId, id, source, game) + && CardUtil.checkCanTargetTotalValueLimit( + this.getTargets(), id, MageObject::getManaValue, 6, game + ); + } + + @Override + public Set possibleTargets(UUID sourceControllerId, Ability source, Game game) { + return CardUtil.checkPossibleTargetsTotalValueLimit( + this.getTargets(), + super.possibleTargets(sourceControllerId, source, game), + MageObject::getManaValue, 6, game + ); + } + + @Override + public String getMessage(Game game) { + // shows selected total + int selectedValue = this + .getTargets() + .stream() + .map(game::getObject) + .filter(Objects::nonNull) + .mapToInt(MageObject::getManaValue) + .sum(); + return super.getMessage(game) + " (selected total mana value " + selectedValue + ")"; + } +} diff --git a/Mage.Sets/src/mage/cards/p/PiaNalaarChiefMechanic.java b/Mage.Sets/src/mage/cards/p/PiaNalaarChiefMechanic.java index 59f4a8d8b49..f52834a4cc9 100644 --- a/Mage.Sets/src/mage/cards/p/PiaNalaarChiefMechanic.java +++ b/Mage.Sets/src/mage/cards/p/PiaNalaarChiefMechanic.java @@ -1,6 +1,5 @@ package mage.cards.p; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.OneOrMoreCombatDamagePlayerTriggeredAbility; @@ -9,9 +8,9 @@ import mage.abilities.costs.common.PayEnergyCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.counter.GetEnergyCountersControllerEffect; import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; -import mage.constants.*; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.*; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; @@ -19,8 +18,9 @@ import mage.game.permanent.token.NalaarAetherjetToken; import mage.game.permanent.token.Token; import mage.players.Player; +import java.util.UUID; + /** - * * @author Jmlundeen */ public final class PiaNalaarChiefMechanic extends CardImpl { @@ -33,7 +33,7 @@ public final class PiaNalaarChiefMechanic extends CardImpl { public PiaNalaarChiefMechanic(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{U}{R}"); - + this.supertype.add(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.ARTIFICER); @@ -64,7 +64,7 @@ class PiaNalaarChiefMechanicEffect extends OneShotEffect { public PiaNalaarChiefMechanicEffect() { super(Outcome.Benefit); staticText = "you may pay one or more {E}. If you do, create an X/X colorless Vehicle artifact token " + - "named Nalaar Aetherjet with flying and crew 2, where X is the amount of {E} spent this way"; + "named Nalaar Aetherjet with flying and crew 2, where X is the amount of {E} paid this way"; } public PiaNalaarChiefMechanicEffect(final PiaNalaarChiefMechanicEffect effect) { @@ -95,4 +95,4 @@ class PiaNalaarChiefMechanicEffect extends OneShotEffect { } return false; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/p/PiaNalaarConsulOfRevival.java b/Mage.Sets/src/mage/cards/p/PiaNalaarConsulOfRevival.java index d1afbbf6bb9..d1d824a73c4 100644 --- a/Mage.Sets/src/mage/cards/p/PiaNalaarConsulOfRevival.java +++ b/Mage.Sets/src/mage/cards/p/PiaNalaarConsulOfRevival.java @@ -1,17 +1,18 @@ package mage.cards.p; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.PlayLandOrCastSpellFromExileTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.filter.FilterPermanent; -import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.token.ThopterColorlessToken; import java.util.UUID; @@ -38,7 +39,7 @@ public final class PiaNalaarConsulOfRevival extends CardImpl { ))); // Whenever you play a land from exile or cast a spell from exile, create a 1/1 colorless Thopter artifact creature token with flying. - this.addAbility(new PiaNalaarConsulOfRevivalTriggeredAbility()); + this.addAbility(new PlayLandOrCastSpellFromExileTriggeredAbility(new CreateTokenEffect(new ThopterColorlessToken()))); } private PiaNalaarConsulOfRevival(final PiaNalaarConsulOfRevival card) { @@ -50,31 +51,3 @@ public final class PiaNalaarConsulOfRevival extends CardImpl { return new PiaNalaarConsulOfRevival(this); } } - -class PiaNalaarConsulOfRevivalTriggeredAbility extends TriggeredAbilityImpl { - - PiaNalaarConsulOfRevivalTriggeredAbility() { - super(Zone.BATTLEFIELD, new CreateTokenEffect(new ThopterColorlessToken())); - setTriggerPhrase("Whenever you play a land from exile or cast a spell from exile, "); - } - - private PiaNalaarConsulOfRevivalTriggeredAbility(final PiaNalaarConsulOfRevivalTriggeredAbility ability) { - super(ability); - } - - @Override - public PiaNalaarConsulOfRevivalTriggeredAbility copy() { - return new PiaNalaarConsulOfRevivalTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.LAND_PLAYED - || event.getType() == GameEvent.EventType.SPELL_CAST; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return isControlledBy(event.getPlayerId()) && event.getZone() == Zone.EXILED; - } -} diff --git a/Mage.Sets/src/mage/cards/p/PicnicRuiner.java b/Mage.Sets/src/mage/cards/p/PicnicRuiner.java index a5c00fb58a7..5d1dbfc9463 100644 --- a/Mage.Sets/src/mage/cards/p/PicnicRuiner.java +++ b/Mage.Sets/src/mage/cards/p/PicnicRuiner.java @@ -3,7 +3,6 @@ package mage.cards.p; import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.condition.common.FerociousCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.effects.common.counter.DistributeCountersEffect; import mage.abilities.keyword.DoubleStrikeAbility; @@ -31,11 +30,9 @@ public final class PicnicRuiner extends AdventureCard { this.toughness = new MageInt(2); // Whenever Picnic Ruiner attacks while you control a creature with power 4 or greater, Picnic Ruiner gains double strike until end of turn. - this.addAbility(new ConditionalTriggeredAbility( - new AttacksTriggeredAbility(new GainAbilitySourceEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn), false), - FerociousCondition.instance, - "Whenever {this} attacks while you control a creature with power 4 or greater, {this} gains double strike until end of turn." - )); + this.addAbility(new AttacksTriggeredAbility( + new GainAbilitySourceEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn), false + ).withTriggerCondition(FerociousCondition.instance)); // Stolen Goodies // Distribute three +1/+1 counters among any number of target creatures you control. diff --git a/Mage.Sets/src/mage/cards/p/PilgrimOfJustice.java b/Mage.Sets/src/mage/cards/p/PilgrimOfJustice.java index e0355b3a919..d1b9a9c6a3c 100644 --- a/Mage.Sets/src/mage/cards/p/PilgrimOfJustice.java +++ b/Mage.Sets/src/mage/cards/p/PilgrimOfJustice.java @@ -6,19 +6,15 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.PreventionEffectImpl; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.abilities.keyword.ProtectionAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.FilterObject; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.Game; -import mage.game.events.DamageEvent; -import mage.game.events.GameEvent; -import mage.game.events.PreventDamageEvent; -import mage.game.events.PreventedDamageEvent; -import mage.target.TargetSource; import java.util.UUID; @@ -27,6 +23,12 @@ import java.util.UUID; */ public final class PilgrimOfJustice extends CardImpl { + private static final FilterSource filter = new FilterSource("red source"); + + static { + filter.add(new ColorPredicate(ObjectColor.RED)); + } + public PilgrimOfJustice(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); this.subtype.add(SubType.HUMAN); @@ -38,7 +40,10 @@ public final class PilgrimOfJustice extends CardImpl { // Protection from red this.addAbility(ProtectionAbility.from(ObjectColor.RED)); // {W}, Sacrifice Pilgrim of Justice: The next time a red source of your choice would deal damage this turn, prevent that damage. - Ability ability = new SimpleActivatedAbility(new PilgrimOfJusticeEffect(), new ManaCostsImpl<>("{W}")); + Ability ability = new SimpleActivatedAbility( + new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, false, filter), + new ManaCostsImpl<>("{W}") + ); ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); } @@ -51,60 +56,4 @@ public final class PilgrimOfJustice extends CardImpl { public PilgrimOfJustice copy() { return new PilgrimOfJustice(this); } -} - -class PilgrimOfJusticeEffect extends PreventionEffectImpl { - - private static final FilterObject filter = new FilterObject("red source"); - - static { - filter.add(new ColorPredicate(ObjectColor.RED)); - } - - private TargetSource target; - - public PilgrimOfJusticeEffect() { - super(Duration.EndOfTurn); - target = new TargetSource(filter); - - staticText = "The next time a red source of your choice would deal damage to you this turn, prevent that damage"; - } - - private PilgrimOfJusticeEffect(final PilgrimOfJusticeEffect effect) { - super(effect); - this.target = effect.target.copy(); - } - - @Override - public PilgrimOfJusticeEffect copy() { - return new PilgrimOfJusticeEffect(this); - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - GameEvent preventEvent = new PreventDamageEvent(event.getTargetId(), source.getSourceId(), source, source.getControllerId(), event.getAmount(), ((DamageEvent) event).isCombatDamage()); - if (!game.replaceEvent(preventEvent)) { - int damage = event.getAmount(); - event.setAmount(0); - this.used = true; // one time use - game.fireEvent(new PreventedDamageEvent(event.getTargetId(), source.getSourceId(), source, source.getControllerId(), damage)); - return true; - } - return false; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (!this.used && super.applies(event, source, game)) { - return event.getTargetId().equals(source.getControllerId()) && event.getSourceId().equals(target.getFirstTarget()); - } - return false; - } - -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/p/PilgrimOfVirtue.java b/Mage.Sets/src/mage/cards/p/PilgrimOfVirtue.java index 74507f97418..d5931e44263 100644 --- a/Mage.Sets/src/mage/cards/p/PilgrimOfVirtue.java +++ b/Mage.Sets/src/mage/cards/p/PilgrimOfVirtue.java @@ -6,19 +6,15 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.PreventionEffectImpl; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.abilities.keyword.ProtectionAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.FilterObject; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.Game; -import mage.game.events.DamageEvent; -import mage.game.events.GameEvent; -import mage.game.events.PreventDamageEvent; -import mage.game.events.PreventedDamageEvent; -import mage.target.TargetSource; import java.util.UUID; @@ -27,6 +23,12 @@ import java.util.UUID; */ public final class PilgrimOfVirtue extends CardImpl { + private static final FilterSource filter = new FilterSource("black source"); + + static { + filter.add(new ColorPredicate(ObjectColor.BLACK)); + } + public PilgrimOfVirtue(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); this.subtype.add(SubType.HUMAN); @@ -38,7 +40,10 @@ public final class PilgrimOfVirtue extends CardImpl { // Protection from black this.addAbility(ProtectionAbility.from(ObjectColor.BLACK)); // {W}, Sacrifice Pilgrim of Virtue: The next time a black source of your choice would deal damage this turn, prevent that damage. - Ability ability = new SimpleActivatedAbility(new PilgrimOfVirtueEffect(), new ManaCostsImpl<>("{W}")); + Ability ability = new SimpleActivatedAbility( + new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, false, filter), + new ManaCostsImpl<>("{W}") + ); ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); } @@ -51,60 +56,4 @@ public final class PilgrimOfVirtue extends CardImpl { public PilgrimOfVirtue copy() { return new PilgrimOfVirtue(this); } -} - -class PilgrimOfVirtueEffect extends PreventionEffectImpl { - - private static final FilterObject filter = new FilterObject("black source"); - - static { - filter.add(new ColorPredicate(ObjectColor.BLACK)); - } - - private final TargetSource target; - - public PilgrimOfVirtueEffect() { - super(Duration.EndOfTurn); - target = new TargetSource(filter); - - staticText = "The next time a black source of your choice would deal damage to you this turn, prevent that damage"; - } - - private PilgrimOfVirtueEffect(final PilgrimOfVirtueEffect effect) { - super(effect); - this.target = effect.target.copy(); - } - - @Override - public PilgrimOfVirtueEffect copy() { - return new PilgrimOfVirtueEffect(this); - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - GameEvent preventEvent = new PreventDamageEvent(event.getTargetId(), source.getSourceId(), source, source.getControllerId(), event.getAmount(), ((DamageEvent) event).isCombatDamage()); - if (!game.replaceEvent(preventEvent)) { - int damage = event.getAmount(); - event.setAmount(0); - this.used = true; // one time use - game.fireEvent(new PreventedDamageEvent(event.getTargetId(), source.getSourceId(), source, source.getControllerId(), damage)); - return true; - } - return false; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (!this.used && super.applies(event, source, game)) { - return event.getTargetId().equals(source.getControllerId()) && event.getSourceId().equals(target.getFirstTarget()); - } - return false; - } - -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/p/Plaguecrafter.java b/Mage.Sets/src/mage/cards/p/Plaguecrafter.java index 77fc9965d6a..2f6a81beadb 100644 --- a/Mage.Sets/src/mage/cards/p/Plaguecrafter.java +++ b/Mage.Sets/src/mage/cards/p/Plaguecrafter.java @@ -55,7 +55,7 @@ class PlaguecrafterEffect extends OneShotEffect { PlaguecrafterEffect() { super(Outcome.Benefit); - this.staticText = "each player sacrifices a creature or planeswalker. " + this.staticText = "each player sacrifices a creature or planeswalker of their choice. " + "Each player who can't discards a card."; } diff --git a/Mage.Sets/src/mage/cards/p/PlanarOverlay.java b/Mage.Sets/src/mage/cards/p/PlanarOverlay.java index 9f48e4f04c0..0cde8ce2b62 100644 --- a/Mage.Sets/src/mage/cards/p/PlanarOverlay.java +++ b/Mage.Sets/src/mage/cards/p/PlanarOverlay.java @@ -10,7 +10,7 @@ import mage.filter.common.FilterLandPermanent; import mage.game.Game; import mage.players.Player; import mage.target.Target; -import mage.target.common.TargetLandPermanent; +import mage.target.TargetPermanent; import java.util.HashSet; import java.util.Set; @@ -66,7 +66,7 @@ class PlanarOverlayEffect extends OneShotEffect { FilterLandPermanent filter = new FilterLandPermanent(landName + " to return to hand"); filter.add(landName.getPredicate()); filter.add(TargetController.YOU.getControllerPredicate()); - Target target = new TargetLandPermanent(1, 1, filter, true); + Target target = new TargetPermanent(1, 1, filter, true); if (target.canChoose(player.getId(), source, game)) { player.chooseTarget(outcome, target, source, game); lands.add(game.getPermanent(target.getFirstTarget())); diff --git a/Mage.Sets/src/mage/cards/p/PlaneboundAccomplice.java b/Mage.Sets/src/mage/cards/p/PlaneboundAccomplice.java index acfe5033a1b..2cd15c08492 100644 --- a/Mage.Sets/src/mage/cards/p/PlaneboundAccomplice.java +++ b/Mage.Sets/src/mage/cards/p/PlaneboundAccomplice.java @@ -22,6 +22,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInHand; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -91,7 +92,7 @@ class PlaneboundAccompliceEffect extends OneShotEffect { if (!controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { return false; } - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return true; } @@ -102,4 +103,4 @@ class PlaneboundAccompliceEffect extends OneShotEffect { game.addDelayedTriggeredAbility(delayedAbility, source); return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/p/PlateArmor.java b/Mage.Sets/src/mage/cards/p/PlateArmor.java index 87f413a895d..d9acafdbf23 100644 --- a/Mage.Sets/src/mage/cards/p/PlateArmor.java +++ b/Mage.Sets/src/mage/cards/p/PlateArmor.java @@ -1,7 +1,5 @@ package mage.cards.p; -import java.util.UUID; - import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.CostAdjuster; @@ -14,18 +12,20 @@ import mage.abilities.hint.Hint; import mage.abilities.hint.ValueHint; import mage.abilities.keyword.EquipAbility; import mage.abilities.keyword.WardAbility; -import mage.constants.*; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.filter.common.FilterEquipmentPermanent; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author weirddan455 */ public final class PlateArmor extends CardImpl { @@ -66,11 +66,10 @@ public final class PlateArmor extends CardImpl { enum PlateArmorAdjuster implements CostAdjuster { instance; - private static final FilterEquipmentPermanent filter = new FilterEquipmentPermanent("Other Equipment you control"); + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.EQUIPMENT, "Other Equipment you control"); static { filter.add(AnotherPredicate.instance); - filter.add(TargetController.YOU.getControllerPredicate()); } private static final DynamicValue equipmentCount = new PermanentsOnBattlefieldCount(filter); diff --git a/Mage.Sets/src/mage/cards/p/PolisCrusher.java b/Mage.Sets/src/mage/cards/p/PolisCrusher.java index 00b0f5d917c..1b615461828 100644 --- a/Mage.Sets/src/mage/cards/p/PolisCrusher.java +++ b/Mage.Sets/src/mage/cards/p/PolisCrusher.java @@ -17,7 +17,7 @@ import mage.filter.FilterCard; import mage.filter.common.FilterEnchantmentCard; import mage.filter.common.FilterEnchantmentPermanent; import mage.target.TargetPermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -52,7 +52,7 @@ public final class PolisCrusher extends CardImpl { // Whenever Polis Crusher deals combat damage to a player, if Polis Crusher is monstrous, destroy target enchantment that player controls. TriggeredAbility ability = new DealsCombatDamageToAPlayerTriggeredAbility(new DestroyTargetEffect(), false, true); ability.addTarget(new TargetPermanent(filterPermanent)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, MonstrousCondition.instance, "Whenever {this} deals combat damage to a player, if {this} is monstrous, destroy target enchantment that player controls.")); diff --git a/Mage.Sets/src/mage/cards/p/Polliwallop.java b/Mage.Sets/src/mage/cards/p/Polliwallop.java index 5ea92391255..319908c8a88 100644 --- a/Mage.Sets/src/mage/cards/p/Polliwallop.java +++ b/Mage.Sets/src/mage/cards/p/Polliwallop.java @@ -1,18 +1,12 @@ package mage.cards.p; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.common.AffinityEffect; import mage.abilities.effects.common.DamageWithPowerFromOneToAnotherTargetEffect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.AffinityAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AffinityType; import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.StaticFilters; -import mage.filter.common.FilterControlledPermanent; import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; @@ -23,14 +17,11 @@ import java.util.UUID; */ public final class Polliwallop extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.FROG, "Frogs"); - private static final Hint hint = new ValueHint("Frogs you control", new PermanentsOnBattlefieldCount(filter)); - public Polliwallop(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{G}"); - // This spell costs {1} less to cast for each Frog you control. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filter)).setRuleAtTheTop(true).addHint(hint)); + // Affinity for Frogs + this.addAbility(new AffinityAbility(AffinityType.FROGS)); // Target creature you control deals damage equal to twice its power to target creature you don't control. this.getSpellAbility().addEffect(new DamageWithPowerFromOneToAnotherTargetEffect("", 2)); diff --git a/Mage.Sets/src/mage/cards/p/PortalMage.java b/Mage.Sets/src/mage/cards/p/PortalMage.java index 2f2c2c130c2..c2fdc3c120f 100644 --- a/Mage.Sets/src/mage/cards/p/PortalMage.java +++ b/Mage.Sets/src/mage/cards/p/PortalMage.java @@ -3,8 +3,8 @@ package mage.cards.p; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.IsStepCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.combat.ReselectDefenderAttackedByTargetEffect; import mage.abilities.keyword.FlashAbility; import mage.cards.CardImpl; @@ -21,6 +21,8 @@ import java.util.UUID; */ public final class PortalMage extends CardImpl { + private static final Condition condition = new IsStepCondition(PhaseStep.DECLARE_ATTACKERS, false); + public PortalMage(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); @@ -33,11 +35,9 @@ public final class PortalMage extends CardImpl { this.addAbility(FlashAbility.getInstance()); // When Portal Mage enters the battlefield during the declare attackers step, you may reselect which player or planeswalker target attacking creature is attacking. - Ability ability = new ConditionalTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new ReselectDefenderAttackedByTargetEffect(true), true), - new IsStepCondition(PhaseStep.DECLARE_ATTACKERS, false), - "When {this} enters during the declare attackers step, you may reselect which player or permanent target attacking creature is attacking. " - + "(It can't attack its controller or their permanents)"); + Ability ability = new EntersBattlefieldTriggeredAbility( + new ReselectDefenderAttackedByTargetEffect(true), true + ).withTriggerCondition(condition); ability.addTarget(new TargetAttackingCreature()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/p/PostmortemLunge.java b/Mage.Sets/src/mage/cards/p/PostmortemLunge.java index 1267092a481..fc5556482bc 100644 --- a/Mage.Sets/src/mage/cards/p/PostmortemLunge.java +++ b/Mage.Sets/src/mage/cards/p/PostmortemLunge.java @@ -21,6 +21,7 @@ import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetadjustment.XManaValueTargetAdjuster; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -73,7 +74,7 @@ class PostmortemLungeEffect extends OneShotEffect { return false; } if (cardOwner.moveCards(card, Zone.BATTLEFIELD, source, game)) { - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); effect.setTargetPointer(new FixedTarget(permanent, game)); diff --git a/Mage.Sets/src/mage/cards/p/PowderKeg.java b/Mage.Sets/src/mage/cards/p/PowderKeg.java index 003ef0cdb6b..2b26afdf4bc 100644 --- a/Mage.Sets/src/mage/cards/p/PowderKeg.java +++ b/Mage.Sets/src/mage/cards/p/PowderKeg.java @@ -1,26 +1,21 @@ - package mage.cards.p; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyAllEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.ComparisonType; -import mage.constants.Outcome; -import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ManaValuePredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.filter.predicate.mageobject.ManaValueEqualToCountersSourceCountPredicate; + +import java.util.UUID; /** * @@ -28,6 +23,17 @@ import mage.game.permanent.Permanent; */ public final class PowderKeg extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent( + "each artifact and creature with mana value equal to the number of fuse counters on {this}" + ); + static { + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.CREATURE.getPredicate() + )); + filter.add(new ManaValueEqualToCountersSourceCountPredicate(CounterType.FUSE)); + } + public PowderKeg(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); @@ -35,7 +41,7 @@ public final class PowderKeg extends CardImpl { this.addAbility(new BeginningOfUpkeepTriggeredAbility(new AddCountersSourceEffect(CounterType.FUSE.createInstance(), true), true)); // {T}, Sacrifice Powder Keg: Destroy each artifact and creature with converted mana cost equal to the number of fuse counters on Powder Keg. - Ability ability = new SimpleActivatedAbility(new PowderKegEffect(), new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(new DestroyAllEffect(filter), new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); } @@ -49,37 +55,3 @@ public final class PowderKeg extends CardImpl { return new PowderKeg(this); } } - -class PowderKegEffect extends OneShotEffect { - - PowderKegEffect() { - super(Outcome.DestroyPermanent); - staticText = "Destroy each artifact and creature with mana value equal to the number of fuse counters on {this}"; - } - - private PowderKegEffect(final PowderKegEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (sourcePermanent == null) { - return false; - } - int count = sourcePermanent.getCounters(game).getCount(CounterType.FUSE); - FilterPermanent filter = new FilterPermanent(); - filter.add(Predicates.or(CardType.ARTIFACT.getPredicate(), CardType.CREATURE.getPredicate())); - filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, count)); - for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source, game)) { - perm.destroy(source, game, false); - } - return true; - } - - @Override - public PowderKegEffect copy() { - return new PowderKegEffect(this); - } - -} diff --git a/Mage.Sets/src/mage/cards/p/PrahvSpiresOfOrder.java b/Mage.Sets/src/mage/cards/p/PrahvSpiresOfOrder.java index 5d2ab8b1010..bd031e3d675 100644 --- a/Mage.Sets/src/mage/cards/p/PrahvSpiresOfOrder.java +++ b/Mage.Sets/src/mage/cards/p/PrahvSpiresOfOrder.java @@ -2,31 +2,30 @@ package mage.cards.p; import java.util.UUID; + import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.common.PreventDamageBySourceEffect; +import mage.abilities.effects.common.PreventDamageByChosenSourceEffect; import mage.abilities.mana.ColorlessManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; /** - * * @author LevelX2 */ public final class PrahvSpiresOfOrder extends CardImpl { public PrahvSpiresOfOrder(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // {T}: Add {C}. this.addAbility(new ColorlessManaAbility()); // {4}{W}{U}, {T}: Prevent all damage a source of your choice would deal this turn. - Ability ability = new SimpleActivatedAbility(new PreventDamageBySourceEffect(), new ManaCostsImpl<>("{4}{W}{U}")); + Ability ability = new SimpleActivatedAbility(new PreventDamageByChosenSourceEffect(), new ManaCostsImpl<>("{4}{W}{U}")); ability.addCost(new TapSourceCost()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/p/PreWarFormalwear.java b/Mage.Sets/src/mage/cards/p/PreWarFormalwear.java index 6686688cf4e..a52ada586b6 100644 --- a/Mage.Sets/src/mage/cards/p/PreWarFormalwear.java +++ b/Mage.Sets/src/mage/cards/p/PreWarFormalwear.java @@ -21,6 +21,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -90,7 +91,7 @@ class PreWarFormalwerEffect extends OneShotEffect { return false; } player.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { new AttachEffect(Outcome.BoostCreature) .setTargetPointer(new FixedTarget(permanent.getId())) diff --git a/Mage.Sets/src/mage/cards/p/PreeminentCaptain.java b/Mage.Sets/src/mage/cards/p/PreeminentCaptain.java index 759499df687..52a27a5fbba 100644 --- a/Mage.Sets/src/mage/cards/p/PreeminentCaptain.java +++ b/Mage.Sets/src/mage/cards/p/PreeminentCaptain.java @@ -17,6 +17,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInHand; +import mage.util.CardUtil; import java.util.UUID; @@ -77,7 +78,7 @@ class PreeminentCaptainEffect extends OneShotEffect { Card card = controller.getHand().get(cardId, game); if (card != null) { if (controller.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, true, null)) { - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { game.getCombat().addAttackingCreature(permanent.getId(), game); } diff --git a/Mage.Sets/src/mage/cards/p/PresumedDead.java b/Mage.Sets/src/mage/cards/p/PresumedDead.java index d5593cc391e..cb319fc9216 100644 --- a/Mage.Sets/src/mage/cards/p/PresumedDead.java +++ b/Mage.Sets/src/mage/cards/p/PresumedDead.java @@ -16,6 +16,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; +import mage.util.CardUtil; import java.util.UUID; @@ -71,7 +72,7 @@ class PresumedDeadEffect extends OneShotEffect { } controller.moveCards(card, Zone.BATTLEFIELD, source, game, false, false, true, null); game.processAction(); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { permanent.setSuspected(true, game, source); } diff --git a/Mage.Sets/src/mage/cards/p/PriestOfForgottenGods.java b/Mage.Sets/src/mage/cards/p/PriestOfForgottenGods.java index a91a681c5ab..bb7fc2f8436 100644 --- a/Mage.Sets/src/mage/cards/p/PriestOfForgottenGods.java +++ b/Mage.Sets/src/mage/cards/p/PriestOfForgottenGods.java @@ -51,7 +51,7 @@ public final class PriestOfForgottenGods extends CardImpl { ); ability.addEffect( new SacrificeEffect(StaticFilters.FILTER_PERMANENT_CREATURE, 1, "") - .setText("and sacrifice a creature") + .setText("and sacrifice a creature of their choice") ); ability.addCost(new SacrificeTargetCost(2, filter)); ability.addEffect(new BasicManaEffect(Mana.BlackMana(2)).setText("You add {B}{B}")); diff --git a/Mage.Sets/src/mage/cards/p/PriestOfTheCrossing.java b/Mage.Sets/src/mage/cards/p/PriestOfTheCrossing.java index f35d63dfdad..09ee13e628d 100644 --- a/Mage.Sets/src/mage/cards/p/PriestOfTheCrossing.java +++ b/Mage.Sets/src/mage/cards/p/PriestOfTheCrossing.java @@ -9,6 +9,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; +import mage.constants.TargetController; import mage.counters.CounterType; import mage.filter.StaticFilters; @@ -30,8 +31,13 @@ public class PriestOfTheCrossing extends CardImpl { // At the beginning of each end step, put X +1/+1 counters on each creature you control, where X is the number of creatures that died under your control this turn. this.addAbility(new BeginningOfEndStepTriggeredAbility( - new AddCountersAllEffect(CounterType.P1P1.createInstance(), CreaturesYouControlDiedCount.instance, StaticFilters.FILTER_CONTROLLED_CREATURE) - .setText("put X +1/+1 counters on each creature you control, where X is the number of creatures that died under your control this turn"))); + TargetController.ANY, + new AddCountersAllEffect( + CounterType.P1P1.createInstance(), CreaturesYouControlDiedCount.instance, + StaticFilters.FILTER_CONTROLLED_CREATURE + ).setText("put X +1/+1 counters on each creature you control, where X is " + + "the number of creatures that died under your control this turn"), false + )); } public PriestOfTheCrossing(PriestOfTheCrossing card) { diff --git a/Mage.Sets/src/mage/cards/p/PriestOfYawgmoth.java b/Mage.Sets/src/mage/cards/p/PriestOfYawgmoth.java index 4931d713ffe..23c0c02957c 100644 --- a/Mage.Sets/src/mage/cards/p/PriestOfYawgmoth.java +++ b/Mage.Sets/src/mage/cards/p/PriestOfYawgmoth.java @@ -6,7 +6,7 @@ import mage.Mana; import mage.abilities.Ability; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.dynamicvalue.common.HighestCMCOfPermanentValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.dynamicvalue.common.SacrificeCostManaValue; import mage.abilities.mana.DynamicManaAbility; import mage.cards.CardImpl; @@ -14,7 +14,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.filter.StaticFilters; -import mage.target.common.TargetControlledPermanent; import java.util.UUID; @@ -36,7 +35,7 @@ public final class PriestOfYawgmoth extends CardImpl { new TapSourceCost(), "add an amount of {B} equal to the sacrificed artifact's mana value", false, - new HighestCMCOfPermanentValue(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT, true) + GreatestAmongPermanentsValue.MANAVALUE_OTHER_CONTROLLED_ARTIFACTS ); ability.addCost(new SacrificeTargetCost(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/p/PrimeSpeakerZegana.java b/Mage.Sets/src/mage/cards/p/PrimeSpeakerZegana.java index a3ef20c1f5b..32446183fa2 100644 --- a/Mage.Sets/src/mage/cards/p/PrimeSpeakerZegana.java +++ b/Mage.Sets/src/mage/cards/p/PrimeSpeakerZegana.java @@ -1,10 +1,9 @@ package mage.cards.p; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.dynamicvalue.common.SourcePermanentPowerValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -15,14 +14,10 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; import mage.counters.CounterType; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; import java.util.UUID; /** - * * @author Plopman */ public final class PrimeSpeakerZegana extends CardImpl { @@ -37,10 +32,13 @@ public final class PrimeSpeakerZegana extends CardImpl { this.toughness = new MageInt(1); //Prime Speaker Zegana enters the battlefield with X +1/+1 counters on it, where X is the greatest power among other creatures you control. - Effect effect = new AddCountersSourceEffect(CounterType.P1P1.createInstance(0), - new GreatestPowerCount(), true); + Effect effect = new AddCountersSourceEffect( + CounterType.P1P1.createInstance(0), + GreatestAmongPermanentsValue.POWER_OTHER_CONTROLLED_CREATURES, + true + ); effect.setText("with X +1/+1 counters on it, where X is the greatest power among other creatures you control."); - this.addAbility(new EntersBattlefieldAbility(effect)); + this.addAbility(new EntersBattlefieldAbility(effect).addHint(GreatestAmongPermanentsValue.POWER_OTHER_CONTROLLED_CREATURES.getHint())); //When Prime Speaker Zegana enters the battlefield, draw cards equal to its power. this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(SourcePermanentPowerValue.NOT_NEGATIVE) .setText("draw cards equal to its power"))); @@ -54,33 +52,4 @@ public final class PrimeSpeakerZegana extends CardImpl { public PrimeSpeakerZegana copy() { return new PrimeSpeakerZegana(this); } -} - -class GreatestPowerCount implements DynamicValue { - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - int value = 0; - for (Permanent creature : game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), sourceAbility.getControllerId(), game)) { - if (creature != null && creature.getPower().getValue() > value && !sourceAbility.getSourceId().equals(creature.getId())) { - value = creature.getPower().getValue(); - } - } - return value; - } - - @Override - public GreatestPowerCount copy() { - return new GreatestPowerCount(); - } - - @Override - public String toString() { - return "X"; - } - - @Override - public String getMessage() { - return "greatest power among other creatures you control"; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/p/PrimordialHydra.java b/Mage.Sets/src/mage/cards/p/PrimordialHydra.java index 607c850a9e9..b0f7a839133 100644 --- a/Mage.Sets/src/mage/cards/p/PrimordialHydra.java +++ b/Mage.Sets/src/mage/cards/p/PrimordialHydra.java @@ -1,7 +1,6 @@ package mage.cards.p; import mage.MageInt; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; @@ -11,6 +10,7 @@ import mage.abilities.effects.common.DoubleCountersSourceEffect; import mage.abilities.effects.common.EntersBattlefieldWithXCountersEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.keyword.TrampleAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -24,8 +24,7 @@ import java.util.UUID; */ public final class PrimordialHydra extends CardImpl { - private static final Condition condition = new SourceHasCounterCondition(CounterType.P1P1, 10, Integer.MAX_VALUE); - private static final String staticText = "{this} has trample as long as it has ten or more +1/+1 counters on it"; + private static final Condition condition = new SourceHasCounterCondition(CounterType.P1P1, 10); public PrimordialHydra(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{X}{G}{G}"); @@ -39,13 +38,12 @@ public final class PrimordialHydra extends CardImpl { this.addAbility(new EntersBattlefieldAbility(new EntersBattlefieldWithXCountersEffect(CounterType.P1P1.createInstance()))); // At the beginning of your upkeep, double the number of +1/+1 counters on Primordial Hydra. - this.addAbility(new BeginningOfUpkeepTriggeredAbility( - new DoubleCountersSourceEffect(CounterType.P1P1) - )); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new DoubleCountersSourceEffect(CounterType.P1P1))); // Primordial Hydra has trample as long as it has ten or more +1/+1 counters on it. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( - new GainAbilitySourceEffect(TrampleAbility.getInstance()), condition, staticText + new GainAbilitySourceEffect(TrampleAbility.getInstance()), condition, + "{this} has trample as long as it has ten or more +1/+1 counters on it" ))); } diff --git a/Mage.Sets/src/mage/cards/p/PrishesWanderings.java b/Mage.Sets/src/mage/cards/p/PrishesWanderings.java new file mode 100644 index 00000000000..5465d4f1e5c --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PrishesWanderings.java @@ -0,0 +1,98 @@ +package mage.cards.p; + +import mage.abilities.Ability; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PrishesWanderings extends CardImpl { + + public PrishesWanderings(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}"); + + // Search your library for a basic land card or Town card, put it onto the battlefield tapped, then shuffle. When you search your library this way, put a +1/+1 counter on target creature you control. + this.getSpellAbility().addEffect(new PrishesWanderingsEffect()); + } + + private PrishesWanderings(final PrishesWanderings card) { + super(card); + } + + @Override + public PrishesWanderings copy() { + return new PrishesWanderings(this); + } +} + +class PrishesWanderingsEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterCard("basic land card or Town card"); + + static { + filter.add(Predicates.or( + Predicates.and( + SuperType.BASIC.getPredicate(), + CardType.LAND.getPredicate() + ), + SubType.TOWN.getPredicate() + )); + } + + PrishesWanderingsEffect() { + super(Outcome.Benefit); + staticText = "search your library for a basic land card or Town card, put it onto the battlefield tapped, " + + "then shuffle. When you search your library this way, put a +1/+1 counter on target creature you control"; + } + + private PrishesWanderingsEffect(final PrishesWanderingsEffect effect) { + super(effect); + } + + @Override + public PrishesWanderingsEffect copy() { + return new PrishesWanderingsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + TargetCardInLibrary target = new TargetCardInLibrary(filter); + if (player.searchLibrary(target, source, game)) { + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), false + ); + ability.addTarget(new TargetControlledCreaturePermanent()); + game.fireReflexiveTriggeredAbility(ability, source); + } + Card card = player.getLibrary().getCard(target.getFirstTarget(), game); + if (card != null) { + player.revealCards(source, new CardsImpl(card), game); + player.moveCards( + card, Zone.BATTLEFIELD, source, game, true, + false, false, null + ); + } + player.shuffleLibrary(source, game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/p/PrismArray.java b/Mage.Sets/src/mage/cards/p/PrismArray.java index 6e6bbf82592..50be1fc6da3 100644 --- a/Mage.Sets/src/mage/cards/p/PrismArray.java +++ b/Mage.Sets/src/mage/cards/p/PrismArray.java @@ -1,7 +1,6 @@ package mage.cards.p; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; @@ -14,23 +13,23 @@ import mage.abilities.effects.keyword.ScryEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; import mage.counters.CounterType; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class PrismArray extends CardImpl { public PrismArray(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{4}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{U}"); // Converge — Prism Array enters the battlefield with a crystal counter on it for each color of mana spent to cast it. this.addAbility(new EntersBattlefieldAbility( new AddCountersSourceEffect(CounterType.CRYSTAL.createInstance(), ColorsOfManaSpentToCastCount.getInstance(), true), - null, "Converge — {this} enters with a +1/+1 counter on it for each color of mana spent to cast it.", null)); + null, "Converge — {this} enters with a crystal counter on it for each color of mana spent to cast it.", null)); // Remove a crystal counter from Prism Array: Tap target creature. Ability ability = new SimpleActivatedAbility( diff --git a/Mage.Sets/src/mage/cards/p/PrismaticCircle.java b/Mage.Sets/src/mage/cards/p/PrismaticCircle.java index 630f20e22a3..1b3e4b9c3af 100644 --- a/Mage.Sets/src/mage/cards/p/PrismaticCircle.java +++ b/Mage.Sets/src/mage/cards/p/PrismaticCircle.java @@ -1,31 +1,33 @@ package mage.cards.p; -import java.util.UUID; -import mage.ObjectColor; -import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.ChooseColorEffect; -import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.abilities.keyword.CumulativeUpkeepAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterObject; -import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.Game; +import mage.filter.FilterSource; +import mage.filter.predicate.mageobject.ChosenColorPredicate; + +import java.util.UUID; /** - * * @author TheElk801 */ public final class PrismaticCircle extends CardImpl { + private static FilterSource filter = new FilterSource("a source of your choice of the chosen color"); + + static { + filter.add(ChosenColorPredicate.TRUE); + } + public PrismaticCircle(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); @@ -36,7 +38,10 @@ public final class PrismaticCircle extends CardImpl { this.addAbility(new AsEntersBattlefieldAbility(new ChooseColorEffect(Outcome.Neutral))); // {1}: The next time a source of your choice of the chosen color would deal damage to you this turn, prevent that damage. - this.addAbility(new SimpleActivatedAbility(new PrismaticCircleEffect(), new ManaCostsImpl<>("{1}"))); + this.addAbility(new SimpleActivatedAbility( + new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, true, filter), + new ManaCostsImpl<>("{1}") + )); } private PrismaticCircle(final PrismaticCircle card) { @@ -47,29 +52,4 @@ public final class PrismaticCircle extends CardImpl { public PrismaticCircle copy() { return new PrismaticCircle(this); } -} - -class PrismaticCircleEffect extends PreventNextDamageFromChosenSourceToYouEffect { - - PrismaticCircleEffect() { - super(Duration.EndOfTurn); - staticText = "The next time a source of your choice of the chosen color would deal damage to you this turn, prevent that damage."; - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - FilterObject filter = targetSource.getFilter(); - filter.add(new ColorPredicate((ObjectColor) game.getState().getValue(source.getSourceId() + "_color"))); - } - - private PrismaticCircleEffect(final PrismaticCircleEffect effect) { - super(effect); - } - - @Override - public PrismaticCircleEffect copy() { - return new PrismaticCircleEffect(this); - } - -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/p/PristineAngel.java b/Mage.Sets/src/mage/cards/p/PristineAngel.java index 5e997d5d86c..fa8cdd04e51 100644 --- a/Mage.Sets/src/mage/cards/p/PristineAngel.java +++ b/Mage.Sets/src/mage/cards/p/PristineAngel.java @@ -55,7 +55,7 @@ public final class PristineAngel extends CardImpl { new ConditionalContinuousEffect( new GainAbilitySourceEffect(new ProtectionAbility(filter), Duration.WhileOnBattlefield), SourceTappedCondition.UNTAPPED, - "As long as {this} is untapped, it has protection from artifacts and from all colors"))); + "As long as {this} is untapped, it has protection from artifacts and from each color"))); // Whenever you cast a spell, you may untap Pristine Angel. this.addAbility(new SpellCastControllerTriggeredAbility(new UntapSourceEffect(), true)); } diff --git a/Mage.Sets/src/mage/cards/p/ProfanePrayers.java b/Mage.Sets/src/mage/cards/p/ProfanePrayers.java index 9b0119395c6..4fe514bff76 100644 --- a/Mage.Sets/src/mage/cards/p/ProfanePrayers.java +++ b/Mage.Sets/src/mage/cards/p/ProfanePrayers.java @@ -2,6 +2,8 @@ package mage.cards.p; import java.util.UUID; + +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.GainLifeEffect; @@ -23,14 +25,18 @@ public final class ProfanePrayers extends CardImpl { static { filter.add(SubType.CLERIC.getPredicate()); } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); public ProfanePrayers(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{B}{B}"); // Profane Prayers deals X damage to any target and you gain X life, where X is the number of Clerics on the battlefield. - this.getSpellAbility().addEffect(new DamageTargetEffect(new PermanentsOnBattlefieldCount(filter))); + this.getSpellAbility().addEffect(new DamageTargetEffect(xValue) + .setText("{this} deals X damage to any target")); this.getSpellAbility().addTarget(new TargetAnyTarget()); - this.getSpellAbility().addEffect(new GainLifeEffect(new PermanentsOnBattlefieldCount(filter))); + this.getSpellAbility().addEffect(new GainLifeEffect(xValue) + .setText("and you gain X life, where X is the number of Clerics on the battlefield")); } private ProfanePrayers(final ProfanePrayers card) { diff --git a/Mage.Sets/src/mage/cards/p/ProfessorHojo.java b/Mage.Sets/src/mage/cards/p/ProfessorHojo.java new file mode 100644 index 00000000000..92eef1471b2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/ProfessorHojo.java @@ -0,0 +1,159 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.ActivatedAbility; +import mage.abilities.common.BecomesTargetAnyTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterStackObject; +import mage.filter.StaticFilters; +import mage.filter.predicate.Predicate; +import mage.game.Controllable; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.StackObject; +import mage.util.CardUtil; +import mage.watchers.Watcher; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ProfessorHojo extends CardImpl { + + private static final FilterStackObject filter = new FilterStackObject(); + + static { + filter.add(ProfessorHojoPredicate.instance); + } + + public ProfessorHojo(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SCIENTIST); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // The first activated ability you activate during your turn that targets a creature you control costs {2} less to activate. + this.addAbility(new SimpleStaticAbility(new ProfessorHojoEffect()), new ProfessorHojoWatcher()); + + // Whenever one or more creatures you control become the target of an activated ability, draw a card. This ability triggers only once each turn. + this.addAbility(new BecomesTargetAnyTriggeredAbility( + new DrawCardSourceControllerEffect(1), + StaticFilters.FILTER_CONTROLLED_CREATURE, + filter, SetTargetPointer.NONE, false + ).setTriggerPhrase("Whenever one or more creatures you control " + + "become the target of an activated ability, ").setTriggersLimitEachTurn(1)); + } + + private ProfessorHojo(final ProfessorHojo card) { + super(card); + } + + @Override + public ProfessorHojo copy() { + return new ProfessorHojo(this); + } +} + +enum ProfessorHojoPredicate implements Predicate { + instance; + + @Override + public boolean apply(StackObject input, Game game) { + return input instanceof Ability && ((Ability) input).isActivatedAbility(); + } +} + +class ProfessorHojoEffect extends CostModificationEffectImpl { + + ProfessorHojoEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST); + staticText = "the first activated ability you activate during your turn " + + "that targets a creature you control costs {2} less to activate"; + } + + private ProfessorHojoEffect(final ProfessorHojoEffect effect) { + super(effect); + } + + @Override + public ProfessorHojoEffect copy() { + return new ProfessorHojoEffect(this); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + CardUtil.reduceCost(abilityToModify, 2); + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + return game.isActivePlayer(source.getControllerId()) + && abilityToModify.isControlledBy(source.getControllerId()) + && !ProfessorHojoWatcher.checkPlayer(game, source) + && CardUtil + .getAllSelectedTargets(abilityToModify, game) + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .filter(permanent -> permanent.isCreature(game)) + .map(Controllable::getControllerId) + .anyMatch(source::isControlledBy); + } +} + +class ProfessorHojoWatcher extends Watcher { + + private final Set set = new HashSet<>(); + + ProfessorHojoWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.ACTIVATED_ABILITY) { + return; + } + StackObject stackObject = game.getStack().getStackObject(event.getSourceId()); + if (stackObject != null + && stackObject.getStackAbility() instanceof ActivatedAbility + && CardUtil + .getAllSelectedTargets(stackObject.getStackAbility(), game) + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .filter(permanent -> permanent.isCreature(game)) + .map(Controllable::getControllerId) + .anyMatch(stackObject::isControlledBy)) { + set.add(stackObject.getControllerId()); + } + } + + @Override + public void reset() { + super.reset(); + set.clear(); + } + + static boolean checkPlayer(Game game, Ability source) { + return game + .getState() + .getWatcher(ProfessorHojoWatcher.class) + .set + .contains(source.getControllerId()); + } +} diff --git a/Mage.Sets/src/mage/cards/p/ProgenitorsIcon.java b/Mage.Sets/src/mage/cards/p/ProgenitorsIcon.java index 2b74e7b4430..58711a9d2f8 100644 --- a/Mage.Sets/src/mage/cards/p/ProgenitorsIcon.java +++ b/Mage.Sets/src/mage/cards/p/ProgenitorsIcon.java @@ -91,11 +91,11 @@ class ProgenitorsIconAsThoughEffect extends AsThoughEffectImpl { return false; } Card card = game.getCard(sourceId); - if (card == null){ + if (card == null) { return false; } - return card.getSubtype().contains(subType); + return card.hasSubtype(subType, game); } } diff --git a/Mage.Sets/src/mage/cards/p/PromptoArgentum.java b/Mage.Sets/src/mage/cards/p/PromptoArgentum.java new file mode 100644 index 00000000000..6d19bfa2ada --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PromptoArgentum.java @@ -0,0 +1,49 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; +import mage.game.permanent.token.TreasureToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PromptoArgentum extends CardImpl { + + public PromptoArgentum(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SCOUT); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Selfie Shot -- Whenever you cast a noncreature spell, if at least four mana was spent to cast it, create a Treasure token. + this.addAbility(new SpellCastControllerTriggeredAbility( + new CreateTokenEffect(new TreasureToken()), + StaticFilters.FILTER_NONCREATURE_SPELL_FOUR_MANA_SPENT, false + ).withFlavorWord("Selfie Shot")); + } + + private PromptoArgentum(final PromptoArgentum card) { + super(card); + } + + @Override + public PromptoArgentum copy() { + return new PromptoArgentum(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/ProsperTomeBound.java b/Mage.Sets/src/mage/cards/p/ProsperTomeBound.java index 6d47b03d9fc..a616e9ccb69 100644 --- a/Mage.Sets/src/mage/cards/p/ProsperTomeBound.java +++ b/Mage.Sets/src/mage/cards/p/ProsperTomeBound.java @@ -1,11 +1,11 @@ package mage.cards.p; import mage.MageInt; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.PlayCardTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.ExileTopXMayPlayUntilEffect; import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -57,7 +57,7 @@ class ProsperTomeBoundTriggeredAbility extends PlayCardTriggeredAbility { ProsperTomeBoundTriggeredAbility() { super(TargetController.YOU, Zone.BATTLEFIELD, new CreateTokenEffect(new TreasureToken())); - this.flavorWord = "Pact Boon"; + this.withFlavorWord("Pact Boon"); setTriggerPhrase("Whenever you play a card from exile, "); } diff --git a/Mage.Sets/src/mage/cards/p/ProwlingPangolin.java b/Mage.Sets/src/mage/cards/p/ProwlingPangolin.java index 714b1231788..177524ca497 100644 --- a/Mage.Sets/src/mage/cards/p/ProwlingPangolin.java +++ b/Mage.Sets/src/mage/cards/p/ProwlingPangolin.java @@ -49,7 +49,7 @@ class ProwlingPangolinEffect extends OneShotEffect { ProwlingPangolinEffect() { super(Outcome.Sacrifice); - this.staticText = "any player may sacrifice two creatures. If a player does, sacrifice {this}"; + this.staticText = "any player may sacrifice two creatures of their choice. If a player does, sacrifice {this}"; } private ProwlingPangolinEffect(final ProwlingPangolinEffect effect) { diff --git a/Mage.Sets/src/mage/cards/p/ProwlingSerpopard.java b/Mage.Sets/src/mage/cards/p/ProwlingSerpopard.java index 7764ad489a0..4f939194d59 100644 --- a/Mage.Sets/src/mage/cards/p/ProwlingSerpopard.java +++ b/Mage.Sets/src/mage/cards/p/ProwlingSerpopard.java @@ -1,7 +1,6 @@ package mage.cards.p; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.common.SimpleStaticAbility; @@ -9,13 +8,13 @@ import mage.abilities.effects.common.CantBeCounteredControlledEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; import mage.filter.FilterSpell; +import java.util.UUID; + /** - * * @author fireshoes */ public final class ProwlingSerpopard extends CardImpl { @@ -38,7 +37,7 @@ public final class ProwlingSerpopard extends CardImpl { this.addAbility(new CantBeCounteredSourceAbility()); // Creature spells you control can't be countered. - this.addAbility(new SimpleStaticAbility(new CantBeCounteredControlledEffect(filterTarget, null, Duration.WhileOnBattlefield))); + this.addAbility(new SimpleStaticAbility(new CantBeCounteredControlledEffect(filterTarget, Duration.WhileOnBattlefield))); } private ProwlingSerpopard(final ProwlingSerpopard card) { diff --git a/Mage.Sets/src/mage/cards/p/PsemillaMeletianPoet.java b/Mage.Sets/src/mage/cards/p/PsemillaMeletianPoet.java new file mode 100644 index 00000000000..fc781f8da70 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PsemillaMeletianPoet.java @@ -0,0 +1,117 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.common.FilterControlledEnchantmentPermanent; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.token.NymphToken; +import mage.game.stack.Spell; +import mage.watchers.common.SpellsCastWatcher; + +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class PsemillaMeletianPoet extends CardImpl { + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + new FilterControlledEnchantmentPermanent("you control five or more enchantments"), + ComparisonType.MORE_THAN, 4 + ); + private static final Hint hint = new ValueHint( + "Enchantments you control", new PermanentsOnBattlefieldCount(new FilterControlledEnchantmentPermanent()) + ); + + public PsemillaMeletianPoet(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.BARD); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Whenever you cast your first enchantment spell each turn, create a 2/2 white Nymph enchantment creature token. + this.addAbility(new PsemillaMeletianPoetTriggeredAbility()); + + // At the beginning of each combat, if you control five or more enchantments, Psemilla, Meletian Poet gets +4/+4 and gains lifelink until end of turn. + Ability ability = new BeginningOfCombatTriggeredAbility( + TargetController.ANY, + new BoostSourceEffect(4, 4, Duration.EndOfTurn) + .setText("{this} gets +4/+4"), + false + ).withInterveningIf(condition); + ability.addEffect(new GainAbilitySourceEffect( + LifelinkAbility.getInstance(), Duration.EndOfTurn + ).setText("and gains lifelink until end of turn")); + this.addAbility(ability.addHint(hint)); + } + + private PsemillaMeletianPoet(final PsemillaMeletianPoet card) { + super(card); + } + + @Override + public PsemillaMeletianPoet copy() { + return new PsemillaMeletianPoet(this); + } +} + +class PsemillaMeletianPoetTriggeredAbility extends TriggeredAbilityImpl { + + PsemillaMeletianPoetTriggeredAbility() { + super(Zone.BATTLEFIELD, new CreateTokenEffect(new NymphToken())); + setTriggerPhrase("Whenever you cast your first enchantment spell each turn, "); + } + + private PsemillaMeletianPoetTriggeredAbility(final PsemillaMeletianPoetTriggeredAbility ability) { + super(ability); + } + + @Override + public PsemillaMeletianPoetTriggeredAbility copy() { + return new PsemillaMeletianPoetTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!isControlledBy(event.getPlayerId())) { + return false; + } + Spell spell = game.getSpell(event.getTargetId()); + if (spell == null || !spell.isEnchantment(game)) { + return false; + } + List spells = game + .getState() + .getWatcher(SpellsCastWatcher.class) + .getSpellsCastThisTurn(getControllerId()) + .stream() + .filter(s -> s.isEnchantment(game)) + .collect(Collectors.toList()); + return spells.size() == 1 && spells.get(0).getId().equals(event.getTargetId()); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PsychicWhorl.java b/Mage.Sets/src/mage/cards/p/PsychicWhorl.java index f35c9339f29..a0dd1da2c3a 100644 --- a/Mage.Sets/src/mage/cards/p/PsychicWhorl.java +++ b/Mage.Sets/src/mage/cards/p/PsychicWhorl.java @@ -32,7 +32,7 @@ public final class PsychicWhorl extends CardImpl { this.getSpellAbility().addTarget(new TargetOpponent()); this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new SurveilEffect(2), condition, - "then if you control a Rat, surveil 2" + "Then if you control a Rat, surveil 2" )); this.getSpellAbility().addHint(hint); } diff --git a/Mage.Sets/src/mage/cards/p/PsychoticEpisode.java b/Mage.Sets/src/mage/cards/p/PsychoticEpisode.java index 5cdee0e6bd3..0b00c5478bd 100644 --- a/Mage.Sets/src/mage/cards/p/PsychoticEpisode.java +++ b/Mage.Sets/src/mage/cards/p/PsychoticEpisode.java @@ -75,7 +75,7 @@ class PsychoticEpisodeEffect extends OneShotEffect { Card card = game.getCard(targetCard.getFirstTarget()); if (card != null) { game.informPlayers(card.getLogName() + " was chosen."); - player.putCardsOnBottomOfLibrary(card, game, source, true); + player.putCardsOnBottomOfLibrary(card, game, source); } } return true; diff --git a/Mage.Sets/src/mage/cards/p/PugnaciousHammerskull.java b/Mage.Sets/src/mage/cards/p/PugnaciousHammerskull.java index 447462af894..ef7f7d03c2d 100644 --- a/Mage.Sets/src/mage/cards/p/PugnaciousHammerskull.java +++ b/Mage.Sets/src/mage/cards/p/PugnaciousHammerskull.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -23,7 +22,7 @@ import java.util.UUID; */ public final class PugnaciousHammerskull extends CardImpl { - private static final FilterPermanent filter = new FilterControlledPermanent(SubType.DINOSAUR); + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.DINOSAUR, "you don't control another Dinosaur"); static { filter.add(AnotherPredicate.instance); @@ -40,10 +39,9 @@ public final class PugnaciousHammerskull extends CardImpl { this.toughness = new MageInt(6); // Whenever Pugnacious Hammerskull attacks while you don't control another Dinosaur, put a stun counter on it. - this.addAbility(new ConditionalTriggeredAbility( - new AttacksTriggeredAbility(new AddCountersSourceEffect(CounterType.STUN.createInstance())), condition, - "Whenever {this} attacks while you don't control another Dinosaur, put a stun counter on it." - )); + this.addAbility(new AttacksTriggeredAbility( + new AddCountersSourceEffect(CounterType.STUN.createInstance()).setText("put a stun counter on it") + ).withTriggerCondition(condition)); } private PugnaciousHammerskull(final PugnaciousHammerskull card) { diff --git a/Mage.Sets/src/mage/cards/p/PuppeteerClique.java b/Mage.Sets/src/mage/cards/p/PuppeteerClique.java index 6e874521b63..aa5ac379349 100644 --- a/Mage.Sets/src/mage/cards/p/PuppeteerClique.java +++ b/Mage.Sets/src/mage/cards/p/PuppeteerClique.java @@ -24,6 +24,7 @@ import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCardInOpponentsGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -89,7 +90,7 @@ class PuppeteerCliqueEffect extends OneShotEffect { if (controller == null || !controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { return false; } - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/p/PurphorosBronzeBlooded.java b/Mage.Sets/src/mage/cards/p/PurphorosBronzeBlooded.java index 51d72791039..dce14584cbd 100644 --- a/Mage.Sets/src/mage/cards/p/PurphorosBronzeBlooded.java +++ b/Mage.Sets/src/mage/cards/p/PurphorosBronzeBlooded.java @@ -28,6 +28,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInHand; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -114,7 +115,7 @@ class PurphurosBronzeBloodedEffect extends OneShotEffect { if (card == null || !controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { return false; } - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return true; } diff --git a/Mage.Sets/src/mage/cards/p/PushTheLimit.java b/Mage.Sets/src/mage/cards/p/PushTheLimit.java index 1ee2960811f..7ec0e82411b 100644 --- a/Mage.Sets/src/mage/cards/p/PushTheLimit.java +++ b/Mage.Sets/src/mage/cards/p/PushTheLimit.java @@ -1,51 +1,47 @@ package mage.cards.p; +import mage.abilities.Ability; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.SacrificeTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityAllEffect; +import mage.abilities.effects.common.continuous.VehiclesBecomeArtifactCreatureEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.targetpointer.FixedTargets; +import mage.util.CardUtil; + import java.util.List; import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; -import mage.MageObjectReference; -import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.*; -import mage.abilities.effects.common.continuous.BecomesSubtypeAllEffect; -import mage.abilities.effects.common.continuous.CreaturesBecomeOtherTypeEffect; -import mage.abilities.effects.common.continuous.GainAbilityAllEffect; -import mage.abilities.effects.common.continuous.VehiclesBecomeArtifactCreatureEffect; -import mage.abilities.keyword.HasteAbility; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.constants.*; -import mage.filter.FilterCard; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.MageObjectReferencePredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.targetpointer.FixedTargets; - /** * * @author Jmlundeen */ public final class PushTheLimit extends CardImpl { + private static final FilterPermanent filterCreatures = new FilterControlledPermanent("Creatures you control"); public PushTheLimit(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{R}{R}"); - // Return all Mount and Vehicle cards from your graveyard to the battlefield. Sacrifice them at the beginning of the next end step. this.getSpellAbility().addEffect(new PushTheLimitEffect()); + // Vehicles you control become artifact creatures until end of turn. Creatures you control gain haste until end of turn. this.getSpellAbility().addEffect(new VehiclesBecomeArtifactCreatureEffect(Duration.EndOfTurn) .concatBy("
    ")); @@ -63,6 +59,7 @@ public final class PushTheLimit extends CardImpl { } class PushTheLimitEffect extends OneShotEffect { + private static final FilterCard filter = new FilterCard("Mount and Vehicle cards"); static { filter.add(Predicates.or( @@ -70,13 +67,14 @@ class PushTheLimitEffect extends OneShotEffect { SubType.VEHICLE.getPredicate() )); } - public PushTheLimitEffect() { + + PushTheLimitEffect() { super(Outcome.PutCreatureInPlay); staticText = "return all " + filter.getMessage() + " from your graveyard to the battlefield. " + "Sacrifice them at the beginning of the next end step."; } - public PushTheLimitEffect(final PushTheLimitEffect effect) { + private PushTheLimitEffect(final PushTheLimitEffect effect) { super(effect); } @@ -96,7 +94,7 @@ class PushTheLimitEffect extends OneShotEffect { false, false, false, null); if (result) { List permanentsToSac = cards.stream() - .map(card -> game.getPermanent(card.getId())) + .map(card -> CardUtil.getPermanentFromCardPutToBattlefield(card, game)) .filter(Objects::nonNull) .collect(Collectors.toList()); Effect sacrificeEffect = new SacrificeTargetEffect("sacrifice them", source.getControllerId()); diff --git a/Mage.Sets/src/mage/cards/p/PyreswipeHawk.java b/Mage.Sets/src/mage/cards/p/PyreswipeHawk.java index 36487417a63..5c850d40655 100644 --- a/Mage.Sets/src/mage/cards/p/PyreswipeHawk.java +++ b/Mage.Sets/src/mage/cards/p/PyreswipeHawk.java @@ -1,13 +1,11 @@ package mage.cards.p; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.ExpendTriggeredAbility; -import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.abilities.keyword.FlyingAbility; @@ -17,8 +15,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.filter.StaticFilters; -import mage.game.Game; import mage.target.common.TargetArtifactPermanent; import java.util.UUID; @@ -44,7 +40,8 @@ public final class PyreswipeHawk extends CardImpl { // Whenever Pyreswipe Hawk attacks, it gets +X/+0 until end of turn, where X is the greatest mana value among artifacts you control. this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect( - PyreswipeHawkValue.instance, StaticValue.get(0), Duration.EndOfTurn, "it" + GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_ARTIFACTS, + StaticValue.get(0), Duration.EndOfTurn, "it" ))); // Whenever you expend 6, gain control of up to one target artifact for as long as you control Pyreswipe Hawk. @@ -63,37 +60,4 @@ public final class PyreswipeHawk extends CardImpl { public PyreswipeHawk copy() { return new PyreswipeHawk(this); } -} - -enum PyreswipeHawkValue implements DynamicValue { - instance; - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - return game - .getBattlefield() - .getActivePermanents( - StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT, - sourceAbility.getControllerId(), sourceAbility, game - ) - .stream() - .mapToInt(MageObject::getManaValue) - .max() - .orElse(0); - } - - @Override - public PyreswipeHawkValue copy() { - return this; - } - - @Override - public String getMessage() { - return "the greatest mana value among artifacts you control"; - } - - @Override - public String toString() { - return "X"; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/q/QiqirnMerchant.java b/Mage.Sets/src/mage/cards/q/QiqirnMerchant.java new file mode 100644 index 00000000000..322421e20a9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/q/QiqirnMerchant.java @@ -0,0 +1,80 @@ +package mage.cards.q; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.CostAdjuster; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.DrawDiscardControllerEffect; +import mage.abilities.effects.common.InfoEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class QiqirnMerchant extends CardImpl { + + public QiqirnMerchant(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.subtype.add(SubType.BEAST); + this.subtype.add(SubType.CITIZEN); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // {1}, {T}: Draw a card, then discard a card. + Ability ability = new SimpleActivatedAbility( + new DrawDiscardControllerEffect(1, 1), new GenericManaCost(1) + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + + // {7}, {T}, Sacrifice this creature: Draw three cards. This ability costs {1} less to activate for each Town you control. + ability = new SimpleActivatedAbility(new DrawCardSourceControllerEffect(3), new GenericManaCost(7)); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeSourceCost()); + ability.addEffect(new InfoEffect("This ability costs {1} less to activate for each Town you control")); + this.addAbility(ability.setCostAdjuster(QiqirnMerchantAdjuster.instance).addHint(QiqirnMerchantAdjuster.getHint())); + } + + private QiqirnMerchant(final QiqirnMerchant card) { + super(card); + } + + @Override + public QiqirnMerchant copy() { + return new QiqirnMerchant(this); + } +} + +enum QiqirnMerchantAdjuster implements CostAdjuster { + instance; + + private static final DynamicValue cardsCount = new PermanentsOnBattlefieldCount(new FilterControlledPermanent(SubType.TOWN)); + private static final Hint hint = new ValueHint("Towns you control", cardsCount); + + static Hint getHint() { + return hint; + } + + @Override + public void reduceCost(Ability ability, Game game) { + int count = cardsCount.calculate(game, ability, null); + CardUtil.reduceCost(ability, count); + } +} diff --git a/Mage.Sets/src/mage/cards/q/Quagnoth.java b/Mage.Sets/src/mage/cards/q/Quagnoth.java index aeaad7a581c..b9819c458f0 100644 --- a/Mage.Sets/src/mage/cards/q/Quagnoth.java +++ b/Mage.Sets/src/mage/cards/q/Quagnoth.java @@ -31,7 +31,7 @@ public final class Quagnoth extends CardImpl { // When a spell or ability an opponent controls causes you to discard Quagnoth, return it to your hand. this.addAbility(new DiscardedByOpponentTriggeredAbility(new ReturnToHandSourceEffect() - .setText("return it to your hand"), true)); + .setText("return it to your hand"))); } private Quagnoth(final Quagnoth card) { diff --git a/Mage.Sets/src/mage/cards/q/QueenBrahne.java b/Mage.Sets/src/mage/cards/q/QueenBrahne.java new file mode 100644 index 00000000000..53f265e04aa --- /dev/null +++ b/Mage.Sets/src/mage/cards/q/QueenBrahne.java @@ -0,0 +1,45 @@ +package mage.cards.q; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.ProwessAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.game.permanent.token.BlackWizardToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class QueenBrahne extends CardImpl { + + public QueenBrahne(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.NOBLE); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Prowess + this.addAbility(new ProwessAbility()); + + // Whenever Queen Brahne attacks, create a 0/1 black Wizard creature token with "Whenever you cast a noncreature spell, this token deals 1 damage to each opponent." + this.addAbility(new AttacksTriggeredAbility(new CreateTokenEffect(new BlackWizardToken()))); + } + + private QueenBrahne(final QueenBrahne card) { + super(card); + } + + @Override + public QueenBrahne copy() { + return new QueenBrahne(this); + } +} diff --git a/Mage.Sets/src/mage/cards/q/QuestForRenewal.java b/Mage.Sets/src/mage/cards/q/QuestForRenewal.java index c149b9cd6ca..8bf379f74ae 100644 --- a/Mage.Sets/src/mage/cards/q/QuestForRenewal.java +++ b/Mage.Sets/src/mage/cards/q/QuestForRenewal.java @@ -1,9 +1,8 @@ - package mage.cards.q; -import java.util.UUID; import mage.abilities.common.BecomesTappedTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.continuous.UntapAllDuringEachOtherPlayersUntapStepEffect; @@ -11,28 +10,33 @@ import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.StaticFilters; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class QuestForRenewal extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.QUEST, 4); + public QuestForRenewal(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}"); // Whenever a creature you control becomes tapped, you may put a quest counter on Quest for Renewal. - this.addAbility(new BecomesTappedTriggeredAbility(new AddCountersSourceEffect(CounterType.QUEST.createInstance()), - true, StaticFilters.FILTER_CONTROLLED_A_CREATURE)); + this.addAbility(new BecomesTappedTriggeredAbility( + new AddCountersSourceEffect(CounterType.QUEST.createInstance()), + true, StaticFilters.FILTER_CONTROLLED_A_CREATURE + )); // As long as there are four or more quest counters on Quest for Renewal, untap all creatures you control during each other player's untap step. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( new UntapAllDuringEachOtherPlayersUntapStepEffect(StaticFilters.FILTER_CONTROLLED_CREATURES), - new SourceHasCounterCondition(CounterType.QUEST, 4, Integer.MAX_VALUE), - "As long as there are four or more quest counters on {this}, untap all creatures you control during each other player's untap step."))); + condition, "As long as there are four or more quest counters on {this}, " + + "untap all creatures you control during each other player's untap step." + ))); } private QuestForRenewal(final QuestForRenewal card) { diff --git a/Mage.Sets/src/mage/cards/q/QuestForTheGoblinLord.java b/Mage.Sets/src/mage/cards/q/QuestForTheGoblinLord.java index 2c8e9d977b8..20f20d6f1b2 100644 --- a/Mage.Sets/src/mage/cards/q/QuestForTheGoblinLord.java +++ b/Mage.Sets/src/mage/cards/q/QuestForTheGoblinLord.java @@ -1,39 +1,44 @@ package mage.cards.q; -import java.util.UUID; -import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.FilterPermanent; -import mage.filter.StaticFilters; import mage.filter.common.FilterControlledPermanent; +import java.util.UUID; + /** - * * @author jeffwadsworth */ public final class QuestForTheGoblinLord extends CardImpl { - private static final String rule = "As long as {this} has five or more quest counters on it, creatures you control get +2/+0"; - private static final FilterPermanent goblinFilter = new FilterControlledPermanent(SubType.GOBLIN, "a Goblin"); + private static final FilterPermanent goblinFilter = new FilterControlledPermanent(SubType.GOBLIN, "a Goblin you control"); + private static final Condition condition = new SourceHasCounterCondition(CounterType.QUEST, 5); public QuestForTheGoblinLord(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{R}"); // Whenever a Goblin you control enters, you may put a quest counter on Quest for the Goblin Lord. - this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.QUEST.createInstance()), goblinFilter, true)); + this.addAbility(new EntersBattlefieldAllTriggeredAbility( + new AddCountersSourceEffect(CounterType.QUEST.createInstance()), goblinFilter, true + )); // As long as Quest for the Goblin Lord has five or more quest counters on it, creatures you control get +2/+0. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( - new BoostAllEffect(2, 0, Duration.WhileOnBattlefield, StaticFilters.FILTER_CONTROLLED_CREATURES, false), - new SourceHasCounterCondition(CounterType.QUEST, 5, Integer.MAX_VALUE), rule))); + new BoostControlledEffect(2, 0, Duration.WhileOnBattlefield), condition, + "as long as {this} has five or more quest counters on it, creatures you control get +2/+0" + ))); } private QuestForTheGoblinLord(final QuestForTheGoblinLord card) { diff --git a/Mage.Sets/src/mage/cards/q/QuestForTheHolyRelic.java b/Mage.Sets/src/mage/cards/q/QuestForTheHolyRelic.java index c010b787e1a..a2bec259701 100644 --- a/Mage.Sets/src/mage/cards/q/QuestForTheHolyRelic.java +++ b/Mage.Sets/src/mage/cards/q/QuestForTheHolyRelic.java @@ -24,6 +24,7 @@ import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetControlledCreaturePermanent; +import mage.util.CardUtil; import java.util.UUID; @@ -91,7 +92,7 @@ class QuestForTheHolyRelicEffect extends OneShotEffect { if (controller.searchLibrary(target, source, game)) { Card card = controller.getLibrary().getCard(target.getFirstTarget(), game); if (card != null && controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { - Permanent equipment = game.getPermanent(card.getId()); + Permanent equipment = CardUtil.getPermanentFromCardPutToBattlefield(card, game); Target targetCreature = new TargetControlledCreaturePermanent(); if (equipment != null && controller.choose(Outcome.BoostCreature, targetCreature, source, game)) { Permanent permanent = game.getPermanent(targetCreature.getFirstTarget()); diff --git a/Mage.Sets/src/mage/cards/q/QuestForUlasTemple.java b/Mage.Sets/src/mage/cards/q/QuestForUlasTemple.java index 4688f34e747..2ec43db5f13 100644 --- a/Mage.Sets/src/mage/cards/q/QuestForUlasTemple.java +++ b/Mage.Sets/src/mage/cards/q/QuestForUlasTemple.java @@ -28,7 +28,7 @@ import java.util.UUID; public final class QuestForUlasTemple extends CardImpl { private static final FilterCard filter - = new FilterCreatureCard("Kraken, Leviathan, Octopus, or Serpent creature card"); + = new FilterCreatureCard("a Kraken, Leviathan, Octopus, or Serpent creature card"); static { filter.add(Predicates.or( diff --git a/Mage.Sets/src/mage/cards/q/QuestingBeast.java b/Mage.Sets/src/mage/cards/q/QuestingBeast.java index 4be886eed59..87b9fa0c4a5 100644 --- a/Mage.Sets/src/mage/cards/q/QuestingBeast.java +++ b/Mage.Sets/src/mage/cards/q/QuestingBeast.java @@ -14,13 +14,14 @@ import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.FilterPermanent; import mage.filter.common.FilterPlaneswalkerPermanent; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.PreventDamageEvent; import mage.game.permanent.Permanent; -import mage.target.common.TargetPlaneswalkerPermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.TargetPermanent; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -28,7 +29,7 @@ import java.util.UUID; * @author TheElk801, notgreat */ public final class QuestingBeast extends CardImpl { - private static final FilterPlaneswalkerPermanent filter = new FilterPlaneswalkerPermanent("planeswalker that player controls"); + private static final FilterPermanent filter = new FilterPlaneswalkerPermanent("planeswalker that player controls"); public QuestingBeast(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}"); @@ -55,8 +56,8 @@ public final class QuestingBeast extends CardImpl { // Whenever Questing Beast deals combat damage to an opponent, it deals that much damage to target planeswalker that player controls. Ability ability = new DealsDamageToOpponentTriggeredAbility(new DamageTargetEffect(SavedDamageValue.MUCH), false, true, true); - ability.addTarget(new TargetPlaneswalkerPermanent(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.addTarget(new TargetPermanent(filter)); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/q/QuicksilverFountain.java b/Mage.Sets/src/mage/cards/q/QuicksilverFountain.java index bb41b51fe1a..0c07dbbcf08 100644 --- a/Mage.Sets/src/mage/cards/q/QuicksilverFountain.java +++ b/Mage.Sets/src/mage/cards/q/QuicksilverFountain.java @@ -1,14 +1,14 @@ package mage.cards.q; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.BecomesBasicLandTargetEffect; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -19,6 +19,7 @@ import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import mage.target.TargetPermanent; import mage.target.common.TargetLandPermanent; import mage.target.targetadjustment.TargetAdjuster; @@ -68,7 +69,7 @@ enum QuicksilverFountainAdjuster implements TargetAdjuster { FilterLandPermanent filter = new FilterLandPermanent(); filter.add(Predicates.not(SubType.ISLAND.getPredicate())); filter.add(TargetController.ACTIVE.getControllerPredicate()); - TargetLandPermanent target = new TargetLandPermanent(1, 1, filter, false); + TargetPermanent target = new TargetPermanent(1, 1, filter, false); target.setTargetController(activePlayer.getId()); ability.getTargets().add(target); } diff --git a/Mage.Sets/src/mage/cards/q/QuicksilverGeyser.java b/Mage.Sets/src/mage/cards/q/QuicksilverGeyser.java index 179edbfc166..e3d672aa5a9 100644 --- a/Mage.Sets/src/mage/cards/q/QuicksilverGeyser.java +++ b/Mage.Sets/src/mage/cards/q/QuicksilverGeyser.java @@ -1,27 +1,25 @@ - package mage.cards.q; -import java.util.UUID; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.StaticFilters; -import mage.target.common.TargetNonlandPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author anonymous */ public final class QuicksilverGeyser extends CardImpl { public QuicksilverGeyser(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{4}{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{U}"); // Return up to two target nonland permanents to their owners' hands. this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); - this.getSpellAbility().addTarget(new TargetNonlandPermanent(0, 2, StaticFilters.FILTER_PERMANENTS_NON_LAND, false)); + this.getSpellAbility().addTarget(new TargetPermanent(0, 2, StaticFilters.FILTER_PERMANENTS_NON_LAND)); } private QuicksilverGeyser(final QuicksilverGeyser card) { diff --git a/Mage.Sets/src/mage/cards/q/QuilledCharger.java b/Mage.Sets/src/mage/cards/q/QuilledCharger.java index 1d41b2ae941..783f98b3680 100644 --- a/Mage.Sets/src/mage/cards/q/QuilledCharger.java +++ b/Mage.Sets/src/mage/cards/q/QuilledCharger.java @@ -32,7 +32,7 @@ public final class QuilledCharger extends CardImpl { Ability ability = new AttacksWhileSaddledTriggeredAbility( new BoostSourceEffect(1, 2, Duration.EndOfTurn).setText("it gets +1/+2") ); - ability.addEffect(new GainAbilitySourceEffect(new MenaceAbility(false)) + ability.addEffect(new GainAbilitySourceEffect(new MenaceAbility(false), Duration.EndOfTurn) .setText("and gains menace until end of turn")); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/q/QuilledGreatwurm.java b/Mage.Sets/src/mage/cards/q/QuilledGreatwurm.java index 4d9ded412ed..5f5cf52e503 100644 --- a/Mage.Sets/src/mage/cards/q/QuilledGreatwurm.java +++ b/Mage.Sets/src/mage/cards/q/QuilledGreatwurm.java @@ -11,7 +11,6 @@ import mage.abilities.costs.Costs; import mage.abilities.costs.CostsImpl; import mage.abilities.costs.common.RemoveCounterCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.dynamicvalue.common.SavedDamageValue; import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.common.counter.AddCountersTargetEffect; @@ -44,11 +43,10 @@ public final class QuilledGreatwurm extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // Whenever a creature you control deals combat damage during your turn, put that many +1/+1 counters on it. - this.addAbility(new ConditionalTriggeredAbility(new DealsDamageToAnyTriggeredAbility( - Zone.BATTLEFIELD, new AddCountersTargetEffect( - CounterType.P1P1.createInstance(), SavedDamageValue.MANY - ), StaticFilters.FILTER_CONTROLLED_A_CREATURE, SetTargetPointer.PERMANENT, true, false - ), MyTurnCondition.instance, "Whenever a creature you control deals combat damage during your turn, put that many +1/+1 counters on it")); + this.addAbility(new DealsDamageToAnyTriggeredAbility( + Zone.BATTLEFIELD, new AddCountersTargetEffect(CounterType.P1P1.createInstance(), SavedDamageValue.MANY), + StaticFilters.FILTER_CONTROLLED_A_CREATURE, SetTargetPointer.PERMANENT, true, false + ).withTriggerCondition(MyTurnCondition.instance)); // You may cast this card from your graveyard by removing six counters from among creatures you control in addition to paying its other costs. this.addAbility(new SimpleStaticAbility(Zone.ALL, new QuilledGreatwurmEffect()).setIdentifier(MageIdentifier.QuilledGreatwurmAlternateCast)); diff --git a/Mage.Sets/src/mage/cards/q/QuillmaneBaku.java b/Mage.Sets/src/mage/cards/q/QuillmaneBaku.java index 1df63b3abe7..ffd15b4f2cf 100644 --- a/Mage.Sets/src/mage/cards/q/QuillmaneBaku.java +++ b/Mage.Sets/src/mage/cards/q/QuillmaneBaku.java @@ -37,7 +37,7 @@ public final class QuillmaneBaku extends CardImpl { this.toughness = new MageInt(3); // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Quillmane Baku. - this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, true)); // {1}, {T}, Remove X ki counters from Quillmane Baku: Return target creature with mana value X or less to its owner's hand. Ability ability = new SimpleActivatedAbility(new ReturnToHandTargetEffect(), new GenericManaCost(1)); diff --git a/Mage.Sets/src/mage/cards/q/QuinaQuGourmet.java b/Mage.Sets/src/mage/cards/q/QuinaQuGourmet.java new file mode 100644 index 00000000000..e555c00d6fc --- /dev/null +++ b/Mage.Sets/src/mage/cards/q/QuinaQuGourmet.java @@ -0,0 +1,99 @@ +package mage.cards.q; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.events.CreateTokenEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.token.FrogGreenToken; +import mage.game.permanent.token.Token; +import mage.util.CardUtil; + +import java.util.Map; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class QuinaQuGourmet extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.FROG, "Frog"); + + public QuinaQuGourmet(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.QU); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // If one or more tokens would be created under your control, those tokens plus a 1/1 green Frog creature token are created instead. + this.addAbility(new SimpleStaticAbility(new QuinaQuGourmetEffect())); + + // {2}, Sacrifice a Frog: Put a +1/+1 counter on Quina. + Ability ability = new SimpleActivatedAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new GenericManaCost(2) + ); + ability.addCost(new SacrificeTargetCost(filter)); + this.addAbility(ability); + } + + private QuinaQuGourmet(final QuinaQuGourmet card) { + super(card); + } + + @Override + public QuinaQuGourmet copy() { + return new QuinaQuGourmet(this); + } +} + +class QuinaQuGourmetEffect extends ReplacementEffectImpl { + + QuinaQuGourmetEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "if one or more tokens would be created under your control, " + + "those tokens plus a 1/1 green Frog creature token are created instead"; + } + + private QuinaQuGourmetEffect(final QuinaQuGourmetEffect effect) { + super(effect); + } + + @Override + public QuinaQuGourmetEffect copy() { + return new QuinaQuGourmetEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.CREATE_TOKEN; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.isControlledBy(event.getPlayerId()); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Map tokens = ((CreateTokenEvent) event).getTokens(); + Token token = CardUtil + .castStream(tokens.values(), FrogGreenToken.class) + .findAny() + .orElseGet(FrogGreenToken::new); + tokens.compute(token, CardUtil::setOrIncrementValue); + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/q/QuintoriusLoremaster.java b/Mage.Sets/src/mage/cards/q/QuintoriusLoremaster.java index d4c3db7f3f2..602d0c6b8f5 100644 --- a/Mage.Sets/src/mage/cards/q/QuintoriusLoremaster.java +++ b/Mage.Sets/src/mage/cards/q/QuintoriusLoremaster.java @@ -36,7 +36,6 @@ import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; -import java.util.Objects; import java.util.Optional; import java.util.UUID; @@ -173,7 +172,7 @@ class QuintoriusLoremasterReplacementEffect extends ReplacementEffectImpl { Card card = mor.getCard(game); return controller != null && card != null - && controller.putCardsOnBottomOfLibrary(card, game, source, false); + && controller.putCardsOnBottomOfLibrary(card, game, source); } @Override diff --git a/Mage.Sets/src/mage/cards/q/QuistisTrepe.java b/Mage.Sets/src/mage/cards/q/QuistisTrepe.java index c3614f37b37..668f1dc4968 100644 --- a/Mage.Sets/src/mage/cards/q/QuistisTrepe.java +++ b/Mage.Sets/src/mage/cards/q/QuistisTrepe.java @@ -36,7 +36,7 @@ public final class QuistisTrepe extends CardImpl { Ability ability = new EntersBattlefieldTriggeredAbility( new MayCastTargetCardEffect(CastManaAdjustment.AS_THOUGH_ANY_MANA_TYPE, true) ); - this.getSpellAbility().addTarget(new TargetCardInGraveyard(filter)); + ability.addTarget(new TargetCardInGraveyard(filter)); this.addAbility(ability.withFlavorWord("Blue Magic")); } diff --git a/Mage.Sets/src/mage/cards/q/QutrubForayer.java b/Mage.Sets/src/mage/cards/q/QutrubForayer.java new file mode 100644 index 00000000000..4ec312371c3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/q/QutrubForayer.java @@ -0,0 +1,50 @@ +package mage.cards.q; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; +import mage.target.common.TargetCardInASingleGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class QutrubForayer extends CardImpl { + + public QutrubForayer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.ZOMBIE); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // When this creature enters, choose one -- + // * Destroy target creature that was dealt damage this turn. + Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect()); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_DAMAGED_THIS_TURN)); + + // * Exile up to two target cards from a single graveyard. + ability.addMode(new Mode(new ExileTargetEffect()).addTarget(new TargetCardInASingleGraveyard(0, 2, StaticFilters.FILTER_CARD_CARDS))); + this.addAbility(ability); + } + + private QutrubForayer(final QutrubForayer card) { + super(card); + } + + @Override + public QutrubForayer copy() { + return new QutrubForayer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RabidGnaw.java b/Mage.Sets/src/mage/cards/r/RabidGnaw.java index 5d3c98857f6..253b99df499 100644 --- a/Mage.Sets/src/mage/cards/r/RabidGnaw.java +++ b/Mage.Sets/src/mage/cards/r/RabidGnaw.java @@ -22,7 +22,7 @@ public final class RabidGnaw extends CardImpl { // Target creature you control gets +1/+0 until end of turn. Then it deals damage equal to its power to target creature you don't control. this.getSpellAbility().addEffect(new BoostTargetEffect(1, 0)); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - this.getSpellAbility().addEffect(new DamageWithPowerFromOneToAnotherTargetEffect("then it")); + this.getSpellAbility().addEffect(new DamageWithPowerFromOneToAnotherTargetEffect("Then it")); this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); } diff --git a/Mage.Sets/src/mage/cards/r/RalZarek.java b/Mage.Sets/src/mage/cards/r/RalZarek.java index df7330f31e0..d1476001c12 100644 --- a/Mage.Sets/src/mage/cards/r/RalZarek.java +++ b/Mage.Sets/src/mage/cards/r/RalZarek.java @@ -1,7 +1,5 @@ - package mage.cards.r; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.effects.Effect; @@ -12,20 +10,22 @@ import mage.abilities.effects.common.UntapTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.FilterPermanent; import mage.filter.predicate.other.AnotherTargetPredicate; +import mage.game.Controllable; import mage.game.Game; import mage.game.turn.TurnMod; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetAnyTarget; import mage.target.targetpointer.SecondTargetPointer; +import java.util.Optional; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class RalZarek extends CardImpl { @@ -64,7 +64,6 @@ public final class RalZarek extends CardImpl { // -7: Flip five coins. Take an extra turn after this one for each coin that comes up heads. this.addAbility(new LoyaltyAbility(new RalZarekExtraTurnsEffect(), -7)); - } private RalZarek(final RalZarek card) { @@ -95,15 +94,19 @@ class RalZarekExtraTurnsEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - for (int i = 0; i < 5; i++) { - if (controller.flipCoin(source, game, false)) { - game.getState().getTurnMods().add(new TurnMod(source.getControllerId()).withExtraTurn()); - } - } - return true; + int amount = Optional + .ofNullable(source) + .map(Controllable::getControllerId) + .map(game::getPlayer) + .map(player -> player + .flipCoins(source, game, 5, true) + .stream() + .mapToInt(x -> x ? 1 : 0) + .sum() + ).orElse(0); + for (int i = 0; i < amount; i++) { + game.getState().getTurnMods().add(new TurnMod(source.getControllerId()).withExtraTurn()); } - return false; + return true; } } diff --git a/Mage.Sets/src/mage/cards/r/RallyTheAncestors.java b/Mage.Sets/src/mage/cards/r/RallyTheAncestors.java index 8e7492b429f..e847ebaa09b 100644 --- a/Mage.Sets/src/mage/cards/r/RallyTheAncestors.java +++ b/Mage.Sets/src/mage/cards/r/RallyTheAncestors.java @@ -81,7 +81,7 @@ class RallyTheAncestorsEffect extends OneShotEffect { List toExile = new ArrayList<>(cards.size()); for (Card card : cards) { if (card != null) { - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { toExile.add(permanent); } diff --git a/Mage.Sets/src/mage/cards/r/RampagingGrowth.java b/Mage.Sets/src/mage/cards/r/RampagingGrowth.java index 5c7ef1b571b..e6def5abe3d 100644 --- a/Mage.Sets/src/mage/cards/r/RampagingGrowth.java +++ b/Mage.Sets/src/mage/cards/r/RampagingGrowth.java @@ -16,6 +16,7 @@ import mage.game.permanent.token.custom.CreatureToken; import mage.players.Player; import mage.target.common.TargetCardInLibrary; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -72,7 +73,7 @@ class RampagingGrowthEffect extends OneShotEffect { return true; } player.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { game.addEffect(new BecomesCreatureTargetEffect( new CreatureToken(4, 3, "", SubType.INSECT diff --git a/Mage.Sets/src/mage/cards/r/RandomEncounter.java b/Mage.Sets/src/mage/cards/r/RandomEncounter.java new file mode 100644 index 00000000000..3318b08cd93 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RandomEncounter.java @@ -0,0 +1,99 @@ +package mage.cards.r; + +import mage.abilities.Ability; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.FlashbackAbility; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.targetpointer.FixedTargets; +import mage.util.CardUtil; + +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class RandomEncounter extends CardImpl { + + public RandomEncounter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{R}{R}"); + + // Shuffle your library, then mill four cards. Put each creature card milled this way onto the battlefield. They gain haste. At the beginning of the next end step, return those creatures to their owner's hand. + this.getSpellAbility().addEffect(new RandomEncounterEffect()); + + // Flashback {6}{R}{R} + this.addAbility(new FlashbackAbility(this, new ManaCostsImpl<>("{6}{R}{R}"))); + } + + private RandomEncounter(final RandomEncounter card) { + super(card); + } + + @Override + public RandomEncounter copy() { + return new RandomEncounter(this); + } +} + +class RandomEncounterEffect extends OneShotEffect { + + RandomEncounterEffect() { + super(Outcome.Benefit); + staticText = "shuffle your library, then mill four cards. Put each " + + "creature card milled this way onto the battlefield. They gain haste. " + + "At the beginning of the next end step, return those creatures to their owner's hand"; + } + + private RandomEncounterEffect(final RandomEncounterEffect effect) { + super(effect); + } + + @Override + public RandomEncounterEffect copy() { + return new RandomEncounterEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null || !player.getLibrary().hasCards()) { + return false; + } + player.shuffleLibrary(source, game); + Cards cards = new CardsImpl(player.millCards(4, source, game).getCards(StaticFilters.FILTER_CARD_CREATURE, game)); + player.moveCards(cards, Zone.BATTLEFIELD, source, game); + Set permanents = cards + .getCards(game) + .stream() + .map(card -> CardUtil.getPermanentFromCardPutToBattlefield(card, game)) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + game.addEffect(new GainAbilityTargetEffect( + HasteAbility.getInstance(), Duration.Custom + ).setTargetPointer(new FixedTargets(permanents, game)), source); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility( + new ReturnToHandTargetEffect() + .setText("return those creatures to their owner's hand") + .setTargetPointer(new FixedTargets(permanents, game)) + ), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RapidAugmenter.java b/Mage.Sets/src/mage/cards/r/RapidAugmenter.java index 5eb5962a0ec..c4df7a5e52d 100644 --- a/Mage.Sets/src/mage/cards/r/RapidAugmenter.java +++ b/Mage.Sets/src/mage/cards/r/RapidAugmenter.java @@ -3,6 +3,7 @@ package mage.cards.r; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; import mage.abilities.common.EntersBattlefieldCastTriggeredAbility; import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect; @@ -44,16 +45,16 @@ public final class RapidAugmenter extends CardImpl { this.addAbility(HasteAbility.getInstance()); // Whenever another creature you control with base power 1 enters, it gains haste until end of turn. - Ability ability = new EntersBattlefieldControlledTriggeredAbility( + Ability ability = new EntersBattlefieldAllTriggeredAbility( Zone.BATTLEFIELD, new GainAbilityTargetEffect(HasteAbility.getInstance()), filterBP1, false, SetTargetPointer.PERMANENT); this.addAbility(ability); // Whenever another creature you control enters, if it wasn't cast, put a +1/+1 counter on Rapid Augmenter // and Rapid Augmenter can't be blocked this turn. Ability ability2 = new EntersBattlefieldCastTriggeredAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect( - CounterType.P1P1.createInstance()), StaticFilters.FILTER_ANOTHER_CREATURE_YOU_CONTROL, + CounterType.P1P1.createInstance()), StaticFilters.FILTER_ANOTHER_CREATURE, false, SetTargetPointer.PERMANENT, false); - ability2.addEffect(new CantBeBlockedSourceEffect(Duration.EndOfTurn).concatBy(" and ")); + ability2.addEffect(new CantBeBlockedSourceEffect(Duration.EndOfTurn).concatBy("and")); this.addAbility(ability2); } @@ -65,4 +66,4 @@ public final class RapidAugmenter extends CardImpl { public RapidAugmenter copy() { return new RapidAugmenter(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/r/RashmiEternitiesCrafter.java b/Mage.Sets/src/mage/cards/r/RashmiEternitiesCrafter.java index 46e7d191989..7fffd79eca4 100644 --- a/Mage.Sets/src/mage/cards/r/RashmiEternitiesCrafter.java +++ b/Mage.Sets/src/mage/cards/r/RashmiEternitiesCrafter.java @@ -1,29 +1,26 @@ package mage.cards.r; -import java.util.UUID; -import mage.ApprovingObject; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SpellCastControllerTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.cards.CardsImpl; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.constants.Zone; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; import mage.players.Player; +import mage.util.CardUtil; import mage.watchers.common.SpellsCastWatcher; +import java.util.UUID; + /** - * * @author emerald000 */ public final class RashmiEternitiesCrafter extends CardImpl { @@ -56,6 +53,7 @@ class RashmiEternitiesCrafterTriggeredAbility extends SpellCastControllerTrigger RashmiEternitiesCrafterTriggeredAbility() { super(new RashmiEternitiesCrafterEffect(), false); + setTriggerPhrase("Whenever you cast your first spell each turn, "); } private RashmiEternitiesCrafterTriggeredAbility(final RashmiEternitiesCrafterTriggeredAbility ability) { @@ -69,27 +67,11 @@ class RashmiEternitiesCrafterTriggeredAbility extends SpellCastControllerTrigger @Override public boolean checkTrigger(GameEvent event, Game game) { - if (super.checkTrigger(event, game)) { - SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class); - if (watcher != null && watcher.getCount(event.getPlayerId()) == 1) { - Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null) { - for (Effect effect : getEffects()) { - effect.setValue("RashmiEternitiesCrafterCMC", spell.getManaValue()); - } - return true; - } - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever you cast your first spell each turn, reveal the top card " - + "of your library. If it's a nonland card with mana value " - + "less than that spell's, you may cast it without paying " - + "its mana cost. If you don't cast the revealed card, put it into your hand."; + return super.checkTrigger(event, game) + && game + .getState() + .getWatcher(SpellsCastWatcher.class) + .getCount(event.getPlayerId()) == 1; } } @@ -97,10 +79,8 @@ class RashmiEternitiesCrafterEffect extends OneShotEffect { RashmiEternitiesCrafterEffect() { super(Outcome.PlayForFree); - this.staticText = "reveal the top card of your library. If it's a nonland" - + " card with mana value less than that spell's, you may " - + "cast it without paying its mana cost. If you don't cast the " - + "revealed card, put it into your hand"; + this.staticText = "reveal the top card of your library. You may cast it without paying its mana cost " + + "if it's a spell with lesser mana value. If you don't cast it, put it into your hand."; } private RashmiEternitiesCrafterEffect(final RashmiEternitiesCrafterEffect effect) { @@ -114,32 +94,22 @@ class RashmiEternitiesCrafterEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - boolean cardWasCast = false; - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Card card = controller.getLibrary().getFromTop(game); - if (card != null) { - controller.revealCards("Rashmi, Eternities Crafter", new CardsImpl(card), game); - if (card.isLand(game)) { - controller.moveCards(card, Zone.HAND, source, game); - return true; - } - Object cmcObject = this.getValue("RashmiEternitiesCrafterCMC"); - if (cmcObject != null - && card.getManaValue() < (int) cmcObject - && controller.chooseUse(Outcome.PlayForFree, "Cast " + card.getName() - + " without paying its mana cost?", source, game)) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - cardWasCast = controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - } - if (!cardWasCast) { - controller.moveCards(card, Zone.HAND, source, game); - } - return true; - } + Player player = game.getPlayer(source.getControllerId()); + Spell spell = (Spell) getValue("spellCast"); + if (player == null || spell == null) { + return false; } - return false; + Card card = player.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + player.revealCards("Rashmi, Eternities Crafter", new CardsImpl(card), game); + FilterCard filter = new FilterCard(); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, spell.getManaValue())); + CardUtil.castSpellWithAttributesForFree(player, source, game, card, filter); + if (Zone.LIBRARY.match(game.getState().getZone(card.getId()))) { + player.moveCards(card, Zone.HAND, source, game); + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/r/RatchetBomb.java b/Mage.Sets/src/mage/cards/r/RatchetBomb.java index 12693d8b64b..50cace9fc6d 100644 --- a/Mage.Sets/src/mage/cards/r/RatchetBomb.java +++ b/Mage.Sets/src/mage/cards/r/RatchetBomb.java @@ -1,21 +1,20 @@ package mage.cards.r; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyAllEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; import mage.counters.CounterType; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterNonlandPermanent; +import mage.filter.predicate.mageobject.ManaValueEqualToCountersSourceCountPredicate; + +import java.util.UUID; /** * @@ -23,6 +22,13 @@ import mage.game.permanent.Permanent; */ public final class RatchetBomb extends CardImpl { + private static final FilterPermanent filter = new FilterNonlandPermanent( + "each nonland permanent with mana value equal to the number of charge counters on {this}" + ); + static { + filter.add(new ManaValueEqualToCountersSourceCountPredicate(CounterType.CHARGE)); + } + public RatchetBomb (UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); @@ -30,7 +36,7 @@ public final class RatchetBomb extends CardImpl { this.addAbility(new SimpleActivatedAbility(new AddCountersSourceEffect(CounterType.CHARGE.createInstance()), new TapSourceCost())); // {T}, Sacrifice Ratchet Bomb: Destroy each nonland permanent with a converted mana cost equal to the number of charge counters on Ratchet Bomb. - Ability ability = new SimpleActivatedAbility(new RatchetBombEffect(), new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(new DestroyAllEffect(filter), new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); } @@ -45,39 +51,3 @@ public final class RatchetBomb extends CardImpl { } } - -class RatchetBombEffect extends OneShotEffect { - - RatchetBombEffect() { - super(Outcome.DestroyPermanent); - staticText = "Destroy each nonland permanent with mana value equal to the number of charge counters on {this}"; - } - - private RatchetBombEffect(final RatchetBombEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent p = game.getPermanent(source.getSourceId()); - if (p == null) { - p = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - if (p == null) { - return false; - } - } - int count = p.getCounters(game).getCount(CounterType.CHARGE); - for (Permanent perm : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_NON_LAND, source.getControllerId(), source, game)) { - if (perm.getManaValue() == count) { - perm.destroy(source, game, false); - } - } - return true; - } - - @Override - public RatchetBombEffect copy() { - return new RatchetBombEffect(this); - } - -} diff --git a/Mage.Sets/src/mage/cards/r/Ratonhnhaketon.java b/Mage.Sets/src/mage/cards/r/Ratonhnhaketon.java new file mode 100644 index 00000000000..da46c679dc9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/Ratonhnhaketon.java @@ -0,0 +1,152 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.condition.common.SourceHasntDealtDamageThisGameCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.decorator.ConditionalRestrictionEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.combat.CantBeBlockedSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.HexproofAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.AssassinMenaceToken; +import mage.game.permanent.token.Token; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; +import mage.util.CardUtil; +import mage.watchers.common.DealtDamageThisGameWatcher; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Ratonhnhaketon extends CardImpl { + + public Ratonhnhaketon(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{U}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ASSASSIN); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // As long as Ratonhnhaketon hasn't dealt damage yet, it has hexproof and can't be blocked. + Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(HexproofAbility.getInstance()), + SourceHasntDealtDamageThisGameCondition.instance, + "as long as {this} hasn't dealt damage yet, it has hexproof" + )); + ability.addEffect(new ConditionalRestrictionEffect( + new CantBeBlockedSourceEffect(), + SourceHasntDealtDamageThisGameCondition.instance, + "and can't be blocked" + )); + this.addAbility(ability.addHint(SourceHasntDealtDamageThisGameCondition.getHint()), new DealtDamageThisGameWatcher()); + + // Whenever Ratonhnhaketon deals combat damage to a player, create a 1/1 black Assassin creature token with menace. When you do, return target Equipment card from your graveyard to the battlefield, then attach it to that token. + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new RatonhnhaketonTokenEffect())); + } + + private Ratonhnhaketon(final Ratonhnhaketon card) { + super(card); + } + + @Override + public Ratonhnhaketon copy() { + return new Ratonhnhaketon(this); + } +} + +class RatonhnhaketonTokenEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterCard("Equipment card from your graveyard"); + + static { + filter.add(SubType.EQUIPMENT.getPredicate()); + } + + RatonhnhaketonTokenEffect() { + super(Outcome.Benefit); + staticText = "create a 1/1 black Assassin creature token with menace. When you do, return " + + "target Equipment card from your graveyard to the battlefield, then attach it to that token"; + } + + private RatonhnhaketonTokenEffect(final RatonhnhaketonTokenEffect effect) { + super(effect); + } + + @Override + public RatonhnhaketonTokenEffect copy() { + return new RatonhnhaketonTokenEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Token token = new AssassinMenaceToken(); + token.putOntoBattlefield(1, game, source); + for (UUID tokenId : token.getLastAddedTokenIds()) { + Permanent permanent = game.getPermanent(tokenId); + if (permanent == null) { + continue; + } + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new RatonhnhaketonReturnEffect(tokenId), false + ); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + game.fireReflexiveTriggeredAbility(ability, source); + } + return true; + } +} + +class RatonhnhaketonReturnEffect extends OneShotEffect { + + private final UUID tokenId; + + RatonhnhaketonReturnEffect(UUID tokenId) { + super(Outcome.Benefit); + staticText = "return target Equipment card from your graveyard to the battlefield, then attach it to that token"; + this.tokenId = tokenId; + } + + private RatonhnhaketonReturnEffect(final RatonhnhaketonReturnEffect effect) { + super(effect); + this.tokenId = effect.tokenId; + } + + @Override + public RatonhnhaketonReturnEffect copy() { + return new RatonhnhaketonReturnEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(getTargetPointer().getFirst(game, source)); + if (player == null) { + return false; + } + player.moveCards(card, Zone.BATTLEFIELD, source, game); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); + if (permanent == null) { + return false; + } + Optional.ofNullable(tokenId) + .map(game::getPermanent) + .ifPresent(p -> p.addAttachment(permanent.getId(), source, game)); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RaubahnBullOfAlaMhigo.java b/Mage.Sets/src/mage/cards/r/RaubahnBullOfAlaMhigo.java new file mode 100644 index 00000000000..b4710b45bb7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RaubahnBullOfAlaMhigo.java @@ -0,0 +1,56 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.dynamicvalue.common.SourcePermanentPowerValue; +import mage.abilities.effects.common.AttachTargetToTargetEffect; +import mage.abilities.keyword.WardAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; +import mage.target.common.TargetAttackingCreature; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RaubahnBullOfAlaMhigo extends CardImpl { + + + public RaubahnBullOfAlaMhigo(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Ward--Pay life equal to Raubahn's power. + this.addAbility(new WardAbility(new PayLifeCost( + SourcePermanentPowerValue.NOT_NEGATIVE, "life equal to {this}'s power" + ))); + + // Whenever Raubahn attacks, attach up to one target Equipment you control to target attacking creature. + Ability ability = new AttacksTriggeredAbility(new AttachTargetToTargetEffect()); + ability.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT)); + ability.addTarget(new TargetAttackingCreature()); + this.addAbility(ability); + } + + private RaubahnBullOfAlaMhigo(final RaubahnBullOfAlaMhigo card) { + super(card); + } + + @Override + public RaubahnBullOfAlaMhigo copy() { + return new RaubahnBullOfAlaMhigo(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RaulTroubleShooter.java b/Mage.Sets/src/mage/cards/r/RaulTroubleShooter.java index 6a7723eee0d..301cbec3813 100644 --- a/Mage.Sets/src/mage/cards/r/RaulTroubleShooter.java +++ b/Mage.Sets/src/mage/cards/r/RaulTroubleShooter.java @@ -1,7 +1,7 @@ package mage.cards.r; import mage.MageInt; -import mage.abilities.common.CastFromGraveyardOnceEachTurnAbility; +import mage.abilities.common.CastFromGraveyardOnceDuringEachOfYourTurnAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.common.MillCardsEachPlayerEffect; @@ -39,7 +39,7 @@ public final class RaulTroubleShooter extends CardImpl { this.toughness = new MageInt(4); // Once during each of your turns, you may cast a spell from among cards in your graveyard that were milled this turn. - this.addAbility(new CastFromGraveyardOnceEachTurnAbility(filter), new CardsMilledWatcher()); + this.addAbility(new CastFromGraveyardOnceDuringEachOfYourTurnAbility(filter), new CardsMilledWatcher()); // {T}: Each player mills a card. this.addAbility(new SimpleActivatedAbility( diff --git a/Mage.Sets/src/mage/cards/r/RazorGolem.java b/Mage.Sets/src/mage/cards/r/RazorGolem.java index 529ff524b6f..b8a0f9cce0a 100644 --- a/Mage.Sets/src/mage/cards/r/RazorGolem.java +++ b/Mage.Sets/src/mage/cards/r/RazorGolem.java @@ -1,30 +1,29 @@ - package mage.cards.r; -import java.util.UUID; import mage.MageInt; -import mage.abilities.keyword.AffinityForLandTypeAbility; +import mage.abilities.keyword.AffinityAbility; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AffinityType; import mage.constants.CardType; import mage.constants.SubType; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class RazorGolem extends CardImpl { public RazorGolem(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{6}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{6}"); this.subtype.add(SubType.GOLEM); - this.power = new MageInt(3); this.toughness = new MageInt(4); // Affinity for Plains - this.addAbility(new AffinityForLandTypeAbility(SubType.PLAINS, "Plains")); + this.addAbility(new AffinityAbility(AffinityType.PLAINS)); // Vigilance this.addAbility(VigilanceAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/r/ReachTheHorizon.java b/Mage.Sets/src/mage/cards/r/ReachTheHorizon.java new file mode 100644 index 00000000000..1eb9f434a06 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ReachTheHorizon.java @@ -0,0 +1,49 @@ +package mage.cards.r; + +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.target.common.TargetCardWithDifferentNameInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ReachTheHorizon extends CardImpl { + + private static final FilterCard filter = new FilterCard("basic land cards and/or Town cards with different names"); + + static { + filter.add(Predicates.or( + Predicates.and( + SuperType.BASIC.getPredicate(), + CardType.LAND.getPredicate() + ), + SubType.TOWN.getPredicate() + )); + } + + public ReachTheHorizon(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{G}"); + + // Search your library for up to two basic land cards and/or Town cards with different names, put them onto the battlefield tapped, then shuffle. + this.getSpellAbility().addEffect(new SearchLibraryPutInPlayEffect( + new TargetCardWithDifferentNameInLibrary(0, 2, filter), true + )); + } + + private ReachTheHorizon(final ReachTheHorizon card) { + super(card); + } + + @Override + public ReachTheHorizon copy() { + return new ReachTheHorizon(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RealmbreakerTheInvasionTree.java b/Mage.Sets/src/mage/cards/r/RealmbreakerTheInvasionTree.java index fa73e1def6f..662d159dc30 100644 --- a/Mage.Sets/src/mage/cards/r/RealmbreakerTheInvasionTree.java +++ b/Mage.Sets/src/mage/cards/r/RealmbreakerTheInvasionTree.java @@ -25,6 +25,7 @@ import mage.target.common.TargetCardInGraveyard; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetOpponent; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -103,7 +104,7 @@ class RealmbreakerTheInvasionTreeEffect extends OneShotEffect { return false; } controller.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/r/ReapersScythe.java b/Mage.Sets/src/mage/cards/r/ReapersScythe.java new file mode 100644 index 00000000000..bff531ce512 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ReapersScythe.java @@ -0,0 +1,132 @@ +package mage.cards.r; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CountersSourceCount; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.continuous.AddCardSubtypeAttachedEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.EquipAbility; +import mage.abilities.keyword.JobSelectAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.WatcherScope; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.watchers.Watcher; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ReapersScythe extends CardImpl { + + private static final DynamicValue xValue = new CountersSourceCount(CounterType.SOUL); + + public ReapersScythe(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{B}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Job select + this.addAbility(new JobSelectAbility()); + + // At the beginning of your end step, put a soul counter on this Equipment for each player who lost life this turn. + this.addAbility(new BeginningOfEndStepTriggeredAbility(new AddCountersSourceEffect( + CounterType.SOUL.createInstance(), ReapersScytheValue.instance + )).addHint(ReapersScytheValue.getHint()), new ReapersScytheWatcher()); + + // Equipped creature gets +1/+1 for each soul counter on this Equipment and is an Assassin in addition to its other types. + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(xValue, xValue)); + ability.addEffect(new AddCardSubtypeAttachedEffect( + SubType.ASSASSIN, AttachmentType.EQUIPMENT + ).setText("and is an Assassin in addition to its other types")); + this.addAbility(ability); + + // Death Sickle -- Equip {2} + this.addAbility(new EquipAbility(2).withFlavorWord("Death Sickle")); + } + + private ReapersScythe(final ReapersScythe card) { + super(card); + } + + @Override + public ReapersScythe copy() { + return new ReapersScythe(this); + } +} + +enum ReapersScytheValue implements DynamicValue { + instance; + private static final Hint hint = new ValueHint("Players who lost life this turn", instance); + + public static Hint getHint() { + return hint; + } + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return ReapersScytheWatcher.getCount(game, sourceAbility); + } + + @Override + public ReapersScytheValue copy() { + return this; + } + + @Override + public String getMessage() { + return "player who lost life this turn"; + } + + @Override + public String toString() { + return "1"; + } +} + +class ReapersScytheWatcher extends Watcher { + + private final Set set = new HashSet<>(); + + ReapersScytheWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.LOST_LIFE) { + set.add(event.getTargetId()); + } + } + + @Override + public void reset() { + super.reset(); + set.clear(); + } + + static int getCount(Game game, Ability source) { + return game + .getState() + .getWatcher(ReapersScytheWatcher.class) + .set + .stream() + .filter(game.getPlayer(source.getControllerId())::hasPlayerInRange) + .mapToInt(x -> 1) + .sum(); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RebelSalvo.java b/Mage.Sets/src/mage/cards/r/RebelSalvo.java index b06205cdef2..700e985dcbf 100644 --- a/Mage.Sets/src/mage/cards/r/RebelSalvo.java +++ b/Mage.Sets/src/mage/cards/r/RebelSalvo.java @@ -1,20 +1,14 @@ package mage.cards.r; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.common.AffinityEffect; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.continuous.LoseAbilityTargetEffect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.AffinityAbility; import mage.abilities.keyword.IndestructibleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AffinityType; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterControlledPermanent; import mage.target.common.TargetCreatureOrPlaneswalker; import java.util.UUID; @@ -24,18 +18,11 @@ import java.util.UUID; */ public final class RebelSalvo extends CardImpl { - private static final FilterControlledPermanent filter - = new FilterControlledPermanent(SubType.EQUIPMENT, "Equipment"); - - private static final Hint hint = new ValueHint( - "Equipment you control", new PermanentsOnBattlefieldCount(filter) - ); - public RebelSalvo(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); // Affinity for Equipment - this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filter)).addHint(hint)); + this.addAbility(new AffinityAbility(AffinityType.EQUIPMENT)); // Rebel Salvo deals 5 damage to target creature or planeswalker. That permanent loses indestructible until end of turn. this.getSpellAbility().addEffect(new DamageTargetEffect(5)); diff --git a/Mage.Sets/src/mage/cards/r/RebornHero.java b/Mage.Sets/src/mage/cards/r/RebornHero.java index 7909cda325c..c62ac0b030e 100644 --- a/Mage.Sets/src/mage/cards/r/RebornHero.java +++ b/Mage.Sets/src/mage/cards/r/RebornHero.java @@ -40,7 +40,7 @@ public final class RebornHero extends CardImpl { new ReturnSourceFromGraveyardToBattlefieldEffect(), new ManaCostsImpl<>("{W}{W}") ))), ThresholdCondition.instance, "As long as seven or more cards are in " + "your graveyard, {this} has \"When {this} dies, you may pay {W}{W}. If you do, " + - "return {this} to the battlefield under your control.\"" + "return this card to the battlefield under your control.\"" )).setAbilityWord(AbilityWord.THRESHOLD)); } diff --git a/Mage.Sets/src/mage/cards/r/RebukingCeremony.java b/Mage.Sets/src/mage/cards/r/RebukingCeremony.java index b6caf0e1b9a..29fa4db4c95 100644 --- a/Mage.Sets/src/mage/cards/r/RebukingCeremony.java +++ b/Mage.Sets/src/mage/cards/r/RebukingCeremony.java @@ -18,7 +18,7 @@ public final class RebukingCeremony extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{G}{G}"); // Put two target artifacts on top of their owners' libraries. - getSpellAbility().addEffect(new PutOnLibraryTargetEffect(true)); + getSpellAbility().addEffect(new PutOnLibraryTargetEffect(true, "put two target artifacts on top of their owners' libraries")); getSpellAbility().addTarget(new TargetArtifactPermanent(2)); } diff --git a/Mage.Sets/src/mage/cards/r/ReckonerBankbuster.java b/Mage.Sets/src/mage/cards/r/ReckonerBankbuster.java index 61b07acf81c..c84fa9dc2ec 100644 --- a/Mage.Sets/src/mage/cards/r/ReckonerBankbuster.java +++ b/Mage.Sets/src/mage/cards/r/ReckonerBankbuster.java @@ -17,6 +17,7 @@ import mage.abilities.keyword.CrewAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.SubType; import mage.counters.CounterType; import mage.game.permanent.token.PilotCrewToken; @@ -29,7 +30,7 @@ import java.util.UUID; */ public final class ReckonerBankbuster extends CardImpl { - private static final Condition condition = new SourceHasCounterCondition(CounterType.CHARGE, 0, 0); + private static final Condition condition = new SourceHasCounterCondition(CounterType.CHARGE, ComparisonType.EQUAL_TO, 0); public ReckonerBankbuster(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); @@ -50,7 +51,7 @@ public final class ReckonerBankbuster extends CardImpl { ability.addEffect(new ConditionalOneShotEffect( new CreateTokenEffect(new TreasureToken()).withAdditionalTokens(new PilotCrewToken()), condition, "Then if there are no charge counters on {this}, create a Treasure token and a 1/1 colorless " + - "Pilot creature token with \"This creature crews Vehicles as though its power were 2 greater.\"" + "Pilot creature token with \"This token crews Vehicles as though its power were 2 greater.\"" )); ability.addCost(new TapSourceCost()); ability.addCost(new RemoveCountersSourceCost(CounterType.CHARGE.createInstance())); diff --git a/Mage.Sets/src/mage/cards/r/RedMagesRapier.java b/Mage.Sets/src/mage/cards/r/RedMagesRapier.java new file mode 100644 index 00000000000..3d0f9e818ee --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RedMagesRapier.java @@ -0,0 +1,59 @@ +package mage.cards.r; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.continuous.AddCardSubtypeAttachedEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.abilities.keyword.JobSelectAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RedMagesRapier extends CardImpl { + + public RedMagesRapier(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{R}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Job select + this.addAbility(new JobSelectAbility()); + + // Equipped creature has "Whenever you cast a noncreature spell, this creature gets +2/+0 until end of turn" and is a Wizard in addition to its other types. + Ability ability = new SimpleStaticAbility(new GainAbilityAttachedEffect( + new SpellCastControllerTriggeredAbility( + new BoostSourceEffect(2, 0, Duration.EndOfTurn), + StaticFilters.FILTER_SPELL_A_NON_CREATURE, false + ), AttachmentType.EQUIPMENT + ).setText("equipped creature has \"Whenever you cast a noncreature spell, " + + "this creature gets +2/+0 until end of turn\"")); + ability.addEffect(new AddCardSubtypeAttachedEffect( + SubType.WIZARD, AttachmentType.EQUIPMENT + ).setText("and is a Wizard in addition to its other types")); + this.addAbility(ability); + + // Equip {3} + this.addAbility(new EquipAbility(3)); + } + + private RedMagesRapier(final RedMagesRapier card) { + super(card); + } + + @Override + public RedMagesRapier copy() { + return new RedMagesRapier(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RegenerationsRestored.java b/Mage.Sets/src/mage/cards/r/RegenerationsRestored.java index b69745f0c21..74151b0a14a 100644 --- a/Mage.Sets/src/mage/cards/r/RegenerationsRestored.java +++ b/Mage.Sets/src/mage/cards/r/RegenerationsRestored.java @@ -3,6 +3,7 @@ package mage.cards.r; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.OneShotEffect; @@ -14,6 +15,7 @@ import mage.abilities.keyword.VanishingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.Outcome; import mage.constants.Zone; import mage.counters.CounterType; @@ -49,15 +51,15 @@ public final class RegenerationsRestored extends CardImpl { class RegenerationsRestoredTriggeredAbility extends TriggeredAbilityImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.TIME, ComparisonType.EQUAL_TO, 0); + RegenerationsRestoredTriggeredAbility() { super(Zone.BATTLEFIELD, new ScryEffect(1, false)); this.addEffect(new GainLifeEffect(1).concatBy("and")); - this.addEffect(new ConditionalOneShotEffect( - new RegenerationsRestoredEffect(), - new SourceHasCounterCondition(CounterType.TIME, 0, 0) - ).concatBy("Then")); - + new RegenerationsRestoredEffect(), condition, "Then if {this} has no " + + "time counters on it, exile it. When you do, take an extra turn after this one" + )); this.setTriggerPhrase("Whenever one or more time counters are removed from {this}, "); } @@ -112,4 +114,4 @@ class RegenerationsRestoredEffect extends OneShotEffect { return false; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/r/RejoinTheFight.java b/Mage.Sets/src/mage/cards/r/RejoinTheFight.java new file mode 100644 index 00000000000..6edd8604bb2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RejoinTheFight.java @@ -0,0 +1,91 @@ +package mage.cards.r; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.MillCardsControllerEffect; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RejoinTheFight extends CardImpl { + + public RejoinTheFight(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{B}"); + + // Mill three cards. Then starting with the next opponent in turn order, each opponent chooses a creature card in your graveyard that hasn't been chosen. Return each card chosen this way to the battlefield under your control. + this.getSpellAbility().addEffect(new MillCardsControllerEffect(3)); + this.getSpellAbility().addEffect(new RejoinTheFightEffect()); + } + + private RejoinTheFight(final RejoinTheFight card) { + super(card); + } + + @Override + public RejoinTheFight copy() { + return new RejoinTheFight(this); + } +} + +class RejoinTheFightEffect extends OneShotEffect { + + RejoinTheFightEffect() { + super(Outcome.Benefit); + staticText = "Then starting with the next opponent in turn order, each opponent chooses " + + "a creature card in your graveyard that hasn't been chosen. " + + "Return each card chosen this way to the battlefield under your control"; + } + + private RejoinTheFightEffect(final RejoinTheFightEffect effect) { + super(effect); + } + + @Override + public RejoinTheFightEffect copy() { + return new RejoinTheFightEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Cards cards = new CardsImpl(controller.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURE, game)); + if (cards.isEmpty()) { + return false; + } + Cards toPlay = new CardsImpl(); + for (UUID opponentId : game.getOpponents(source.getControllerId())) { + if (cards.isEmpty()) { + break; + } + Player opponent = game.getPlayer(opponentId); + if (opponent == null) { + continue; + } + TargetCard target = new TargetCardInGraveyard(StaticFilters.FILTER_CARD_CREATURE); + target.withNotTarget(true); + opponent.choose(outcome, cards, target, source, game); + Card card = game.getCard(target.getFirstTarget()); + if (card == null) { + continue; + } + game.informPlayers(opponent.getLogName() + " chooses " + card.getLogName()); + toPlay.add(card); + cards.remove(card); + } + return controller.moveCards(toPlay, Zone.BATTLEFIELD, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RelentlessXATM092.java b/Mage.Sets/src/mage/cards/r/RelentlessXATM092.java new file mode 100644 index 00000000000..e9102b07657 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RelentlessXATM092.java @@ -0,0 +1,51 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldWithCounterEffect; +import mage.abilities.effects.common.combat.CantBeBlockedByOneEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RelentlessXATM092 extends CardImpl { + + public RelentlessXATM092(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{6}"); + + this.subtype.add(SubType.ROBOT); + this.subtype.add(SubType.SPIDER); + this.power = new MageInt(6); + this.toughness = new MageInt(5); + + // This creature can't be blocked except by three or more creatures. + this.addAbility(new SimpleStaticAbility(new CantBeBlockedByOneEffect(3))); + + // {8}: Return this card from your graveyard to the battlefield tapped with a finality counter on it. + this.addAbility(new SimpleActivatedAbility( + Zone.GRAVEYARD, + new ReturnSourceFromGraveyardToBattlefieldWithCounterEffect( + CounterType.FINALITY.createInstance(), true + ), new GenericManaCost(8) + )); + } + + private RelentlessXATM092(final RelentlessXATM092 card) { + super(card); + } + + @Override + public RelentlessXATM092 copy() { + return new RelentlessXATM092(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RelicBane.java b/Mage.Sets/src/mage/cards/r/RelicBane.java index 4fe114741f3..16d801be4c6 100644 --- a/Mage.Sets/src/mage/cards/r/RelicBane.java +++ b/Mage.Sets/src/mage/cards/r/RelicBane.java @@ -1,23 +1,19 @@ - package mage.cards.r; -import java.util.UUID; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.LoseLifeSourceControllerEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EnchantAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AttachmentType; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.target.TargetPermanent; import mage.target.common.TargetArtifactPermanent; +import java.util.UUID; + /** * * @author LoneFox @@ -33,10 +29,11 @@ public final class RelicBane extends CardImpl { this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.Detriment)); this.addAbility(new EnchantAbility(auraTarget)); + // Enchanted artifact has "At the beginning of your upkeep, you lose 2 life." this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( - new BeginningOfUpkeepTriggeredAbility(new LoseLifeSourceControllerEffect(2) - ), AttachmentType.AURA))); + new BeginningOfUpkeepTriggeredAbility(new LoseLifeSourceControllerEffect(2)), + AttachmentType.AURA, Duration.WhileOnBattlefield, null, "artifact"))); } private RelicBane(final RelicBane card) { diff --git a/Mage.Sets/src/mage/cards/r/ReluctantRoleModel.java b/Mage.Sets/src/mage/cards/r/ReluctantRoleModel.java new file mode 100644 index 00000000000..91e27171d9d --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ReluctantRoleModel.java @@ -0,0 +1,95 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.abilityword.SurvivalAbility; +import mage.abilities.common.DiesThisOrAnotherTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCounterChoiceSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.counters.Counter; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.CounterAnyPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ReluctantRoleModel extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); + + static { + filter.add(CounterAnyPredicate.instance); + } + + public ReluctantRoleModel(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SURVIVOR); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Survival -- At the beginning of your second main phase, if this creature is tapped, put a flying, lifelink, or +1/+1 counter on it. + this.addAbility(new SurvivalAbility(new AddCounterChoiceSourceEffect( + CounterType.FLYING, CounterType.LIFELINK, CounterType.P1P1 + ).setText("put a flying, lifelink, or +1/+1 counter on it"))); + + // Whenever this creature or another creature you control dies, if it had counters on it, put those counters on up to one target creature. + Ability ability = new DiesThisOrAnotherTriggeredAbility( + new ReluctantRoleModelEffect(), false, filter + ).setApplyFilterOnSource(true); + ability.addTarget(new TargetCreaturePermanent(0, 1)); + this.addAbility(ability); + } + + private ReluctantRoleModel(final ReluctantRoleModel card) { + super(card); + } + + @Override + public ReluctantRoleModel copy() { + return new ReluctantRoleModel(this); + } +} + +class ReluctantRoleModelEffect extends OneShotEffect { + + ReluctantRoleModelEffect() { + super(Outcome.Benefit); + staticText = "if it had counters on it, put those counters on up to one target creature"; + } + + private ReluctantRoleModelEffect(final ReluctantRoleModelEffect effect) { + super(effect); + } + + @Override + public ReluctantRoleModelEffect copy() { + return new ReluctantRoleModelEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = (Permanent) getValue("creatureDied"); + Permanent creature = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (permanent == null || creature == null) { + return false; + } + for (Counter counter : permanent.getCounters(game).values()) { + creature.addCounters(counter.copy(), source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RemoteFarm.java b/Mage.Sets/src/mage/cards/r/RemoteFarm.java index 83584b66def..035c8f0da5c 100644 --- a/Mage.Sets/src/mage/cards/r/RemoteFarm.java +++ b/Mage.Sets/src/mage/cards/r/RemoteFarm.java @@ -1,11 +1,9 @@ - package mage.cards.r; -import java.util.UUID; import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; -import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.costs.common.TapSourceCost; @@ -17,17 +15,21 @@ import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.Zone; import mage.counters.CounterType; +import java.util.UUID; + /** - * * @author Plopman */ public final class RemoteFarm extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.DEPLETION, ComparisonType.EQUAL_TO, 0); + public RemoteFarm(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // Remote Farm enters the battlefield tapped with two depletion counters on it. Ability etbAbility = new EntersBattlefieldAbility( @@ -35,11 +37,15 @@ public final class RemoteFarm extends CardImpl { ); etbAbility.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance(2))); this.addAbility(etbAbility); + // {tap}, Remove a depletion counter from Remote Farm: Add {W}{W}. If there are no depletion counters on Remote Farm, sacrifice it. Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, Mana.WhiteMana(2), new TapSourceCost()); ability.addCost(new RemoveCountersSourceCost(CounterType.DEPLETION.createInstance(1))); - ability.addEffect(new ConditionalOneShotEffect(new SacrificeSourceEffect(), new SourceHasCounterCondition(CounterType.DEPLETION, 0,0), "If there are no depletion counters on {this}, sacrifice it")); - this.addAbility(ability); + ability.addEffect(new ConditionalOneShotEffect( + new SacrificeSourceEffect(), condition, + "If there are no depletion counters on {this}, sacrifice it" + )); + this.addAbility(ability); } private RemoteFarm(final RemoteFarm card) { diff --git a/Mage.Sets/src/mage/cards/r/RenoAndRude.java b/Mage.Sets/src/mage/cards/r/RenoAndRude.java index 1332616b45e..b1fae8b0778 100644 --- a/Mage.Sets/src/mage/cards/r/RenoAndRude.java +++ b/Mage.Sets/src/mage/cards/r/RenoAndRude.java @@ -56,7 +56,7 @@ class RenoAndRudeEffect extends OneShotEffect { RenoAndRudeEffect() { super(Outcome.Benefit); - staticText = ", exile the top card of that player's library. " + + staticText = "exile the top card of that player's library. " + "Then you may sacrifice another creature or artifact. If you do, " + "you may play the exiled card this turn, and mana of any type can be spent to cast it"; } diff --git a/Mage.Sets/src/mage/cards/r/RepulsiveMutation.java b/Mage.Sets/src/mage/cards/r/RepulsiveMutation.java index 44a2aeace9e..8f341f0f143 100644 --- a/Mage.Sets/src/mage/cards/r/RepulsiveMutation.java +++ b/Mage.Sets/src/mage/cards/r/RepulsiveMutation.java @@ -2,7 +2,7 @@ package mage.cards.r; import java.util.UUID; -import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.dynamicvalue.common.GetXValue; import mage.abilities.effects.common.CounterUnlessPaysEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; @@ -16,7 +16,6 @@ import mage.target.common.TargetControlledCreaturePermanent; import mage.target.targetpointer.SecondTargetPointer; /** - * * @author DominionSpy */ public final class RepulsiveMutation extends CardImpl { @@ -30,11 +29,11 @@ public final class RepulsiveMutation extends CardImpl { getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); // Then counter up to one target spell unless its controller pays mana equal to the greatest power among creatures you control. - getSpellAbility().addEffect(new CounterUnlessPaysEffect(GreatestPowerAmongControlledCreaturesValue.instance) + getSpellAbility().addEffect(new CounterUnlessPaysEffect(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES) .setTargetPointer(new SecondTargetPointer()) .setText("Then counter up to one target spell unless its controller pays mana equal to the greatest power among creatures you control.")); getSpellAbility().addTarget(new TargetSpell(0, 1, StaticFilters.FILTER_SPELL)); - getSpellAbility().addHint(GreatestPowerAmongControlledCreaturesValue.getHint()); + getSpellAbility().addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint()); } private RepulsiveMutation(final RepulsiveMutation card) { diff --git a/Mage.Sets/src/mage/cards/r/ResentfulRevelation.java b/Mage.Sets/src/mage/cards/r/ResentfulRevelation.java new file mode 100644 index 00000000000..831108d0d3b --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ResentfulRevelation.java @@ -0,0 +1,38 @@ +package mage.cards.r; + +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +import mage.abilities.keyword.FlashbackAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.PutCards; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ResentfulRevelation extends CardImpl { + + public ResentfulRevelation(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}"); + + // Look at the top three cards of your library. Put one of them into your hand and the rest into your graveyard. + this.getSpellAbility().addEffect(new LookLibraryAndPickControllerEffect( + 3, 1, PutCards.HAND, PutCards.GRAVEYARD + )); + + // Flashback {6}{B} + this.addAbility(new FlashbackAbility(this, new ManaCostsImpl<>("{6}{B}"))); + } + + private ResentfulRevelation(final ResentfulRevelation card) { + super(card); + } + + @Override + public ResentfulRevelation copy() { + return new ResentfulRevelation(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/ResonanceTechnician.java b/Mage.Sets/src/mage/cards/r/ResonanceTechnician.java new file mode 100644 index 00000000000..92d8a5e82e8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/ResonanceTechnician.java @@ -0,0 +1,74 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.common.TapVariableTargetCost; +import mage.abilities.dynamicvalue.common.GetXValue; +import mage.abilities.effects.common.CopyTargetStackObjectEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.keyword.InvestigateEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.filter.FilterSpell; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterInstantOrSorcerySpell; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.target.TargetSpell; +import mage.target.targetadjustment.ManaValueTargetAdjuster; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ResonanceTechnician extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledArtifactPermanent("untapped artifacts you control"); + private static final FilterSpell filter2 = new FilterInstantOrSorcerySpell("instant or sorcery spell you control with mana value X"); + + static { + filter.add(TappedPredicate.UNTAPPED); + } + + public ResonanceTechnician(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U/R}{U/R}"); + + this.subtype.add(SubType.WEIRD); + this.subtype.add(SubType.DETECTIVE); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Resonance Technician enters the battlefield, you may discard a card. If you do, investigate twice. + this.addAbility(new EntersBattlefieldTriggeredAbility( + new DoIfCostPaid(new InvestigateEffect(2), new DiscardCardCost()) + )); + + // {T}, Tap X untapped artifacts you control: Copy target instant or sorcery spell you control with mana value X. You may choose new targets for the copy. + Ability ability = new SimpleActivatedAbility(new CopyTargetStackObjectEffect(), new TapSourceCost()); + ability.addCost(new TapVariableTargetCost(filter)); + ability.addTarget(new TargetSpell(filter2)); + ability.setTargetAdjuster(new ManaValueTargetAdjuster(GetXValue.instance, ComparisonType.EQUAL_TO)); + this.addAbility(ability); + } + + private ResonanceTechnician(final ResonanceTechnician card) { + super(card); + } + + @Override + public ResonanceTechnician copy() { + return new ResonanceTechnician(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/ResourcefulDefense.java b/Mage.Sets/src/mage/cards/r/ResourcefulDefense.java index f6dcb3cd044..790377d630d 100644 --- a/Mage.Sets/src/mage/cards/r/ResourcefulDefense.java +++ b/Mage.Sets/src/mage/cards/r/ResourcefulDefense.java @@ -91,7 +91,7 @@ class ResourcefulDefenseMoveCounterEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); Permanent fromPermanent = game.getPermanent(source.getFirstTarget()); Permanent toPermanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if(controller == null || fromPermanent == null || toPermanent == null) { + if (controller == null || fromPermanent == null || toPermanent == null) { return false; } @@ -143,7 +143,7 @@ class ResourcefulDefenseTriggeredAbility extends LeavesBattlefieldAllTriggeredAb ResourcefulDefenseTriggeredAbility() { super(new ResourcefulDefenseLeaveEffect(), StaticFilters.FILTER_CONTROLLED_PERMANENT); - setTriggerPhrase("Whenever a creature you control leaves the battlefield, if it had counters on it, "); + setTriggerPhrase("Whenever a permanent you control leaves the battlefield, if it had counters on it, "); } private ResourcefulDefenseTriggeredAbility(final ResourcefulDefenseTriggeredAbility ability) { diff --git a/Mage.Sets/src/mage/cards/r/RestorationMagic.java b/Mage.Sets/src/mage/cards/r/RestorationMagic.java new file mode 100644 index 00000000000..a3102219a51 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RestorationMagic.java @@ -0,0 +1,70 @@ +package mage.cards.r; + +import mage.abilities.Mode; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.HexproofAbility; +import mage.abilities.keyword.IndestructibleAbility; +import mage.abilities.keyword.TieredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RestorationMagic extends CardImpl { + + public RestorationMagic(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}"); + + // Tiered + this.addAbility(new TieredAbility(this)); + + // * Cure -- {0} -- Target permanent gains hexproof and indestructible until end of turn. + this.getSpellAbility().addEffect(new GainAbilityTargetEffect(HexproofAbility.getInstance()) + .setText("target permanent gains hexproof")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect(IndestructibleAbility.getInstance()) + .setText("and indestructible until end of turn")); + this.getSpellAbility().addTarget(new TargetPermanent()); + this.getSpellAbility().withFirstModeCost(new GenericManaCost(0)); + this.getSpellAbility().withFirstModeFlavorWord("Cure"); + + // * Cura -- {1} -- Target permanent gains hexproof and indestructible until end of turn. You gain 3 life. + this.getSpellAbility().addMode(new Mode(new GainAbilityTargetEffect(HexproofAbility.getInstance()) + .setText("target permanent gains hexproof")) + .addEffect(new GainAbilityTargetEffect(IndestructibleAbility.getInstance()) + .setText("and indestructible until end of turn")) + .addEffect(new GainLifeEffect(3)) + .addTarget(new TargetPermanent()) + .withCost(new GenericManaCost(1)).withFlavorWord("Cura")); + + // * Curaga -- {3}{W} -- Permanents you control gain hexproof and indestructible until end of turn. You gain 6 life. + this.getSpellAbility().addMode(new Mode(new GainAbilityControlledEffect( + HexproofAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT + ).setText("permanents you control gain hexproof")) + .addEffect(new GainAbilityControlledEffect( + IndestructibleAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT + ).setText("and indestructible until end of turn")) + .addEffect(new GainLifeEffect(6)) + .withCost(new ManaCostsImpl<>("{3}{W}")) + .withFlavorWord("Curaga")); + } + + private RestorationMagic(final RestorationMagic card) { + super(card); + } + + @Override + public RestorationMagic copy() { + return new RestorationMagic(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/ReturnOfTheWildspeaker.java b/Mage.Sets/src/mage/cards/r/ReturnOfTheWildspeaker.java index 69e01e5b724..29f03ebce23 100644 --- a/Mage.Sets/src/mage/cards/r/ReturnOfTheWildspeaker.java +++ b/Mage.Sets/src/mage/cards/r/ReturnOfTheWildspeaker.java @@ -1,21 +1,18 @@ package mage.cards.r; -import mage.MageInt; -import mage.MageObject; -import mage.abilities.Ability; import mage.abilities.Mode; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.hint.Hint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.game.Game; import java.util.UUID; @@ -24,19 +21,23 @@ import java.util.UUID; */ public final class ReturnOfTheWildspeaker extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("non-Human creatures"); + private static final FilterPermanent filter = new FilterControlledCreaturePermanent("non-Human creatures you control"); static { filter.add(Predicates.not(SubType.HUMAN.getPredicate())); } + private static final GreatestAmongPermanentsValue xValue = new GreatestAmongPermanentsValue(GreatestAmongPermanentsValue.Quality.Power, filter); + private static final Hint hint = xValue.getHint(); + public ReturnOfTheWildspeaker(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{G}"); // Choose one — // • Draw cards equal to the greatest power among non-Human creatures you control. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(ReturnOfTheWildspeakerValue.instance) + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(xValue) .setText("draw cards equal to the greatest power among non-Human creatures you control")); + this.getSpellAbility().addHint(hint); // • Non-Human creatures you control get +3/+3 until end of turn. this.getSpellAbility().addMode(new Mode( @@ -52,31 +53,4 @@ public final class ReturnOfTheWildspeaker extends CardImpl { public ReturnOfTheWildspeaker copy() { return new ReturnOfTheWildspeaker(this); } -} - -enum ReturnOfTheWildspeakerValue implements DynamicValue { - instance; - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - return game.getBattlefield() - .getAllActivePermanents(sourceAbility.getControllerId()) - .stream() - .filter(permanent1 -> permanent1.isCreature(game)) - .filter(permanent -> !permanent.hasSubtype(SubType.HUMAN, game)) - .map(MageObject::getPower) - .mapToInt(MageInt::getValue) - .max() - .orElse(0); - } - - @Override - public DynamicValue copy() { - return instance; - } - - @Override - public String getMessage() { - return ""; - } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/r/ReturnTriumphant.java b/Mage.Sets/src/mage/cards/r/ReturnTriumphant.java index 4f534e62ffb..1ac80cf0ca9 100644 --- a/Mage.Sets/src/mage/cards/r/ReturnTriumphant.java +++ b/Mage.Sets/src/mage/cards/r/ReturnTriumphant.java @@ -15,6 +15,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -72,7 +73,7 @@ class ReturnTriumphantAbility extends OneShotEffect { return false; } player.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } @@ -82,4 +83,4 @@ class ReturnTriumphantAbility extends OneShotEffect { .apply(game, source); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/r/ReturnUponTheTide.java b/Mage.Sets/src/mage/cards/r/ReturnUponTheTide.java index bc6eb45acd0..feb5364baf1 100644 --- a/Mage.Sets/src/mage/cards/r/ReturnUponTheTide.java +++ b/Mage.Sets/src/mage/cards/r/ReturnUponTheTide.java @@ -16,6 +16,7 @@ import mage.game.permanent.Permanent; import mage.game.permanent.token.ElfWarriorToken; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; +import mage.util.CardUtil; import java.util.UUID; @@ -70,7 +71,7 @@ class ReturnUponTheTideEffect extends OneShotEffect { return false; } player.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/r/RevTitheExtractor.java b/Mage.Sets/src/mage/cards/r/RevTitheExtractor.java index 0de610319c9..d0e5830262f 100644 --- a/Mage.Sets/src/mage/cards/r/RevTitheExtractor.java +++ b/Mage.Sets/src/mage/cards/r/RevTitheExtractor.java @@ -42,7 +42,7 @@ public final class RevTitheExtractor extends CardImpl { ); ability.addEffect(new ExileFaceDownTopNLibraryYouMayPlayAsLongAsExiledTargetEffect(true, CastManaAdjustment.NONE) .setText(", then look at the top card of that player's library and exile it face down. " - + "You may play that card for as long as it remains exiled")); + + "You may cast that card for as long as it remains exiled")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/ReverseDamage.java b/Mage.Sets/src/mage/cards/r/ReverseDamage.java index 24ad5920abe..f38fcc05011 100644 --- a/Mage.Sets/src/mage/cards/r/ReverseDamage.java +++ b/Mage.Sets/src/mage/cards/r/ReverseDamage.java @@ -1,32 +1,30 @@ package mage.cards.r; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.PreventionEffectData; -import mage.abilities.effects.PreventionEffectImpl; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.players.Player; -import mage.target.TargetSource; + +import java.util.UUID; /** - * * @author Quercitron */ public final class ReverseDamage extends CardImpl { public ReverseDamage(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}{W}"); // The next time a source of your choice would deal damage to you this turn, prevent that damage. You gain life equal to the damage prevented this way. - this.getSpellAbility().addEffect(new ReverseDamageEffect()); + this.getSpellAbility().addEffect( + new PreventNextDamageFromChosenSourceEffect( + Duration.EndOfTurn, true, + PreventNextDamageFromChosenSourceEffect.ON_PREVENT_YOU_GAIN_THAT_MUCH_LIFE + ) + ); } private ReverseDamage(final ReverseDamage card) { @@ -37,55 +35,4 @@ public final class ReverseDamage extends CardImpl { public ReverseDamage copy() { return new ReverseDamage(this); } -} - -class ReverseDamageEffect extends PreventionEffectImpl { - - private final TargetSource target; - - public ReverseDamageEffect() { - super(Duration.EndOfTurn, Integer.MAX_VALUE, false, false); - this.staticText = "The next time a source of your choice would deal damage to you this turn, prevent that damage. You gain life equal to the damage prevented this way."; - this.target = new TargetSource(); - } - - private ReverseDamageEffect(final ReverseDamageEffect effect) { - super(effect); - this.target = effect.target.copy(); - } - - @Override - public ReverseDamageEffect copy() { - return new ReverseDamageEffect(this); - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - PreventionEffectData preventionData = preventDamageAction(event, source, game); - this.used = true; - this.discard(); // only one use - if (preventionData.getPreventedDamage() > 0) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - player.gainLife(preventionData.getPreventedDamage(), game, source); - } - } - return true; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (!this.used && super.applies(event, source, game)) { - if (event.getTargetId().equals(source.getControllerId()) && event.getSourceId().equals(target.getFirstTarget())) { - return true; - } - } - return false; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/r/RewardTheFaithful.java b/Mage.Sets/src/mage/cards/r/RewardTheFaithful.java index dd925af7414..d55b67aa461 100644 --- a/Mage.Sets/src/mage/cards/r/RewardTheFaithful.java +++ b/Mage.Sets/src/mage/cards/r/RewardTheFaithful.java @@ -1,16 +1,16 @@ package mage.cards.r; -import java.util.UUID; -import mage.abilities.dynamicvalue.common.HighestManaValueCount; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.GainLifeTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.target.TargetPlayer; +import java.util.UUID; + /** - * * @author nigelzor */ public final class RewardTheFaithful extends CardImpl { @@ -18,9 +18,10 @@ public final class RewardTheFaithful extends CardImpl { public RewardTheFaithful(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}"); - // Any number of target players each gain life equal to the highest converted mana cost among permanents you control. - this.getSpellAbility().addEffect(new GainLifeTargetEffect(new HighestManaValueCount()) - .setText("Any number of target players each gain life equal to the highest mana value among permanents you control.")); + // Any number of target players each gain life equal to the greatest converted mana cost among permanents you control. + this.getSpellAbility().addEffect(new GainLifeTargetEffect(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_PERMANENTS) + .setText("Any number of target players each gain life equal to the greatest mana value among permanents you control.")); + this.getSpellAbility().addHint(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_PERMANENTS.getHint()); this.getSpellAbility().addTarget(new TargetPlayer(0, Integer.MAX_VALUE, false)); } diff --git a/Mage.Sets/src/mage/cards/r/RhonassMonument.java b/Mage.Sets/src/mage/cards/r/RhonassMonument.java index 7c3db3a68d3..bb5b755a1d4 100644 --- a/Mage.Sets/src/mage/cards/r/RhonassMonument.java +++ b/Mage.Sets/src/mage/cards/r/RhonassMonument.java @@ -1,6 +1,5 @@ package mage.cards.r; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; @@ -15,30 +14,25 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SuperType; -import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; import mage.target.common.TargetControlledCreaturePermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class RhonassMonument extends CardImpl { private static final FilterCard filter = new FilterCard("Green creature spells"); - private static final FilterSpell filter2 = new FilterSpell("a creature spell"); static { filter.add(Predicates.and(new ColorPredicate(ObjectColor.GREEN), CardType.CREATURE.getPredicate())); } - static { - filter2.add(CardType.CREATURE.getPredicate()); - } - public RhonassMonument(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); @@ -49,7 +43,7 @@ public final class RhonassMonument extends CardImpl { // Whenever you cast a creature spell, target creature you control gets +2/+2 and gains trample until end of turn. Ability ability = new SpellCastControllerTriggeredAbility(new BoostTargetEffect(2, 2, Duration.EndOfTurn) - .setText("target creature you control gets +2/+2"), filter2, false); + .setText("target creature you control gets +2/+2"), StaticFilters.FILTER_SPELL_A_CREATURE, false); Effect effect = new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn); effect.setText("and gains trample until end of turn"); ability.addEffect(effect); diff --git a/Mage.Sets/src/mage/cards/r/RhoxFaithmender.java b/Mage.Sets/src/mage/cards/r/RhoxFaithmender.java index 72266f131b0..bd9acdd0781 100644 --- a/Mage.Sets/src/mage/cards/r/RhoxFaithmender.java +++ b/Mage.Sets/src/mage/cards/r/RhoxFaithmender.java @@ -1,32 +1,23 @@ - package mage.cards.r; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.replacement.GainDoubleLifeReplacementEffect; import mage.abilities.keyword.LifelinkAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author noxx and jeffwadsworth */ public final class RhoxFaithmender extends CardImpl { public RhoxFaithmender(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); this.subtype.add(SubType.RHINO); this.subtype.add(SubType.MONK); @@ -35,9 +26,9 @@ public final class RhoxFaithmender extends CardImpl { // Lifelink this.addAbility(LifelinkAbility.getInstance()); - + // If you would gain life, you gain twice that much life instead. - this.addAbility(new SimpleStaticAbility(new RhoxFaithmenderEffect())); + this.addAbility(new SimpleStaticAbility(new GainDoubleLifeReplacementEffect())); } private RhoxFaithmender(final RhoxFaithmender card) { @@ -49,36 +40,3 @@ public final class RhoxFaithmender extends CardImpl { return new RhoxFaithmender(this); } } - -class RhoxFaithmenderEffect extends ReplacementEffectImpl { - - RhoxFaithmenderEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "If you would gain life, you gain twice that much life instead"; - } - - private RhoxFaithmenderEffect(final RhoxFaithmenderEffect effect) { - super(effect); - } - - @Override - public RhoxFaithmenderEffect copy() { - return new RhoxFaithmenderEffect(this); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - event.setAmount(CardUtil.overflowMultiply(event.getAmount(), 2)); - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.GAIN_LIFE; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - return event.getPlayerId().equals(source.getControllerId()) && (source.getControllerId() != null); - } -} diff --git a/Mage.Sets/src/mage/cards/r/RhysticCircle.java b/Mage.Sets/src/mage/cards/r/RhysticCircle.java index 3be16bf322d..bfc1ed74086 100644 --- a/Mage.Sets/src/mage/cards/r/RhysticCircle.java +++ b/Mage.Sets/src/mage/cards/r/RhysticCircle.java @@ -1,31 +1,33 @@ package mage.cards.r; -import java.util.UUID; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DoUnlessAnyPlayerPaysEffect; -import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; + +import java.util.UUID; /** - * * @author djbrez */ public final class RhysticCircle extends CardImpl { public RhysticCircle(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}{W}"); // {1}: Any player may pay {1}. If no one does, the next time a source of your choice would deal damage to you this turn, prevent that damage. - this.addAbility(new SimpleActivatedAbility( - new DoUnlessAnyPlayerPaysEffect(new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn),new GenericManaCost(1)), + this.addAbility(new SimpleActivatedAbility( + new DoUnlessAnyPlayerPaysEffect( + new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, true), + new GenericManaCost(1) + ), new ManaCostsImpl<>("{1}"))); } diff --git a/Mage.Sets/src/mage/cards/r/RhythmOfTheWild.java b/Mage.Sets/src/mage/cards/r/RhythmOfTheWild.java index b1c2390c7ef..d72a6fd9845 100644 --- a/Mage.Sets/src/mage/cards/r/RhythmOfTheWild.java +++ b/Mage.Sets/src/mage/cards/r/RhythmOfTheWild.java @@ -46,7 +46,7 @@ public final class RhythmOfTheWild extends CardImpl { // Creature spells you control can't be countered. this.addAbility(new SimpleStaticAbility(new CantBeCounteredControlledEffect( - filter, null, Duration.WhileOnBattlefield + filter, Duration.WhileOnBattlefield ))); // Nontoken creatures you control have riot. diff --git a/Mage.Sets/src/mage/cards/r/RideTheShoopuf.java b/Mage.Sets/src/mage/cards/r/RideTheShoopuf.java new file mode 100644 index 00000000000..26bd99201f4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RideTheShoopuf.java @@ -0,0 +1,47 @@ +package mage.cards.r; + +import mage.abilities.Ability; +import mage.abilities.common.LandfallAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.BecomesCreatureSourceEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.game.permanent.token.custom.CreatureToken; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RideTheShoopuf extends CardImpl { + + public RideTheShoopuf(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}"); + + // Landfall - Whenever a land you control enters, put a +1/+1 counter on target creature you control. + Ability ability = new LandfallAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + + // {5}{G}{G}: This enchantment becomes a 7/7 Beast creature in addition to its other types. + this.addAbility(new SimpleActivatedAbility(new BecomesCreatureSourceEffect(new CreatureToken( + 7, 7, "7/7 Beast creature", SubType.BEAST + ), CardType.ENCHANTMENT, Duration.Custom).withKeepCreatureSubtypes(true), new ManaCostsImpl<>("{5}{G}{G}"))); + } + + private RideTheShoopuf(final RideTheShoopuf card) { + super(card); + } + + @Override + public RideTheShoopuf copy() { + return new RideTheShoopuf(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RidersOfTheMark.java b/Mage.Sets/src/mage/cards/r/RidersOfTheMark.java index deedcdd5fc3..01efb76c249 100644 --- a/Mage.Sets/src/mage/cards/r/RidersOfTheMark.java +++ b/Mage.Sets/src/mage/cards/r/RidersOfTheMark.java @@ -2,24 +2,16 @@ package mage.cards.r; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.AttackedThisTurnSourceCondition; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.AffinityEffect; import mage.abilities.effects.common.CreateTokenEffect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.AffinityAbility; import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.TrampleAbility; import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterControlledPermanent; +import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.HumanSoldierToken; @@ -32,9 +24,6 @@ import java.util.UUID; */ public final class RidersOfTheMark extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.HUMAN, "Humans"); - private static final Hint hint = new ValueHint("Humans you control", new PermanentsOnBattlefieldCount(filter)); - public RidersOfTheMark(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{R}"); @@ -43,8 +32,8 @@ public final class RidersOfTheMark extends CardImpl { this.power = new MageInt(7); this.toughness = new MageInt(4); - // This spell costs {1} less to cast for each Human you control. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filter)).setRuleAtTheTop(true).addHint(hint)); + // Affinity for Humans + this.addAbility(new AffinityAbility(AffinityType.HUMANS)); // Trample this.addAbility(TrampleAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/r/RiftstonePortal.java b/Mage.Sets/src/mage/cards/r/RiftstonePortal.java index ed44ec90d8a..6be6aae4e4a 100644 --- a/Mage.Sets/src/mage/cards/r/RiftstonePortal.java +++ b/Mage.Sets/src/mage/cards/r/RiftstonePortal.java @@ -31,7 +31,7 @@ public final class RiftstonePortal extends CardImpl { // As long as Riftstone Portal is in your graveyard, lands you control have "{T}: Add {G} or {W}." ContinuousEffect effect = new GainAbilityControlledEffect(new GreenManaAbility(), Duration.WhileOnBattlefield, new FilterControlledLandPermanent()); - effect.setText("As long as Riftstone Portal is in your graveyard, lands you control have \"{T}: Add {G} or {W}.\""); + effect.setText("As long as this card is in your graveyard, lands you control have \"{T}: Add {G} or {W}.\""); Ability ability = new SimpleStaticAbility(Zone.GRAVEYARD, effect); effect = new GainAbilityControlledEffect(new WhiteManaAbility(), Duration.WhileOnBattlefield, new FilterControlledLandPermanent()); diff --git a/Mage.Sets/src/mage/cards/r/RighteousAura.java b/Mage.Sets/src/mage/cards/r/RighteousAura.java index 68e05258df6..feb1b53a5df 100644 --- a/Mage.Sets/src/mage/cards/r/RighteousAura.java +++ b/Mage.Sets/src/mage/cards/r/RighteousAura.java @@ -1,29 +1,31 @@ package mage.cards.r; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.PayLifeCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; + +import java.util.UUID; /** - * * @author anonymous */ public final class RighteousAura extends CardImpl { public RighteousAura(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); // {W}, Pay 2 life: The next time a source of your choice would deal damage to you this turn, prevent that damage. - Ability ability = new SimpleActivatedAbility(new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn), new ManaCostsImpl<>("{W}")); + Ability ability = new SimpleActivatedAbility( + new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, true), + new ManaCostsImpl<>("{W}") + ); ability.addCost(new PayLifeCost(2)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/RikkuResourcefulGuardian.java b/Mage.Sets/src/mage/cards/r/RikkuResourcefulGuardian.java new file mode 100644 index 00000000000..360e59dfc59 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RikkuResourcefulGuardian.java @@ -0,0 +1,91 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.combat.CantBeBlockedByAllTargetEffect; +import mage.abilities.effects.common.counter.MoveCounterTargetsEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RikkuResourcefulGuardian extends CardImpl { + + public RikkuResourcefulGuardian(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ARTIFICER); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Whenever you put one or more counters on a creature, until end of turn, that creature can't be blocked by creatures your opponents control. + this.addAbility(new RikkuResourcefulGuardianTriggeredAbility()); + + // Steal -- {1}, {T}: Move a counter from target creature an opponent controls onto target creature you control. Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility(new MoveCounterTargetsEffect(), new GenericManaCost(1)); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability.withFlavorWord("Steal")); + } + + private RikkuResourcefulGuardian(final RikkuResourcefulGuardian card) { + super(card); + } + + @Override + public RikkuResourcefulGuardian copy() { + return new RikkuResourcefulGuardian(this); + } +} + +class RikkuResourcefulGuardianTriggeredAbility extends TriggeredAbilityImpl { + + RikkuResourcefulGuardianTriggeredAbility() { + super(Zone.BATTLEFIELD, new CantBeBlockedByAllTargetEffect( + StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE, Duration.EndOfTurn + ).setText("until end of turn, that creature can't be blocked by creatures your opponents control")); + this.setTriggerPhrase("Whenever you put one or more counters on a creature, "); + } + + private RikkuResourcefulGuardianTriggeredAbility(final RikkuResourcefulGuardianTriggeredAbility ability) { + super(ability); + } + + @Override + public RikkuResourcefulGuardianTriggeredAbility copy() { + return new RikkuResourcefulGuardianTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.COUNTERS_ADDED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return isControlledBy(event.getPlayerId()) + && Optional + .ofNullable(event) + .map(GameEvent::getTargetId) + .map(game::getPermanent) + .map(permanent -> permanent.isCreature(game)) + .orElse(false); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RingOfTheLucii.java b/Mage.Sets/src/mage/cards/r/RingOfTheLucii.java new file mode 100644 index 00000000000..0cd0b348c35 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RingOfTheLucii.java @@ -0,0 +1,49 @@ +package mage.cards.r; + +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SuperType; +import mage.constants.Zone; +import mage.target.common.TargetNonlandPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RingOfTheLucii extends CardImpl { + + public RingOfTheLucii(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); + + this.supertype.add(SuperType.LEGENDARY); + + // {T}: Add {C}{C}. + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(2), new TapSourceCost())); + + // {2}, {T}, Pay 1 life: Tap target nonland permanent. + Ability ability = new SimpleActivatedAbility(new TapTargetEffect(), new GenericManaCost(2)); + ability.addCost(new TapSourceCost()); + ability.addCost(new PayLifeCost(1)); + ability.addTarget(new TargetNonlandPermanent()); + this.addAbility(ability); + } + + private RingOfTheLucii(final RingOfTheLucii card) { + super(card); + } + + @Override + public RingOfTheLucii copy() { + return new RingOfTheLucii(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RiptideEntrancer.java b/Mage.Sets/src/mage/cards/r/RiptideEntrancer.java index 66e9db86583..aa21c7f85de 100644 --- a/Mage.Sets/src/mage/cards/r/RiptideEntrancer.java +++ b/Mage.Sets/src/mage/cards/r/RiptideEntrancer.java @@ -16,7 +16,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; import mage.target.TargetPermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -39,7 +39,7 @@ public final class RiptideEntrancer extends CardImpl { Effect effect = new DoIfCostPaid(new GainControlTargetEffect(Duration.WhileOnBattlefield), new SacrificeSourceCost()); Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(effect, false, true); ability.addTarget(new TargetPermanent(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); // Morph {U}{U} diff --git a/Mage.Sets/src/mage/cards/r/RiptideGearhulk.java b/Mage.Sets/src/mage/cards/r/RiptideGearhulk.java index a1d5f7a4a44..21e4c3ced60 100644 --- a/Mage.Sets/src/mage/cards/r/RiptideGearhulk.java +++ b/Mage.Sets/src/mage/cards/r/RiptideGearhulk.java @@ -13,7 +13,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.target.common.TargetNonlandPermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; /** @@ -41,7 +41,7 @@ public final class RiptideGearhulk extends CardImpl { .setTargetPointer(new EachTargetPointer()); Ability ability = new EntersBattlefieldTriggeredAbility(effect); ability.addTarget(new TargetNonlandPermanent(0, 1)); - ability.setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/RiseOfTheDreadMarn.java b/Mage.Sets/src/mage/cards/r/RiseOfTheDreadMarn.java index d43c026d0ba..adb23cf51aa 100644 --- a/Mage.Sets/src/mage/cards/r/RiseOfTheDreadMarn.java +++ b/Mage.Sets/src/mage/cards/r/RiseOfTheDreadMarn.java @@ -4,6 +4,8 @@ import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; import mage.abilities.keyword.ForetellAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -31,6 +33,7 @@ public final class RiseOfTheDreadMarn extends CardImpl { new ZombieBerserkerToken(), RiseOfTheDreadMarnValue.instance )); this.getSpellAbility().addWatcher(new RiseOfTheDreadMarnWatcher()); + this.getSpellAbility().addHint(RiseOfTheDreadMarnValue.getHint()); // Foretell {B} this.addAbility(new ForetellAbility(this, "{B}")); @@ -48,11 +51,18 @@ public final class RiseOfTheDreadMarn extends CardImpl { enum RiseOfTheDreadMarnValue implements DynamicValue { instance; + private static final Hint hint = new ValueHint("Nontoken creatures that died this turn", instance); + + public static Hint getHint() { + return hint; + } @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { - RiseOfTheDreadMarnWatcher watcher = game.getState().getWatcher(RiseOfTheDreadMarnWatcher.class); - return watcher != null ? watcher.getCreaturesDied() : 0; + return game + .getState() + .getWatcher(RiseOfTheDreadMarnWatcher.class) + .getCreaturesDied(); } @Override @@ -81,9 +91,13 @@ class RiseOfTheDreadMarnWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.ZONE_CHANGE - && ((ZoneChangeEvent) event).isDiesEvent() - && !(((ZoneChangeEvent) event).getTarget() instanceof PermanentToken)) { + if (event.getType() != GameEvent.EventType.ZONE_CHANGE) { + return; + } + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.isDiesEvent() + && zEvent.getTarget().isCreature(game) + && !(zEvent.getTarget() instanceof PermanentToken)) { creaturesDied += 1; } } diff --git a/Mage.Sets/src/mage/cards/r/RishkarsExpertise.java b/Mage.Sets/src/mage/cards/r/RishkarsExpertise.java index 21473b135bf..4af0f05b225 100644 --- a/Mage.Sets/src/mage/cards/r/RishkarsExpertise.java +++ b/Mage.Sets/src/mage/cards/r/RishkarsExpertise.java @@ -1,6 +1,6 @@ package mage.cards.r; -import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.cost.CastFromHandForFreeEffect; @@ -28,10 +28,10 @@ public final class RishkarsExpertise extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{G}{G}"); // Draw cards equal to the greatest power among creatures you control. - Effect effect = new DrawCardSourceControllerEffect(GreatestPowerAmongControlledCreaturesValue.instance); + Effect effect = new DrawCardSourceControllerEffect(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES); effect.setText("Draw cards equal to the greatest power among creatures you control"); this.getSpellAbility().addEffect(effect); - this.getSpellAbility().addHint(GreatestPowerAmongControlledCreaturesValue.getHint()); + this.getSpellAbility().addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint()); // You may cast a card with converted mana cost 5 or less from your hand without paying its mana cost. this.getSpellAbility().addEffect(new CastFromHandForFreeEffect(filter).concatBy("
    ")); diff --git a/Mage.Sets/src/mage/cards/r/RisingWaters.java b/Mage.Sets/src/mage/cards/r/RisingWaters.java index e825b58d4db..b58df3e0fed 100644 --- a/Mage.Sets/src/mage/cards/r/RisingWaters.java +++ b/Mage.Sets/src/mage/cards/r/RisingWaters.java @@ -1,35 +1,36 @@ package mage.cards.r; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DontUntapInControllersUntapStepAllEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.TargetController; import mage.filter.StaticFilters; -import mage.filter.common.FilterLandPermanent; -import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; -import mage.target.common.TargetLandPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author fireshoes */ public final class RisingWaters extends CardImpl { public RisingWaters(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}"); // Lands don't untap during their controllers' untap steps. this.addAbility(new SimpleStaticAbility(new DontUntapInControllersUntapStepAllEffect(Duration.WhileOnBattlefield, TargetController.ANY, StaticFilters.FILTER_LANDS))); - + // At the beginning of each player's upkeep, that player untaps a land they control. this.addAbility(new BeginningOfUpkeepTriggeredAbility(TargetController.EACH_PLAYER, new RisingWatersUntapEffect(), false)); } @@ -63,18 +64,16 @@ class RisingWatersUntapEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(game.getActivePlayerId()); - FilterLandPermanent filter = new FilterLandPermanent("land you control"); - filter.add(new ControllerIdPredicate(game.getActivePlayerId())); - Target target = new TargetLandPermanent(filter); - if (player != null && player.chooseTarget(Outcome.Untap, target, source, game)) { - for (UUID landId : target.getTargets()) { - Permanent land = game.getPermanent(landId); - if (land != null) { - land.untap(game); - } - } - return true; + Target target = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND); + if (player == null || !player.chooseTarget(Outcome.Untap, target, source, game)) { + return false; } - return false; + for (UUID landId : target.getTargets()) { + Permanent land = game.getPermanent(landId); + if (land != null) { + land.untap(game); + } + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/r/RiteOfUndoing.java b/Mage.Sets/src/mage/cards/r/RiteOfUndoing.java index ac49502d802..86d9ea0bd60 100644 --- a/Mage.Sets/src/mage/cards/r/RiteOfUndoing.java +++ b/Mage.Sets/src/mage/cards/r/RiteOfUndoing.java @@ -6,8 +6,9 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.TargetController; +import mage.filter.StaticFilters; import mage.filter.common.FilterNonlandPermanent; -import mage.target.common.TargetNonlandPermanent; +import mage.target.TargetPermanent; import mage.target.targetpointer.EachTargetPointer; import java.util.UUID; @@ -17,12 +18,10 @@ import java.util.UUID; */ public final class RiteOfUndoing extends CardImpl { - private static final FilterNonlandPermanent filterControlled = new FilterNonlandPermanent("nonland permanent you control"); - private static final FilterNonlandPermanent filterNotControlled = new FilterNonlandPermanent("nonland permanent you don't control"); + private static final FilterNonlandPermanent filter = new FilterNonlandPermanent("nonland permanent you don't control"); static { - filterControlled.add(TargetController.YOU.getControllerPredicate()); - filterNotControlled.add(TargetController.NOT_YOU.getControllerPredicate()); + filter.add(TargetController.NOT_YOU.getControllerPredicate()); } public RiteOfUndoing(UUID ownerId, CardSetInfo setInfo) { @@ -33,8 +32,8 @@ public final class RiteOfUndoing extends CardImpl { // Return target nonland permanent you control and target nonland permanent you don't control to their owners' hands. this.getSpellAbility().addEffect(new ReturnToHandTargetEffect().setTargetPointer(new EachTargetPointer())); - this.getSpellAbility().addTarget(new TargetNonlandPermanent(filterControlled)); - this.getSpellAbility().addTarget(new TargetNonlandPermanent(filterNotControlled)); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_NON_LAND)); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); } private RiteOfUndoing(final RiteOfUndoing card) { diff --git a/Mage.Sets/src/mage/cards/r/RithsCharm.java b/Mage.Sets/src/mage/cards/r/RithsCharm.java index fb2454ef123..6f5a3c61817 100644 --- a/Mage.Sets/src/mage/cards/r/RithsCharm.java +++ b/Mage.Sets/src/mage/cards/r/RithsCharm.java @@ -2,10 +2,11 @@ package mage.cards.r; import java.util.UUID; + import mage.abilities.Mode; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DestroyTargetEffect; -import mage.abilities.effects.common.PreventDamageBySourceEffect; +import mage.abilities.effects.common.PreventDamageByChosenSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -13,13 +14,12 @@ import mage.game.permanent.token.SaprolingToken; import mage.target.common.TargetNonBasicLandPermanent; /** - * * @author FenrisulfrX */ public final class RithsCharm extends CardImpl { public RithsCharm(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{R}{G}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}{G}{W}"); // Choose one - Destroy target nonbasic land; this.getSpellAbility().addEffect(new DestroyTargetEffect()); @@ -30,7 +30,7 @@ public final class RithsCharm extends CardImpl { this.getSpellAbility().addMode(mode); // or prevent all damage a source of your choice would deal this turn. - mode = new Mode(new PreventDamageBySourceEffect()); + mode = new Mode(new PreventDamageByChosenSourceEffect()); this.getSpellAbility().addMode(mode); } diff --git a/Mage.Sets/src/mage/cards/r/RivazOfTheClaw.java b/Mage.Sets/src/mage/cards/r/RivazOfTheClaw.java index d206b3ef84d..dccdbe27503 100644 --- a/Mage.Sets/src/mage/cards/r/RivazOfTheClaw.java +++ b/Mage.Sets/src/mage/cards/r/RivazOfTheClaw.java @@ -1,7 +1,7 @@ package mage.cards.r; import mage.MageInt; -import mage.abilities.common.CastFromGraveyardOnceEachTurnAbility; +import mage.abilities.common.CastFromGraveyardOnceDuringEachOfYourTurnAbility; import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.effects.common.ExileSourceEffect; @@ -50,7 +50,7 @@ public final class RivazOfTheClaw extends CardImpl { this.addAbility(new ConditionalAnyColorManaAbility(2, new ConditionalSpellManaBuilder(manaAbilityFilter))); // Once during each of your turns, you may cast a Dragon creature spell from your graveyard. - this.addAbility(new CastFromGraveyardOnceEachTurnAbility(staticAbilityFilter)); + this.addAbility(new CastFromGraveyardOnceDuringEachOfYourTurnAbility(staticAbilityFilter)); // Whenever you cast a Dragon creature spell from your graveyard, it gains "When this creature dies, exile it." this.addAbility(new SpellCastControllerTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/r/RiverDelta.java b/Mage.Sets/src/mage/cards/r/RiverDelta.java index 2c310984a79..10f4012edba 100644 --- a/Mage.Sets/src/mage/cards/r/RiverDelta.java +++ b/Mage.Sets/src/mage/cards/r/RiverDelta.java @@ -1,50 +1,50 @@ - package mage.cards.r; -import java.util.UUID; -import mage.Mana; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; -import mage.abilities.costs.common.TapSourceCost; import mage.abilities.decorator.ConditionalContinuousRuleModifyingEffect; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DontUntapInControllersUntapStepSourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; -import mage.abilities.mana.SimpleManaAbility; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; import mage.counters.CounterType; +import java.util.UUID; + /** - * * @author Luna Skyrise */ public final class RiverDelta extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.DEPLETION); + public RiverDelta(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // River Delta doesn't untap during your untap step if it has a depletion counter on it. - Effect effect = new ConditionalContinuousRuleModifyingEffect(new DontUntapInControllersUntapStepSourceEffect(false, true), - new SourceHasCounterCondition(CounterType.DEPLETION, 1, Integer.MAX_VALUE)); - effect.setText("{this} doesn't untap during your untap step if it has a depletion counter on it"); - Ability ability = new SimpleStaticAbility(effect); - this.addAbility(ability); + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousRuleModifyingEffect( + new DontUntapInControllersUntapStepSourceEffect(false, true), condition + ).setText("{this} doesn't untap during your untap step if it has a depletion counter on it"))); + // At the beginning of your upkeep, remove a depletion counter from River Delta. - Ability ability2 = new BeginningOfUpkeepTriggeredAbility(new RemoveCounterSourceEffect(CounterType.DEPLETION.createInstance())); - this.addAbility(ability2); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new RemoveCounterSourceEffect(CounterType.DEPLETION.createInstance()) + )); + // {tap}: Add {U} or {B}. Put a depletion counter on River Delta. - Ability ability3 = new SimpleManaAbility(Zone.BATTLEFIELD, Mana.BlueMana(1), new TapSourceCost()); - ability3.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance())); - this.addAbility(ability3); - Ability ability4 = new SimpleManaAbility(Zone.BATTLEFIELD, Mana.BlackMana(1), new TapSourceCost()); - ability4.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance())); - this.addAbility(ability4); + Ability ability = new BlueManaAbility(); + ability.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance())); + this.addAbility(ability); + Ability ability2 = new BlackManaAbility(); + ability2.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance())); + this.addAbility(ability2); } private RiverDelta(final RiverDelta card) { diff --git a/Mage.Sets/src/mage/cards/r/RiveteersAscendancy.java b/Mage.Sets/src/mage/cards/r/RiveteersAscendancy.java index 3876d87703f..0961a791324 100644 --- a/Mage.Sets/src/mage/cards/r/RiveteersAscendancy.java +++ b/Mage.Sets/src/mage/cards/r/RiveteersAscendancy.java @@ -16,8 +16,8 @@ import mage.filter.predicate.ObjectSourcePlayerPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCardInYourGraveyard; +import mage.util.CardUtil; -import java.util.Objects; import java.util.UUID; /** @@ -59,18 +59,10 @@ enum RiveteersAscendancyPredicate implements ObjectSourcePlayerPredicate { @Override public boolean apply(ObjectSourcePlayer input, Game game) { - return input - .getObject() - .getManaValue() - < input - .getSource() - .getEffects() - .stream() - .map(effect -> effect.getValue("sacrificedPermanent")) - .filter(Objects::nonNull) - .map(Permanent.class::cast) - .mapToInt(MageObject::getManaValue) - .max() - .orElse(0); + return CardUtil + .getEffectValueFromAbility(input.getSource(), "sacrificedPermanent", Permanent.class) + .map(MageObject::getManaValue) + .filter(x -> input.getObject().getManaValue() < x) + .isPresent(); } } diff --git a/Mage.Sets/src/mage/cards/r/RocHatchling.java b/Mage.Sets/src/mage/cards/r/RocHatchling.java index cab0fa6b356..887e701cdb7 100644 --- a/Mage.Sets/src/mage/cards/r/RocHatchling.java +++ b/Mage.Sets/src/mage/cards/r/RocHatchling.java @@ -1,12 +1,10 @@ - package mage.cards.r; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.continuous.BoostSourceEffect; @@ -14,37 +12,50 @@ import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; import mage.abilities.keyword.FlyingAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; +import mage.constants.ComparisonType; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; import mage.counters.CounterType; +import java.util.UUID; + /** - * * @author LoneFox */ public final class RocHatchling extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.SHELL, ComparisonType.EQUAL_TO, 0); + public RocHatchling(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}"); this.subtype.add(SubType.BIRD); this.power = new MageInt(0); this.toughness = new MageInt(1); // Roc Hatchling enters the battlefield with four shell counters on it. - this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.SHELL.createInstance(4)), "with four shell counters on it")); + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.SHELL.createInstance(4)), + "with four shell counters on it" + )); + // At the beginning of your upkeep, remove a shell counter from Roc Hatchling. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new RemoveCounterSourceEffect(CounterType.SHELL.createInstance()))); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new RemoveCounterSourceEffect(CounterType.SHELL.createInstance()) + )); + // As long as Roc Hatchling has no shell counters on it, it gets +3/+2 and has flying. Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( - new BoostSourceEffect(3, 2, Duration.WhileOnBattlefield), - new SourceHasCounterCondition(CounterType.SHELL, 0, 0), - "As long as {this} has no shell counters on it, it gets +3/+2")); - ability.addEffect(new ConditionalContinuousEffect(new GainAbilitySourceEffect(FlyingAbility.getInstance()), - new SourceHasCounterCondition(CounterType.SHELL, 0, 0), "and has flying")); + new BoostSourceEffect(3, 2, Duration.WhileOnBattlefield), + condition, "as long as {this} has no shell counters on it, it gets +3/+2" + )); + ability.addEffect(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(FlyingAbility.getInstance()), + condition, "and has flying" + )); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/RodOfAbsorption.java b/Mage.Sets/src/mage/cards/r/RodOfAbsorption.java index e3c2ff36421..607d66e6215 100644 --- a/Mage.Sets/src/mage/cards/r/RodOfAbsorption.java +++ b/Mage.Sets/src/mage/cards/r/RodOfAbsorption.java @@ -93,21 +93,25 @@ class RodOfAbsorptionTriggeredAbility extends TriggeredAbilityImpl { class RodOfAbsorptionExileEffect extends ReplacementEffectImpl { - private final MageObjectReference mor; + // we store both Spell and Card to work properly on split cards. + private final MageObjectReference morSpell; + private final MageObjectReference morCard; RodOfAbsorptionExileEffect(Spell spell, Game game) { super(Duration.WhileOnStack, Outcome.Benefit); - this.mor = new MageObjectReference(spell, game); + this.morSpell = new MageObjectReference(spell.getCard(), game); + this.morCard = new MageObjectReference(spell.getMainCard(), game); } private RodOfAbsorptionExileEffect(final RodOfAbsorptionExileEffect effect) { super(effect); - this.mor = effect.mor; + this.morSpell = effect.morSpell; + this.morCard = effect.morCard; } @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Spell sourceSpell = game.getStack().getSpell(event.getTargetId()); + Spell sourceSpell = morSpell.getSpell(game); if (sourceSpell == null || sourceSpell.isCopy()) { return false; } @@ -131,15 +135,10 @@ class RodOfAbsorptionExileEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { ZoneChangeEvent zEvent = ((ZoneChangeEvent) event); - if (zEvent.getFromZone() != Zone.STACK - || zEvent.getToZone() != Zone.GRAVEYARD - || event.getSourceId() == null - || !event.getSourceId().equals(event.getTargetId()) - || mor.getZoneChangeCounter() != game.getState().getZoneChangeCounter(event.getSourceId())) { - return false; - } - Spell spell = game.getStack().getSpell(mor.getSourceId()); - return spell != null && spell.isInstantOrSorcery(game); + return Zone.STACK.equals(zEvent.getFromZone()) + && Zone.GRAVEYARD.equals(zEvent.getToZone()) + && morSpell.refersTo(event.getSourceId(), game) // this is how we check that the spell resolved properly (and was not countered or the like) + && morCard.refersTo(event.getTargetId(), game); // this is how we check that the card being moved is the one we want. } @Override diff --git a/Mage.Sets/src/mage/cards/r/RonaDiscipleOfGix.java b/Mage.Sets/src/mage/cards/r/RonaDiscipleOfGix.java index e3b85be0f7a..c1aee4820f0 100644 --- a/Mage.Sets/src/mage/cards/r/RonaDiscipleOfGix.java +++ b/Mage.Sets/src/mage/cards/r/RonaDiscipleOfGix.java @@ -1,4 +1,3 @@ - package mage.cards.r; import mage.MageInt; @@ -16,7 +15,8 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.common.FilterHistoricCard; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.HistoricPredicate; import mage.game.ExileZone; import mage.game.Game; import mage.target.common.TargetCardInYourGraveyard; @@ -29,6 +29,11 @@ import java.util.UUID; */ public final class RonaDiscipleOfGix extends CardImpl { + private static final FilterCard filter = new FilterCard("historic card from your graveyard"); + static { + filter.add(HistoricPredicate.instance); + } + public RonaDiscipleOfGix(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{B}"); @@ -45,7 +50,7 @@ public final class RonaDiscipleOfGix extends CardImpl { .setText("exile target historic card from your graveyard. (Artifacts, legendaries, and Sagas are historic.)"), true ); - ability.addTarget(new TargetCardInYourGraveyard(new FilterHistoricCard("historic card from your graveyard"))); + ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); // You may cast nonland cards exiled with Rona. diff --git a/Mage.Sets/src/mage/cards/r/RooftopAssassin.java b/Mage.Sets/src/mage/cards/r/RooftopAssassin.java index 12000e5d9ea..3cecafbee66 100644 --- a/Mage.Sets/src/mage/cards/r/RooftopAssassin.java +++ b/Mage.Sets/src/mage/cards/r/RooftopAssassin.java @@ -11,9 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterOpponentsCreaturePermanent; -import mage.filter.predicate.permanent.WasDealtDamageThisTurnPredicate; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import java.util.UUID; @@ -23,13 +21,6 @@ import java.util.UUID; */ public final class RooftopAssassin extends CardImpl { - private static final FilterPermanent filter - = new FilterOpponentsCreaturePermanent("creature an opponent controls that was dealt damage this turn"); - - static { - filter.add(WasDealtDamageThisTurnPredicate.instance); - } - public RooftopAssassin(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); @@ -49,7 +40,7 @@ public final class RooftopAssassin extends CardImpl { // When Rooftop Assassin enters the battlefield, destroy target creature an opponent controls that was dealt damage this turn. Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect()); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_OPPONENTS_CREATURE_DAMAGED_THIS_TURN)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/RookTurret.java b/Mage.Sets/src/mage/cards/r/RookTurret.java new file mode 100644 index 00000000000..66a2423db9c --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RookTurret.java @@ -0,0 +1,44 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.effects.common.DrawDiscardControllerEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RookTurret extends CardImpl { + + public RookTurret(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.CONSTRUCT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever another artifact you control enters, you may draw a card. If you do, discard a card. + this.addAbility(new EntersBattlefieldAllTriggeredAbility( + new DrawDiscardControllerEffect(true), StaticFilters.FILTER_CONTROLLED_ANOTHER_ARTIFACT + )); + } + + private RookTurret(final RookTurret card) { + super(card); + } + + @Override + public RookTurret copy() { + return new RookTurret(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RottenmouthViper.java b/Mage.Sets/src/mage/cards/r/RottenmouthViper.java index 4d55d36b158..a23d3f76b66 100644 --- a/Mage.Sets/src/mage/cards/r/RottenmouthViper.java +++ b/Mage.Sets/src/mage/cards/r/RottenmouthViper.java @@ -71,7 +71,7 @@ class RottenmouthViperEffect extends OneShotEffect{ RottenmouthViperEffect(DynamicValue blightCounterAmount) { super(Outcome.LoseLife); this.blightCounterAmount = blightCounterAmount; - this.staticText = "Then for each blight counter on it, each opponent loses 4 life unless that player sacrifices a nonland permanent or discards a card."; + this.staticText = "Then for each blight counter on it, each opponent loses 4 life unless that player sacrifices a nonland permanent of their choice or discards a card."; } private RottenmouthViperEffect(final RottenmouthViperEffect effect) { diff --git a/Mage.Sets/src/mage/cards/r/RubblebeltRioters.java b/Mage.Sets/src/mage/cards/r/RubblebeltRioters.java index 5639bff276a..d1d1bacb537 100644 --- a/Mage.Sets/src/mage/cards/r/RubblebeltRioters.java +++ b/Mage.Sets/src/mage/cards/r/RubblebeltRioters.java @@ -2,7 +2,7 @@ package mage.cards.r; import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; -import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.HasteAbility; @@ -32,9 +32,9 @@ public final class RubblebeltRioters extends CardImpl { // Whenever Rubblebelt Rioters attacks, it gets +X/+0 until end of turn, where X is the greatest power among creatures you control. this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect( - GreatestPowerAmongControlledCreaturesValue.instance, StaticValue.get(0), + GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES, StaticValue.get(0), Duration.EndOfTurn - ), false).addHint(GreatestPowerAmongControlledCreaturesValue.getHint())); + ), false).addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint())); } private RubblebeltRioters(final RubblebeltRioters card) { diff --git a/Mage.Sets/src/mage/cards/r/RubyDaringTracker.java b/Mage.Sets/src/mage/cards/r/RubyDaringTracker.java index 11b8677b4c0..aaacadc890f 100644 --- a/Mage.Sets/src/mage/cards/r/RubyDaringTracker.java +++ b/Mage.Sets/src/mage/cards/r/RubyDaringTracker.java @@ -3,7 +3,6 @@ package mage.cards.r; import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.condition.common.FerociousCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.HasteAbility; import mage.abilities.mana.GreenManaAbility; @@ -35,11 +34,9 @@ public final class RubyDaringTracker extends CardImpl { this.addAbility(HasteAbility.getInstance()); // Whenever Ruby, Daring Tracker attacks while you control a creature with power 4 or greater, Ruby gets +2/+2 until end of turn. - this.addAbility(new ConditionalTriggeredAbility( - new AttacksTriggeredAbility(new BoostSourceEffect(2, 2, Duration.EndOfTurn), false), - FerociousCondition.instance, - "Whenever {this} attacks while you control a creature with power 4 or greater, {this} gets +2/+2 until end of turn." - )); + this.addAbility(new AttacksTriggeredAbility( + new BoostSourceEffect(2, 2, Duration.EndOfTurn), false + ).withTriggerCondition(FerociousCondition.instance)); // {T}: Add {R} or {G}. this.addAbility(new RedManaAbility()); diff --git a/Mage.Sets/src/mage/cards/r/RufusShinra.java b/Mage.Sets/src/mage/cards/r/RufusShinra.java new file mode 100644 index 00000000000..783415cb0b2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RufusShinra.java @@ -0,0 +1,60 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.game.permanent.token.DarkstarToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RufusShinra extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent("you don't control a creature named Darkstar"); + + static { + filter.add(new NamePredicate("Darkstar")); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.EQUAL_TO, 0); + private static final Hint hint = new ConditionHint(condition); + + public RufusShinra(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.NOBLE); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Whenever Rufus Shinra attacks, if you don't control a creature named Darkstar, create Darkstar, a legendary 2/2 white and black Dog creature token. + this.addAbility(new AttacksTriggeredAbility( + new CreateTokenEffect(new DarkstarToken()) + ).withInterveningIf(condition).addHint(hint)); + } + + private RufusShinra(final RufusShinra card) { + super(card); + } + + @Override + public RufusShinra copy() { + return new RufusShinra(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RumorMonger.java b/Mage.Sets/src/mage/cards/r/RumorMonger.java index 9e254e209d5..82296836051 100644 --- a/Mage.Sets/src/mage/cards/r/RumorMonger.java +++ b/Mage.Sets/src/mage/cards/r/RumorMonger.java @@ -1,28 +1,39 @@ package mage.cards.r; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersTargetEffect; -import mage.abilities.effects.common.counter.MoveCountersTargetsEffect; +import mage.abilities.effects.common.counter.MoveCounterTargetsEffect; import mage.abilities.keyword.BountyAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.other.AnotherTargetPredicate; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; import mage.target.common.TargetOpponentsCreaturePermanent; +import java.util.UUID; + /** - * * @author Styxo */ public final class RumorMonger extends CardImpl { + private static final FilterPermanent filter = new FilterCreaturePermanent("another target creature"); + + static { + filter.add(new AnotherTargetPredicate(2)); + } + public RumorMonger(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}{R}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{R}{G}"); this.subtype.add(SubType.ARCONA); this.subtype.add(SubType.HUNTER); this.power = new MageInt(3); @@ -34,8 +45,9 @@ public final class RumorMonger extends CardImpl { this.addAbility(ability); // Bounty — Whenever a creature an opponent controls with a bounty counter on it dies, you may move a bounty counter from one target creature to another target creatute. - ability = new BountyAbility(new MoveCountersTargetsEffect(CounterType.BOUNTY, 1), false); - ability.addTarget(new TargetOpponentsCreaturePermanent(2)); + ability = new BountyAbility(new MoveCounterTargetsEffect(CounterType.BOUNTY), true); + ability.addTarget(new TargetCreaturePermanent().withChooseHint("to remove a counter from").setTargetTag(1)); + ability.addTarget(new TargetPermanent(filter).setTargetTag(2).withChooseHint("to move a counter to")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/RunawaySteamKin.java b/Mage.Sets/src/mage/cards/r/RunawaySteamKin.java index e4339e4fabd..97e836411ea 100644 --- a/Mage.Sets/src/mage/cards/r/RunawaySteamKin.java +++ b/Mage.Sets/src/mage/cards/r/RunawaySteamKin.java @@ -1,26 +1,27 @@ package mage.cards.r; -import java.util.UUID; import mage.MageInt; import mage.Mana; import mage.ObjectColor; import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.RemoveCountersSourceCost; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.mana.SimpleManaAbility; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.FilterSpell; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class RunawaySteamKin extends CardImpl { @@ -31,6 +32,8 @@ public final class RunawaySteamKin extends CardImpl { filter.add(new ColorPredicate(ObjectColor.RED)); } + private static final Condition condition = new SourceHasCounterCondition(CounterType.P1P1, ComparisonType.FEWER_THAN, 3); + public RunawaySteamKin(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); @@ -39,16 +42,9 @@ public final class RunawaySteamKin extends CardImpl { this.toughness = new MageInt(1); // Whenever you cast a red spell, if Runaway Steam-Kin has fewer than three +1/+1 counters on it, put a +1/+1 counter on Runaway Steam-Kin. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new SpellCastControllerTriggeredAbility( - new AddCountersSourceEffect( - CounterType.P1P1.createInstance() - ), filter, false - ), new SourceHasCounterCondition(CounterType.P1P1, 0, 2), - "Whenever you cast a red spell, " - + "if {this} has fewer than three +1/+1 counters on it, " - + "put a +1/+1 counter on {this}." - )); + this.addAbility(new SpellCastControllerTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), filter, false + ).withInterveningIf(condition)); // Remove three +1/+1 counters from Runaway Steam-Kin: Add {R}{R}{R}. this.addAbility(new SimpleManaAbility( diff --git a/Mage.Sets/src/mage/cards/r/RuneOfProtectionArtifacts.java b/Mage.Sets/src/mage/cards/r/RuneOfProtectionArtifacts.java index b754410f49e..cebc562b4d6 100644 --- a/Mage.Sets/src/mage/cards/r/RuneOfProtectionArtifacts.java +++ b/Mage.Sets/src/mage/cards/r/RuneOfProtectionArtifacts.java @@ -1,36 +1,36 @@ package mage.cards.r; -import java.util.UUID; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.abilities.keyword.CyclingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; -import mage.filter.FilterObject; +import mage.filter.FilterSource; + +import java.util.UUID; /** - * * @author fireshoes */ public final class RuneOfProtectionArtifacts extends CardImpl { - private static final FilterObject filter = new FilterObject("artifact source"); + private static final FilterSource filter = new FilterSource("artifact source"); + static { filter.add(CardType.ARTIFACT.getPredicate()); } public RuneOfProtectionArtifacts(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); // {W}: The next time an artifact source of your choice would deal damage to you this turn, prevent that damage. - Effect effect = new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn, filter); + Effect effect = new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, true, filter); this.addAbility(new SimpleActivatedAbility(effect, new ManaCostsImpl<>("{W}"))); // Cycling {2} ({2}, Discard this card: Draw a card.) this.addAbility(new CyclingAbility(new ManaCostsImpl<>("{2}"))); diff --git a/Mage.Sets/src/mage/cards/r/RuneOfProtectionBlack.java b/Mage.Sets/src/mage/cards/r/RuneOfProtectionBlack.java index ea231aaeb73..4707a3dbaa6 100644 --- a/Mage.Sets/src/mage/cards/r/RuneOfProtectionBlack.java +++ b/Mage.Sets/src/mage/cards/r/RuneOfProtectionBlack.java @@ -1,36 +1,38 @@ package mage.cards.r; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.abilities.keyword.CyclingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.FilterObject; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author Backfir3 */ public final class RuneOfProtectionBlack extends CardImpl { - private static final FilterObject filter = new FilterObject("black source"); + private static final FilterSource filter = new FilterSource("black source"); + static { filter.add(new ColorPredicate(ObjectColor.BLACK)); } public RuneOfProtectionBlack(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); // {W}: The next time a black source of your choice would deal damage to you this turn, prevent that damage. - Effect effect = new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn, filter); + Effect effect = new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, true, filter); this.addAbility(new SimpleActivatedAbility(effect, new ManaCostsImpl<>("{W}"))); // Cycling {2} ({2}, Discard this card: Draw a card.) this.addAbility(new CyclingAbility(new ManaCostsImpl<>("{2}"))); diff --git a/Mage.Sets/src/mage/cards/r/RuneOfProtectionBlue.java b/Mage.Sets/src/mage/cards/r/RuneOfProtectionBlue.java index 299bc336506..dcad39e7ac9 100644 --- a/Mage.Sets/src/mage/cards/r/RuneOfProtectionBlue.java +++ b/Mage.Sets/src/mage/cards/r/RuneOfProtectionBlue.java @@ -1,36 +1,38 @@ package mage.cards.r; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.abilities.keyword.CyclingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.FilterObject; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author Backfir3 */ public final class RuneOfProtectionBlue extends CardImpl { - private static final FilterObject filter = new FilterObject("blue source"); + private static final FilterSource filter = new FilterSource("blue source"); + static { filter.add(new ColorPredicate(ObjectColor.BLUE)); } public RuneOfProtectionBlue(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); // {W}: The next time a blue source of your choice would deal damage to you this turn, prevent that damage. - Effect effect = new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn, filter); + Effect effect = new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, true, filter); this.addAbility(new SimpleActivatedAbility(effect, new ManaCostsImpl<>("{W}"))); // Cycling {2} ({2}, Discard this card: Draw a card.) this.addAbility(new CyclingAbility(new ManaCostsImpl<>("{2}"))); diff --git a/Mage.Sets/src/mage/cards/r/RuneOfProtectionGreen.java b/Mage.Sets/src/mage/cards/r/RuneOfProtectionGreen.java index 17e83c87fa0..eacc032d2f9 100644 --- a/Mage.Sets/src/mage/cards/r/RuneOfProtectionGreen.java +++ b/Mage.Sets/src/mage/cards/r/RuneOfProtectionGreen.java @@ -1,36 +1,38 @@ package mage.cards.r; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.abilities.keyword.CyclingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.FilterObject; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author Backfir3 */ public final class RuneOfProtectionGreen extends CardImpl { - private static final FilterObject filter = new FilterObject("green source"); + private static final FilterSource filter = new FilterSource("green source"); + static { filter.add(new ColorPredicate(ObjectColor.GREEN)); } public RuneOfProtectionGreen(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); // {W}: The next time a green source of your choice would deal damage to you this turn, prevent that damage. - Effect effect = new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn, filter); + Effect effect = new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, true, filter); this.addAbility(new SimpleActivatedAbility(effect, new ManaCostsImpl<>("{W}"))); // Cycling {2} ({2}, Discard this card: Draw a card.) this.addAbility(new CyclingAbility(new ManaCostsImpl<>("{2}"))); diff --git a/Mage.Sets/src/mage/cards/r/RuneOfProtectionLands.java b/Mage.Sets/src/mage/cards/r/RuneOfProtectionLands.java index e620e0b7fa0..7c5b3d779b6 100644 --- a/Mage.Sets/src/mage/cards/r/RuneOfProtectionLands.java +++ b/Mage.Sets/src/mage/cards/r/RuneOfProtectionLands.java @@ -1,36 +1,36 @@ package mage.cards.r; -import java.util.UUID; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.abilities.keyword.CyclingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; -import mage.filter.FilterObject; +import mage.filter.FilterSource; + +import java.util.UUID; /** - * * @author fireshoes */ public final class RuneOfProtectionLands extends CardImpl { - private static final FilterObject filter = new FilterObject("land source"); + private static final FilterSource filter = new FilterSource("land source"); + static { filter.add(CardType.LAND.getPredicate()); } public RuneOfProtectionLands(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); // {W}: The next time a land source of your choice would deal damage to you this turn, prevent that damage. - Effect effect = new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn, filter); + Effect effect = new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, true, filter); this.addAbility(new SimpleActivatedAbility(effect, new ManaCostsImpl<>("{W}"))); // Cycling {2} ({2}, Discard this card: Draw a card.) this.addAbility(new CyclingAbility(new ManaCostsImpl<>("{2}"))); diff --git a/Mage.Sets/src/mage/cards/r/RuneOfProtectionRed.java b/Mage.Sets/src/mage/cards/r/RuneOfProtectionRed.java index 4af3ccfee3b..78b019856ac 100644 --- a/Mage.Sets/src/mage/cards/r/RuneOfProtectionRed.java +++ b/Mage.Sets/src/mage/cards/r/RuneOfProtectionRed.java @@ -1,36 +1,38 @@ package mage.cards.r; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.abilities.keyword.CyclingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.FilterObject; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author Backfir3 */ public final class RuneOfProtectionRed extends CardImpl { - private static final FilterObject filter = new FilterObject("red source"); + private static final FilterSource filter = new FilterSource("red source"); + static { filter.add(new ColorPredicate(ObjectColor.RED)); } public RuneOfProtectionRed(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); // {W}: The next time a red source of your choice would deal damage to you this turn, prevent that damage. - Effect effect = new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn, filter); + Effect effect = new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, true, filter); this.addAbility(new SimpleActivatedAbility(effect, new ManaCostsImpl<>("{W}"))); // Cycling {2} ({2}, Discard this card: Draw a card.) this.addAbility(new CyclingAbility(new ManaCostsImpl<>("{2}"))); diff --git a/Mage.Sets/src/mage/cards/r/RuneOfProtectionWhite.java b/Mage.Sets/src/mage/cards/r/RuneOfProtectionWhite.java index 2624dd6d2ff..b789a8e4751 100644 --- a/Mage.Sets/src/mage/cards/r/RuneOfProtectionWhite.java +++ b/Mage.Sets/src/mage/cards/r/RuneOfProtectionWhite.java @@ -1,36 +1,38 @@ package mage.cards.r; -import java.util.UUID; import mage.ObjectColor; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.abilities.keyword.CyclingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.FilterObject; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author Backfir3 */ public final class RuneOfProtectionWhite extends CardImpl { - private static final FilterObject filter = new FilterObject("white source"); + private static final FilterSource filter = new FilterSource("white source"); + static { filter.add(new ColorPredicate(ObjectColor.WHITE)); } public RuneOfProtectionWhite(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); // {W}: The next time a white source of your choice would deal damage to you this turn, prevent that damage. - Effect effect = new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn, filter); + Effect effect = new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, true, filter); this.addAbility(new SimpleActivatedAbility(effect, new ManaCostsImpl<>("{W}"))); // Cycling {2} ({2}, Discard this card: Draw a card.) this.addAbility(new CyclingAbility(new ManaCostsImpl<>("{2}"))); diff --git a/Mage.Sets/src/mage/cards/r/RushOfKnowledge.java b/Mage.Sets/src/mage/cards/r/RushOfKnowledge.java index d271dced86b..93f60bacfe7 100644 --- a/Mage.Sets/src/mage/cards/r/RushOfKnowledge.java +++ b/Mage.Sets/src/mage/cards/r/RushOfKnowledge.java @@ -1,26 +1,27 @@ package mage.cards.r; -import java.util.UUID; -import mage.abilities.dynamicvalue.common.HighestManaValueCount; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class RushOfKnowledge extends CardImpl { public RushOfKnowledge(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{U}"); - // Draw cards equal to the highest converted mana cost among permanents you control. - DrawCardSourceControllerEffect effect = new DrawCardSourceControllerEffect(new HighestManaValueCount()); - effect.setText("Draw cards equal to the highest mana value among permanents you control"); + // Draw cards equal to the greatest mana value among permanents you control. + DrawCardSourceControllerEffect effect = new DrawCardSourceControllerEffect(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_PERMANENTS); + effect.setText("Draw cards equal to the greatest mana value among permanents you control"); this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addHint(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_PERMANENTS.getHint()); } private RushOfKnowledge(final RushOfKnowledge card) { diff --git a/Mage.Sets/src/mage/cards/r/RustElemental.java b/Mage.Sets/src/mage/cards/r/RustElemental.java index ab148557dd1..190e9ceef41 100644 --- a/Mage.Sets/src/mage/cards/r/RustElemental.java +++ b/Mage.Sets/src/mage/cards/r/RustElemental.java @@ -2,9 +2,9 @@ package mage.cards.r; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.OnEventTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -13,10 +13,8 @@ import mage.constants.SubType; import mage.filter.common.FilterControlledArtifactPermanent; import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetSacrifice; import java.util.UUID; @@ -34,8 +32,9 @@ public final class RustElemental extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); + // At the beginning of your upkeep, sacrifice an artifact other than Rust Elemental. If you can't, tap Rust Elemental and you lose 4 life. - this.addAbility(new OnEventTriggeredAbility(GameEvent.EventType.UPKEEP_STEP_PRE, "beginning of your upkeep", new RustElementalEffect(), false)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new RustElementalEffect())); } private RustElemental(final RustElemental card) { @@ -56,9 +55,9 @@ class RustElementalEffect extends OneShotEffect { filter.add(AnotherPredicate.instance); } - public RustElementalEffect() { + RustElementalEffect() { super(Outcome.Damage); - this.staticText = "sacrifice an artifact other than {this}. If you can't, tap {this} and you lose 4 life."; + this.staticText = "sacrifice another artifact. If you can't, tap {this} and you lose 4 life."; } private RustElementalEffect(final RustElementalEffect effect) { diff --git a/Mage.Sets/src/mage/cards/r/RustmouthOgre.java b/Mage.Sets/src/mage/cards/r/RustmouthOgre.java index e2b48e23a17..4707d7ef988 100644 --- a/Mage.Sets/src/mage/cards/r/RustmouthOgre.java +++ b/Mage.Sets/src/mage/cards/r/RustmouthOgre.java @@ -11,7 +11,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterArtifactPermanent; import mage.target.TargetPermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -33,7 +33,7 @@ public final class RustmouthOgre extends CardImpl { // Whenever Rustmouth Ogre deals combat damage to a player, you may destroy target artifact that player controls. Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new DestroyTargetEffect(), true, true); ability.addTarget(new TargetPermanent(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/RustsporeRam.java b/Mage.Sets/src/mage/cards/r/RustsporeRam.java index 5e90e79648f..5ee2fced92e 100644 --- a/Mage.Sets/src/mage/cards/r/RustsporeRam.java +++ b/Mage.Sets/src/mage/cards/r/RustsporeRam.java @@ -11,6 +11,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; /** @@ -19,19 +20,13 @@ import mage.target.TargetPermanent; */ public final class RustsporeRam extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("Equipment"); - - static { - filter.add(SubType.EQUIPMENT.getPredicate()); - } - public RustsporeRam(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{4}"); this.subtype.add(SubType.SHEEP); this.power = new MageInt(1); this.toughness = new MageInt(3); Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), false); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_EQUIPMENT)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/RydiaSummonerOfMist.java b/Mage.Sets/src/mage/cards/r/RydiaSummonerOfMist.java new file mode 100644 index 00000000000..9b91b5a8772 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RydiaSummonerOfMist.java @@ -0,0 +1,105 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.LandfallAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.GetXValue; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldWithCounterTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.targetadjustment.ManaValueTargetAdjuster; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RydiaSummonerOfMist extends CardImpl { + + private static final FilterCard filter = new FilterCard("Saga card with mana value X from your graveyard"); + + static { + filter.add(SubType.SAGA.getPredicate()); + } + + public RydiaSummonerOfMist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{G}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SHAMAN); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // Landfall -- Whenever a land you control enters, you may discard a card. If you do, draw a card. + this.addAbility(new LandfallAbility(new DoIfCostPaid( + new DrawCardSourceControllerEffect(1), new DiscardCardCost() + ))); + + // Summon -- {X}, {T}: Return target Saga card with mana value X from your graveyard to the battlefield with a finality counter on it. It gains haste until end of turn. Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility(new RydiaSummonerOfMistEffect(), new ManaCostsImpl<>("{X}")); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + ability.setTargetAdjuster(new ManaValueTargetAdjuster(GetXValue.instance, ComparisonType.EQUAL_TO)); + this.addAbility(ability.withFlavorWord("Summon")); + } + + private RydiaSummonerOfMist(final RydiaSummonerOfMist card) { + super(card); + } + + @Override + public RydiaSummonerOfMist copy() { + return new RydiaSummonerOfMist(this); + } +} + +class RydiaSummonerOfMistEffect extends ReturnFromGraveyardToBattlefieldWithCounterTargetEffect { + + RydiaSummonerOfMistEffect() { + super(CounterType.FINALITY.createInstance()); + } + + private RydiaSummonerOfMistEffect(final RydiaSummonerOfMistEffect effect) { + super(effect); + } + + @Override + public RydiaSummonerOfMistEffect copy() { + return new RydiaSummonerOfMistEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + super.apply(game, source); + Optional.ofNullable(getTargetPointer().getFirst(game, source)) + .map(game::getCard) + .map(card -> CardUtil.getPermanentFromCardPutToBattlefield(card, game)) + .ifPresent(permanent -> game.addEffect( + new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn) + .setTargetPointer(new FixedTarget(permanent, game)), source)); + return true; + } + + @Override + public String getText(Mode mode) { + return super.getText(mode) + ". It gains haste until end of turn"; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RydiasReturn.java b/Mage.Sets/src/mage/cards/r/RydiasReturn.java new file mode 100644 index 00000000000..00318ea1af2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RydiasReturn.java @@ -0,0 +1,40 @@ +package mage.cards.r; + +import mage.abilities.Mode; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RydiasReturn extends CardImpl { + + public RydiasReturn(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{G}{G}"); + + // Choose one -- + // * Creatures you control get +3/+3 until end of turn. + this.getSpellAbility().addEffect(new BoostControlledEffect(3, 3, Duration.EndOfTurn)); + + // * Return up to two target permanent cards from your graveyard to your hand. + this.getSpellAbility().addMode(new Mode(new ReturnFromGraveyardToHandTargetEffect()) + .addTarget(new TargetCardInYourGraveyard(0, 2, StaticFilters.FILTER_CARD_PERMANENTS))); + } + + private RydiasReturn(final RydiasReturn card) { + super(card); + } + + @Override + public RydiasReturn copy() { + return new RydiasReturn(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SOLDIERMilitaryProgram.java b/Mage.Sets/src/mage/cards/s/SOLDIERMilitaryProgram.java index 3ce4752aedc..3e3152415ca 100644 --- a/Mage.Sets/src/mage/cards/s/SOLDIERMilitaryProgram.java +++ b/Mage.Sets/src/mage/cards/s/SOLDIERMilitaryProgram.java @@ -3,6 +3,7 @@ package mage.cards.s; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.condition.common.ControlACommanderCondition; +import mage.abilities.effects.OneShotNonTargetEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; @@ -31,11 +32,17 @@ public final class SOLDIERMilitaryProgram extends CardImpl { // At the beginning of combat on your turn, choose one. If you control a commander, you may choose both instead. // * Create a 1/1 white Soldier creature token. Ability ability = new BeginningOfCombatTriggeredAbility(new CreateTokenEffect(new SoldierToken())); + ability.getModes().setChooseText("choose one. If you control a commander, you may choose both instead."); ability.getModes().setMoreCondition(2, ControlACommanderCondition.instance); + ability.getModes().setChooseText( + "choose one. If you control a commander, you may choose both instead." + ); // * Put a +1/+1 counter on each of up to two Soldiers you control. - ability.addMode(new Mode(new AddCountersTargetEffect(CounterType.P1P1.createInstance())) - .addTarget(new TargetPermanent(0, 2, filter))); + ability.addMode(new Mode(new OneShotNonTargetEffect( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()) + .setText("Put a +1/+1 counter on each of up to two Soldiers you control"), + new TargetPermanent(0, 2, filter)))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/Sabotender.java b/Mage.Sets/src/mage/cards/s/Sabotender.java new file mode 100644 index 00000000000..a233bef78ba --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/Sabotender.java @@ -0,0 +1,42 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.LandfallAbility; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Sabotender extends CardImpl { + + public Sabotender(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.PLANT); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Landfall -- Whenever a land you control enters, this creature deals 1 damage to each opponent. + this.addAbility(new LandfallAbility(new DamagePlayersEffect(1, TargetController.OPPONENT))); + } + + private Sabotender(final Sabotender card) { + super(card); + } + + @Override + public Sabotender copy() { + return new Sabotender(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SagesNouliths.java b/Mage.Sets/src/mage/cards/s/SagesNouliths.java index 20b2a425bd9..642afdad19f 100644 --- a/Mage.Sets/src/mage/cards/s/SagesNouliths.java +++ b/Mage.Sets/src/mage/cards/s/SagesNouliths.java @@ -38,7 +38,7 @@ public final class SagesNouliths extends CardImpl { triggeredAbility.addTarget(new TargetAttackingCreature()); ability.addEffect(new GainAbilityAttachedEffect( triggeredAbility, AttachmentType.EQUIPMENT - ).setText("has \"Whenever this creature attacks, untap target attacking creature,\"")); + ).setText(", has \"Whenever this creature attacks, untap target attacking creature,\"")); ability.addEffect(new AddCardSubtypeAttachedEffect( SubType.CLERIC, AttachmentType.EQUIPMENT ).setText("and is a Cleric in addition to its other types")); diff --git a/Mage.Sets/src/mage/cards/s/SaheeliRadiantCreator.java b/Mage.Sets/src/mage/cards/s/SaheeliRadiantCreator.java index 970968117af..52f3aa5c77b 100644 --- a/Mage.Sets/src/mage/cards/s/SaheeliRadiantCreator.java +++ b/Mage.Sets/src/mage/cards/s/SaheeliRadiantCreator.java @@ -1,6 +1,5 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SpellCastControllerTriggeredAbility; @@ -11,12 +10,12 @@ import mage.abilities.effects.common.CreateTokenCopyTargetEffect; import mage.abilities.effects.common.DoWhenCostPaid; import mage.abilities.effects.common.counter.GetEnergyCountersControllerEffect; import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.SuperType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.filter.FilterSpell; import mage.filter.StaticFilters; import mage.filter.predicate.Predicates; @@ -24,13 +23,14 @@ import mage.game.Game; import mage.target.TargetPermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author Jmlundeen */ public final class SaheeliRadiantCreator extends CardImpl { - private static final FilterSpell filter = new FilterSpell("Artificer or artifact spell"); + private static final FilterSpell filter = new FilterSpell("an Artificer or artifact spell"); static { filter.add(Predicates.or( @@ -41,7 +41,7 @@ public final class SaheeliRadiantCreator extends CardImpl { public SaheeliRadiantCreator(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{U}{R}"); - + this.supertype.add(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.ARTIFICER); @@ -105,4 +105,4 @@ class SaheeliRadiantCreatorCopyEffect extends OneShotEffect { effect.sacrificeTokensCreatedAtNextEndStep(game, source); return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/s/SalvageTrader.java b/Mage.Sets/src/mage/cards/s/SalvageTrader.java index 10e7253c9cf..cd9f7dac8e7 100644 --- a/Mage.Sets/src/mage/cards/s/SalvageTrader.java +++ b/Mage.Sets/src/mage/cards/s/SalvageTrader.java @@ -1,6 +1,5 @@ package mage.cards.s; -import java.util.*; import mage.MageInt; import mage.MageItem; import mage.abilities.Ability; @@ -9,17 +8,21 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.common.continuous.ExchangeControlTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.TargetController; import mage.filter.common.FilterArtifactPermanent; import mage.filter.predicate.ObjectSourcePlayer; import mage.filter.predicate.ObjectSourcePlayerPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.StackObject; -import mage.target.common.TargetArtifactPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author NinthWorld */ public final class SalvageTrader extends CardImpl { @@ -38,11 +41,11 @@ public final class SalvageTrader extends CardImpl { new TapSourceCost()); FilterArtifactPermanent filterYou = new FilterArtifactPermanent("artifact you control"); filterYou.add(TargetController.YOU.getControllerPredicate()); - ability.addTarget(new TargetArtifactPermanent(filterYou)); + ability.addTarget(new TargetPermanent(filterYou)); FilterArtifactPermanent filterOpponent = new FilterArtifactPermanent("artifact an opponent controls with the same casting cost as your targeted artifact"); filterOpponent.add(TargetController.OPPONENT.getControllerPredicate()); filterOpponent.add(new SameCastingCostPredicate()); - ability.addTarget(new TargetArtifactPermanent(filterOpponent)); + ability.addTarget(new TargetPermanent(filterOpponent)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SamutTheDrivingForce.java b/Mage.Sets/src/mage/cards/s/SamutTheDrivingForce.java index 23211469d08..9c982004f92 100644 --- a/Mage.Sets/src/mage/cards/s/SamutTheDrivingForce.java +++ b/Mage.Sets/src/mage/cards/s/SamutTheDrivingForce.java @@ -18,7 +18,7 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.FilterCard; import mage.filter.StaticFilters; -import mage.filter.common.FilterNoncreatureCard; +import mage.filter.predicate.Predicates; import java.util.UUID; @@ -27,7 +27,10 @@ import java.util.UUID; */ public final class SamutTheDrivingForce extends CardImpl { - private static final FilterCard filter = new FilterNoncreatureCard("noncreature spells"); + private static final FilterCard filter = new FilterCard("noncreature spells"); + static { + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); + } public SamutTheDrivingForce(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{G}{W}"); diff --git a/Mage.Sets/src/mage/cards/s/SamwiseGamgee.java b/Mage.Sets/src/mage/cards/s/SamwiseGamgee.java index e5108ef9fd4..87c4fb3fe9d 100644 --- a/Mage.Sets/src/mage/cards/s/SamwiseGamgee.java +++ b/Mage.Sets/src/mage/cards/s/SamwiseGamgee.java @@ -16,12 +16,11 @@ import mage.filter.FilterCard; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledPermanent; -import mage.filter.common.FilterHistoricCard; import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.predicate.mageobject.HistoricPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.game.permanent.token.FoodToken; import mage.target.common.TargetCardInYourGraveyard; -import mage.target.common.TargetControlledPermanent; import java.util.UUID; @@ -32,11 +31,12 @@ public final class SamwiseGamgee extends CardImpl { private static final FilterPermanent filter = new FilterControlledCreaturePermanent("another nontoken creature"); private static final FilterControlledPermanent filter2 = new FilterControlledPermanent(SubType.FOOD, "Foods"); - private static final FilterCard filter3 = new FilterHistoricCard("historic card from your graveyard"); + private static final FilterCard filter3 = new FilterCard("historic card from your graveyard"); static { filter.add(AnotherPredicate.instance); filter.add(TokenPredicate.FALSE); + filter3.add(HistoricPredicate.instance); } public SamwiseGamgee(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/s/SanctumSpirit.java b/Mage.Sets/src/mage/cards/s/SanctumSpirit.java index cfc17b42039..0faca217120 100644 --- a/Mage.Sets/src/mage/cards/s/SanctumSpirit.java +++ b/Mage.Sets/src/mage/cards/s/SanctumSpirit.java @@ -1,20 +1,20 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.DiscardCardCost; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.keyword.IndestructibleAbility; -import mage.constants.SubType; import mage.abilities.keyword.LifelinkAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; -import mage.filter.common.FilterHistoricCard; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.HistoricPredicate; + +import java.util.UUID; /** * @@ -22,6 +22,11 @@ import mage.filter.common.FilterHistoricCard; */ public final class SanctumSpirit extends CardImpl { + private static final FilterCard filter = new FilterCard("a historic card"); + static { + filter.add(HistoricPredicate.instance); + } + public SanctumSpirit(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); @@ -36,7 +41,7 @@ public final class SanctumSpirit extends CardImpl { this.addAbility( new SimpleActivatedAbility( new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn), - new DiscardCardCost(new FilterHistoricCard()) + new DiscardCardCost(filter) ) ); } diff --git a/Mage.Sets/src/mage/cards/s/SandGolem.java b/Mage.Sets/src/mage/cards/s/SandGolem.java index c54428fb8e6..cba5c7b495b 100644 --- a/Mage.Sets/src/mage/cards/s/SandGolem.java +++ b/Mage.Sets/src/mage/cards/s/SandGolem.java @@ -32,7 +32,7 @@ public final class SandGolem extends CardImpl { new ReturnSourceFromGraveyardToBattlefieldWithCounterEffect(CounterType.P1P1.createInstance(), false))); effect.setText("return {this} from your graveyard to the battlefield with a +1/+1 counter on it at the beginning of the next end step"); - this.addAbility(new DiscardedByOpponentTriggeredAbility(effect, true)); + this.addAbility(new DiscardedByOpponentTriggeredAbility(effect)); } private SandGolem(final SandGolem card) { diff --git a/Mage.Sets/src/mage/cards/s/SandstoneNeedle.java b/Mage.Sets/src/mage/cards/s/SandstoneNeedle.java index f4c019a1bd8..4fd89ce73f2 100644 --- a/Mage.Sets/src/mage/cards/s/SandstoneNeedle.java +++ b/Mage.Sets/src/mage/cards/s/SandstoneNeedle.java @@ -1,11 +1,9 @@ - package mage.cards.s; -import java.util.UUID; import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; -import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.costs.common.TapSourceCost; @@ -17,17 +15,21 @@ import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.Zone; import mage.counters.CounterType; +import java.util.UUID; + /** - * * @author Plopman */ public final class SandstoneNeedle extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.DEPLETION, ComparisonType.EQUAL_TO, 0); + public SandstoneNeedle(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // Sandstone Needle enters the battlefield tapped with two depletion counters on it. Ability etbAbility = new EntersBattlefieldAbility( @@ -35,10 +37,14 @@ public final class SandstoneNeedle extends CardImpl { ); etbAbility.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance(2))); this.addAbility(etbAbility); + // {tap}, Remove a depletion counter from Sandstone Needle: Add {R}{R}. If there are no depletion counters on Sandstone Needle, sacrifice it. Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, Mana.RedMana(2), new TapSourceCost()); ability.addCost(new RemoveCountersSourceCost(CounterType.DEPLETION.createInstance(1))); - ability.addEffect(new ConditionalOneShotEffect(new SacrificeSourceEffect(), new SourceHasCounterCondition(CounterType.DEPLETION, 0,0), "If there are no depletion counters on {this}, sacrifice it")); + ability.addEffect(new ConditionalOneShotEffect( + new SacrificeSourceEffect(), condition, + "If there are no depletion counters on {this}, sacrifice it" + )); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/Sandworm.java b/Mage.Sets/src/mage/cards/s/Sandworm.java new file mode 100644 index 00000000000..7a483e5a6ce --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/Sandworm.java @@ -0,0 +1,47 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayTargetControllerEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetLandPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Sandworm extends CardImpl { + + public Sandworm(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}"); + + this.subtype.add(SubType.WORM); + this.power = new MageInt(5); + this.toughness = new MageInt(4); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // When this creature enters, destroy target land. Its controller may search their library for a basic land card, put it onto the battlefield tapped, then shuffle. + Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect()); + ability.addEffect(new SearchLibraryPutInPlayTargetControllerEffect(true)); + ability.addTarget(new TargetLandPermanent()); + this.addAbility(ability); + } + + private Sandworm(final Sandworm card) { + super(card); + } + + @Override + public Sandworm copy() { + return new Sandworm(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SaprazzanSkerry.java b/Mage.Sets/src/mage/cards/s/SaprazzanSkerry.java index ba09afc094f..5b5d55327fa 100644 --- a/Mage.Sets/src/mage/cards/s/SaprazzanSkerry.java +++ b/Mage.Sets/src/mage/cards/s/SaprazzanSkerry.java @@ -1,11 +1,10 @@ package mage.cards.s; -import java.util.UUID; import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; -import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.costs.common.TapSourceCost; @@ -17,17 +16,21 @@ import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.Zone; import mage.counters.CounterType; +import java.util.UUID; + /** - * * @author Plopman */ public final class SaprazzanSkerry extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.DEPLETION, ComparisonType.EQUAL_TO, 0); + public SaprazzanSkerry(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // Saprazzan Skerry enters the battlefield tapped with two depletion counters on it. Ability etbAbility = new EntersBattlefieldAbility( @@ -35,10 +38,14 @@ public final class SaprazzanSkerry extends CardImpl { ); etbAbility.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance(2))); this.addAbility(etbAbility); + // {tap}, Remove a depletion counter from Saprazzan Skerry: Add {U}{U}. If there are no depletion counters on Saprazzan Skerry, sacrifice it. Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, Mana.BlueMana(2), new TapSourceCost()); ability.addCost(new RemoveCountersSourceCost(CounterType.DEPLETION.createInstance(1))); - ability.addEffect(new ConditionalOneShotEffect(new SacrificeSourceEffect(), new SourceHasCounterCondition(CounterType.DEPLETION, 0,0), "If there are no depletion counters on {this}, sacrifice it")); + ability.addEffect(new ConditionalOneShotEffect( + new SacrificeSourceEffect(), condition, + "If there are no depletion counters on {this}, sacrifice it" + )); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SarahJaneSmith.java b/Mage.Sets/src/mage/cards/s/SarahJaneSmith.java index 7e7144c1c9e..799f88f9693 100644 --- a/Mage.Sets/src/mage/cards/s/SarahJaneSmith.java +++ b/Mage.Sets/src/mage/cards/s/SarahJaneSmith.java @@ -9,8 +9,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.filter.FilterSpell; -import mage.filter.common.FilterHistoricSpell; +import mage.filter.StaticFilters; import java.util.UUID; @@ -19,8 +18,6 @@ import java.util.UUID; */ public final class SarahJaneSmith extends CardImpl { - private static final FilterSpell filter = new FilterHistoricSpell(); - public SarahJaneSmith(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); @@ -32,7 +29,7 @@ public final class SarahJaneSmith extends CardImpl { // Whenever you cast a historic spell, investigate. This ability triggers only once each turn. this.addAbility(new SpellCastControllerTriggeredAbility( - new InvestigateEffect(), filter, false + new InvestigateEffect(), StaticFilters.FILTER_SPELL_HISTORIC, false ).setTriggersLimitEachTurn(1)); // Doctor's companion diff --git a/Mage.Sets/src/mage/cards/s/SavageOrder.java b/Mage.Sets/src/mage/cards/s/SavageOrder.java index 533a1b6d964..4cc0385b01d 100644 --- a/Mage.Sets/src/mage/cards/s/SavageOrder.java +++ b/Mage.Sets/src/mage/cards/s/SavageOrder.java @@ -1,36 +1,26 @@ package mage.cards.s; -import java.util.UUID; - -import mage.ObjectColor; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.Effect; import mage.abilities.effects.SearchEffect; -import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; -import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; -import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.IndestructibleAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.FilterCard; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.ColorPredicate; import mage.filter.predicate.mageobject.PowerPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInLibrary; import mage.target.targetpointer.FixedTarget; -import mage.util.SubTypes; +import mage.util.CardUtil; + +import java.util.UUID; /** * @@ -92,7 +82,7 @@ class SavageOrderEffect extends SearchEffect { Card card = controller.getLibrary().getCard(target.getFirstTarget(), game); if (card != null) { controller.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { ContinuousEffect effect = new GainAbilityTargetEffect(IndestructibleAbility.getInstance(), Duration.UntilYourNextTurn); effect.setTargetPointer(new FixedTarget(permanent.getId(), permanent.getZoneChangeCounter(game))); diff --git a/Mage.Sets/src/mage/cards/s/SawInHalf.java b/Mage.Sets/src/mage/cards/s/SawInHalf.java index 8a27e8a1e14..7d7d9c93458 100644 --- a/Mage.Sets/src/mage/cards/s/SawInHalf.java +++ b/Mage.Sets/src/mage/cards/s/SawInHalf.java @@ -43,8 +43,8 @@ class SawInHalfEffect extends OneShotEffect { SawInHalfEffect() { super(Outcome.Benefit); staticText = "destroy target creature. If that creature dies this way, its controller creates " + - "two tokens that are copies of that creature, except their base power is half that creature's " + - "power and their base toughness is half that creature's toughness. Round up each time"; + "two tokens that are copies of that creature, except their power is half that creature's " + + "power and their toughness is half that creature's toughness. Round up each time"; } private SawInHalfEffect(final SawInHalfEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SazhKatzroy.java b/Mage.Sets/src/mage/cards/s/SazhKatzroy.java index e1af5896abb..471c30bd44e 100644 --- a/Mage.Sets/src/mage/cards/s/SazhKatzroy.java +++ b/Mage.Sets/src/mage/cards/s/SazhKatzroy.java @@ -16,6 +16,7 @@ import mage.counters.CounterType; import mage.filter.FilterCard; import mage.filter.predicate.Predicates; import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -47,13 +48,14 @@ public final class SazhKatzroy extends CardImpl { // When Sazh Katzroy enters, you may search your library for a Bird or basic land card, reveal it, put it into your hand, then shuffle. this.addAbility(new EntersBattlefieldTriggeredAbility( - new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true) + new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true), true )); // Whenever Sazh Katzroy attacks, put counter on target creature, then double the number of +1/+1 counters on that creature. Ability ability = new AttacksTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); ability.addEffect(new DoubleCountersTargetEffect(CounterType.P1P1) .setText(", then double the number of +1/+1 counters on that creature")); + ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/ScaledHulk.java b/Mage.Sets/src/mage/cards/s/ScaledHulk.java index 1bb3ca44ed0..778add52960 100644 --- a/Mage.Sets/src/mage/cards/s/ScaledHulk.java +++ b/Mage.Sets/src/mage/cards/s/ScaledHulk.java @@ -25,7 +25,7 @@ public final class ScaledHulk extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); // Whenever you cast a Spirit or Arcane spell, Scaled Hulk gets +2/+2 until end of turn. - this.addAbility(new SpellCastControllerTriggeredAbility(new BoostSourceEffect(2, 2, Duration.EndOfTurn), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new BoostSourceEffect(2, 2, Duration.EndOfTurn), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, false)); } private ScaledHulk(final ScaledHulk card) { diff --git a/Mage.Sets/src/mage/cards/s/ScalesOfShale.java b/Mage.Sets/src/mage/cards/s/ScalesOfShale.java index 305fffac10e..7b2927ef02d 100644 --- a/Mage.Sets/src/mage/cards/s/ScalesOfShale.java +++ b/Mage.Sets/src/mage/cards/s/ScalesOfShale.java @@ -1,21 +1,15 @@ package mage.cards.s; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.common.AffinityEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.AffinityAbility; import mage.abilities.keyword.IndestructibleAbility; import mage.abilities.keyword.LifelinkAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AffinityType; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterControlledPermanent; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -25,14 +19,11 @@ import java.util.UUID; */ public final class ScalesOfShale extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.LIZARD, "Lizards"); - private static final Hint hint = new ValueHint("Lizards you control", new PermanentsOnBattlefieldCount(filter)); - public ScalesOfShale(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}"); // This spell costs {1} less to cast for each Lizard you control. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filter)).setRuleAtTheTop(true).addHint(hint)); + this.addAbility(new AffinityAbility(AffinityType.LIZARDS)); // Target creature gets +2/+0 and gains lifelink and indestructible until end of turn. this.getSpellAbility().addEffect(new BoostTargetEffect( diff --git a/Mage.Sets/src/mage/cards/s/SchemaThief.java b/Mage.Sets/src/mage/cards/s/SchemaThief.java index c439e1bb4d1..6b1c81c4009 100644 --- a/Mage.Sets/src/mage/cards/s/SchemaThief.java +++ b/Mage.Sets/src/mage/cards/s/SchemaThief.java @@ -11,7 +11,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterArtifactPermanent; import mage.target.TargetPermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -38,7 +38,7 @@ public final class SchemaThief extends CardImpl { // Whenever Schema Thief deals combat damage to a player, create a token that's a copy of target artifact that player controls. Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new CreateTokenCopyTargetEffect(), false, true); ability.addTarget(new TargetPermanent(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SchemingFence.java b/Mage.Sets/src/mage/cards/s/SchemingFence.java index ae9cff75262..a71d64c5199 100644 --- a/Mage.Sets/src/mage/cards/s/SchemingFence.java +++ b/Mage.Sets/src/mage/cards/s/SchemingFence.java @@ -19,7 +19,6 @@ import mage.target.TargetPermanent; import mage.target.common.TargetNonlandPermanent; import mage.util.CardUtil; -import java.util.Objects; import java.util.Optional; import java.util.UUID; @@ -215,12 +214,9 @@ class SchemingFenceManaEffect extends AsThoughEffectImpl implements AsThoughMana @Override public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) { return source.isControlledBy(playerId) - && affectedAbility - .getEffects() - .stream() - .map(effect -> effect.getValue("schemingFence")) - .filter(Objects::nonNull) - .anyMatch(source.getSourceId()::equals); + && CardUtil.getEffectValueFromAbility(affectedAbility, "schemingFence", UUID.class) + .filter(source.getSourceId()::equals) + .isPresent(); } @Override diff --git a/Mage.Sets/src/mage/cards/s/ScionOfCalamity.java b/Mage.Sets/src/mage/cards/s/ScionOfCalamity.java index f22ce9b6086..f4191d15b57 100644 --- a/Mage.Sets/src/mage/cards/s/ScionOfCalamity.java +++ b/Mage.Sets/src/mage/cards/s/ScionOfCalamity.java @@ -11,7 +11,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -36,7 +36,7 @@ public final class ScionOfCalamity extends CardImpl { // Whenever Scion of Calamity deals combat damage to a player, destroy target artifact or enchantment that player controls. Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new DestroyTargetEffect(), false, true); ability.addTarget(new TargetPermanent(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/ScionOfDarkness.java b/Mage.Sets/src/mage/cards/s/ScionOfDarkness.java index 7f6fb900001..4340d3a8624 100644 --- a/Mage.Sets/src/mage/cards/s/ScionOfDarkness.java +++ b/Mage.Sets/src/mage/cards/s/ScionOfDarkness.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; import mage.target.common.TargetCardInGraveyard; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -39,7 +39,7 @@ public final class ScionOfDarkness extends CardImpl { // Whenever Scion of Darkness deals combat damage to a player, you may put target creature card from that player's graveyard onto the battlefield under your control. Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(), true, true); ability.addTarget(new TargetCardInGraveyard(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster(true)); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster(true)); this.addAbility(ability); // Cycling {3} diff --git a/Mage.Sets/src/mage/cards/s/ScorpionSentinel.java b/Mage.Sets/src/mage/cards/s/ScorpionSentinel.java new file mode 100644 index 00000000000..186f1a65ae7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ScorpionSentinel.java @@ -0,0 +1,52 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.hint.common.LandsYouControlHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ScorpionSentinel extends CardImpl { + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, ComparisonType.MORE_THAN, 7 + ); + + public ScorpionSentinel(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}{U}"); + + this.subtype.add(SubType.ROBOT); + this.subtype.add(SubType.SCORPION); + this.power = new MageInt(1); + this.toughness = new MageInt(4); + + // As long as you control seven or more lands, this creature gets +3/+0. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostSourceEffect(3, 0, Duration.WhileOnBattlefield), + condition, "as long as you control seven or more lands, this creature gets +3/+0" + )).addHint(LandsYouControlHint.instance)); + } + + private ScorpionSentinel(final ScorpionSentinel card) { + super(card); + } + + @Override + public ScorpionSentinel copy() { + return new ScorpionSentinel(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/ScrapWelder.java b/Mage.Sets/src/mage/cards/s/ScrapWelder.java index 8d19b814347..39f518599f7 100644 --- a/Mage.Sets/src/mage/cards/s/ScrapWelder.java +++ b/Mage.Sets/src/mage/cards/s/ScrapWelder.java @@ -25,6 +25,7 @@ import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetadjustment.XManaValueTargetAdjuster; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -84,7 +85,7 @@ class ScrapWelderEffect extends OneShotEffect { return false; } controller.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance()); effect.setTargetPointer(new FixedTarget(permanent, game)); diff --git a/Mage.Sets/src/mage/cards/s/ScreamingSwarm.java b/Mage.Sets/src/mage/cards/s/ScreamingSwarm.java index 4a3fed21002..249cb4fbc23 100644 --- a/Mage.Sets/src/mage/cards/s/ScreamingSwarm.java +++ b/Mage.Sets/src/mage/cards/s/ScreamingSwarm.java @@ -86,7 +86,7 @@ class ScreamingSwarmEffect extends OneShotEffect { ScreamingSwarmEffect() { super(Outcome.Benefit); - staticText = "put {this} from your graveyard into your library second from the top"; + staticText = "put this card from your graveyard into your library second from the top"; } private ScreamingSwarmEffect(final ScreamingSwarmEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/ScreamsFromWithin.java b/Mage.Sets/src/mage/cards/s/ScreamsFromWithin.java index e0826275125..6037655954e 100644 --- a/Mage.Sets/src/mage/cards/s/ScreamsFromWithin.java +++ b/Mage.Sets/src/mage/cards/s/ScreamsFromWithin.java @@ -63,7 +63,7 @@ class ScreamsFromWithinEffect extends OneShotEffect { ScreamsFromWithinEffect() { super(Outcome.PutCardInPlay); - staticText = "return {this} from your graveyard to the battlefield"; + staticText = "return this card from your graveyard to the battlefield"; } private ScreamsFromWithinEffect(final ScreamsFromWithinEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/ScytheOfTheWretched.java b/Mage.Sets/src/mage/cards/s/ScytheOfTheWretched.java index 8292093b41f..0eb34facb97 100644 --- a/Mage.Sets/src/mage/cards/s/ScytheOfTheWretched.java +++ b/Mage.Sets/src/mage/cards/s/ScytheOfTheWretched.java @@ -15,8 +15,10 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.StaticFilters; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -67,14 +69,17 @@ class ScytheOfTheWretchedReanimateEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Card card = game.getCard(getTargetPointer().getFirst(game, source)); Player controller = game.getPlayer(source.getControllerId()); - if (card != null && controller != null) { - controller.moveCards(card, Zone.BATTLEFIELD, source, game); - Effect effect = new AttachEffect(Outcome.AddAbility); - effect.setTargetPointer(new FixedTarget(card.getId())); - effect.apply(game, source); - return true; + if (card == null || controller == null) { + return false; } - return false; + controller.moveCards(card, Zone.BATTLEFIELD, source, game); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); + if (permanent != null) { + Effect effect = new AttachEffect(Outcome.AddAbility); + effect.setTargetPointer(new FixedTarget(permanent.getId(), game)); + effect.apply(game, source); + } + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/s/SeasonOfGathering.java b/Mage.Sets/src/mage/cards/s/SeasonOfGathering.java index 5c6e94ba823..c873192b4a9 100644 --- a/Mage.Sets/src/mage/cards/s/SeasonOfGathering.java +++ b/Mage.Sets/src/mage/cards/s/SeasonOfGathering.java @@ -1,14 +1,11 @@ package mage.cards.s; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; - import mage.abilities.Ability; import mage.abilities.Mode; -import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.*; +import mage.abilities.effects.common.DestroyAllEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.TrampleAbility; import mage.abilities.keyword.VigilanceAbility; @@ -27,8 +24,11 @@ import mage.players.Player; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author jimga150 */ public final class SeasonOfGathering extends CardImpl { @@ -52,11 +52,11 @@ public final class SeasonOfGathering extends CardImpl { // {P}{P}{P} -- Draw cards equal to the greatest power among creatures you control. Mode mode3 = new Mode( - new DrawCardSourceControllerEffect(GreatestPowerAmongControlledCreaturesValue.instance) + new DrawCardSourceControllerEffect(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES) .setText("Draw cards equal to the greatest power among creatures you control.") ); this.getSpellAbility().addMode(mode3.withPawPrintValue(3)); - this.getSpellAbility().addHint(GreatestPowerAmongControlledCreaturesValue.getHint()); + this.getSpellAbility().addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint()); } private SeasonOfGathering(final SeasonOfGathering card) { diff --git a/Mage.Sets/src/mage/cards/s/SeasonedTactician.java b/Mage.Sets/src/mage/cards/s/SeasonedTactician.java index 909dff399bb..92890863a14 100644 --- a/Mage.Sets/src/mage/cards/s/SeasonedTactician.java +++ b/Mage.Sets/src/mage/cards/s/SeasonedTactician.java @@ -1,37 +1,37 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.ExileFromTopOfLibraryCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; + +import java.util.UUID; /** - * * @author LoneFox - */ public final class SeasonedTactician extends CardImpl { public SeasonedTactician(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.ADVISOR); this.power = new MageInt(1); this.toughness = new MageInt(3); // {3}, Exile the top four cards of your library: The next time a source of your choice would deal damage to you this turn, prevent that damage. - Ability ability = new SimpleActivatedAbility(new PreventNextDamageFromChosenSourceToYouEffect(Duration.EndOfTurn), - new ManaCostsImpl<>("{3}")); + Ability ability = new SimpleActivatedAbility( + new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, true), + new ManaCostsImpl<>("{3}") + ); ability.addCost(new ExileFromTopOfLibraryCost(4)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SeasonedWarrenguard.java b/Mage.Sets/src/mage/cards/s/SeasonedWarrenguard.java index ade01547b90..d4e447e51b9 100644 --- a/Mage.Sets/src/mage/cards/s/SeasonedWarrenguard.java +++ b/Mage.Sets/src/mage/cards/s/SeasonedWarrenguard.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.hint.ConditionHint; import mage.abilities.hint.Hint; @@ -13,7 +12,9 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.filter.StaticFilters; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.permanent.TokenPredicate; import java.util.UUID; @@ -22,8 +23,13 @@ import java.util.UUID; */ public final class SeasonedWarrenguard extends CardImpl { - private static final Condition condition - = new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_CREATURE_TOKEN, true); + private static final FilterPermanent filter = new FilterControlledPermanent("you control a token"); + + static { + filter.add(TokenPredicate.TRUE); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter, true); private static final Hint hint = new ConditionHint(condition, "You control a token"); public SeasonedWarrenguard(UUID ownerId, CardSetInfo setInfo) { @@ -35,10 +41,9 @@ public final class SeasonedWarrenguard extends CardImpl { this.toughness = new MageInt(2); // Whenever Seasoned Warrenguard attacks while you control a token, Seasoned Warrenguard gets +2/+0 until end of turn. - this.addAbility(new ConditionalTriggeredAbility( - new AttacksTriggeredAbility(new BoostSourceEffect(2, 0, Duration.EndOfTurn)), - condition, "Whenever {this} attacks while you control a token, {this} gets +2/+0 until end of turn" - ).addHint(hint)); + this.addAbility(new AttacksTriggeredAbility( + new BoostSourceEffect(2, 0, Duration.EndOfTurn) + ).withRuleTextReplacement(false).withTriggerCondition(condition).addHint(hint)); } private SeasonedWarrenguard(final SeasonedWarrenguard card) { diff --git a/Mage.Sets/src/mage/cards/s/SeiferAlmasy.java b/Mage.Sets/src/mage/cards/s/SeiferAlmasy.java new file mode 100644 index 00000000000..c9f69e3823f --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SeiferAlmasy.java @@ -0,0 +1,64 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksAloneControlledTriggeredAbility; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.effects.common.MayCastTargetCardEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.common.FilterInstantOrSorceryCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SeiferAlmasy extends CardImpl { + + private static final FilterCard filter = new FilterInstantOrSorceryCard( + "instant or sorcery card with mana value 3 or less from your graveyard" + ); + + static { + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); + } + + public SeiferAlmasy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.KNIGHT); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Whenever a creature you control attacks alone, it gains double strike until end of turn. + this.addAbility(new AttacksAloneControlledTriggeredAbility( + new GainAbilityTargetEffect(DoubleStrikeAbility.getInstance()) + .setText("it gains double strike until end of turn") + )); + + // Fire Cross -- Whenever Seifer Almasy deals combat damage to a player, you may cast target instant or sorcery card with mana value 3 or less from your graveyard without paying its mana cost. If that spell would be put into your graveyard, exile it instead. + Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility( + new MayCastTargetCardEffect(CastManaAdjustment.WITHOUT_PAYING_MANA_COST, true) + ); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability.withFlavorWord("Fire Cross")); + } + + private SeiferAlmasy(final SeiferAlmasy card) { + super(card); + } + + @Override + public SeiferAlmasy copy() { + return new SeiferAlmasy(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SelfDestruct.java b/Mage.Sets/src/mage/cards/s/SelfDestruct.java index 9c563c32492..fb3b01e108f 100644 --- a/Mage.Sets/src/mage/cards/s/SelfDestruct.java +++ b/Mage.Sets/src/mage/cards/s/SelfDestruct.java @@ -35,8 +35,8 @@ public final class SelfDestruct extends CardImpl { // Target creature you control deals X damage to any other target and X damage to itself, where X is its power. this.getSpellAbility().addEffect(new SelfDestructEffect()); - this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - this.getSpellAbility().addTarget(new TargetAnyTarget().setTargetTag(2)); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent().setTargetTag(1)); + this.getSpellAbility().addTarget(new TargetAnyTarget(filter).setTargetTag(2)); } private SelfDestruct(final SelfDestruct card) { diff --git a/Mage.Sets/src/mage/cards/s/SelvalaHeartOfTheWilds.java b/Mage.Sets/src/mage/cards/s/SelvalaHeartOfTheWilds.java index 6acdd4fcd86..f7d73720fb5 100644 --- a/Mage.Sets/src/mage/cards/s/SelvalaHeartOfTheWilds.java +++ b/Mage.Sets/src/mage/cards/s/SelvalaHeartOfTheWilds.java @@ -1,15 +1,14 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.mana.ManaEffect; import mage.abilities.effects.mana.AddManaInAnyCombinationEffect; +import mage.abilities.effects.mana.ManaEffect; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -21,8 +20,9 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** - * * @author maxlebedev */ public final class SelvalaHeartOfTheWilds extends CardImpl { @@ -41,11 +41,11 @@ public final class SelvalaHeartOfTheWilds extends CardImpl { // {G}, {T}: Add X mana in any combination of colors, where X is the greatest power among creatures you control. ManaEffect manaEffect = new AddManaInAnyCombinationEffect( - GreatestPowerAmongControlledCreaturesValue.instance, GreatestPowerAmongControlledCreaturesValue.instance, + GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES, GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES, ColoredManaSymbol.W, ColoredManaSymbol.U, ColoredManaSymbol.B, ColoredManaSymbol.R, ColoredManaSymbol.G); Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, manaEffect, new ManaCostsImpl<>("{G}")); ability.addCost(new TapSourceCost()); - ability.addHint(GreatestPowerAmongControlledCreaturesValue.getHint()); + ability.addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SephirothFabledSOLDIER.java b/Mage.Sets/src/mage/cards/s/SephirothFabledSOLDIER.java index 2ce8c407227..b0e3b72d950 100644 --- a/Mage.Sets/src/mage/cards/s/SephirothFabledSOLDIER.java +++ b/Mage.Sets/src/mage/cards/s/SephirothFabledSOLDIER.java @@ -13,6 +13,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.StaticFilters; +import mage.target.common.TargetOpponent; import mage.watchers.common.AbilityResolvedWatcher; import java.util.UUID; @@ -47,6 +48,7 @@ public final class SephirothFabledSOLDIER extends CardImpl { ); ability.addEffect(new GainLifeEffect(1).concatBy("and")); ability.addEffect(new IfAbilityHasResolvedXTimesEffect(4, new TransformSourceEffect())); + ability.addTarget(new TargetOpponent()); this.addAbility(ability, new AbilityResolvedWatcher()); } diff --git a/Mage.Sets/src/mage/cards/s/SephirothFallenHero.java b/Mage.Sets/src/mage/cards/s/SephirothFallenHero.java index 62be3e2c439..c69a8801ef1 100644 --- a/Mage.Sets/src/mage/cards/s/SephirothFallenHero.java +++ b/Mage.Sets/src/mage/cards/s/SephirothFallenHero.java @@ -28,12 +28,10 @@ import java.util.UUID; */ public final class SephirothFallenHero extends CardImpl { - private static final FilterPermanent filter = new FilterControlledCreaturePermanent(); - private static final FilterPermanent filter2 = new FilterPermanent("a modified creature"); + private static final FilterPermanent filter = new FilterControlledCreaturePermanent("a modified creature"); static { filter.add(ModifiedPredicate.instance); - filter2.add(ModifiedPredicate.instance); } public SephirothFallenHero(UUID ownerId, CardSetInfo setInfo) { @@ -48,7 +46,9 @@ public final class SephirothFallenHero extends CardImpl { // Jenova Cells -- Whenever Sephiroth attacks, you may put a cell counter on target creature. Until end of turn, each modified creature you control has base power and toughness 7/5. Ability ability = new AttacksTriggeredAbility(new SephirothFallenHeroEffect()); - ability.addEffect(new SetBasePowerToughnessAllEffect(7, 5, Duration.EndOfTurn, filter)); + ability.addEffect(new SetBasePowerToughnessAllEffect( + 7, 5, Duration.EndOfTurn, filter + ).setText("until end of turn, each modified creature you control has base power and toughness 7/5")); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability.withFlavorWord("Jenova Cells")); @@ -56,7 +56,7 @@ public final class SephirothFallenHero extends CardImpl { ability = new SimpleActivatedAbility( Zone.GRAVEYARD, new ReturnSourceFromGraveyardToBattlefieldEffect(true), new GenericManaCost(3) ); - ability.addCost(new SacrificeTargetCost(filter2)); + ability.addCost(new SacrificeTargetCost(filter)); this.addAbility(ability.withFlavorWord("The Reunion")); } diff --git a/Mage.Sets/src/mage/cards/s/SepulchralPrimordial.java b/Mage.Sets/src/mage/cards/s/SepulchralPrimordial.java index e9ca547de63..40f47af3a78 100644 --- a/Mage.Sets/src/mage/cards/s/SepulchralPrimordial.java +++ b/Mage.Sets/src/mage/cards/s/SepulchralPrimordial.java @@ -19,7 +19,7 @@ import mage.game.Game; import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCardInOpponentsGraveyard; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import java.util.HashSet; import java.util.Set; @@ -45,7 +45,7 @@ public final class SepulchralPrimordial extends CardImpl { // target creature card from that player's graveyard onto the battlefield under your control. Ability ability = new EntersBattlefieldTriggeredAbility(new SepulchralPrimordialEffect(), false); ability.addTarget(new TargetCardInOpponentsGraveyard(0, 1, filter)); - ability.setTargetAdjuster(new ForEachOpponentTargetsAdjuster(true)); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(true, true)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/Seraph.java b/Mage.Sets/src/mage/cards/s/Seraph.java index 8b552d90b92..4b7483063f2 100644 --- a/Mage.Sets/src/mage/cards/s/Seraph.java +++ b/Mage.Sets/src/mage/cards/s/Seraph.java @@ -1,6 +1,5 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; @@ -10,20 +9,19 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.SacrificeTargetEffect; -import mage.constants.SubType; import mage.abilities.keyword.FlyingAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; +import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +import java.util.UUID; /** * @@ -75,18 +73,19 @@ class SeraphEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Card creatureCard = game.getCard(getTargetPointer().getFirst(game, source)); - if (controller != null - && creatureCard != null - && game.getState().getZone(creatureCard.getId()) == Zone.GRAVEYARD) { // must be still in the graveyard - controller.moveCards(creatureCard, Zone.BATTLEFIELD, source, game, false, false, false, null); - OneShotEffect effect = new SacrificeTargetEffect(); + if (controller == null || creatureCard == null) { + return false; + } + controller.moveCards(creatureCard, Zone.BATTLEFIELD, source, game, false, false, false, null); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(creatureCard, game); + if (permanent != null) { + SacrificeTargetEffect effect = new SacrificeTargetEffect(); effect.setText("Sacrifice this if Seraph leaves the battlefield or its current controller loses control of it."); - effect.setTargetPointer(new FixedTarget(creatureCard.getId())); + effect.setTargetPointer(new FixedTarget(permanent, game)); SeraphDelayedTriggeredAbility dTA = new SeraphDelayedTriggeredAbility(effect, source.getSourceId()); game.addDelayedTriggeredAbility(dTA, source); - return true; } - return false; + return true; } @Override @@ -117,15 +116,7 @@ class SeraphDelayedTriggeredAbility extends DelayedTriggeredAbility { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.LOST_CONTROL - && event.getTargetId().equals(seraph)) { - return true; - } - if (event.getType() == GameEvent.EventType.ZONE_CHANGE - && event.getTargetId().equals(seraph)) { - return true; - } - return false; + return event.getTargetId().equals(seraph); } @Override diff --git a/Mage.Sets/src/mage/cards/s/SerraDisciple.java b/Mage.Sets/src/mage/cards/s/SerraDisciple.java index 3b301726f7f..65784381b24 100644 --- a/Mage.Sets/src/mage/cards/s/SerraDisciple.java +++ b/Mage.Sets/src/mage/cards/s/SerraDisciple.java @@ -10,15 +10,12 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.filter.FilterSpell; -import mage.filter.common.FilterHistoricSpell; +import mage.filter.StaticFilters; import java.util.UUID; public final class SerraDisciple extends CardImpl { - private static final FilterSpell filter = new FilterHistoricSpell(); - public SerraDisciple(UUID ownerId, CardSetInfo cardSetInfo) { super(ownerId, cardSetInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); subtype.add(SubType.BIRD, SubType.CLERIC); @@ -31,7 +28,7 @@ public final class SerraDisciple extends CardImpl { // Whenever you cast a historic spell, Serra Disciple gets +1/+1 until end of turn addAbility(new SpellCastControllerTriggeredAbility( - new BoostSourceEffect(1, 1, Duration.EndOfTurn), filter, false + new BoostSourceEffect(1, 1, Duration.EndOfTurn), StaticFilters.FILTER_SPELL_HISTORIC, false )); } diff --git a/Mage.Sets/src/mage/cards/s/SerratedArrows.java b/Mage.Sets/src/mage/cards/s/SerratedArrows.java index e65cf0ba033..64cb51a52f1 100644 --- a/Mage.Sets/src/mage/cards/s/SerratedArrows.java +++ b/Mage.Sets/src/mage/cards/s/SerratedArrows.java @@ -1,34 +1,35 @@ package mage.cards.s; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; +import mage.constants.ComparisonType; import mage.counters.CounterType; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; /** - * * @author LevelX2 */ public final class SerratedArrows extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.ARROWHEAD, ComparisonType.EQUAL_TO, 0); + public SerratedArrows(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); // Serrated Arrows enters the battlefield with three arrowhead counters on it. Effect effect = new AddCountersSourceEffect(CounterType.ARROWHEAD.createInstance(3)); @@ -36,16 +37,12 @@ public final class SerratedArrows extends CardImpl { this.addAbility(new EntersBattlefieldAbility(effect)); // At the beginning of your upkeep, if there are no arrowhead counters on Serrated Arrows, sacrifice it. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility(new SacrificeSourceEffect()), - new SourceHasCounterCondition(CounterType.ARROWHEAD, 0, 0), - "At the beginning of your upkeep, if there are no arrowhead counters on {this}, sacrifice it" - )); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SacrificeSourceEffect().setText("sacrifice it")).withInterveningIf(condition)); // {T}, Remove an arrowhead counter from Serrated Arrows: Put a -1/-1 counter on target creature. Ability ability = new SimpleActivatedAbility( - new AddCountersTargetEffect(CounterType.M1M1.createInstance()), - new TapSourceCost()); + new AddCountersTargetEffect(CounterType.M1M1.createInstance()), new TapSourceCost() + ); ability.addCost(new RemoveCountersSourceCost(CounterType.ARROWHEAD.createInstance())); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/SerumPowder.java b/Mage.Sets/src/mage/cards/s/SerumPowder.java index 8200726143d..42530ac3040 100644 --- a/Mage.Sets/src/mage/cards/s/SerumPowder.java +++ b/Mage.Sets/src/mage/cards/s/SerumPowder.java @@ -48,7 +48,7 @@ public final class SerumPowder extends CardImpl { class SerumPowderReplaceEffect extends ReplacementEffectImpl { SerumPowderReplaceEffect() { super(Duration.EndOfGame, Outcome.Detriment); - staticText = "Any time you could mulligan and {this} is in your hand, you may exile all the cards from your hand, then draw that many cards"; + staticText = "Any time you could mulligan and this card is in your hand, you may exile all the cards from your hand, then draw that many cards"; } private SerumPowderReplaceEffect(final SerumPowderReplaceEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SetonsScout.java b/Mage.Sets/src/mage/cards/s/SetonsScout.java index 747cac21439..5013d005e4d 100644 --- a/Mage.Sets/src/mage/cards/s/SetonsScout.java +++ b/Mage.Sets/src/mage/cards/s/SetonsScout.java @@ -35,7 +35,7 @@ public final class SetonsScout extends CardImpl { // Threshold - Seton's Scout gets +2/+2 as long as seven or more cards are in your graveyard. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), ThresholdCondition.instance, - "As long as seven or more cards are in your graveyard, {this} gets +2/+2" + "{this} gets +2/+2 as long as seven or more cards are in your graveyard" )).setAbilityWord(AbilityWord.THRESHOLD)); } diff --git a/Mage.Sets/src/mage/cards/s/ShacklesOfTreachery.java b/Mage.Sets/src/mage/cards/s/ShacklesOfTreachery.java index 13c9ae218ad..3d77c3434f8 100644 --- a/Mage.Sets/src/mage/cards/s/ShacklesOfTreachery.java +++ b/Mage.Sets/src/mage/cards/s/ShacklesOfTreachery.java @@ -1,6 +1,5 @@ package mage.cards.s; -import mage.MageObject; import mage.abilities.TriggeredAbility; import mage.abilities.common.DealsDamageSourceTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; @@ -12,12 +11,9 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.SubType; import mage.filter.FilterPermanent; -import mage.filter.common.FilterEquipmentPermanent; -import mage.filter.predicate.ObjectSourcePlayer; -import mage.filter.predicate.ObjectSourcePlayerPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.filter.predicate.permanent.AttachedToSourcePredicate; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -29,10 +25,10 @@ import java.util.UUID; public final class ShacklesOfTreachery extends CardImpl { private static final FilterPermanent filter - = new FilterEquipmentPermanent("Equipment attached to it"); + = new FilterPermanent(SubType.EQUIPMENT, "Equipment attached to it"); static { - filter.add(ShacklesOfTreacheryPredicate.instance); + filter.add(AttachedToSourcePredicate.instance); } public ShacklesOfTreachery(UUID ownerId, CardSetInfo setInfo) { @@ -64,13 +60,3 @@ public final class ShacklesOfTreachery extends CardImpl { return new ShacklesOfTreachery(this); } } - -enum ShacklesOfTreacheryPredicate implements ObjectSourcePlayerPredicate { - instance; - - @Override - public boolean apply(ObjectSourcePlayer input, Game game) { - Permanent permanent = input.getSource().getSourcePermanentIfItStillExists(game); - return permanent != null && permanent.getAttachments().contains(input.getObject().getId()); - } -} diff --git a/Mage.Sets/src/mage/cards/s/ShadowMysteriousAssassin.java b/Mage.Sets/src/mage/cards/s/ShadowMysteriousAssassin.java new file mode 100644 index 00000000000..c1e091b2b31 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ShadowMysteriousAssassin.java @@ -0,0 +1,103 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterNonlandPermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetSacrifice; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ShadowMysteriousAssassin extends CardImpl { + + public ShadowMysteriousAssassin(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ASSASSIN); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // Throw -- Whenever Shadow deals combat damage to a player, you may sacrifice another nonland permanent. If you do, draw two cards and each opponent loses life equal to the mana value of the sacrificed permanent. + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new ShadowMysteriousAssassinEffect()).withFlavorWord("Throw")); + } + + private ShadowMysteriousAssassin(final ShadowMysteriousAssassin card) { + super(card); + } + + @Override + public ShadowMysteriousAssassin copy() { + return new ShadowMysteriousAssassin(this); + } +} + +class ShadowMysteriousAssassinEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterNonlandPermanent("another nonland permanent"); + + static { + filter.add(AnotherPredicate.instance); + } + + ShadowMysteriousAssassinEffect() { + super(Outcome.Benefit); + staticText = "you may sacrifice another nonland permanent. If you do, " + + "draw two cards and each opponent loses life equal to the mana value of the sacrificed permanent"; + } + + private ShadowMysteriousAssassinEffect(final ShadowMysteriousAssassinEffect effect) { + super(effect); + } + + @Override + public ShadowMysteriousAssassinEffect copy() { + return new ShadowMysteriousAssassinEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + TargetSacrifice target = new TargetSacrifice(0, 1, filter); + player.choose(Outcome.Sacrifice, target, source, game); + Permanent permanent = game.getPermanent(target.getFirstTarget()); + if (permanent == null || !permanent.sacrifice(source, game)) { + return false; + } + int mv = permanent.getManaValue(); + player.drawCards(2, source, game); + if (mv < 1) { + return true; + } + for (UUID opponentId : game.getOpponents(source.getControllerId())) { + Optional.ofNullable(opponentId) + .map(game::getPlayer) + .ifPresent(opponent -> opponent.loseLife(mv, game, source, false)); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/Shadowbane.java b/Mage.Sets/src/mage/cards/s/Shadowbane.java new file mode 100644 index 00000000000..c3c5be50446 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/Shadowbane.java @@ -0,0 +1,96 @@ +package mage.cards.s; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.PreventionEffectData; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class Shadowbane extends CardImpl { + + public Shadowbane(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); + + // The next time a source of your choice would deal damage to you and/or creatures you control this turn, prevent that damage. If damage from a black source is prevented this way, you gain that much life. + this.getSpellAbility().addEffect(new ShadowbanePreventionEffect()); + } + + private Shadowbane(final Shadowbane card) { + super(card); + } + + @Override + public Shadowbane copy() { + return new Shadowbane(this); + } +} + +class ShadowbanePreventionEffect extends PreventNextDamageFromChosenSourceEffect { + + ShadowbanePreventionEffect() { + super(Duration.EndOfTurn, false, ShadowbanePreventionApplier.instance); + } + + private ShadowbanePreventionEffect(final ShadowbanePreventionEffect effect) { + super(effect); + } + + @Override + public PreventNextDamageFromChosenSourceEffect copy() { + return new ShadowbanePreventionEffect(this); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (!super.applies(event, source, game)) { + return false; + } + UUID controllerId = source.getControllerId(); + UUID targetId = event.getTargetId(); + if (targetId.equals(controllerId)) { + return true; // damage to you + } + Permanent permanent = game.getPermanent(targetId); + return StaticFilters.FILTER_CONTROLLED_CREATURE.match(permanent, controllerId, source, game); // damage to creatures you control + } + +} + +enum ShadowbanePreventionApplier implements PreventNextDamageFromChosenSourceEffect.ApplierOnPrevention { + instance; + + public String getText() { + return "If damage from a black source is prevented this way, you gain that much life"; + } + + public boolean apply(PreventionEffectData data, Target target, GameEvent event, Ability source, Game game) { + if (data == null || data.getPreventedDamage() <= 0) { + return false; + } + MageObject sourceObject = game.getObject(target.getFirstTarget()); + if (!sourceObject.getColor(game).isBlack()) { + return false; + } + int prevented = data.getPreventedDamage(); + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + controller.gainLife(prevented, game, source); + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/ShadowfaxLordOfHorses.java b/Mage.Sets/src/mage/cards/s/ShadowfaxLordOfHorses.java index 5eeb03f8b0a..4256be9eb53 100644 --- a/Mage.Sets/src/mage/cards/s/ShadowfaxLordOfHorses.java +++ b/Mage.Sets/src/mage/cards/s/ShadowfaxLordOfHorses.java @@ -19,6 +19,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInHand; +import mage.util.CardUtil; /** * @@ -99,7 +100,7 @@ class ShadowfaxLordOfHorsesEffect extends OneShotEffect { } player.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, true, null); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { game.getCombat().addAttackingCreature(permanent.getId(), game); } diff --git a/Mage.Sets/src/mage/cards/s/ShallowGrave.java b/Mage.Sets/src/mage/cards/s/ShallowGrave.java index 1d316598ec0..4fb06926b0f 100644 --- a/Mage.Sets/src/mage/cards/s/ShallowGrave.java +++ b/Mage.Sets/src/mage/cards/s/ShallowGrave.java @@ -1,4 +1,3 @@ - package mage.cards.s; import java.util.UUID; @@ -21,6 +20,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @@ -74,7 +74,7 @@ class ShallowGraveEffect extends OneShotEffect { } if (lastCreatureCard != null) { if (controller.moveCards(lastCreatureCard, Zone.BATTLEFIELD, source, game)) { - Permanent returnedCreature = game.getPermanent(lastCreatureCard.getId()); + Permanent returnedCreature = CardUtil.getPermanentFromCardPutToBattlefield(lastCreatureCard, game); if (returnedCreature != null) { FixedTarget blueprintTarget = new FixedTarget(returnedCreature, game); // Gains Haste diff --git a/Mage.Sets/src/mage/cards/s/ShantottoTacticianMagician.java b/Mage.Sets/src/mage/cards/s/ShantottoTacticianMagician.java index 1c87eaab586..9b99032bec0 100644 --- a/Mage.Sets/src/mage/cards/s/ShantottoTacticianMagician.java +++ b/Mage.Sets/src/mage/cards/s/ShantottoTacticianMagician.java @@ -47,7 +47,7 @@ public final class ShantottoTacticianMagician extends CardImpl { ability.addEffect(new ConditionalOneShotEffect( new DrawCardSourceControllerEffect(1), ShantottoTacticianMagicianCondition.instance, - "If X is 4 or greater, draw a card" + "If X is 4 or more, draw a card" )); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/ShardmagesRescue.java b/Mage.Sets/src/mage/cards/s/ShardmagesRescue.java index 5d413c2ed3c..0d6126f591e 100644 --- a/Mage.Sets/src/mage/cards/s/ShardmagesRescue.java +++ b/Mage.Sets/src/mage/cards/s/ShardmagesRescue.java @@ -42,7 +42,7 @@ public final class ShardmagesRescue extends CardImpl { // As long as Shardmage's Rescue entered this turn, enchanted creature has hexproof. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( new GainAbilityAttachedEffect(HexproofAbility.getInstance(), AttachmentType.AURA), - SourceEnteredThisTurnCondition.instance, + SourceEnteredThisTurnCondition.DID, "as long as {this} entered this turn, enchanted creature has hexproof" ))); diff --git a/Mage.Sets/src/mage/cards/s/SharkTyphoon.java b/Mage.Sets/src/mage/cards/s/SharkTyphoon.java index 93f2129b3ae..97e6883c30a 100644 --- a/Mage.Sets/src/mage/cards/s/SharkTyphoon.java +++ b/Mage.Sets/src/mage/cards/s/SharkTyphoon.java @@ -118,6 +118,6 @@ class SharkTyphoonTriggeredAbility extends ZoneChangeTriggeredAbility { @Override public String getRule() { - return "When you cycle {this}, create an X/X blue Shark creature token with flying."; + return "When you cycle this card, create an X/X blue Shark creature token with flying."; } } diff --git a/Mage.Sets/src/mage/cards/s/SharlayanNationOfScholars.java b/Mage.Sets/src/mage/cards/s/SharlayanNationOfScholars.java new file mode 100644 index 00000000000..781acd9ee0c --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SharlayanNationOfScholars.java @@ -0,0 +1,39 @@ +package mage.cards.s; + +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SharlayanNationOfScholars extends CardImpl { + + public SharlayanNationOfScholars(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.subtype.add(SubType.TOWN); + + // This land enters tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {W} or {U}. + this.addAbility(new WhiteManaAbility()); + this.addAbility(new BlueManaAbility()); + } + + private SharlayanNationOfScholars(final SharlayanNationOfScholars card) { + super(card); + } + + @Override + public SharlayanNationOfScholars copy() { + return new SharlayanNationOfScholars(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SharpEyedRookie.java b/Mage.Sets/src/mage/cards/s/SharpEyedRookie.java index c164cb2ccb1..ac0ed0df52a 100644 --- a/Mage.Sets/src/mage/cards/s/SharpEyedRookie.java +++ b/Mage.Sets/src/mage/cards/s/SharpEyedRookie.java @@ -14,6 +14,7 @@ import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.util.CardUtil; import java.util.UUID; @@ -68,17 +69,13 @@ class SharpEyedRookieTriggeredAbility extends EntersBattlefieldAllTriggeredAbili @Override public boolean checkInterveningIfClause(Game game) { Permanent sourcePermanent = getSourcePermanentOrLKI(game); - Permanent permanentEntering = (Permanent) this - .getEffects() - .stream() - .map(effect -> effect.getValue("permanentEnteringBattlefield")) - .findFirst() - .orElse(null); return sourcePermanent != null - && permanentEntering != null && sourcePermanent.isCreature(game) - && permanentEntering.isCreature(game) - && (permanentEntering.getPower().getValue() > sourcePermanent.getPower().getValue() - || permanentEntering.getToughness().getValue() > sourcePermanent.getToughness().getValue()); + && CardUtil + .getEffectValueFromAbility(this, "permanentEnteringBattlefield", Permanent.class) + .filter(permanent -> permanent.isCreature(game)) + .filter(permanent -> sourcePermanent.getPower().getValue() < permanent.getPower().getValue() + || sourcePermanent.getToughness().getValue() < permanent.getToughness().getValue()) + .isPresent(); } } diff --git a/Mage.Sets/src/mage/cards/s/ShaunFatherOfSynths.java b/Mage.Sets/src/mage/cards/s/ShaunFatherOfSynths.java index d4047a1f080..e0f5767d246 100644 --- a/Mage.Sets/src/mage/cards/s/ShaunFatherOfSynths.java +++ b/Mage.Sets/src/mage/cards/s/ShaunFatherOfSynths.java @@ -1,9 +1,7 @@ package mage.cards.s; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.TriggeredAbility; +import mage.abilities.Ability; import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; import mage.abilities.effects.Effect; @@ -14,6 +12,8 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; +import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.AnotherPredicate; @@ -21,8 +21,9 @@ import mage.filter.predicate.permanent.AttackingPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author alexander-novo */ public class ShaunFatherOfSynths extends CardImpl { @@ -41,7 +42,7 @@ public class ShaunFatherOfSynths extends CardImpl { } public ShaunFatherOfSynths(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[] { CardType.CREATURE }, "{3}{U}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{R}"); this.supertype.add(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); @@ -58,11 +59,14 @@ public class ShaunFatherOfSynths extends CardImpl { token.addCardType(CardType.CREATURE); token.addCardType(CardType.ARTIFACT); token.addSubType(SubType.SYNTH); - }).setText( - "create a tapped and attacking token that's a copy of target attacking legendary creature you control other than Shaun, except it's not legendary and it's a Synth artifact creature in addition to its other types"); - TriggeredAbility ability = new AttacksWithCreaturesTriggeredAbility(effect, 1); + }).setText("create a tapped and attacking token that's a copy of target " + + "attacking legendary creature you control other than {this}, " + + "except it's not legendary and it's a Synth artifact creature in addition to its other types"); + Ability ability = new AttacksWithCreaturesTriggeredAbility( + Zone.BATTLEFIELD, effect, 1, + StaticFilters.FILTER_PERMANENT_CREATURES, false, true + ); ability.addTarget(new TargetPermanent(attackFilter)); - ability.setOptional(); this.addAbility(ability); // When Shaun leaves the battlefield, exile all Synth tokens you control. diff --git a/Mage.Sets/src/mage/cards/s/ShieldMare.java b/Mage.Sets/src/mage/cards/s/ShieldMare.java index 1a88cd50c2b..47f62ceef21 100644 --- a/Mage.Sets/src/mage/cards/s/ShieldMare.java +++ b/Mage.Sets/src/mage/cards/s/ShieldMare.java @@ -45,7 +45,7 @@ public final class ShieldMare extends CardImpl { // When Shield Mare enters the battlefield or becomes the target of a spell or ability and opponent controls, you gain 3 life. this.addAbility(new OrTriggeredAbility(Zone.ALL, new GainLifeEffect(3), false, - "Whenever {this} enters or becomes the target of a spell or ability an opponent controls, ", + "When {this} enters or becomes the target of a spell or ability an opponent controls, ", new EntersBattlefieldTriggeredAbility(null), new BecomesTargetSourceTriggeredAbility(null, StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS))); } diff --git a/Mage.Sets/src/mage/cards/s/ShiftingShadow.java b/Mage.Sets/src/mage/cards/s/ShiftingShadow.java index 59cca89b89a..a43a53b55e0 100644 --- a/Mage.Sets/src/mage/cards/s/ShiftingShadow.java +++ b/Mage.Sets/src/mage/cards/s/ShiftingShadow.java @@ -16,6 +16,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import mage.util.CardUtil; import java.util.UUID; @@ -135,7 +136,7 @@ class ShiftingShadowEffect extends OneShotEffect { Permanent creature; if (card != null) { player.moveCards(card, Zone.BATTLEFIELD, source, game); - creature = game.getPermanent(card.getId()); + creature = CardUtil.getPermanentFromCardPutToBattlefield(card, game); } else { creature = null; } diff --git a/Mage.Sets/src/mage/cards/s/ShiftyDoppelganger.java b/Mage.Sets/src/mage/cards/s/ShiftyDoppelganger.java index 719c08d3d3d..516673e472e 100644 --- a/Mage.Sets/src/mage/cards/s/ShiftyDoppelganger.java +++ b/Mage.Sets/src/mage/cards/s/ShiftyDoppelganger.java @@ -20,6 +20,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInHand; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -57,7 +58,7 @@ class ShiftyDoppelgangerExileEffect extends OneShotEffect { super(Outcome.PutCreatureInPlay); this.staticText = "You may put a creature card from your hand onto the battlefield. If you do, " + "that creature gains haste until end of turn. At the beginning of the next end step, " + - "sacrifice that creature. If you do, return {this} to the battlefield"; + "sacrifice that creature. If you do, return this card to the battlefield"; } private ShiftyDoppelgangerExileEffect(final ShiftyDoppelgangerExileEffect effect) { @@ -87,7 +88,7 @@ class ShiftyDoppelgangerExileEffect extends OneShotEffect { return false; } player.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent creature = game.getPermanent(card.getId()); + Permanent creature = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (creature == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/s/ShilgengarSireOfFamine.java b/Mage.Sets/src/mage/cards/s/ShilgengarSireOfFamine.java index 1059de8297a..6849c2ad15f 100644 --- a/Mage.Sets/src/mage/cards/s/ShilgengarSireOfFamine.java +++ b/Mage.Sets/src/mage/cards/s/ShilgengarSireOfFamine.java @@ -97,7 +97,7 @@ enum ShilgengarSireOfFamineCondition implements Condition { .castStream(source.getCosts().stream(), SacrificeTargetCost.class) .map(SacrificeTargetCost::getPermanents) .flatMap(Collection::stream) - .anyMatch(p -> p.getSubtype(game).contains(SubType.ANGEL)); + .anyMatch(p -> p.hasSubtype(SubType.ANGEL, game)); } @Override diff --git a/Mage.Sets/src/mage/cards/s/ShiningShoal.java b/Mage.Sets/src/mage/cards/s/ShiningShoal.java index 10f1f3aca49..95cbe6916bc 100644 --- a/Mage.Sets/src/mage/cards/s/ShiningShoal.java +++ b/Mage.Sets/src/mage/cards/s/ShiningShoal.java @@ -1,17 +1,19 @@ package mage.cards.s; import mage.MageObject; +import mage.MageObjectReference; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.common.ExileFromHandCost; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.ExileFromHandCostCardConvertedMana; -import mage.abilities.effects.common.RedirectDamageFromSourceToTargetEffect; +import mage.abilities.effects.RedirectionEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.Outcome; import mage.constants.SubType; import mage.filter.common.FilterOwnedCard; import mage.filter.predicate.mageobject.ColorPredicate; @@ -50,7 +52,6 @@ public final class ShiningShoal extends CardImpl { this.getSpellAbility().addEffect(new ShiningShoalRedirectDamageTargetEffect( Duration.EndOfTurn, ExileFromHandCostCardConvertedMana.instance )); - this.getSpellAbility().addTarget(new TargetSource()); this.getSpellAbility().addTarget(new TargetAnyTarget()); } @@ -64,9 +65,10 @@ public final class ShiningShoal extends CardImpl { } } -class ShiningShoalRedirectDamageTargetEffect extends RedirectDamageFromSourceToTargetEffect { +class ShiningShoalRedirectDamageTargetEffect extends RedirectionEffect { private final DynamicValue dynamicAmount; + private MageObjectReference mageObjectReference; public ShiningShoalRedirectDamageTargetEffect(Duration duration, DynamicValue dynamicAmount) { super(duration, 0, UsageType.ONE_USAGE_AT_THE_SAME_TIME); @@ -77,6 +79,7 @@ class ShiningShoalRedirectDamageTargetEffect extends RedirectDamageFromSourceToT private ShiningShoalRedirectDamageTargetEffect(final ShiningShoalRedirectDamageTargetEffect effect) { super(effect); this.dynamicAmount = effect.dynamicAmount; + this.mageObjectReference = effect.mageObjectReference; } @Override @@ -88,6 +91,9 @@ class ShiningShoalRedirectDamageTargetEffect extends RedirectDamageFromSourceToT public void init(Ability source, Game game) { super.init(source, game); amountToRedirect = dynamicAmount.calculate(game, source, this); + TargetSource target = new TargetSource(); + target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); + mageObjectReference = new MageObjectReference(target.getFirstTarget(), game); } @Override @@ -96,15 +102,13 @@ class ShiningShoalRedirectDamageTargetEffect extends RedirectDamageFromSourceToT // get source of the damage event MageObject sourceObject = game.getObject(event.getSourceId()); - // get the chosen damage source - MageObject chosenSourceObject = game.getObject(source.getFirstTarget()); // does the source of the damage exist? if (sourceObject == null) { game.informPlayers("Couldn't find source of damage"); return false; } // do the 2 objects match? - if (chosenSourceObject == null || !sourceObject.getId().equals(chosenSourceObject.getId())) { + if (!mageObjectReference.refersTo(sourceObject, game)) { return false; } @@ -114,7 +118,7 @@ class ShiningShoalRedirectDamageTargetEffect extends RedirectDamageFromSourceToT if (permanent != null && permanent.isCreature(game)) { if (permanent.isControlledBy(source.getControllerId())) { // it's your creature - redirectTarget = source.getTargets().get(1); + redirectTarget = source.getTargets().get(0); return true; } } @@ -123,7 +127,7 @@ class ShiningShoalRedirectDamageTargetEffect extends RedirectDamageFromSourceToT if (player != null) { if (player.getId().equals(source.getControllerId())) { // it is you - redirectTarget = source.getTargets().get(1); + redirectTarget = source.getTargets().get(0); return true; } } diff --git a/Mage.Sets/src/mage/cards/s/ShinryuTranscendentRival.java b/Mage.Sets/src/mage/cards/s/ShinryuTranscendentRival.java index 3fdbaeda184..8775979f768 100644 --- a/Mage.Sets/src/mage/cards/s/ShinryuTranscendentRival.java +++ b/Mage.Sets/src/mage/cards/s/ShinryuTranscendentRival.java @@ -120,13 +120,14 @@ class ShinryuTranscendentRivalTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.LOSES; + return event.getType() == GameEvent.EventType.LOST; } @Override public boolean checkTrigger(GameEvent event, Game game) { + int zcc = game.getState().getZoneChangeCounter(this.getSourceId()); return Optional - .ofNullable(this.getId() + "_" + this.getSourceObjectZoneChangeCounter() + "_opponent") + .of(this.getSourceId() + "_" + zcc + "_opponent") .map(game.getState()::getValue) .map(event.getPlayerId()::equals) .orElse(false); diff --git a/Mage.Sets/src/mage/cards/s/ShivaWardenOfIce.java b/Mage.Sets/src/mage/cards/s/ShivaWardenOfIce.java index 44eb95bf35b..cf667f30444 100644 --- a/Mage.Sets/src/mage/cards/s/ShivaWardenOfIce.java +++ b/Mage.Sets/src/mage/cards/s/ShivaWardenOfIce.java @@ -2,7 +2,7 @@ package mage.cards.s; import mage.MageInt; import mage.abilities.common.SagaAbility; -import mage.abilities.effects.common.ExileAndReturnSourceEffect; +import mage.abilities.effects.common.ExileSourceAndReturnFaceUpEffect; import mage.abilities.effects.common.TapAllEffect; import mage.abilities.effects.common.combat.CantBeBlockedTargetEffect; import mage.cards.CardImpl; @@ -49,7 +49,7 @@ public final class ShivaWardenOfIce extends CardImpl { // III -- Cold Snap -- Tap all lands your opponents control. Exile Shiva, then return it to the battlefield. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, ability -> { ability.addEffect(new TapAllEffect(filter)); - ability.addEffect(new ExileAndReturnSourceEffect(PutCards.BATTLEFIELD)); + ability.addEffect(new ExileSourceAndReturnFaceUpEffect()); ability.withFlavorWord("Cold Snap"); }); this.addAbility(sagaAbility); diff --git a/Mage.Sets/src/mage/cards/s/ShorelineLooter.java b/Mage.Sets/src/mage/cards/s/ShorelineLooter.java index a26d4ab8172..04ee4a103ff 100644 --- a/Mage.Sets/src/mage/cards/s/ShorelineLooter.java +++ b/Mage.Sets/src/mage/cards/s/ShorelineLooter.java @@ -40,7 +40,7 @@ public final class ShorelineLooter extends CardImpl { Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new DrawCardSourceControllerEffect(1)); ability.addEffect(new ConditionalOneShotEffect( new DiscardControllerEffect(1), condition, - "then discard a card unless seven or more cards are in your graveyard" + "Then discard a card unless seven or more cards are in your graveyard" )); this.addAbility(ability.setAbilityWord(AbilityWord.THRESHOLD)); } diff --git a/Mage.Sets/src/mage/cards/s/SianiEyeOfTheStorm.java b/Mage.Sets/src/mage/cards/s/SianiEyeOfTheStorm.java index e9ad36d3a34..066f53d975b 100644 --- a/Mage.Sets/src/mage/cards/s/SianiEyeOfTheStorm.java +++ b/Mage.Sets/src/mage/cards/s/SianiEyeOfTheStorm.java @@ -1,23 +1,23 @@ package mage.cards.s; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValuePositiveHint; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.PartnerAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.AbilityPredicate; import mage.filter.predicate.permanent.AttackingPredicate; -import mage.game.Game; -import mage.players.Player; import java.util.UUID; @@ -26,6 +26,16 @@ import java.util.UUID; */ public final class SianiEyeOfTheStorm extends CardImpl { + private static final FilterPermanent filter = new FilterCreaturePermanent("attacking creatures with flying"); + + static { + filter.add(new AbilityPredicate(FlyingAbility.class)); + filter.add(AttackingPredicate.instance); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, null); + private static final Hint hint = new ValuePositiveHint("Number of Rats you control", xValue); + public SianiEyeOfTheStorm(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); @@ -39,7 +49,7 @@ public final class SianiEyeOfTheStorm extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever Siani, Eye of the Storm attacks, scry X, where X is the number of attacking creatures with flying. - this.addAbility(new AttacksTriggeredAbility(new SianiEyeOfTheStormEffect(), false)); + this.addAbility(new AttacksTriggeredAbility(new ScryEffect(xValue)).addHint(hint)); // Partner this.addAbility(PartnerAbility.getInstance()); @@ -53,38 +63,4 @@ public final class SianiEyeOfTheStorm extends CardImpl { public SianiEyeOfTheStorm copy() { return new SianiEyeOfTheStorm(this); } -} - -class SianiEyeOfTheStormEffect extends OneShotEffect { - - private static final FilterPermanent filter = new FilterCreaturePermanent(); - - static { - filter.add(new AbilityPredicate(FlyingAbility.class)); - filter.add(AttackingPredicate.instance); - } - - SianiEyeOfTheStormEffect() { - super(Outcome.Benefit); - staticText = "scry X, where X is the number of attacking creatures with flying"; - } - - private SianiEyeOfTheStormEffect(final SianiEyeOfTheStormEffect effect) { - super(effect); - } - - @Override - public SianiEyeOfTheStormEffect copy() { - return new SianiEyeOfTheStormEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player == null) { - return false; - } - int count = game.getBattlefield().count(filter, source.getControllerId(), source, game); - return count > 0 && player.scry(count, source, game); - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/SidequestCardCollection.java b/Mage.Sets/src/mage/cards/s/SidequestCardCollection.java new file mode 100644 index 00000000000..16874c1afbc --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SidequestCardCollection.java @@ -0,0 +1,44 @@ +package mage.cards.s; + +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.CardsInControllerGraveyardCondition; +import mage.abilities.effects.common.DrawDiscardControllerEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.keyword.TransformAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SidequestCardCollection extends CardImpl { + + private static final Condition condition = new CardsInControllerGraveyardCondition(8); + + public SidequestCardCollection(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}"); + + this.secondSideCardClazz = mage.cards.m.MagickedCard.class; + + // When this enchantment enters, draw three cards, then discard two cards. + this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawDiscardControllerEffect(3, 2))); + + // At the beginning of your end step, if eight or more cards are in your graveyard, transform this enchantment. + this.addAbility(new TransformAbility()); + this.addAbility(new BeginningOfEndStepTriggeredAbility(new TransformSourceEffect()).withInterveningIf(condition)); + } + + private SidequestCardCollection(final SidequestCardCollection card) { + super(card); + } + + @Override + public SidequestCardCollection copy() { + return new SidequestCardCollection(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SidequestHuntTheMark.java b/Mage.Sets/src/mage/cards/s/SidequestHuntTheMark.java new file mode 100644 index 00000000000..4b1ce5df4a5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SidequestHuntTheMark.java @@ -0,0 +1,129 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.TransformAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.constants.WatcherScope; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.token.TreasureToken; +import mage.target.common.TargetCreaturePermanent; +import mage.watchers.Watcher; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SidequestHuntTheMark extends CardImpl { + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + new FilterControlledPermanent(SubType.TREASURE), ComparisonType.MORE_THAN, 2 + ); + private static final Hint hint = new ValueHint( + "Treasures you control", new PermanentsOnBattlefieldCount(new FilterControlledPermanent(SubType.TREASURE)) + ); + + public SidequestHuntTheMark(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{B}{B}"); + + this.secondSideCardClazz = mage.cards.y.YiazmatUltimateMark.class; + + // When this enchantment enters, destroy up to one target creature. + Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect()); + ability.addTarget(new TargetCreaturePermanent(0, 1)); + this.addAbility(ability); + + // At the beginning of your end step, if a creature died under an opponent's control this turn, create a Treasure token. Then if you control three or more Treasures, transform this enchantment. + this.addAbility(new TransformAbility()); + ability = new BeginningOfEndStepTriggeredAbility(new CreateTokenEffect(new TreasureToken())) + .withInterveningIf(SidequestHuntTheMarkCondition.instance); + ability.addEffect(new ConditionalOneShotEffect( + new TransformSourceEffect(), condition, + "Then if you control three or more Treasures, transform {this}" + )); + this.addAbility(ability.addHint(hint).addHint(SidequestHuntTheMarkCondition.getHint()), new SidequestHuntTheMarkWatcher()); + } + + private SidequestHuntTheMark(final SidequestHuntTheMark card) { + super(card); + } + + @Override + public SidequestHuntTheMark copy() { + return new SidequestHuntTheMark(this); + } +} + +enum SidequestHuntTheMarkCondition implements Condition { + instance; + private static final Hint hint = new ConditionHint(instance); + + public static Hint getHint() { + return hint; + } + + @Override + public boolean apply(Game game, Ability source) { + return SidequestHuntTheMarkWatcher.checkPlayer(game, source); + } + + @Override + public String toString() { + return "a creature died under an opponent's control this turn"; + } +} + +class SidequestHuntTheMarkWatcher extends Watcher { + + private static final Set set = new HashSet<>(); + + SidequestHuntTheMarkWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.ZONE_CHANGE) { + return; + } + ZoneChangeEvent zEvent = ((ZoneChangeEvent) event); + if (zEvent.isDiesEvent() && zEvent.getTarget().isCreature(game)) { + set.addAll(game.getOpponents(zEvent.getTarget().getControllerId())); + } + } + + @Override + public void reset() { + super.reset(); + set.clear(); + } + + static boolean checkPlayer(Game game, Ability source) { + return game + .getState() + .getWatcher(SidequestHuntTheMarkWatcher.class) + .set + .contains(source.getControllerId()); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SidequestPlayBlitzball.java b/Mage.Sets/src/mage/cards/s/SidequestPlayBlitzball.java new file mode 100644 index 00000000000..394e1f3b6e4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SidequestPlayBlitzball.java @@ -0,0 +1,148 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.EndOfCombatTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.keyword.TransformAbility; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.WatcherScope; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.DamagedEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SidequestPlayBlitzball extends CardImpl { + + public SidequestPlayBlitzball(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); + + this.secondSideCardClazz = mage.cards.w.WorldChampionCelestialWeapon.class; + + // At the beginning of combat on your turn, target creature you control gets +2/+0 until end of turn. + Ability ability = new BeginningOfCombatTriggeredAbility(new BoostTargetEffect(2, 0)); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + + // At the end of combat on your turn, if a player was dealt 6 or more combat damage this turn, transform this enchantment, then attach it to a creature you control. + this.addAbility(new TransformAbility()); + ability = new EndOfCombatTriggeredAbility( + new TransformSourceEffect(), TargetController.YOU, false + ).withInterveningIf(SidequestPlayBlitzballCondition.instance); + ability.addEffect(new SidequestPlayBlitzballEffect()); + this.addAbility(ability, new SidequestPlayBlitzballWatcher()); + } + + private SidequestPlayBlitzball(final SidequestPlayBlitzball card) { + super(card); + } + + @Override + public SidequestPlayBlitzball copy() { + return new SidequestPlayBlitzball(this); + } +} + +enum SidequestPlayBlitzballCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + return SidequestPlayBlitzballWatcher.checkPlayers(game, source); + } + + @Override + public String toString() { + return "a player was dealt 6 or more combat damage this turn"; + } +} + +class SidequestPlayBlitzballEffect extends OneShotEffect { + + SidequestPlayBlitzballEffect() { + super(Outcome.Benefit); + staticText = ", then attach it to a creature you control"; + } + + private SidequestPlayBlitzballEffect(final SidequestPlayBlitzballEffect effect) { + super(effect); + } + + @Override + public SidequestPlayBlitzballEffect copy() { + return new SidequestPlayBlitzballEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (player == null || permanent == null || !game.getBattlefield().contains( + StaticFilters.FILTER_CONTROLLED_CREATURE, source, game, 1 + )) { + return false; + } + TargetPermanent target = new TargetControlledCreaturePermanent(); + target.withNotTarget(true); + player.choose(outcome, target, source, game); + return Optional + .ofNullable(target.getFirstTarget()) + .map(game::getPermanent) + .map(p -> p.addAttachment(p.getId(), source, game)) + .orElse(false); + } +} + +class SidequestPlayBlitzballWatcher extends Watcher { + + private final Map map = new HashMap<>(); + + SidequestPlayBlitzballWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.DAMAGED_PLAYER && ((DamagedEvent) event).isCombatDamage()) { + map.compute(event.getTargetId(), (u, i) -> i == null ? event.getAmount() : Integer.sum(i, event.getAmount())); + } + } + + @Override + public void reset() { + super.reset(); + map.clear(); + } + + private boolean checkMap(UUID playerId) { + return map.getOrDefault(playerId, 0) >= 6; + } + + static boolean checkPlayers(Game game, Ability source) { + return game + .getState() + .getPlayersInRange(source.getControllerId(), game) + .stream() + .anyMatch(game.getState().getWatcher(SidequestPlayBlitzballWatcher.class)::checkMap); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SidequestRaiseAChocobo.java b/Mage.Sets/src/mage/cards/s/SidequestRaiseAChocobo.java new file mode 100644 index 00000000000..da84b7ecc6d --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SidequestRaiseAChocobo.java @@ -0,0 +1,58 @@ +package mage.cards.s; + +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.TransformAbility; +import mage.abilities.triggers.BeginningOfFirstMainTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.filter.common.FilterControlledPermanent; +import mage.game.permanent.token.ChocoboToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SidequestRaiseAChocobo extends CardImpl { + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + new FilterControlledPermanent(SubType.BIRD, "you control four or more Birds"), + ComparisonType.MORE_THAN, 3 + ); + private static final Hint hint = new ValueHint( + "Birds you control", new PermanentsOnBattlefieldCount(new FilterControlledPermanent(SubType.BIRD)) + ); + + public SidequestRaiseAChocobo(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}"); + + this.secondSideCardClazz = mage.cards.b.BlackChocobo.class; + + // When this enchantment enters, create a 2/2 green Bird creature token with "Whenever a land you control enters, this token gets +1/+0 until end of turn." + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new ChocoboToken()))); + + // At the beginning of your first main phase, if you control four or more Birds, transform this enchantment. + this.addAbility(new TransformAbility()); + this.addAbility(new BeginningOfFirstMainTriggeredAbility(new TransformSourceEffect()) + .withInterveningIf(condition).addHint(hint)); + } + + private SidequestRaiseAChocobo(final SidequestRaiseAChocobo card) { + super(card); + } + + @Override + public SidequestRaiseAChocobo copy() { + return new SidequestRaiseAChocobo(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SigilOfSleep.java b/Mage.Sets/src/mage/cards/s/SigilOfSleep.java index e2d93bd8223..7856263da01 100644 --- a/Mage.Sets/src/mage/cards/s/SigilOfSleep.java +++ b/Mage.Sets/src/mage/cards/s/SigilOfSleep.java @@ -14,7 +14,7 @@ import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -40,7 +40,7 @@ public final class SigilOfSleep extends CardImpl { Effect effect = new ReturnToHandTargetEffect(); ability = new DealsDamageToAPlayerAttachedTriggeredAbility(effect, "enchanted", false, true, false); ability.addTarget(new TargetCreaturePermanent(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SimicAscendancy.java b/Mage.Sets/src/mage/cards/s/SimicAscendancy.java index 597f89cae71..bc8552fd34a 100644 --- a/Mage.Sets/src/mage/cards/s/SimicAscendancy.java +++ b/Mage.Sets/src/mage/cards/s/SimicAscendancy.java @@ -2,18 +2,18 @@ package mage.cards.s; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.dynamicvalue.common.SavedDamageValue; import mage.abilities.effects.common.WinGameSourceControllerEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TargetController; import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; @@ -21,19 +21,23 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; +import java.util.Optional; import java.util.UUID; /** * @author jmharmon */ - public final class SimicAscendancy extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.GROWTH, 20); + public SimicAscendancy(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{G}{U}"); // {1}{G}{U}: Put a +1/+1 counter on target creature you control. - Ability ability = new SimpleActivatedAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), new ManaCostsImpl<>("{1}{G}{U}")); + Ability ability = new SimpleActivatedAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), new ManaCostsImpl<>("{1}{G}{U}") + ); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); @@ -41,14 +45,7 @@ public final class SimicAscendancy extends CardImpl { this.addAbility(new SimicAscendancyTriggeredAbility()); // At the beginning of your upkeep, if Simic Ascendancy has twenty or more growth counters on it, you win the game. - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility( - Zone.BATTLEFIELD, TargetController.YOU, new WinGameSourceControllerEffect(), - false - ), new SourceHasCounterCondition(CounterType.GROWTH, 20, Integer.MAX_VALUE), - "At the beginning of your upkeep, if {this} has twenty " + - "or more growth counters on it, you win the game" - )); + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new WinGameSourceControllerEffect()).withInterveningIf(condition)); } private SimicAscendancy(final SimicAscendancy card) { @@ -64,7 +61,8 @@ public final class SimicAscendancy extends CardImpl { class SimicAscendancyTriggeredAbility extends TriggeredAbilityImpl { SimicAscendancyTriggeredAbility() { - super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.GROWTH.createInstance()), false); + super(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.GROWTH.createInstance(), SavedDamageValue.MANY), false); + this.setTriggerPhrase("Whenever one or more +1/+1 counters are put on a creature you control, "); } private SimicAscendancyTriggeredAbility(final SimicAscendancyTriggeredAbility ability) { @@ -83,27 +81,21 @@ class SimicAscendancyTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getData().equals(CounterType.P1P1.getName()) && event.getAmount() > 0) { - Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); - if (permanent == null) { - permanent = game.getPermanentEntering(event.getTargetId()); - } - if (permanent != null - && !event.getTargetId().equals(this.getSourceId()) - && permanent.isCreature(game) - && permanent.isControlledBy(this.getControllerId())) { - this.getEffects().clear(); - if (event.getAmount() > 0) { - this.addEffect(new AddCountersSourceEffect(CounterType.GROWTH.createInstance(event.getAmount()))); - } - return true; - } + if (!event.getData().equals(CounterType.P1P1.getName()) || event.getAmount() < 1) { + return false; } - return false; - } - - @Override - public String getRule() { - return "Whenever one or more +1/+1 counters are put on a creature you control, put that many growth counters on {this}."; + Permanent permanent = Optional + .ofNullable(event) + .map(GameEvent::getTargetId) + .map(game::getPermanentOrLKIBattlefield) + .orElseGet(() -> game.getPermanentEntering(event.getTargetId())); + if (permanent == null + || event.getTargetId().equals(this.getSourceId()) + || !permanent.isCreature(game) + || !permanent.isControlledBy(this.getControllerId())) { + return false; + } + this.getEffects().setValue("damage", event.getAmount()); + return true; } } diff --git a/Mage.Sets/src/mage/cards/s/SimicGuildmage.java b/Mage.Sets/src/mage/cards/s/SimicGuildmage.java index 861474e5e91..b70db6158b3 100644 --- a/Mage.Sets/src/mage/cards/s/SimicGuildmage.java +++ b/Mage.Sets/src/mage/cards/s/SimicGuildmage.java @@ -6,30 +6,32 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.MoveCounterTargetsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; import mage.counters.CounterType; -import mage.filter.Filter; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.common.FilterEnchantmentPermanent; import mage.filter.predicate.ObjectSourcePlayer; import mage.filter.predicate.ObjectSourcePlayerPredicate; import mage.filter.predicate.Predicates; import mage.filter.predicate.other.AnotherTargetPredicate; +import mage.filter.predicate.permanent.AttachedToPredicate; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.filter.predicate.permanent.PermanentIdPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.StackObject; import mage.players.Player; -import mage.target.Target; +import mage.target.TargetImpl; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.Optional; import java.util.UUID; /** @@ -37,10 +39,13 @@ import java.util.UUID; */ public final class SimicGuildmage extends CardImpl { - private static final FilterEnchantmentPermanent auraFilter = new FilterEnchantmentPermanent("Aura"); + private static final FilterPermanent filter = new FilterCreaturePermanent("another target creature with the same controller"); + private static final FilterPermanent auraFilter = new FilterPermanent(SubType.AURA, "Aura enchanting a permanent"); static { - auraFilter.add(SubType.AURA.getPredicate()); + filter.add(new AnotherTargetPredicate(2)); + filter.add(SameControllerPredicate.instance); + auraFilter.add(new AttachedToPredicate(StaticFilters.FILTER_PERMANENT)); } public SimicGuildmage(UUID ownerId, CardSetInfo setInfo) { @@ -51,26 +56,15 @@ public final class SimicGuildmage extends CardImpl { this.toughness = new MageInt(2); // {1}{G}: Move a +1/+1 counter from target creature onto another target creature with the same controller. - Ability countersAbility = new SimpleActivatedAbility(new MoveCounterFromTargetToTargetEffect(), new ManaCostsImpl<>("{1}{G}")); - TargetCreaturePermanent target = new TargetCreaturePermanent( - new FilterCreaturePermanent("creature (you take counter from)")); - target.setTargetTag(1); - countersAbility.addTarget(target); - - FilterCreaturePermanent filter = new FilterCreaturePermanent( - "another target creature with the same controller (counter goes to)"); - filter.add(new AnotherTargetPredicate(2)); - filter.add(new SameControllerPredicate()); - TargetCreaturePermanent target2 = new TargetCreaturePermanent(filter); - target2.setTargetTag(2); - countersAbility.addTarget(target2); + Ability countersAbility = new SimpleActivatedAbility(new MoveCounterTargetsEffect(CounterType.P1P1), new ManaCostsImpl<>("{1}{G}")); + countersAbility.addTarget(new TargetCreaturePermanent().withChooseHint("to remove a counter from").setTargetTag(1)); + countersAbility.addTarget(new TargetPermanent(filter).withChooseHint("to move a counter to").setTargetTag(2)); this.addAbility(countersAbility); // {1}{U}: Attach target Aura enchanting a permanent to another permanent with the same controller. Ability auraAbility = new SimpleActivatedAbility(new MoveAuraEffect(), new ManaCostsImpl<>("{1}{U}")); auraAbility.addTarget(new TargetPermanent(auraFilter)); this.addAbility(auraAbility); - } private SimicGuildmage(final SimicGuildmage card) { @@ -83,68 +77,29 @@ public final class SimicGuildmage extends CardImpl { } } -class MoveCounterFromTargetToTargetEffect extends OneShotEffect { - - MoveCounterFromTargetToTargetEffect() { - super(Outcome.Detriment); - this.staticText = "Move a +1/+1 counter from target creature onto another target creature with the same controller"; - } - - private MoveCounterFromTargetToTargetEffect(final MoveCounterFromTargetToTargetEffect effect) { - super(effect); - } - - @Override - public MoveCounterFromTargetToTargetEffect copy() { - return new MoveCounterFromTargetToTargetEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Permanent fromPermanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - Permanent toPermanent = null; - if (source.getTargets().size() > 1) { - toPermanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - } - if (fromPermanent == null || toPermanent == null || !fromPermanent.isControlledBy(toPermanent.getControllerId())) { - return false; - } - fromPermanent.removeCounters(CounterType.P1P1.createInstance(1), source, game); - toPermanent.addCounters(CounterType.P1P1.createInstance(1), source.getControllerId(), source, game); - return true; - } - return false; - - } -} - -class SameControllerPredicate implements ObjectSourcePlayerPredicate { +enum SameControllerPredicate implements ObjectSourcePlayerPredicate { + instance; @Override public boolean apply(ObjectSourcePlayer input, Game game) { StackObject source = game.getStack().getStackObject(input.getSourceId()); - if (source != null) { - if (source.getStackAbility().getTargets().isEmpty() - || source.getStackAbility().getTargets().get(0).getTargets().isEmpty()) { - return true; - } - Permanent firstTarget = game.getPermanent( - source.getStackAbility().getTargets().get(0).getTargets().get(0)); - Permanent inputPermanent = game.getPermanent(input.getObject().getId()); - if (firstTarget != null && inputPermanent != null) { - return firstTarget.isControlledBy(inputPermanent.getControllerId()); - } + if (source == null + || source.getStackAbility().getTargets().isEmpty() + || source.getStackAbility().getTargets().get(0).getTargets().isEmpty()) { + return true; } - return true; + Permanent firstTarget = game.getPermanent(source.getStackAbility().getTargets().get(0).getTargets().get(0)); + Permanent inputPermanent = game.getPermanent(input.getObject().getId()); + if (firstTarget == null || inputPermanent == null) { + return true; + } + return firstTarget.isControlledBy(inputPermanent.getControllerId()); } @Override public String toString() { return "Target with the same controller"; } - } class MoveAuraEffect extends OneShotEffect { @@ -175,40 +130,33 @@ class MoveAuraEffect extends OneShotEffect { */ Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Permanent aura = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (aura == null) { - return true; - } - Permanent fromPermanent = game.getPermanent(aura.getAttachedTo()); - if (fromPermanent == null) { - return false; - } - boolean passed = true; - Target chosenPermanentToAttachAuras = aura.getSpellAbility().getTargets().get(0).copy(); - chosenPermanentToAttachAuras.withNotTarget(true); - Filter filterChoice = chosenPermanentToAttachAuras.getFilter(); - filterChoice.add(new ControllerIdPredicate(fromPermanent.getControllerId())); - filterChoice.add(Predicates.not(new PermanentIdPredicate(fromPermanent.getId()))); - chosenPermanentToAttachAuras.withTargetName("a different " + filterChoice.getMessage() + " with the same controller as the " + filterChoice.getMessage() + " the target aura is attached to"); - if (chosenPermanentToAttachAuras.canChoose(source.getControllerId(), source, game) - && controller.choose(Outcome.Neutral, chosenPermanentToAttachAuras, source, game)) { - Permanent permanentToAttachAura = game.getPermanent(chosenPermanentToAttachAuras.getFirstTarget()); - if (permanentToAttachAura != null) { - // Check for protection - if (permanentToAttachAura.cantBeAttachedBy(aura, source, game, true)) { - passed = false; - } - if (passed) { - fromPermanent.removeAttachment(aura.getId(), source, game); - permanentToAttachAura.addAttachment(aura.getId(), source, game); - return true; - } - } - } - return true; + Permanent aura = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (controller == null || aura == null) { + return false; } - - return false; + FilterPermanent filter = new FilterPermanent( + "permanent" + Optional + .of(aura) + .map(Permanent::getAttachedTo) + .map(game::getControllerId) + .map(game::getPlayer) + .map(Player::getName) + .map(s -> " controlled by" + s) + .orElse("") + ); + filter.add(new ControllerIdPredicate(game.getControllerId(aura.getAttachedTo()))); + filter.add(Predicates.not(new PermanentIdPredicate(aura.getAttachedTo()))); + if (!game.getBattlefield().contains(filter, source.getControllerId(), source, game, 1)) { + return false; + } + TargetPermanent target = new TargetPermanent(filter); + target.withNotTarget(true); + controller.choose(outcome, target, source, game); + return Optional + .of(target) + .map(TargetImpl::getFirstTarget) + .map(game::getPermanent) + .map(permanent -> permanent.addAttachment(aura.getId(), source, game)) + .orElse(false); } } diff --git a/Mage.Sets/src/mage/cards/s/SinUnendingCataclysm.java b/Mage.Sets/src/mage/cards/s/SinUnendingCataclysm.java new file mode 100644 index 00000000000..243b8d02f25 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SinUnendingCataclysm.java @@ -0,0 +1,135 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutSourceCountersOnTargetEffect; +import mage.abilities.effects.common.ShuffleIntoLibrarySourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.Counter; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.CounterAnyPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class SinUnendingCataclysm extends CardImpl { + + public SinUnendingCataclysm(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{G}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.LEVIATHAN); + this.subtype.add(SubType.AVATAR); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // As Sin enters, remove all counters from any number of artifacts, creatures, and enchantments. Sin enters with X +1/+1 counters on it, where X is twice the number of counters removed this way. + this.addAbility(new AsEntersBattlefieldAbility(new SinUnendingCataclysmEffect())); + + // When Sin dies, put its counters on target creature you control, then shuffle this card into its owner's library. + Ability ability = new DiesSourceTriggeredAbility(new PutSourceCountersOnTargetEffect()); + ability.addEffect(new ShuffleIntoLibrarySourceEffect().setText(", then shuffle this card into its owner's library")); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + private SinUnendingCataclysm(final SinUnendingCataclysm card) { + super(card); + } + + @Override + public SinUnendingCataclysm copy() { + return new SinUnendingCataclysm(this); + } +} + +class SinUnendingCataclysmEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterPermanent("artifacts, creatures, and enchantments"); + + static { + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.CREATURE.getPredicate(), + CardType.ENCHANTMENT.getPredicate() + )); + filter.add(CounterAnyPredicate.instance); + } + + SinUnendingCataclysmEffect() { + super(Outcome.Benefit); + staticText = "remove all counters from any number of artifacts, creatures, and enchantments. " + + "{this} enters with X +1/+1 counters on it, where X is twice the number of counters removed this way"; + } + + private SinUnendingCataclysmEffect(final SinUnendingCataclysmEffect effect) { + super(effect); + } + + @Override + public SinUnendingCataclysmEffect copy() { + return new SinUnendingCataclysmEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + TargetPermanent target = new TargetPermanent(0, Integer.MAX_VALUE, filter, true); + player.choose(outcome, target, source, game); + int count = 0; + for (UUID targetId : target.getTargets()) { + Permanent permanent = game.getPermanent(targetId); + if (permanent == null) { + continue; + } + Set counters = permanent.getCounters(game) + .entrySet() + .stream() + .map(e -> CounterType.findByName(e.getKey()).createInstance(e.getValue().getCount())) + .collect(Collectors.toSet()); + for (Counter counter : counters) { + count += permanent.removeCounters(counter, source, game); + } + } + if (count < 1) { + return false; + } + Counter counter = CounterType.P1P1.createInstance(2 * count); + Optional.ofNullable(source) + .map(Ability::getSourceId) + .map(game::getPermanentEntering) + .ifPresent(permanent -> permanent.addCounters(counter, source, game)); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SinisterWaltz.java b/Mage.Sets/src/mage/cards/s/SinisterWaltz.java index 0edef4561a7..3c08be3637f 100644 --- a/Mage.Sets/src/mage/cards/s/SinisterWaltz.java +++ b/Mage.Sets/src/mage/cards/s/SinisterWaltz.java @@ -69,7 +69,7 @@ class SinisterWaltzEffect extends OneShotEffect { } player.moveCards(cards, Zone.BATTLEFIELD, source, game); if (card != null) { - player.putCardsOnBottomOfLibrary(card, game, source, false); + player.putCardsOnBottomOfLibrary(card, game, source); } return true; } diff --git a/Mage.Sets/src/mage/cards/s/SireOfTheStorm.java b/Mage.Sets/src/mage/cards/s/SireOfTheStorm.java index 5f0d1e4df7d..ef3142f4e9a 100644 --- a/Mage.Sets/src/mage/cards/s/SireOfTheStorm.java +++ b/Mage.Sets/src/mage/cards/s/SireOfTheStorm.java @@ -26,7 +26,7 @@ public final class SireOfTheStorm extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(3); this.addAbility(FlyingAbility.getInstance()); - this.addAbility(new SpellCastControllerTriggeredAbility(new DrawCardSourceControllerEffect(1), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new DrawCardSourceControllerEffect(1), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, true)); } private SireOfTheStorm(final SireOfTheStorm card) { diff --git a/Mage.Sets/src/mage/cards/s/Sirocco.java b/Mage.Sets/src/mage/cards/s/Sirocco.java new file mode 100644 index 00000000000..a85ea85db6e --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/Sirocco.java @@ -0,0 +1,92 @@ +package mage.cards.s; + +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetPlayer; + +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Sirocco extends CardImpl { + + public Sirocco(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); + + // Target player reveals their hand. For each blue instant card revealed this way, that player discards that card unless they pay 4 life. + this.getSpellAbility().addEffect(new SiroccoEffect()); + this.getSpellAbility().addTarget(new TargetPlayer()); + } + + private Sirocco(final Sirocco card) { + super(card); + } + + @Override + public Sirocco copy() { + return new Sirocco(this); + } +} + +class SiroccoEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterCard("blue instant card"); + + static { + filter.add(CardType.INSTANT.getPredicate()); + filter.add(new ColorPredicate(ObjectColor.BLUE)); + } + + SiroccoEffect() { + super(Outcome.Benefit); + staticText = "target player reveals their hand. For each blue instant card revealed this way, " + + "that player discards that card unless they pay 4 life"; + } + + private SiroccoEffect(final SiroccoEffect effect) { + super(effect); + } + + @Override + public SiroccoEffect copy() { + return new SiroccoEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); + if (player == null || player.getHand().isEmpty()) { + return false; + } + player.revealCards(source, player.getHand(), game); + Set cards = player.getHand().getCards(filter, game); + if (cards.isEmpty()) { + return true; + } + for (Card card : cards) { + Cost cost = new PayLifeCost(4); + if (!cost.canPay(source, source, player.getId(), game) + || !player.chooseUse( + outcome, "Pay 4 life or discard " + card.getIdName() + '?', + null, "Pay life", "Discard", source, game + ) || !cost.pay(source, game, source, player.getId(), true)) { + player.discard(card, false, source, game); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SithMagic.java b/Mage.Sets/src/mage/cards/s/SithMagic.java index f43dd8ff16e..989072d96ca 100644 --- a/Mage.Sets/src/mage/cards/s/SithMagic.java +++ b/Mage.Sets/src/mage/cards/s/SithMagic.java @@ -31,6 +31,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import mage.watchers.common.LifeLossOtherFromCombatWatcher; /** @@ -85,7 +86,7 @@ class SithMagicEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null && card != null) { if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { - Permanent creature = game.getPermanent(card.getId()); + Permanent creature = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (creature != null) { // gains haste ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); @@ -137,11 +138,8 @@ class SithMagicReplacementEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getTargetId().equals(source.getFirstTarget()) + return event.getTargetId().equals(source.getFirstTarget()) && ((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD - && ((ZoneChangeEvent) event).getToZone() != Zone.EXILED) { - return true; - } - return false; + && ((ZoneChangeEvent) event).getToZone() != Zone.EXILED; } } diff --git a/Mage.Sets/src/mage/cards/s/SkirkCommando.java b/Mage.Sets/src/mage/cards/s/SkirkCommando.java index 1dd7cbb1ddd..14dc245e0ab 100644 --- a/Mage.Sets/src/mage/cards/s/SkirkCommando.java +++ b/Mage.Sets/src/mage/cards/s/SkirkCommando.java @@ -13,7 +13,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; import mage.target.TargetPermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -35,7 +35,7 @@ public final class SkirkCommando extends CardImpl { //Whenever Skirk Commando deals combat damage to a player, you may have it deal 2 damage to target creature that player controls. Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new DamageTargetEffect(2), true, true); ability.addTarget(new TargetPermanent(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); //Morph {2}{R} (You may cast this card face down as a 2/2 creature for 3. Turn it face up any time for its morph cost.) diff --git a/Mage.Sets/src/mage/cards/s/SkullmaneBaku.java b/Mage.Sets/src/mage/cards/s/SkullmaneBaku.java index 2ae43cc87df..c6f1edcd178 100644 --- a/Mage.Sets/src/mage/cards/s/SkullmaneBaku.java +++ b/Mage.Sets/src/mage/cards/s/SkullmaneBaku.java @@ -37,7 +37,7 @@ public final class SkullmaneBaku extends CardImpl { this.toughness = new MageInt(1); // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Skullmane Baku. - this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, true)); // {1}, {T}, Remove X ki counters from Skullmane Baku: Target creature gets -X/-X until end of turn. Ability ability = new SimpleActivatedAbility(new BoostTargetEffect(xValue, xValue, Duration.EndOfTurn), new GenericManaCost(1)); diff --git a/Mage.Sets/src/mage/cards/s/Skullsnatcher.java b/Mage.Sets/src/mage/cards/s/Skullsnatcher.java index 8a9a5332f67..8a82f7a47a4 100644 --- a/Mage.Sets/src/mage/cards/s/Skullsnatcher.java +++ b/Mage.Sets/src/mage/cards/s/Skullsnatcher.java @@ -16,7 +16,7 @@ import mage.filter.FilterCard; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.permanent.UnblockedPredicate; import mage.target.common.TargetCardInGraveyard; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -47,7 +47,7 @@ public final class Skullsnatcher extends CardImpl { Effect effect = new ExileTargetEffect(null, "", Zone.GRAVEYARD); Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(effect, false, true); ability.addTarget(new TargetCardInGraveyard(0, 2, filterGraveyardCard)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster(true)); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster(true)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SkyBlessedSamurai.java b/Mage.Sets/src/mage/cards/s/SkyBlessedSamurai.java index fc5ed5454ba..c867847c845 100644 --- a/Mage.Sets/src/mage/cards/s/SkyBlessedSamurai.java +++ b/Mage.Sets/src/mage/cards/s/SkyBlessedSamurai.java @@ -1,19 +1,13 @@ package mage.cards.s; import mage.MageInt; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.common.AffinityEffect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.AffinityAbility; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AffinityType; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterControlledEnchantmentPermanent; -import mage.filter.common.FilterControlledPermanent; import java.util.UUID; @@ -22,9 +16,6 @@ import java.util.UUID; */ public final class SkyBlessedSamurai extends CardImpl { - static final FilterControlledPermanent filter = new FilterControlledEnchantmentPermanent("enchantments"); - private static final Hint hint = new ValueHint("Enchantments you control", new PermanentsOnBattlefieldCount(filter)); - public SkyBlessedSamurai(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{6}{W}"); @@ -33,8 +24,8 @@ public final class SkyBlessedSamurai extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); - // This spell costs {1} less to cast for each enchantment you control. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filter)).addHint(hint)); + // Affinity for enchantments + this.addAbility(new AffinityAbility(AffinityType.ENCHANTMENTS)); // Flying this.addAbility(FlyingAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/s/SkyfireKirin.java b/Mage.Sets/src/mage/cards/s/SkyfireKirin.java index 384c0a73074..b9e313ac4b3 100644 --- a/Mage.Sets/src/mage/cards/s/SkyfireKirin.java +++ b/Mage.Sets/src/mage/cards/s/SkyfireKirin.java @@ -44,7 +44,7 @@ public final class SkyfireKirin extends CardImpl { // Whenever you cast a Spirit or Arcane spell, you may gain control of target creature with that spell's converted mana cost until end of turn. Ability ability = new SpellCastControllerTriggeredAbility( new SkyfireKirinEffect(), - StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, + StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, true, SetTargetPointer.SPELL ); ability.addTarget(new TargetCreaturePermanent()); diff --git a/Mage.Sets/src/mage/cards/s/SlashOfLight.java b/Mage.Sets/src/mage/cards/s/SlashOfLight.java new file mode 100644 index 00000000000..52348e35100 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SlashOfLight.java @@ -0,0 +1,48 @@ +package mage.cards.s; + +import mage.abilities.dynamicvalue.AdditiveDynamicValue; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SlashOfLight extends CardImpl { + + private static final DynamicValue xValue = new AdditiveDynamicValue( + new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_CREATURES), + new PermanentsOnBattlefieldCount(new FilterControlledPermanent(SubType.EQUIPMENT)) + ); + private static final Hint hint = new ValueHint("Creatures you control plus Equipment you control", xValue); + + public SlashOfLight(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); + + // Slash of Light deals damage equal to the number of creatures you control plus the number of Equipment you control to target creature. + this.getSpellAbility().addEffect(new DamageTargetEffect(xValue) + .setText("{this} deals damage equal to the number of creatures you control " + + "plus the number of Equipment you control to target creature")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private SlashOfLight(final SlashOfLight card) { + super(card); + } + + @Override + public SlashOfLight copy() { + return new SlashOfLight(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SleepMagic.java b/Mage.Sets/src/mage/cards/s/SleepMagic.java new file mode 100644 index 00000000000..8c4a951c560 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SleepMagic.java @@ -0,0 +1,55 @@ +package mage.cards.s; + +import mage.abilities.common.DealtDamageAttachedTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DontUntapInControllersUntapStepEnchantedEffect; +import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.abilities.effects.common.TapEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SleepMagic extends CardImpl { + + public SleepMagic(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{U}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(new EnchantAbility(auraTarget)); + + // When this Aura enters, tap enchanted creature. + this.addAbility(new EntersBattlefieldTriggeredAbility(new TapEnchantedEffect())); + + // Enchanted creature doesn't untap during its controller's untap step. + this.addAbility(new SimpleStaticAbility(new DontUntapInControllersUntapStepEnchantedEffect())); + + // When enchanted creature is dealt damage, sacrifice this Aura. + this.addAbility(new DealtDamageAttachedTriggeredAbility(new SacrificeSourceEffect())); + } + + private SleepMagic(final SleepMagic card) { + super(card); + } + + @Override + public SleepMagic copy() { + return new SleepMagic(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SmokeShroud.java b/Mage.Sets/src/mage/cards/s/SmokeShroud.java index 23b30e2b6d8..c9e4a5f2961 100644 --- a/Mage.Sets/src/mage/cards/s/SmokeShroud.java +++ b/Mage.Sets/src/mage/cards/s/SmokeShroud.java @@ -1,22 +1,18 @@ package mage.cards.s; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAnyReturnSourceFromGraveyardAttachedToItTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.FlyingAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; +import mage.filter.common.FilterControlledPermanent; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -27,7 +23,7 @@ import java.util.UUID; */ public final class SmokeShroud extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent(SubType.NINJA, "a Ninja"); + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.NINJA, "a Ninja you control"); public SmokeShroud(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); @@ -49,10 +45,7 @@ public final class SmokeShroud extends CardImpl { this.addAbility(ability); // When a Ninja you control enters, you may return Smoke Shroud from your graveyard to the battlefield attached to that creature. - this.addAbility(new EntersBattlefieldControlledTriggeredAbility( - Zone.GRAVEYARD, new SmokeShroudEffect(), filter, true, - SetTargetPointer.PERMANENT - ).setTriggerPhrase("When a Ninja you control enters, ")); + this.addAbility(new EntersBattlefieldAnyReturnSourceFromGraveyardAttachedToItTriggeredAbility(filter)); } private SmokeShroud(final SmokeShroud card) { @@ -64,35 +57,3 @@ public final class SmokeShroud extends CardImpl { return new SmokeShroud(this); } } - -class SmokeShroudEffect extends OneShotEffect { - - SmokeShroudEffect() { - super(Outcome.Benefit); - staticText = "return {this} from your graveyard to the battlefield attached to that creature"; - } - - private SmokeShroudEffect(final SmokeShroudEffect effect) { - super(effect); - } - - @Override - public SmokeShroudEffect copy() { - return new SmokeShroudEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Card sourceCard = source.getSourceCardIfItStillExists(game); - Permanent permanent = game.getPermanent(this.getTargetPointer().getFirst(game, source)); - Player controller = game.getPlayer(source.getControllerId()); - if (sourceCard != null && permanent != null && controller != null) { - game.getState().setValue("attachTo:" + sourceCard.getId(), permanent); - if (controller.moveCards(sourceCard, Zone.BATTLEFIELD, source, game)) { - permanent.addAttachment(sourceCard.getId(), source, game); - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/s/SnappingThragg.java b/Mage.Sets/src/mage/cards/s/SnappingThragg.java index e5849354032..476f684aceb 100644 --- a/Mage.Sets/src/mage/cards/s/SnappingThragg.java +++ b/Mage.Sets/src/mage/cards/s/SnappingThragg.java @@ -13,7 +13,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; import mage.target.TargetPermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -35,7 +35,7 @@ public final class SnappingThragg extends CardImpl { // Whenever Snapping Thragg deals combat damage to a player, you may have it deal 3 damage to target creature that player controls. Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new DamageTargetEffect(3), true, true); ability.addTarget(new TargetPermanent(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); // Morph {4}{R}{R} diff --git a/Mage.Sets/src/mage/cards/s/Snort.java b/Mage.Sets/src/mage/cards/s/Snort.java new file mode 100644 index 00000000000..e615353e662 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/Snort.java @@ -0,0 +1,98 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.FlashbackAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; + +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Snort extends CardImpl { + + public Snort(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}"); + + // Each player may discard their hand and draw five cards. Then Snort deals 5 damage to each opponent who discarded their hand this way. + this.getSpellAbility().addEffect(new SnortEffect()); + + // Flashback {5}{R} + this.addAbility(new FlashbackAbility(this, new ManaCostsImpl<>("{5}{R}"))); + } + + private Snort(final Snort card) { + super(card); + } + + @Override + public Snort copy() { + return new Snort(this); + } +} + +class SnortEffect extends OneShotEffect { + + SnortEffect() { + super(Outcome.Benefit); + staticText = "each player may discard their hand and draw five cards. " + + "Then {this} deals 5 damage to each opponent who discarded their hand this way"; + } + + private SnortEffect(final SnortEffect effect) { + super(effect); + } + + @Override + public SnortEffect copy() { + return new SnortEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Set toDiscard = new HashSet<>(); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } else if (player.chooseUse( + Outcome.DrawCard, + source.isControlledBy(playerId) + ? "Discard your hand and draw five cards?" + : "Discard your hand, draw five cards, and be dealt 5 damage?", + source, game + )) { + game.informPlayers(player.getLogName() + " chooses to discard and draw five"); + toDiscard.add(playerId); + } else { + game.informPlayers(player.getLogName() + " chooses not to discard"); + } + } + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Optional.ofNullable(playerId) + .filter(toDiscard::contains) + .map(game::getPlayer) + .ifPresent(player -> { + player.discard(player.getHand(), false, source, game); + player.drawCards(5, source, game); + }); + } + for (UUID playerId : game.getOpponents(source.getControllerId())) { + Optional.ofNullable(playerId) + .filter(toDiscard::contains) + .map(game::getPlayer) + .ifPresent(player -> player.damage(5, source, game)); + } + return !toDiscard.isEmpty(); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SnowVilliers.java b/Mage.Sets/src/mage/cards/s/SnowVilliers.java new file mode 100644 index 00000000000..8e966163dfe --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SnowVilliers.java @@ -0,0 +1,46 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.CreaturesYouControlCount; +import mage.abilities.effects.common.continuous.SetBasePowerSourceEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SnowVilliers extends CardImpl { + + public SnowVilliers(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.REBEL); + this.subtype.add(SubType.MONK); + this.power = new MageInt(0); + this.toughness = new MageInt(3); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Snow Villiers's power is equal to the number of creatures you control. + this.addAbility(new SimpleStaticAbility(new SetBasePowerSourceEffect(CreaturesYouControlCount.instance))); + } + + private SnowVilliers(final SnowVilliers card) { + super(card); + } + + @Override + public SnowVilliers copy() { + return new SnowVilliers(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/Soilshaper.java b/Mage.Sets/src/mage/cards/s/Soilshaper.java index 57d2bb86c5b..0ef7ce7f28e 100644 --- a/Mage.Sets/src/mage/cards/s/Soilshaper.java +++ b/Mage.Sets/src/mage/cards/s/Soilshaper.java @@ -30,7 +30,7 @@ public final class Soilshaper extends CardImpl { this.toughness = new MageInt(1); // Whenever you cast a Spirit or Arcane spell, target land becomes a 3/3 creature until end of turn. It's still a land. - Ability ability = new SpellCastControllerTriggeredAbility(new BecomesCreatureTargetEffect(new CreatureToken(3, 3), false, true, Duration.EndOfTurn), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false); + Ability ability = new SpellCastControllerTriggeredAbility(new BecomesCreatureTargetEffect(new CreatureToken(3, 3), false, true, Duration.EndOfTurn), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, false); ability.addTarget(new TargetLandPermanent()); this.addAbility(ability); } @@ -44,4 +44,4 @@ public final class Soilshaper extends CardImpl { return new Soilshaper(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/s/SoldeviAdnate.java b/Mage.Sets/src/mage/cards/s/SoldeviAdnate.java index 02213349755..64b491d0f77 100644 --- a/Mage.Sets/src/mage/cards/s/SoldeviAdnate.java +++ b/Mage.Sets/src/mage/cards/s/SoldeviAdnate.java @@ -7,7 +7,7 @@ import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.dynamicvalue.common.HighestCMCOfPermanentValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.dynamicvalue.common.SacrificeCostManaValue; import mage.abilities.mana.DynamicManaAbility; import mage.cards.CardImpl; @@ -17,7 +17,6 @@ import mage.constants.SubType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; -import mage.target.common.TargetControlledPermanent; import java.util.UUID; @@ -32,6 +31,8 @@ public final class SoldeviAdnate extends CardImpl { filter.add(Predicates.or(new ColorPredicate(ObjectColor.BLACK), CardType.ARTIFACT.getPredicate())); } + private static final GreatestAmongPermanentsValue netValue = new GreatestAmongPermanentsValue(GreatestAmongPermanentsValue.Quality.ManaValue, filter); + public SoldeviAdnate(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); this.subtype.add(SubType.HUMAN); @@ -41,8 +42,7 @@ public final class SoldeviAdnate extends CardImpl { // {T}, Sacrifice a black or artifact creature: Add an amount of {B} equal to the sacrificed creature's converted mana cost. Ability ability = new DynamicManaAbility(Mana.BlackMana(1), SacrificeCostManaValue.CREATURE, new TapSourceCost(), - "add an amount of {B} equal to the sacrificed creature's mana value", false, - new HighestCMCOfPermanentValue(filter, true)); + "add an amount of {B} equal to the sacrificed creature's mana value", false, netValue); ability.addCost(new SacrificeTargetCost(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SonicScrewdriver.java b/Mage.Sets/src/mage/cards/s/SonicScrewdriver.java index bd7709073a4..8096c3e3733 100644 --- a/Mage.Sets/src/mage/cards/s/SonicScrewdriver.java +++ b/Mage.Sets/src/mage/cards/s/SonicScrewdriver.java @@ -13,7 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.common.FilterArtifactPermanent; import mage.filter.predicate.mageobject.AnotherPredicate; -import mage.target.common.TargetArtifactPermanent; +import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -42,7 +42,7 @@ public final class SonicScrewdriver extends CardImpl { new GenericManaCost(1) ); ability.addCost(new TapSourceCost()); - ability.addTarget(new TargetArtifactPermanent(filter)); + ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); // {2}, {T}: Scry 1. diff --git a/Mage.Sets/src/mage/cards/s/SontaranGeneral.java b/Mage.Sets/src/mage/cards/s/SontaranGeneral.java index 25fb8b4d00d..18a69b25e27 100644 --- a/Mage.Sets/src/mage/cards/s/SontaranGeneral.java +++ b/Mage.Sets/src/mage/cards/s/SontaranGeneral.java @@ -13,7 +13,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; import java.util.UUID; @@ -44,7 +44,7 @@ public final class SontaranGeneral extends CardImpl { ability.addEffect(new CantBlockTargetEffect(Duration.EndOfTurn).setTargetPointer(new EachTargetPointer()) .setText("Those creatures can't block this turn.")); ability.addTarget(new TargetCreaturePermanent(0, 1)); - ability.setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SorceresssSchemes.java b/Mage.Sets/src/mage/cards/s/SorceresssSchemes.java new file mode 100644 index 00000000000..1b61d54f950 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SorceresssSchemes.java @@ -0,0 +1,56 @@ +package mage.cards.s; + +import mage.Mana; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.mana.BasicManaEffect; +import mage.abilities.keyword.FlashbackAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.common.TargetCardInYourGraveyardOrExile; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class SorceresssSchemes extends CardImpl { + + private static final FilterCard filter = new FilterCard("exiled card with flashback you own"); + + static { + filter.add(TargetController.YOU.getOwnerPredicate()); + filter.add(new AbilityPredicate(FlashbackAbility.class)); + } + + public SorceresssSchemes(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}"); + + // Return target instant or sorcery card from your graveyard or exiled card with flashback you own to your hand. Add {R}. + this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); + this.getSpellAbility().addTarget( + new TargetCardInYourGraveyardOrExile( + StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY_FROM_YOUR_GRAVEYARD, filter + ) + ); + this.getSpellAbility().addEffect(new BasicManaEffect(Mana.RedMana(1))); + + // Flashback {4}{R} + this.addAbility(new FlashbackAbility(this, new ManaCostsImpl<>("{4}{R}"))); + + } + + private SorceresssSchemes(final SorceresssSchemes card) { + super(card); + } + + @Override + public SorceresssSchemes copy() { + return new SorceresssSchemes(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SoulEcho.java b/Mage.Sets/src/mage/cards/s/SoulEcho.java index 08162fb7a2a..8fd1d2317fb 100644 --- a/Mage.Sets/src/mage/cards/s/SoulEcho.java +++ b/Mage.Sets/src/mage/cards/s/SoulEcho.java @@ -1,21 +1,23 @@ - package mage.cards.s; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.decorator.ConditionalOneShotEffect; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.EntersBattlefieldWithXCountersEffect; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.effects.common.continuous.DontLoseByZeroOrLessLifeEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Duration; +import mage.constants.Outcome; import mage.counters.CounterType; import mage.game.Game; import mage.game.events.DamageEvent; @@ -27,13 +29,14 @@ import mage.target.common.TargetOpponent; import java.util.UUID; /** - * * @author L_J */ public final class SoulEcho extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.ECHO, ComparisonType.EQUAL_TO, 0); + public SoulEcho(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{X}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{X}{W}{W}"); // Soul Echo enters the battlefield with X echo counters on it. this.addAbility(new EntersBattlefieldAbility(new EntersBattlefieldWithXCountersEffect(CounterType.ECHO.createInstance()))); @@ -43,8 +46,12 @@ public final class SoulEcho extends CardImpl { // At the beginning of your upkeep, sacrifice Soul Echo if there are no echo counters on it. // Otherwise, target opponent may choose that for each 1 damage that would be dealt to you until your next upkeep, you remove an echo counter from Soul Echo instead. - Effect effect = new ConditionalOneShotEffect(new SacrificeSourceEffect(), new SoulEchoOpponentsChoiceEffect(), new SourceHasCounterCondition(CounterType.ECHO, 0, 0), "sacrifice {this} if there are no echo counters on it. Otherwise, target opponent may choose that for each 1 damage that would be dealt to you until your next upkeep, you remove an echo counter from {this} instead"); - Ability ability = new BeginningOfUpkeepTriggeredAbility(effect); + Ability ability = new BeginningOfUpkeepTriggeredAbility(new ConditionalOneShotEffect( + new SacrificeSourceEffect(), new SoulEchoOpponentsChoiceEffect(), + condition, "sacrifice {this} if there are no echo counters on it. " + + "Otherwise, target opponent may choose that for each 1 damage that would be " + + "dealt to you until your next upkeep, you remove an echo counter from {this} instead" + )); ability.addTarget(new TargetOpponent()); this.addAbility(ability); } @@ -64,7 +71,7 @@ class SoulEchoOpponentsChoiceEffect extends OneShotEffect { SoulEchoOpponentsChoiceEffect() { super(Outcome.PreventDamage); staticText = "target opponent may choose that for each 1 damage that would be dealt to you " + - "until your next upkeep, you remove an echo counter from {this} instead"; + "until your next upkeep, you remove an echo counter from {this} instead"; } private SoulEchoOpponentsChoiceEffect(final SoulEchoOpponentsChoiceEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SoulOfMagma.java b/Mage.Sets/src/mage/cards/s/SoulOfMagma.java index fa7299d412a..fa59ca5f2d0 100644 --- a/Mage.Sets/src/mage/cards/s/SoulOfMagma.java +++ b/Mage.Sets/src/mage/cards/s/SoulOfMagma.java @@ -25,7 +25,7 @@ public final class SoulOfMagma extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - Ability ability = new SpellCastControllerTriggeredAbility(new DamageTargetEffect(1), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false); + Ability ability = new SpellCastControllerTriggeredAbility(new DamageTargetEffect(1), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, false); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SoulcipherBoard.java b/Mage.Sets/src/mage/cards/s/SoulcipherBoard.java index 66aafa4875d..538dcae5506 100644 --- a/Mage.Sets/src/mage/cards/s/SoulcipherBoard.java +++ b/Mage.Sets/src/mage/cards/s/SoulcipherBoard.java @@ -17,6 +17,7 @@ import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.PutCards; import mage.constants.TargetController; import mage.counters.CounterType; @@ -29,7 +30,7 @@ import java.util.UUID; */ public final class SoulcipherBoard extends CardImpl { - private static final Condition condition = new SourceHasCounterCondition(CounterType.OMEN, 0, 0); + private static final Condition condition = new SourceHasCounterCondition(CounterType.OMEN, ComparisonType.EQUAL_TO, 0); public SoulcipherBoard(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{U}"); diff --git a/Mage.Sets/src/mage/cards/s/SoulgorgerOrgg.java b/Mage.Sets/src/mage/cards/s/SoulgorgerOrgg.java index 700736c6f08..dce856d8e84 100644 --- a/Mage.Sets/src/mage/cards/s/SoulgorgerOrgg.java +++ b/Mage.Sets/src/mage/cards/s/SoulgorgerOrgg.java @@ -88,7 +88,7 @@ class SoulgorgerOrggGainLifeEffect extends OneShotEffect { SoulgorgerOrggGainLifeEffect() { super(Outcome.GainLife); - staticText = "you gain life equal to the life you lost when it entered the battlefield"; + staticText = "you gain life equal to the life you lost when it entered"; } private SoulgorgerOrggGainLifeEffect(final SoulgorgerOrggGainLifeEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/SparkMage.java b/Mage.Sets/src/mage/cards/s/SparkMage.java index 8eabdbbd9f1..f3b5b2acdbb 100644 --- a/Mage.Sets/src/mage/cards/s/SparkMage.java +++ b/Mage.Sets/src/mage/cards/s/SparkMage.java @@ -11,7 +11,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterCreaturePermanent; import mage.target.TargetPermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -36,7 +36,7 @@ public final class SparkMage extends CardImpl { new DamageTargetEffect(1), true, true ).withRuleTextReplacement(false); ability.addTarget(new TargetPermanent(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/Sparksmith.java b/Mage.Sets/src/mage/cards/s/Sparksmith.java index 40fd6b3e42f..7a11696bfc3 100644 --- a/Mage.Sets/src/mage/cards/s/Sparksmith.java +++ b/Mage.Sets/src/mage/cards/s/Sparksmith.java @@ -6,6 +6,7 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.DamageControllerEffect; import mage.abilities.effects.common.DamageTargetEffect; @@ -27,6 +28,7 @@ public final class Sparksmith extends CardImpl { static { filter.add(SubType.GOBLIN.getPredicate()); } + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); public Sparksmith(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{R}"); @@ -36,9 +38,11 @@ public final class Sparksmith extends CardImpl { this.toughness = new MageInt(1); // {tap}: Sparksmith deals X damage to target creature and X damage to you, where X is the number of Goblins on the battlefield. - Ability ability = new SimpleActivatedAbility(new DamageTargetEffect(new PermanentsOnBattlefieldCount(filter)), new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(new DamageTargetEffect(xValue) + .setText("{this} deals X damage to target creature"), new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); - ability.addEffect(new DamageControllerEffect(new PermanentsOnBattlefieldCount(filter))); + ability.addEffect(new DamageControllerEffect(xValue) + .setText("and X damage to you, where X is the number of Goblins on the battlefield")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SpectralSearchlight.java b/Mage.Sets/src/mage/cards/s/SpectralSearchlight.java index 3891bacf41f..342afa6ab7e 100644 --- a/Mage.Sets/src/mage/cards/s/SpectralSearchlight.java +++ b/Mage.Sets/src/mage/cards/s/SpectralSearchlight.java @@ -3,7 +3,6 @@ package mage.cards.s; import mage.Mana; import mage.abilities.Ability; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.common.ChoosePlayerEffect; import mage.abilities.effects.mana.ManaEffect; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; @@ -14,6 +13,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; import mage.players.Player; +import mage.target.TargetPlayer; import java.util.ArrayList; import java.util.List; @@ -30,8 +30,6 @@ public final class SpectralSearchlight extends CardImpl { // {T}: Choose a player. That player adds one mana of any color they chooses. ManaEffect effect = new SpectralSearchlightManaEffect(); Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); - // choosing player as first effect, before adding mana effect - ability.getEffects().add(0, new ChoosePlayerEffect(Outcome.PutManaInPool)); this.addAbility(ability); } @@ -49,7 +47,7 @@ class SpectralSearchlightManaEffect extends ManaEffect { SpectralSearchlightManaEffect() { super(); - this.staticText = "That player adds one mana of any color they choose"; + this.staticText = "Choose a player. That player adds one mana of any color they choose"; } private SpectralSearchlightManaEffect(final SpectralSearchlightManaEffect effect) { @@ -58,7 +56,13 @@ class SpectralSearchlightManaEffect extends ManaEffect { @Override public Player getPlayer(Game game, Ability source) { - return game.getPlayer((UUID) game.getState().getValue(source.getSourceId() + "_player")); + if (!game.inCheckPlayableState()) { + TargetPlayer target = new TargetPlayer(1, 1, true); + if (target.choose(Outcome.PutManaInPool, source.getControllerId(), source, game)) { + return game.getPlayer(target.getFirstTarget()); + } + } + return game.getPlayer(source.getControllerId()); // Count as controller's potential mana for card playability } @Override diff --git a/Mage.Sets/src/mage/cards/s/SpellRupture.java b/Mage.Sets/src/mage/cards/s/SpellRupture.java index 3077c3f0b9e..d36433b8e15 100644 --- a/Mage.Sets/src/mage/cards/s/SpellRupture.java +++ b/Mage.Sets/src/mage/cards/s/SpellRupture.java @@ -2,19 +2,14 @@ package mage.cards.s; import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.Mode; import mage.abilities.costs.Cost; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.OneShotEffect; -import mage.abilities.hint.ValueHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.game.stack.StackObject; import mage.players.Player; import mage.target.TargetSpell; @@ -33,7 +28,7 @@ public final class SpellRupture extends CardImpl { // Counter target spell unless its controller pays {X}, where X is the greatest power among creatures you control. this.getSpellAbility().addEffect(new SpellRuptureCounterUnlessPaysEffect()); this.getSpellAbility().addTarget(new TargetSpell()); - this.getSpellAbility().addHint(new ValueHint("Greatest power among your creatures", new GreatestPowerCountCreatureYouControl())); + this.getSpellAbility().addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint()); } private SpellRupture(final SpellRupture card) { @@ -65,50 +60,22 @@ class SpellRuptureCounterUnlessPaysEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { StackObject spell = game.getStack().getStackObject(getTargetPointer().getFirst(game, source)); - if (spell != null) { - Player player = game.getPlayer(spell.getControllerId()); - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source); - if (player != null && controller != null && sourceObject != null) { - int maxPower = new GreatestPowerCountCreatureYouControl().calculate(game, source, this); - Cost cost = ManaUtil.createManaCost(maxPower, true); - if (player.chooseUse(Outcome.Benefit, "Pay " + cost.getText() + "? (otherwise " + spell.getName() + " will be countered)", source, game) - && cost.pay(source, game, source, player.getId(), false)) { - return true; - } - game.informPlayers(sourceObject.getName() + ": cost wasn't payed - countering " + spell.getName()); - return game.getStack().counter(source.getFirstTarget(), source, game); - } + if (spell == null) { + return false; } - return false; - } -} - -class GreatestPowerCountCreatureYouControl implements DynamicValue { - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - int value = 0; - for (Permanent creature : game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), sourceAbility.getControllerId(), game)) { - if (creature != null && creature.getPower().getValue() > value) { - value = creature.getPower().getValue(); - } + Player player = game.getPlayer(spell.getControllerId()); + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source); + if (player == null || controller == null || sourceObject == null) { + return false; } - return value; + int maxPower = GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.calculate(game, source, this); + Cost cost = ManaUtil.createManaCost(maxPower, true); + if (player.chooseUse(Outcome.Benefit, "Pay " + cost.getText() + ", where X is " + maxPower + "? (otherwise " + spell.getName() + " will be countered)", source, game) + && cost.pay(source, game, source, player.getId(), false)) { + return true; + } + game.informPlayers(sourceObject.getName() + ": cost wasn't payed - countering " + spell.getName()); + return game.getStack().counter(source.getFirstTarget(), source, game); } - - @Override - public GreatestPowerCountCreatureYouControl copy() { - return new GreatestPowerCountCreatureYouControl(); - } - - @Override - public String toString() { - return "X"; - } - - @Override - public String getMessage() { - return "greatest power among creatures you control"; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/SpellbaneCentaur.java b/Mage.Sets/src/mage/cards/s/SpellbaneCentaur.java index ba4643d18f8..5c543b02a3f 100644 --- a/Mage.Sets/src/mage/cards/s/SpellbaneCentaur.java +++ b/Mage.Sets/src/mage/cards/s/SpellbaneCentaur.java @@ -1,7 +1,6 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.ObjectColor; import mage.abilities.common.SimpleStaticAbility; @@ -9,16 +8,16 @@ import mage.abilities.effects.common.CantBeTargetedAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; import mage.filter.FilterObject; import mage.filter.FilterStackObject; import mage.filter.StaticFilters; import mage.filter.predicate.mageobject.ColorPredicate; +import java.util.UUID; + /** - * * @author fireshoes */ public final class SpellbaneCentaur extends CardImpl { @@ -26,11 +25,12 @@ public final class SpellbaneCentaur extends CardImpl { private static final FilterObject filter = new FilterStackObject("blue spells or abilities from blue sources"); static { + // TODO: seems incorrect. It should be Or Predictate on blue spell or abilities from blue sources. filter.add(new ColorPredicate(ObjectColor.BLUE)); } public SpellbaneCentaur(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); this.subtype.add(SubType.CENTAUR); this.power = new MageInt(3); this.toughness = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/s/Spellgyre.java b/Mage.Sets/src/mage/cards/s/Spellgyre.java index 5a99c5e960e..1c0bfc6b358 100644 --- a/Mage.Sets/src/mage/cards/s/Spellgyre.java +++ b/Mage.Sets/src/mage/cards/s/Spellgyre.java @@ -25,7 +25,7 @@ public final class Spellgyre extends CardImpl { this.getSpellAbility().addTarget(new TargetSpell()); // * Surveil 2, then draw two cards. - this.getSpellAbility().addMode(new Mode(new SurveilEffect(2)) + this.getSpellAbility().addMode(new Mode(new SurveilEffect(2, false)) .addEffect(new DrawCardSourceControllerEffect(2).concatBy(", then"))); } diff --git a/Mage.Sets/src/mage/cards/s/SpellweaverHelix.java b/Mage.Sets/src/mage/cards/s/SpellweaverHelix.java index af8ef841e10..89bbba6f492 100644 --- a/Mage.Sets/src/mage/cards/s/SpellweaverHelix.java +++ b/Mage.Sets/src/mage/cards/s/SpellweaverHelix.java @@ -148,7 +148,7 @@ class SpellweaverHelixTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever a player casts a card, if it has the same name as one of the cards exiled with Spellweaver Helix, you may copy the other. If you do, you may cast the copy without paying its mana cost."; + return "Whenever a player casts a card, if it has the same name as one of the cards exiled with {this}, you may copy the other. If you do, you may cast the copy without paying its mana cost."; } } diff --git a/Mage.Sets/src/mage/cards/s/Spelunking.java b/Mage.Sets/src/mage/cards/s/Spelunking.java index 626206733ba..547eb054d69 100644 --- a/Mage.Sets/src/mage/cards/s/Spelunking.java +++ b/Mage.Sets/src/mage/cards/s/Spelunking.java @@ -4,16 +4,17 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.EnterUntappedAllEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInHand; @@ -28,10 +29,12 @@ public final class Spelunking extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); // When Spelunking enters the battlefield, draw a card, then you may put a land card from your hand onto the battlefield. If you put a Cave onto the battlefield this way, you gain 4 life. - this.addAbility(new EntersBattlefieldTriggeredAbility(new SpelunkingEffect())); + Ability ability = new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1)); + ability.addEffect(new SpelunkingEffect()); + this.addAbility(ability); // Lands you control enter the battlefield untapped. - this.addAbility(new SimpleStaticAbility(new SpelunkingReplacementEffect())); + this.addAbility(new SimpleStaticAbility(new EnterUntappedAllEffect(StaticFilters.FILTER_CONTROLLED_PERMANENT_LANDS))); } private Spelunking(final Spelunking card) { @@ -48,7 +51,7 @@ class SpelunkingEffect extends OneShotEffect { SpelunkingEffect() { super(Outcome.Benefit); - staticText = "draw a card, then you may put a land card from your hand onto the battlefield. " + staticText = ", then you may put a land card from your hand onto the battlefield. " + "If you put a Cave onto the battlefield this way, you gain 4 life"; } @@ -67,75 +70,16 @@ class SpelunkingEffect extends OneShotEffect { if (controller == null) { return false; } - - // draw a card - controller.drawCards(1, source, game); - - // you may put a land card from your hand onto the battlefield TargetCardInHand target = new TargetCardInHand(0, 1, StaticFilters.FILTER_CARD_LAND); - if (controller.choose(Outcome.PutLandInPlay, target, source, game)) { - Card landInHand = game.getCard(target.getFirstTarget()); - if (landInHand != null) { - controller.moveCards(landInHand, Zone.BATTLEFIELD, source, game); - if (landInHand.getSubtype(game).contains(SubType.CAVE)) { - // If you put a Cave onto the battlefield this way, you gain 4 life - controller.gainLife(4, game, source); - } - } + controller.choose(Outcome.PutLandInPlay, target, source, game); + Card landInHand = game.getCard(target.getFirstTarget()); + if (landInHand == null) { + return false; + } + controller.moveCards(landInHand, Zone.BATTLEFIELD, source, game); + if (landInHand.hasSubtype(SubType.CAVE, game)) { + controller.gainLife(4, game, source); } - return true; } - -} - -/** - * Inspired by {@link mage.cards.g.GondGate} - */ -class SpelunkingReplacementEffect extends ReplacementEffectImpl { - - SpelunkingReplacementEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "Lands you control enter the battlefield untapped"; - } - - private SpelunkingReplacementEffect(final SpelunkingReplacementEffect effect) { - super(effect); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); - if (target != null) { - target.setTapped(false); - } - - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - Permanent sourceObject = game.getPermanent(source.getSourceId()); - if (sourceObject == null) { - return false; - } - - Permanent targetObject = ((EntersTheBattlefieldEvent) event).getTarget(); - if (targetObject == null) { - return false; - } - - return !sourceObject.getId().equals(targetObject.getId()) - && targetObject.isControlledBy(source.getControllerId()); - } - - @Override - public SpelunkingReplacementEffect copy() { - return new SpelunkingReplacementEffect(this); - } } diff --git a/Mage.Sets/src/mage/cards/s/SphinxOfTheFinalWord.java b/Mage.Sets/src/mage/cards/s/SphinxOfTheFinalWord.java index 851e3815f3f..82c17b73310 100644 --- a/Mage.Sets/src/mage/cards/s/SphinxOfTheFinalWord.java +++ b/Mage.Sets/src/mage/cards/s/SphinxOfTheFinalWord.java @@ -1,7 +1,6 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.common.SimpleStaticAbility; @@ -11,42 +10,42 @@ import mage.abilities.keyword.HexproofAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; import mage.filter.FilterSpell; import mage.filter.predicate.Predicates; +import java.util.UUID; + /** - * * @author fireshoes */ public final class SphinxOfTheFinalWord extends CardImpl { - + private static final FilterSpell filterTarget = new FilterSpell("Instant and sorcery spells you control"); static { filterTarget.add(Predicates.or(CardType.INSTANT.getPredicate(), (CardType.SORCERY.getPredicate()))); } - + public SphinxOfTheFinalWord(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U}{U}"); this.subtype.add(SubType.SPHINX); this.power = new MageInt(5); this.toughness = new MageInt(5); // Sphinx of the Final Word can't be countered. this.addAbility(new CantBeCounteredSourceAbility()); - + // Flying this.addAbility(FlyingAbility.getInstance()); - + // Hexproof this.addAbility(HexproofAbility.getInstance()); - + // Instant and sorcery spells you control can't be countered. - this.addAbility(new SimpleStaticAbility(new CantBeCounteredControlledEffect(filterTarget, null, Duration.WhileOnBattlefield))); + this.addAbility(new SimpleStaticAbility(new CantBeCounteredControlledEffect(filterTarget, Duration.WhileOnBattlefield))); } private SphinxOfTheFinalWord(final SphinxOfTheFinalWord card) { diff --git a/Mage.Sets/src/mage/cards/s/SpireGolem.java b/Mage.Sets/src/mage/cards/s/SpireGolem.java index 995174df778..42b08a6ad39 100644 --- a/Mage.Sets/src/mage/cards/s/SpireGolem.java +++ b/Mage.Sets/src/mage/cards/s/SpireGolem.java @@ -1,30 +1,30 @@ - package mage.cards.s; -import java.util.UUID; import mage.MageInt; -import mage.abilities.keyword.AffinityForLandTypeAbility; +import mage.abilities.keyword.AffinityAbility; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AffinityType; import mage.constants.CardType; import mage.constants.SubType; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class SpireGolem extends CardImpl { public SpireGolem(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{6}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{6}"); this.subtype.add(SubType.GOLEM); - this.power = new MageInt(2); this.toughness = new MageInt(4); // Affinity for Islands - this.addAbility(new AffinityForLandTypeAbility(SubType.ISLAND, "Islands")); + this.addAbility(new AffinityAbility(AffinityType.ISLANDS)); + // Flying this.addAbility(FlyingAbility.getInstance()); } diff --git a/Mage.Sets/src/mage/cards/s/SpiritSistersCall.java b/Mage.Sets/src/mage/cards/s/SpiritSistersCall.java index d6414f6bd71..f810742ff29 100644 --- a/Mage.Sets/src/mage/cards/s/SpiritSistersCall.java +++ b/Mage.Sets/src/mage/cards/s/SpiritSistersCall.java @@ -26,6 +26,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @@ -106,13 +107,12 @@ class SpiritSistersCallReturnToBattlefieldEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - UUID targetId = source.getFirstTarget(); - Card card = game.getCard(targetId); - if (controller == null || card == null || game.getState().getZone(targetId) != Zone.GRAVEYARD) { + Card card = game.getCard(source.getFirstTarget()); + if (controller == null || card == null || game.getState().getZone(card.getId()) != Zone.GRAVEYARD) { return false; } controller.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(targetId); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { ContinuousEffect effect = new GainAbilityTargetEffect( new SimpleStaticAbility(new LeaveBattlefieldExileSourceReplacementEffect("this permanent")), diff --git a/Mage.Sets/src/mage/cards/s/Spiritualize.java b/Mage.Sets/src/mage/cards/s/Spiritualize.java index eb7f321f6de..143b0e2ac33 100644 --- a/Mage.Sets/src/mage/cards/s/Spiritualize.java +++ b/Mage.Sets/src/mage/cards/s/Spiritualize.java @@ -1,23 +1,22 @@ package mage.cards.s; -import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.dynamicvalue.common.SavedDamageValue; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.GainLifeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** * * @author cbt33, Unknown (Glimpse of Nature), LevelX2 (Armadillo Cloak) @@ -47,9 +46,9 @@ public final class Spiritualize extends CardImpl { class SpiritualizeTriggeredAbility extends DelayedTriggeredAbility { - public SpiritualizeTriggeredAbility() { + SpiritualizeTriggeredAbility() { super(new GainLifeEffect(SavedDamageValue.MUCH), Duration.EndOfTurn, false); - setTriggerPhrase("Whenever target creature deals damage, "); + setTriggerPhrase("Until end of turn, whenever target creature deals damage, "); } private SpiritualizeTriggeredAbility(final SpiritualizeTriggeredAbility ability) { diff --git a/Mage.Sets/src/mage/cards/s/SpotterThopter.java b/Mage.Sets/src/mage/cards/s/SpotterThopter.java index 172dffb0c48..4b0faad6e7f 100644 --- a/Mage.Sets/src/mage/cards/s/SpotterThopter.java +++ b/Mage.Sets/src/mage/cards/s/SpotterThopter.java @@ -1,19 +1,15 @@ package mage.cards.s; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.dynamicvalue.common.SourcePermanentPowerValue; +import mage.abilities.effects.keyword.ScryEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.PrototypeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import java.util.UUID; @@ -36,7 +32,9 @@ public final class SpotterThopter extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Spotter Thopter enters the battlefield, scry X, where X is its power. - this.addAbility(new EntersBattlefieldTriggeredAbility(new SpotterThopterEffect())); + this.addAbility(new EntersBattlefieldTriggeredAbility( + new ScryEffect(SourcePermanentPowerValue.NOT_NEGATIVE) + .setText("scry X, where X is its power"))); } private SpotterThopter(final SpotterThopter card) { @@ -48,31 +46,3 @@ public final class SpotterThopter extends CardImpl { return new SpotterThopter(this); } } - -class SpotterThopterEffect extends OneShotEffect { - - SpotterThopterEffect() { - super(Outcome.Benefit); - staticText = "scry X, where X is its power"; - } - - private SpotterThopterEffect(final SpotterThopterEffect effect) { - super(effect); - } - - @Override - public SpotterThopterEffect copy() { - return new SpotterThopterEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - Permanent permanent = source.getSourcePermanentOrLKI(game); - if (player == null || permanent == null) { - return false; - } - int power = permanent.getPower().getValue(); - return power > 0 && player.scry(power, source, game); - } -} diff --git a/Mage.Sets/src/mage/cards/s/Standstill.java b/Mage.Sets/src/mage/cards/s/Standstill.java index f27b9895fce..75d73bf1c08 100644 --- a/Mage.Sets/src/mage/cards/s/Standstill.java +++ b/Mage.Sets/src/mage/cards/s/Standstill.java @@ -1,34 +1,32 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SpellCastAllTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.SetTargetPointer; +import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; /** - * * @author Plopman */ public final class Standstill extends CardImpl { public Standstill(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{U}"); - + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); // When a player casts a spell, sacrifice Standstill. If you do, each of that player's opponents draws three cards. - this.addAbility(new SpellCastTriggeredAbility()); + this.addAbility(new SpellCastAllTriggeredAbility( + new StandstillEffect(), StaticFilters.FILTER_SPELL_A, false, SetTargetPointer.PLAYER + ).setTriggerPhrase("When a player casts a spell, ")); } private Standstill(final Standstill card) { @@ -41,38 +39,6 @@ public final class Standstill extends CardImpl { } } -class SpellCastTriggeredAbility extends TriggeredAbilityImpl { - - public SpellCastTriggeredAbility() { - super(Zone.BATTLEFIELD, new StandstillEffect(), false); - } - - private SpellCastTriggeredAbility(final SpellCastTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.SPELL_CAST; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - this.getEffects().get(0).setTargetPointer(new FixedTarget(event.getPlayerId())); - return true; - } - - @Override - public String getRule() { - return "When a player casts a spell, sacrifice Standstill. If you do, each of that player's opponents draws three cards."; - } - - @Override - public SpellCastTriggeredAbility copy() { - return new SpellCastTriggeredAbility(this); - } -} - class StandstillEffect extends OneShotEffect { StandstillEffect() { @@ -91,17 +57,15 @@ class StandstillEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - if (permanent.sacrifice(source, game)) { - for (UUID uuid : game.getOpponents(this.getTargetPointer().getFirst(game, source))) { - Player player = game.getPlayer(uuid); - if (player != null) { - player.drawCards(3, source, game); - } + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent != null && permanent.sacrifice(source, game)) { + for (UUID uuid : game.getOpponents(this.getTargetPointer().getFirst(game, source))) { + Player player = game.getPlayer(uuid); + if (player != null) { + player.drawCards(3, source, game); } - return true; } + return true; } return false; } diff --git a/Mage.Sets/src/mage/cards/s/StarseerMentor.java b/Mage.Sets/src/mage/cards/s/StarseerMentor.java index dd0ae2906ae..5e4c4bb4674 100644 --- a/Mage.Sets/src/mage/cards/s/StarseerMentor.java +++ b/Mage.Sets/src/mage/cards/s/StarseerMentor.java @@ -54,7 +54,7 @@ public final class StarseerMentor extends CardImpl { "Sacrifice a nonland permanent or discard a card to prevent losing 3 life?" ) ), YouGainedOrLostLifeCondition.instance, "At the beginning of your end step, if you gained or lost life this turn, " - + "target opponent loses 3 life unless they sacrifice a nonland permanent or discard a card." + + "target opponent loses 3 life unless they sacrifice a nonland permanent of their choice or discard a card." ); ability.addTarget(new TargetOpponent()); ability.addWatcher(new PlayerGainedLifeWatcher()); diff --git a/Mage.Sets/src/mage/cards/s/SteamVines.java b/Mage.Sets/src/mage/cards/s/SteamVines.java index 60de93f25f5..d6455b1d108 100644 --- a/Mage.Sets/src/mage/cards/s/SteamVines.java +++ b/Mage.Sets/src/mage/cards/s/SteamVines.java @@ -43,7 +43,8 @@ public final class SteamVines extends CardImpl { // When enchanted land becomes tapped, destroy it and Steam Vines deals 1 damage to that land's controller. // That player attaches Steam Vines to a land of their choice. - this.addAbility(new BecomesTappedAttachedTriggeredAbility(new SteamVinesEffect(), "enchanted land")); + this.addAbility(new BecomesTappedAttachedTriggeredAbility(new SteamVinesEffect(), "enchanted land") + .setTriggerPhrase("When enchanted land becomes tapped, ")); } diff --git a/Mage.Sets/src/mage/cards/s/SteelSabotage.java b/Mage.Sets/src/mage/cards/s/SteelSabotage.java index bafd92e25c0..82b0acc36cb 100644 --- a/Mage.Sets/src/mage/cards/s/SteelSabotage.java +++ b/Mage.Sets/src/mage/cards/s/SteelSabotage.java @@ -1,28 +1,34 @@ - package mage.cards.s; -import java.util.UUID; import mage.abilities.Mode; import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterArtifactSpell; +import mage.filter.FilterSpell; import mage.target.TargetSpell; import mage.target.common.TargetArtifactPermanent; +import java.util.UUID; + /** * * @author North */ public final class SteelSabotage extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("artifact spell"); + static { + filter.add(CardType.ARTIFACT.getPredicate()); + } + public SteelSabotage(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}"); // Choose one - Counter target artifact spell; or return target artifact to its owner's hand. this.getSpellAbility().addEffect(new CounterTargetEffect()); - this.getSpellAbility().addTarget(new TargetSpell(new FilterArtifactSpell())); + this.getSpellAbility().addTarget(new TargetSpell(filter)); Mode mode = new Mode(new ReturnToHandTargetEffect()); mode.addTarget(new TargetArtifactPermanent()); this.getSpellAbility().addMode(mode); diff --git a/Mage.Sets/src/mage/cards/s/SternJudge.java b/Mage.Sets/src/mage/cards/s/SternJudge.java index 6edc9cba012..45af0d85a05 100644 --- a/Mage.Sets/src/mage/cards/s/SternJudge.java +++ b/Mage.Sets/src/mage/cards/s/SternJudge.java @@ -69,8 +69,8 @@ class SternJudgeEffect extends OneShotEffect { if (player == null) { continue; } - player.loseLife(game.getBattlefield().count( - filter, source.getSourceId(), source, game + player.loseLife(game.getBattlefield().countAll( + filter, player.getId(), game ), game, source, false); } return true; diff --git a/Mage.Sets/src/mage/cards/s/StingbladeAssassin.java b/Mage.Sets/src/mage/cards/s/StingbladeAssassin.java index 4887d761588..e3cd1c8b2a0 100644 --- a/Mage.Sets/src/mage/cards/s/StingbladeAssassin.java +++ b/Mage.Sets/src/mage/cards/s/StingbladeAssassin.java @@ -10,9 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterOpponentsCreaturePermanent; -import mage.filter.predicate.permanent.WasDealtDamageThisTurnPredicate; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import java.util.UUID; @@ -22,14 +20,6 @@ import java.util.UUID; */ public final class StingbladeAssassin extends CardImpl { - private static final FilterPermanent filter = new FilterOpponentsCreaturePermanent( - "creature an opponent controls that was dealt damage this turn" - ); - - static { - filter.add(WasDealtDamageThisTurnPredicate.instance); - } - public StingbladeAssassin(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); @@ -46,7 +36,7 @@ public final class StingbladeAssassin extends CardImpl { // When Stingblade Assassin enters the battlefield, destroy target creature an opponent controls that was dealt damage this turn. Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect()); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_OPPONENTS_CREATURE_DAMAGED_THIS_TURN)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/StolenUniform.java b/Mage.Sets/src/mage/cards/s/StolenUniform.java new file mode 100644 index 00000000000..7eb0c03ed8a --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StolenUniform.java @@ -0,0 +1,183 @@ +package mage.cards.s; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.GainControlTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.targetpointer.EachTargetPointer; +import mage.target.targetpointer.FixedTarget; +import mage.target.targetpointer.SecondTargetPointer; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class StolenUniform extends CardImpl { + + public StolenUniform(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}"); + + // Choose target creature you control and target Equipment. Gain control of that Equipment until end of turn. Attach it to the chosen creature. When you lose control of that Equipment this turn, if it's attached to a creature you control, unattach it. + this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.EndOfTurn) + .setText("choose target creature you control and target Equipment. " + + "Gain control of that Equipment until end of turn") + .setTargetPointer(new SecondTargetPointer())); + this.getSpellAbility().addEffect(new StolenUniformAttachEffect()); + this.getSpellAbility().addEffect(new StolenUniformTriggerEffect()); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_EQUIPMENT)); + } + + private StolenUniform(final StolenUniform card) { + super(card); + } + + @Override + public StolenUniform copy() { + return new StolenUniform(this); + } +} + +class StolenUniformAttachEffect extends OneShotEffect { + + StolenUniformAttachEffect() { + super(Outcome.Benefit); + staticText = "Attach it to the chosen creature"; + this.setTargetPointer(new EachTargetPointer()); + } + + private StolenUniformAttachEffect(final StolenUniformAttachEffect effect) { + super(effect); + } + + @Override + public StolenUniformAttachEffect copy() { + return new StolenUniformAttachEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + List permanents = this.getTargetPointer() + .getTargets(game, source) + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + return permanents.size() >= 2 && permanents.get(1).addAttachment(permanents.get(0).getId(), source, game); + } +} + +class StolenUniformTriggerEffect extends OneShotEffect { + + StolenUniformTriggerEffect() { + super(Outcome.Benefit); + staticText = "When you lose control of that Equipment this turn, " + + "if it's attached to a creature you control, unattach it"; + this.setTargetPointer(new SecondTargetPointer()); + } + + private StolenUniformTriggerEffect(final StolenUniformTriggerEffect effect) { + super(effect); + } + + @Override + public StolenUniformTriggerEffect copy() { + return new StolenUniformTriggerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (permanent == null) { + return false; + } + game.addDelayedTriggeredAbility(new StolenUniformTriggeredAbility(permanent, game), source); + return true; + } +} + +class StolenUniformTriggeredAbility extends DelayedTriggeredAbility { + + private final MageObjectReference mor; + + StolenUniformTriggeredAbility(Permanent permanent, Game game) { + super(new StolenUniformUnattachEffect(permanent, game), Duration.EndOfTurn, true, false); + setTriggerPhrase("When you lose control of that Equipment this turn, "); + this.mor = new MageObjectReference(permanent, game); + } + + private StolenUniformTriggeredAbility(final StolenUniformTriggeredAbility ability) { + super(ability); + this.mor = ability.mor; + } + + @Override + public StolenUniformTriggeredAbility copy() { + return new StolenUniformTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.LOST_CONTROL; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return isControlledBy(event.getPlayerId()) + && mor.zoneCounterIsCurrent(game) + && event.getTargetId().equals(mor.getSourceId()); + } + + @Override + public boolean checkInterveningIfClause(Game game) { + return Optional + .ofNullable(mor.getPermanent(game)) + .map(Permanent::getAttachedTo) + .map(game::getControllerId) + .map(this::isControlledBy) + .orElse(false); + } +} + +class StolenUniformUnattachEffect extends OneShotEffect { + + StolenUniformUnattachEffect(Permanent permanent, Game game) { + super(Outcome.Benefit); + staticText = "unattach it"; + this.setTargetPointer(new FixedTarget(permanent, game)); + } + + private StolenUniformUnattachEffect(final StolenUniformUnattachEffect effect) { + super(effect); + } + + @Override + public StolenUniformUnattachEffect copy() { + return new StolenUniformUnattachEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Optional.ofNullable(getTargetPointer().getFirst(game, source)) + .map(game::getPermanent) + .ifPresent(permanent -> permanent.unattach(game)); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/StoneTongueBasilisk.java b/Mage.Sets/src/mage/cards/s/StoneTongueBasilisk.java index 16284273755..763c34ee152 100644 --- a/Mage.Sets/src/mage/cards/s/StoneTongueBasilisk.java +++ b/Mage.Sets/src/mage/cards/s/StoneTongueBasilisk.java @@ -36,7 +36,7 @@ public final class StoneTongueBasilisk extends CardImpl { // Threshold - As long as seven or more cards are in your graveyard, all creatures able to block Stone-Tongue Basilisk do so. this.addAbility(new SimpleStaticAbility(new ConditionalRequirementEffect( new MustBeBlockedByAllSourceEffect(), ThresholdCondition.instance, "As long as seven " + - "or more cards are in your graveyard, all creatures able to block {this} do so" + "or more cards are in your graveyard, all creatures able to block {this} do so" )).setAbilityWord(AbilityWord.THRESHOLD)); } diff --git a/Mage.Sets/src/mage/cards/s/StonehewerGiant.java b/Mage.Sets/src/mage/cards/s/StonehewerGiant.java index b68c1b31b85..af781b25e30 100644 --- a/Mage.Sets/src/mage/cards/s/StonehewerGiant.java +++ b/Mage.Sets/src/mage/cards/s/StonehewerGiant.java @@ -22,6 +22,7 @@ import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetControlledCreaturePermanent; +import mage.util.CardUtil; import java.util.UUID; @@ -90,7 +91,7 @@ class StonehewerGiantEffect extends OneShotEffect { Card card = controller.getLibrary().getCard(target.getFirstTarget(), game); if (card != null) { controller.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent equipment = game.getPermanent(card.getId()); + Permanent equipment = CardUtil.getPermanentFromCardPutToBattlefield(card, game); Target targetCreature = new TargetControlledCreaturePermanent(); targetCreature.withNotTarget(true); if (equipment != null && controller.choose(Outcome.BoostCreature, targetCreature, source, game)) { diff --git a/Mage.Sets/src/mage/cards/s/StoryCircle.java b/Mage.Sets/src/mage/cards/s/StoryCircle.java index c39f8929f05..eba8e5d13d6 100644 --- a/Mage.Sets/src/mage/cards/s/StoryCircle.java +++ b/Mage.Sets/src/mage/cards/s/StoryCircle.java @@ -1,38 +1,43 @@ package mage.cards.s; -import java.util.UUID; -import mage.ObjectColor; -import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.ChooseColorEffect; -import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToYouEffect; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterObject; -import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.Game; +import mage.filter.FilterSource; +import mage.filter.predicate.mageobject.ChosenColorPredicate; + +import java.util.UUID; /** - * * @author LoneFox */ public final class StoryCircle extends CardImpl { + private static FilterSource filter = new FilterSource("a source of your choice of the chosen color"); + + static { + filter.add(ChosenColorPredicate.TRUE); + } + public StoryCircle(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{W}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}{W}"); // As Story Circle enters the battlefield, choose a color. this.addAbility(new AsEntersBattlefieldAbility(new ChooseColorEffect(Outcome.Neutral))); // {W}: The next time a source of your choice of the chosen color would deal damage to you this turn, prevent that damage. - this.addAbility(new SimpleActivatedAbility(new StoryCircleEffect(), new ManaCostsImpl<>("{W}"))); + this.addAbility(new SimpleActivatedAbility( + new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, true, filter), + new ManaCostsImpl<>("{W}") + )); } private StoryCircle(final StoryCircle card) { @@ -43,29 +48,4 @@ public final class StoryCircle extends CardImpl { public StoryCircle copy() { return new StoryCircle(this); } -} - -class StoryCircleEffect extends PreventNextDamageFromChosenSourceToYouEffect { - - StoryCircleEffect() { - super(Duration.EndOfTurn); - staticText = "The next time a source of your choice of the chosen color would deal damage to you this turn, prevent that damage."; - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - FilterObject filter = targetSource.getFilter(); - filter.add(new ColorPredicate((ObjectColor) game.getState().getValue(source.getSourceId() + "_color"))); - } - - private StoryCircleEffect(final StoryCircleEffect effect) { - super(effect); - } - - @Override - public StoryCircleEffect copy() { - return new StoryCircleEffect(this); - } - -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/StragoAndRelm.java b/Mage.Sets/src/mage/cards/s/StragoAndRelm.java new file mode 100644 index 00000000000..ef49bd3a27c --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StragoAndRelm.java @@ -0,0 +1,133 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Controllable; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetOpponent; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +import java.util.Arrays; +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class StragoAndRelm extends CardImpl { + + public StragoAndRelm(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Sketch and Lore -- {2}{R}, {T}: Target opponent exiles cards from the top of their library until they exile an instant, sorcery, or creature card. You may cast that card without paying its mana cost. If you cast a creature spell this way, it gains haste and "At the beginning of the end step, sacrifice this creature." Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility(new StragoAndRelmEffect(), new ManaCostsImpl<>("{2}{R}")); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability.withFlavorWord("Sketch and Lore")); + } + + private StragoAndRelm(final StragoAndRelm card) { + super(card); + } + + @Override + public StragoAndRelm copy() { + return new StragoAndRelm(this); + } +} + +class StragoAndRelmEffect extends OneShotEffect { + + private enum StragoAndRelmTracker implements CardUtil.SpellCastTracker { + instance; + + @Override + public boolean checkCard(Card card, Game game) { + return true; + } + + @Override + public void addCard(Card card, Ability source, Game game) { + if (!card.isCreature(game)) { + return; + } + for (ContinuousEffect effect : Arrays.asList( + new GainAbilityTargetEffect(HasteAbility.getInstance()), + new GainAbilityTargetEffect(new BeginningOfEndStepTriggeredAbility(new SacrificeSourceEffect())) + )) { + effect.setTargetPointer(new FixedTarget(card.getId())); + game.getState().addEffect(effect, card.getSpellAbility()); + game.addEffect(effect, source); + } + } + } + + StragoAndRelmEffect() { + super(Outcome.Benefit); + staticText = "target opponent exiles cards from the top of their library until they exile an " + + "instant, sorcery, or creature card. You may cast that card without paying its mana cost. " + + "If you cast a creature spell this way, it gains haste and \"At the beginning of the end step, sacrifice this creature.\""; + } + + private StragoAndRelmEffect(final StragoAndRelmEffect effect) { + super(effect); + } + + @Override + public StragoAndRelmEffect copy() { + return new StragoAndRelmEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player opponent = game.getPlayer(getTargetPointer().getFirst(game, source)); + if (opponent == null) { + return false; + } + Card card = getCard(opponent, game, source); + if (card == null) { + return false; + } + Optional.ofNullable(source) + .map(Controllable::getControllerId) + .map(game::getPlayer) + .ifPresent(player -> CardUtil.castSpellWithAttributesForFree( + opponent, source, game, new CardsImpl(card), + StaticFilters.FILTER_CARD, StragoAndRelmTracker.instance + )); + return true; + } + + private static Card getCard(Player player, Game game, Ability source) { + for (Card card : player.getLibrary().getCards(game)) { + player.moveCards(card, Zone.EXILED, source, game); + if (card.isInstantOrSorcery(game) || card.isCreature(game)) { + return card; + } + } + return null; + } +} diff --git a/Mage.Sets/src/mage/cards/s/StrefanMaurerProgenitor.java b/Mage.Sets/src/mage/cards/s/StrefanMaurerProgenitor.java index 9def4f0a77c..ad37091dd74 100644 --- a/Mage.Sets/src/mage/cards/s/StrefanMaurerProgenitor.java +++ b/Mage.Sets/src/mage/cards/s/StrefanMaurerProgenitor.java @@ -30,6 +30,7 @@ import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetCardInHand; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import mage.watchers.common.PlayerLostLifeWatcher; import java.util.*; @@ -121,7 +122,7 @@ class StrefanMaurerProgenitorPlayVampireEffect extends OneShotEffect { player.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, true, null); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/s/StrengthOfArms.java b/Mage.Sets/src/mage/cards/s/StrengthOfArms.java index fb99e485ade..594aaa8abf2 100644 --- a/Mage.Sets/src/mage/cards/s/StrengthOfArms.java +++ b/Mage.Sets/src/mage/cards/s/StrengthOfArms.java @@ -1,4 +1,3 @@ - package mage.cards.s; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; @@ -9,33 +8,26 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.SubType; -import mage.filter.common.FilterControlledPermanent; +import mage.filter.StaticFilters; import mage.game.permanent.token.HumanSoldierToken; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; /** - * * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ public final class StrengthOfArms extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("If you control an Equipment,"); - static { - filter.add(SubType.EQUIPMENT.getPredicate()); - } - public StrengthOfArms(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{W}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}"); // Target creature gets +2/+2 until end of turn. // If you control an Equipment, create a 1/1 white Human Soldier creature token. this.getSpellAbility().addEffect(new BoostTargetEffect(2, 2, Duration.EndOfTurn)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new CreateTokenEffect(new HumanSoldierToken()), - new PermanentsOnTheBattlefieldCondition(filter), + new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT), "If you control an Equipment, create a 1/1 white Human Soldier creature token.")); } diff --git a/Mage.Sets/src/mage/cards/s/StrengthTestingHammer.java b/Mage.Sets/src/mage/cards/s/StrengthTestingHammer.java index 3ae943bd288..78c8302abc0 100644 --- a/Mage.Sets/src/mage/cards/s/StrengthTestingHammer.java +++ b/Mage.Sets/src/mage/cards/s/StrengthTestingHammer.java @@ -31,11 +31,7 @@ public final class StrengthTestingHammer extends CardImpl { this.subtype.add(SubType.EQUIPMENT); - //Whenever equipped creature attacks, roll a six-sided die. - // That creature gets +X/+0 until end of turn, where X is the result. - // Then, if it has the greatest power or is tied for greatest power among creatures on the battlefield, draw a card. - - + // Whenever equipped creature attacks, roll a six-sided die. That creature gets +X/+0 until end of turn, where X is the result. Then, if it has the greatest power or is tied for greatest power among creatures on the battlefield, draw a card. TriggeredAbility ability = new AttacksAttachedTriggeredAbility(new StrengthTestingHammerEffect()); ability.addEffect(new ConditionalOneShotEffect(new DrawCardSourceControllerEffect(1), StrengthTestingHammerCondition.instance).setText("Then if it has the greatest power or is tied for greatest power among creatures on the battlefield, draw a card.")); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/StrikeTeamCommando.java b/Mage.Sets/src/mage/cards/s/StrikeTeamCommando.java index 286968a9a20..a867cef6ad8 100644 --- a/Mage.Sets/src/mage/cards/s/StrikeTeamCommando.java +++ b/Mage.Sets/src/mage/cards/s/StrikeTeamCommando.java @@ -1,7 +1,6 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -14,10 +13,11 @@ import mage.constants.SubType; import mage.filter.common.FilterArtifactPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.target.common.TargetArtifactPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author Styxo */ public final class StrikeTeamCommando extends CardImpl { @@ -29,7 +29,7 @@ public final class StrikeTeamCommando extends CardImpl { } public StrikeTeamCommando(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G/W}{G/W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G/W}{G/W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.REBEL); this.power = new MageInt(3); @@ -37,7 +37,7 @@ public final class StrikeTeamCommando extends CardImpl { // When Strike Team Commando enters the battlefield, you may destroy target artifact without spaceflight. Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), true); - ability.addTarget(new TargetArtifactPermanent(filter)); + ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/StuckInSummonersSanctum.java b/Mage.Sets/src/mage/cards/s/StuckInSummonersSanctum.java new file mode 100644 index 00000000000..cea1dcd4b96 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StuckInSummonersSanctum.java @@ -0,0 +1,58 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.CantActivateAbilitiesAttachedEffect; +import mage.abilities.effects.common.DontUntapInControllersUntapStepEnchantedEffect; +import mage.abilities.effects.common.TapEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class StuckInSummonersSanctum extends CardImpl { + + public StuckInSummonersSanctum(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); + + this.subtype.add(SubType.AURA); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Enchant artifact or creature + TargetPermanent auraTarget = new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_CREATURE); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(new EnchantAbility(auraTarget)); + + // When this Aura enters, tap enchanted permanent. + this.addAbility(new EntersBattlefieldTriggeredAbility(new TapEnchantedEffect("permanent"))); + + // Enchanted permanent doesn't untap during its controller's untap step and its activated abilities can't be activated. + Ability ability = new SimpleStaticAbility(new DontUntapInControllersUntapStepEnchantedEffect("permanent")); + ability.addEffect(new CantActivateAbilitiesAttachedEffect().setText("and its activated abilities can't be activated")); + this.addAbility(ability); + } + + private StuckInSummonersSanctum(final StuckInSummonersSanctum card) { + super(card); + } + + @Override + public StuckInSummonersSanctum copy() { + return new StuckInSummonersSanctum(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SummonAlexander.java b/Mage.Sets/src/mage/cards/s/SummonAlexander.java new file mode 100644 index 00000000000..6a752a4b835 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SummonAlexander.java @@ -0,0 +1,63 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.common.PreventAllDamageToAllEffect; +import mage.abilities.effects.common.TapAllEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SummonAlexander extends CardImpl { + + public SummonAlexander(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.SAGA); + this.subtype.add(SubType.CONSTRUCT); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + this.nightCard = true; + this.color.setWhite(true); + + // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I, II -- Prevent all damage that would be dealt to creatures you control this turn. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, + new PreventAllDamageToAllEffect( + Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURES_CONTROLLED + ) + ); + + // III -- Tap all creatures your opponents control. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_III, + new TapAllEffect(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURES) + ); + this.addAbility(sagaAbility.withShowSacText(true)); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + } + + private SummonAlexander(final SummonAlexander card) { + super(card); + } + + @Override + public SummonAlexander copy() { + return new SummonAlexander(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SummonChocoMog.java b/Mage.Sets/src/mage/cards/s/SummonChocoMog.java new file mode 100644 index 00000000000..159deeb6a31 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SummonChocoMog.java @@ -0,0 +1,48 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SagaChapter; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SummonChocoMog extends CardImpl { + + public SummonChocoMog(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{2}{W}"); + + this.subtype.add(SubType.SAGA); + this.subtype.add(SubType.BIRD); + this.subtype.add(SubType.MOOGLE); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.) + SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_IV); + + // I, II, III, IV -- Stampede! -- Other creatures you control get +1/+0 until end of turn. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_IV, ability -> { + ability.addEffect(new BoostControlledEffect(1, 0, Duration.EndOfTurn, true)); + ability.withFlavorWord("Stampede!"); + }); + this.addAbility(sagaAbility); + } + + private SummonChocoMog(final SummonChocoMog card) { + super(card); + } + + @Override + public SummonChocoMog copy() { + return new SummonChocoMog(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SummonEsperMaduin.java b/Mage.Sets/src/mage/cards/s/SummonEsperMaduin.java new file mode 100644 index 00000000000..a8015ec0963 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SummonEsperMaduin.java @@ -0,0 +1,103 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.mana.BasicManaEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SummonEsperMaduin extends CardImpl { + + public SummonEsperMaduin(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.SAGA); + this.subtype.add(SubType.ELEMENTAL); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + this.nightCard = true; + this.color.setGreen(true); + + // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I -- Reveal the top card of your library. If it's a permanent card, put it into your hand. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new SummonEsperMaduinEffect()); + + // II -- Add {G}{G}. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II, new BasicManaEffect(Mana.GreenMana(2))); + + // III -- Other creatures you control get +2/+2 and gain trample until end of turn. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_III, + new BoostControlledEffect( + 2, 2, Duration.EndOfTurn, true + ).setText("other creatures you control get +2/+2"), + new GainAbilityControlledEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_PERMANENT_CREATURE, true + ).setText("and gain trample until end of turn") + ); + this.addAbility(sagaAbility.withShowSacText(true)); + } + + private SummonEsperMaduin(final SummonEsperMaduin card) { + super(card); + } + + @Override + public SummonEsperMaduin copy() { + return new SummonEsperMaduin(this); + } +} + +class SummonEsperMaduinEffect extends OneShotEffect { + + SummonEsperMaduinEffect() { + super(Outcome.Benefit); + staticText = "reveal the top card of your library. If it's a permanent card, put it into your hand"; + } + + private SummonEsperMaduinEffect(final SummonEsperMaduinEffect effect) { + super(effect); + } + + @Override + public SummonEsperMaduinEffect copy() { + return new SummonEsperMaduinEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Card card = player.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + player.revealCards(source, new CardsImpl(card), game); + if (card.isPermanent(game)) { + player.moveCards(card, Zone.HAND, source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SummonEsperRamuh.java b/Mage.Sets/src/mage/cards/s/SummonEsperRamuh.java index 35b1615f771..125dda66ac6 100644 --- a/Mage.Sets/src/mage/cards/s/SummonEsperRamuh.java +++ b/Mage.Sets/src/mage/cards/s/SummonEsperRamuh.java @@ -47,7 +47,9 @@ public final class SummonEsperRamuh extends CardImpl { // I -- Judgment Bolt -- This creature deals damage equal to the number of noncreature, nonland cards in your graveyard to target creature an opponent controls. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, ability -> { - ability.addEffect(new DamageTargetEffect(xValue)); + ability.addEffect(new DamageTargetEffect(xValue) + .setText("{this} deals damage equal to the number of noncreature, nonland " + + "cards in your graveyard to target creature an opponent controls")); ability.addTarget(new TargetOpponentsCreaturePermanent()); ability.withFlavorWord("Judgment Bolt"); }); diff --git a/Mage.Sets/src/mage/cards/s/SummonEsperValigarmanda.java b/Mage.Sets/src/mage/cards/s/SummonEsperValigarmanda.java new file mode 100644 index 00000000000..3a2c1b4614a --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SummonEsperValigarmanda.java @@ -0,0 +1,160 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.SagaAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CountersSourceCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.MayCastTargetCardEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.HasteAbility; +import mage.cards.*; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInExile; +import mage.target.common.TargetCardInGraveyard; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SummonEsperValigarmanda extends CardImpl { + + public SummonEsperValigarmanda(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{3}{R}"); + + this.subtype.add(SubType.SAGA); + this.subtype.add(SubType.DRAKE); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.) + SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_IV); + + // I -- Exile an instant or sorcery card from each graveyard. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_I, + new SummonEsperValigarmandaExileEffect() + ); + + // II, III, IV -- Add {R} for each lore counter on this Saga. You may cast an instant or sorcery card exiled with this Saga, and mana of any type can be spent to cast that spell. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_II, SagaChapter.CHAPTER_IV, + new SummonEsperValigarmandaCastEffect() + ); + this.addAbility(sagaAbility); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Haste + this.addAbility(HasteAbility.getInstance()); + } + + private SummonEsperValigarmanda(final SummonEsperValigarmanda card) { + super(card); + } + + @Override + public SummonEsperValigarmanda copy() { + return new SummonEsperValigarmanda(this); + } +} + +class SummonEsperValigarmandaExileEffect extends OneShotEffect { + + SummonEsperValigarmandaExileEffect() { + super(Outcome.Benefit); + staticText = "exile an instant or sorcery card from each graveyard"; + } + + private SummonEsperValigarmandaExileEffect(final SummonEsperValigarmandaExileEffect effect) { + super(effect); + } + + @Override + public SummonEsperValigarmandaExileEffect copy() { + return new SummonEsperValigarmandaExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Cards cards = new CardsImpl(); + TargetCard target = new TargetCardInGraveyard(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY); + target.withNotTarget(true); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null || player.getGraveyard().count(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, game) < 1) { + continue; + } + target.clearChosen(); + controller.choose(outcome, player.getGraveyard(), target, source, game); + cards.add(game.getCard(target.getFirstTarget())); + } + return !cards.isEmpty() + && controller.moveCardsToExile( + cards.getCards(game), source, game, true, + CardUtil.getExileZoneId(game, source, 1), + CardUtil.getSourceName(game, source) + ); + } +} + +class SummonEsperValigarmandaCastEffect extends OneShotEffect { + + private static final DynamicValue xValue = new CountersSourceCount(CounterType.LORE); + + SummonEsperValigarmandaCastEffect() { + super(Outcome.Benefit); + staticText = "add {R} for each lore counter on {this}. " + + "You may cast an instant or sorcery card exiled with {this}, " + + "and mana of any type can be spent to cast that spell"; + } + + private SummonEsperValigarmandaCastEffect(final SummonEsperValigarmandaCastEffect effect) { + super(effect); + } + + @Override + public SummonEsperValigarmandaCastEffect copy() { + return new SummonEsperValigarmandaCastEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + int count = xValue.calculate(game, source, this); + if (count > 0) { + player.getManaPool().addMana(Mana.RedMana(count), game, source); + } + TargetCard target = new TargetCardInExile( + 0, 1, + StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, + CardUtil.getExileZoneId(game, source) + ); + target.withNotTarget(true); + target.withChooseHint("to cast"); + player.choose(outcome, target, source, game); + Card card = game.getCard(target.getFirstTarget()); + return card != null + && new MayCastTargetCardEffect(CastManaAdjustment.AS_THOUGH_ANY_MANA_TYPE) + .setTargetPointer(new FixedTarget(card, game)) + .apply(game, source); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SummonFatChocobo.java b/Mage.Sets/src/mage/cards/s/SummonFatChocobo.java new file mode 100644 index 00000000000..dff528c3279 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SummonFatChocobo.java @@ -0,0 +1,60 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.game.permanent.token.ChocoboToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SummonFatChocobo extends CardImpl { + + public SummonFatChocobo(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{4}{G}"); + + this.subtype.add(SubType.SAGA); + this.subtype.add(SubType.BIRD); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.) + SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_IV); + + // I -- Wark -- Create a 2/2 green Bird creature token with "Whenever a land you control enters, this token gets +1/+0 until end of turn." + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, ability -> { + ability.addEffect(new CreateTokenEffect(new ChocoboToken())); + ability.withFlavorWord("Wark"); + }); + + // II, III, IV -- Kerplunk -- Creatures you control gain trample until end of turn. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II, SagaChapter.CHAPTER_IV, ability -> { + ability.addEffect(new GainAbilityControlledEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_PERMANENT_CREATURES + )); + ability.withFlavorWord("Kerplunk"); + }); + this.addAbility(sagaAbility); + } + + private SummonFatChocobo(final SummonFatChocobo card) { + super(card); + } + + @Override + public SummonFatChocobo copy() { + return new SummonFatChocobo(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SummonFenrir.java b/Mage.Sets/src/mage/cards/s/SummonFenrir.java new file mode 100644 index 00000000000..3f064954b5a --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SummonFenrir.java @@ -0,0 +1,77 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.SagaAbility; +import mage.abilities.common.delayed.AddCounterNextSpellDelayedTriggeredAbility; +import mage.abilities.condition.common.ControlsCreatureGreatestPowerCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SummonFenrir extends CardImpl { + + private static final Hint hint = new ConditionHint( + ControlsCreatureGreatestPowerCondition.instance, + "You control a creature with the greatest power" + ); + + public SummonFenrir(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.SAGA); + this.subtype.add(SubType.WOLF); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I -- Crescent Fang -- Search your library for a basic land card, put it onto the battlefield tapped, then shuffle. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, ability -> { + ability.addEffect(new SearchLibraryPutInPlayEffect( + new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true + )); + ability.withFlavorWord("Crescent Fang"); + }); + + // II -- Heavenward Howl -- When you next cast a creature spell this turn, that creature enters with an additional +1/+1 counter on it. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II, ability -> { + ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new AddCounterNextSpellDelayedTriggeredAbility())); + ability.withFlavorWord("Heavenward Howl"); + }); + + // III -- Ecliptic Growl -- Draw a card if you control the creature with the greatest power or tied for the greatest power. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, ability -> { + ability.addEffect(new ConditionalOneShotEffect( + new DrawCardSourceControllerEffect(1), ControlsCreatureGreatestPowerCondition.instance, + "draw a card if you control the creature with the greatest power or tied for the greatest power" + )); + ability.withFlavorWord("Ecliptic Growl"); + }); + this.addAbility(sagaAbility.addHint(hint)); + } + + private SummonFenrir(final SummonFenrir card) { + super(card); + } + + @Override + public SummonFenrir copy() { + return new SummonFenrir(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SummonGFCerberus.java b/Mage.Sets/src/mage/cards/s/SummonGFCerberus.java new file mode 100644 index 00000000000..65592498f6d --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SummonGFCerberus.java @@ -0,0 +1,93 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SagaAbility; +import mage.abilities.common.delayed.CopyNextSpellDelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.keyword.SurveilEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.stack.Spell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SummonGFCerberus extends CardImpl { + + public SummonGFCerberus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{2}{R}{R}"); + + this.subtype.add(SubType.SAGA); + this.subtype.add(SubType.DOG); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I -- Surveil 1. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new SurveilEffect(1)); + + // II -- Double -- When you next cast an instant or sorcery spell this turn, copy it. You may choose new targets for the copy. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II, ability -> { + ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new CopyNextSpellDelayedTriggeredAbility())); + ability.withFlavorWord("Double"); + }); + + // III -- Triple -- When you next cast an instant or sorcery spell this turn, copy it twice. You may choose new targets for the copies. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, ability -> { + ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new CopyNextSpellDelayedTriggeredAbility( + StaticFilters.FILTER_SPELL_INSTANT_OR_SORCERY, new SummonGFCerberusEffect(), + "When you next cast an instant or sorcery spell this turn, " + + "copy it twice. You may choose new targets for the copies." + ))); + ability.withFlavorWord("Triple"); + }); + this.addAbility(sagaAbility); + } + + private SummonGFCerberus(final SummonGFCerberus card) { + super(card); + } + + @Override + public SummonGFCerberus copy() { + return new SummonGFCerberus(this); + } +} + +class SummonGFCerberusEffect extends OneShotEffect { + + SummonGFCerberusEffect() { + super(Outcome.Benefit); + } + + private SummonGFCerberusEffect(final SummonGFCerberusEffect effect) { + super(effect); + } + + @Override + public SummonGFCerberusEffect copy() { + return new SummonGFCerberusEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Spell spell = (Spell) getValue("spellCast"); + if (spell != null) { + spell.createCopyOnStack(game, source, source.getControllerId(), true, 2); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SummonGFIfrit.java b/Mage.Sets/src/mage/cards/s/SummonGFIfrit.java new file mode 100644 index 00000000000..10f00088365 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SummonGFIfrit.java @@ -0,0 +1,56 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.Mana; +import mage.abilities.common.SagaAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.mana.BasicManaEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SagaChapter; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SummonGFIfrit extends CardImpl { + + public SummonGFIfrit(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.SAGA); + this.subtype.add(SubType.DEMON); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.) + SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_IV); + + // I, II -- You may discard a card. If you do, draw a card. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, + new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new DiscardCardCost()) + ); + + // III, IV -- Add {R}. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_III, SagaChapter.CHAPTER_IV, + new BasicManaEffect(Mana.RedMana(1)) + ); + this.addAbility(sagaAbility); + } + + private SummonGFIfrit(final SummonGFIfrit card) { + super(card); + } + + @Override + public SummonGFIfrit copy() { + return new SummonGFIfrit(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SummonKnightsOfRound.java b/Mage.Sets/src/mage/cards/s/SummonKnightsOfRound.java index 9b3a4e17b34..ce5289506a0 100644 --- a/Mage.Sets/src/mage/cards/s/SummonKnightsOfRound.java +++ b/Mage.Sets/src/mage/cards/s/SummonKnightsOfRound.java @@ -35,11 +35,14 @@ public final class SummonKnightsOfRound extends CardImpl { SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_V); // I, II, III, IV -- Create three 2/2 white Knight creature tokens. - sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_IV, new CreateTokenEffect(new WaylayToken())); + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_IV, + new CreateTokenEffect(new WaylayToken(), 3) + ); // V -- Ultimate End -- Other creatures you control get +2/+2 until end of turn. Put an indestructible counter on each of them. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_V, ability -> { - ability.addEffect(new BoostControlledEffect(2, 2, Duration.EndOfTurn)); + ability.addEffect(new BoostControlledEffect(2, 2, Duration.EndOfTurn, true)); ability.addEffect(new AddCountersAllEffect( CounterType.INDESTRUCTIBLE.createInstance(), StaticFilters.FILTER_OTHER_CONTROLLED_CREATURES diff --git a/Mage.Sets/src/mage/cards/s/SummonLeviathan.java b/Mage.Sets/src/mage/cards/s/SummonLeviathan.java index e3398a1f4a2..e690bd28a75 100644 --- a/Mage.Sets/src/mage/cards/s/SummonLeviathan.java +++ b/Mage.Sets/src/mage/cards/s/SummonLeviathan.java @@ -14,7 +14,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SagaChapter; import mage.constants.SubType; -import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.events.GameEvent; @@ -27,10 +27,11 @@ import java.util.UUID; */ public final class SummonLeviathan extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent(); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(); static { filter.add(Predicates.not(SubType.KRAKEN.getPredicate())); + filter.add(Predicates.not(SubType.LEVIATHAN.getPredicate())); filter.add(Predicates.not(SubType.MERFOLK.getPredicate())); filter.add(Predicates.not(SubType.OCTOPUS.getPredicate())); filter.add(Predicates.not(SubType.SERPENT.getPredicate())); @@ -102,6 +103,7 @@ class SummonLeviathanTriggeredAbility extends DelayedTriggeredAbility { && (permanent.hasSubtype(SubType.KRAKEN, game) || permanent.hasSubtype(SubType.LEVIATHAN, game) || permanent.hasSubtype(SubType.MERFOLK, game) - || permanent.hasSubtype(SubType.OCTOPUS, game)); + || permanent.hasSubtype(SubType.OCTOPUS, game) + || permanent.hasSubtype(SubType.SERPENT, game)); } } diff --git a/Mage.Sets/src/mage/cards/s/SummonPrimalOdin.java b/Mage.Sets/src/mage/cards/s/SummonPrimalOdin.java index 4d65029bb48..b095557ffe5 100644 --- a/Mage.Sets/src/mage/cards/s/SummonPrimalOdin.java +++ b/Mage.Sets/src/mage/cards/s/SummonPrimalOdin.java @@ -47,7 +47,7 @@ public final class SummonPrimalOdin extends CardImpl { new DealsCombatDamageToAPlayerTriggeredAbility( new LoseGameTargetPlayerEffect(), false, true ), Duration.Custom - )); + ).setText("{this} gains \"Whenever this creature deals combat damage to a player, that player loses the game.\"")); ability.withFlavorWord("Zantetsuken"); }); diff --git a/Mage.Sets/src/mage/cards/s/SummonTitan.java b/Mage.Sets/src/mage/cards/s/SummonTitan.java new file mode 100644 index 00000000000..f48129bb4cd --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SummonTitan.java @@ -0,0 +1,76 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.SagaAbility; +import mage.abilities.dynamicvalue.common.LandsYouControlCount; +import mage.abilities.effects.Effects; +import mage.abilities.effects.common.MillCardsControllerEffect; +import mage.abilities.effects.common.ReturnFromYourGraveyardToBattlefieldAllEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.hint.common.LandsYouControlHint; +import mage.abilities.keyword.ReachAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SagaChapter; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SummonTitan extends CardImpl { + + public SummonTitan(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{3}{G}{G}"); + + this.subtype.add(SubType.SAGA); + this.subtype.add(SubType.GIANT); + this.power = new MageInt(7); + this.toughness = new MageInt(7); + + // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I - Mill five cards. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new MillCardsControllerEffect(5)); + + // II - Return all land cards from your graveyard to the battlefield tapped. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_II, + new ReturnFromYourGraveyardToBattlefieldAllEffect(StaticFilters.FILTER_CARD_LANDS, true) + ); + + // III - Until end of turn, another target creature you control gains trample and gets +X/+X, where X is the number of lands you control. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_III, + new Effects( + new GainAbilityTargetEffect(TrampleAbility.getInstance()) + .setText("until end of turn, another target creature you control gains trample"), + new BoostTargetEffect(LandsYouControlCount.instance, LandsYouControlCount.instance) + .setText("and gets +X/+X, where X is the number of lands you control") + ), new TargetPermanent(StaticFilters.FILTER_ANOTHER_TARGET_CREATURE_YOU_CONTROL) + ); + this.addAbility(sagaAbility.addHint(LandsYouControlHint.instance)); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + } + + private SummonTitan(final SummonTitan card) { + super(card); + } + + @Override + public SummonTitan copy() { + return new SummonTitan(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SummonValefor.java b/Mage.Sets/src/mage/cards/s/SummonValefor.java new file mode 100644 index 00000000000..f368ee8377b --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SummonValefor.java @@ -0,0 +1,147 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.Effects; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Controllable; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.*; + +/** + * @author TheElk801 + */ +public final class SummonValefor extends CardImpl { + + public SummonValefor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{4}{U}"); + + this.subtype.add(SubType.SAGA); + this.subtype.add(SubType.DRAKE); + this.power = new MageInt(5); + this.toughness = new MageInt(4); + + // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.) + SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_IV); + + // I - Sonic Wings -- Each opponent chooses a creature with the greatest mana value among creatures they control. Return those creatures to their owners' hands. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, ability -> { + ability.addEffect(new SummonValeforEffect()); + ability.withFlavorWord("Sonic Wings"); + }); + + // II, III, IV - Tap up to one target creature and put a stun counter on it. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_II, SagaChapter.CHAPTER_IV, + new Effects( + new TapTargetEffect(), + new AddCountersTargetEffect(CounterType.STUN.createInstance()) + .setText("and put a stun counter on it") + ), new TargetCreaturePermanent(0, 1) + ); + this.addAbility(sagaAbility); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + } + + private SummonValefor(final SummonValefor card) { + super(card); + } + + @Override + public SummonValefor copy() { + return new SummonValefor(this); + } +} + +class SummonValeforEffect extends OneShotEffect { + + enum SummonValeforPredicate implements ObjectSourcePlayerPredicate { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + return input + .getObject() + .getManaValue() + >= game + .getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_CONTROLLED_CREATURE, + input.getPlayerId(), input.getSource(), game + ) + .stream() + .mapToInt(MageObject::getManaValue) + .max() + .orElse(0); + } + } + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("creature you control with the greatest mana value"); + + static { + filter.add(SummonValeforPredicate.instance); + } + + SummonValeforEffect() { + super(Outcome.Benefit); + staticText = "each opponent chooses a creature with the greatest mana value " + + "among creatures they control. Return those creatures to their owners' hands"; + } + + private SummonValeforEffect(final SummonValeforEffect effect) { + super(effect); + } + + @Override + public SummonValeforEffect copy() { + return new SummonValeforEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Set permanents = new HashSet<>(); + for (UUID opponentId : game.getOpponents(source.getControllerId())) { + Player player = game.getPlayer(opponentId); + if (player == null || !game.getBattlefield().contains( + StaticFilters.FILTER_CONTROLLED_CREATURE, opponentId, source, game, 1 + )) { + continue; + } + TargetPermanent target = new TargetPermanent(filter); + target.withNotTarget(true); + player.choose(Outcome.ReturnToHand, target, source, game); + permanents.add(game.getPermanent(target.getFirstTarget())); + } + permanents.removeIf(Objects::isNull); + if (permanents.isEmpty()) { + return false; + } + Optional.ofNullable(source) + .map(Controllable::getControllerId) + .map(game::getPlayer) + .ifPresent(player -> player.moveCards(permanents, Zone.HAND, source, game)); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SummonYojimbo.java b/Mage.Sets/src/mage/cards/s/SummonYojimbo.java index 7ac21cbea3b..061328fcbd1 100644 --- a/Mage.Sets/src/mage/cards/s/SummonYojimbo.java +++ b/Mage.Sets/src/mage/cards/s/SummonYojimbo.java @@ -65,6 +65,7 @@ public final class SummonYojimbo extends CardImpl { sagaAbility.addChapterEffect( this, SagaChapter.CHAPTER_II, SagaChapter.CHAPTER_III, new CantAttackYouUnlessPayAllEffect(Duration.UntilYourNextTurn, new GenericManaCost(2)) + .setText("until your next turn, creatures can't attack you unless their controller pays {2} for each of those creatures") ); // IV - Create X Treasure tokens, where X is the number of opponents who control a creature with power 4 or greater. diff --git a/Mage.Sets/src/mage/cards/s/SummoningMateria.java b/Mage.Sets/src/mage/cards/s/SummoningMateria.java index b7cd8a28f80..7a2317cb1b1 100644 --- a/Mage.Sets/src/mage/cards/s/SummoningMateria.java +++ b/Mage.Sets/src/mage/cards/s/SummoningMateria.java @@ -48,7 +48,7 @@ public final class SummoningMateria extends CardImpl { ).setText("and has vigilance")); ability.addEffect(new GainAbilityAttachedEffect( new GreenManaAbility(), AttachmentType.EQUIPMENT - ).setText("and {T}: Add {G}.")); + ).setText("and \"{T}: Add {G}.\"")); this.addAbility(ability); // Equip {2} diff --git a/Mage.Sets/src/mage/cards/s/SunderShaman.java b/Mage.Sets/src/mage/cards/s/SunderShaman.java index 6921a07b61d..8c97e3746c0 100644 --- a/Mage.Sets/src/mage/cards/s/SunderShaman.java +++ b/Mage.Sets/src/mage/cards/s/SunderShaman.java @@ -12,7 +12,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -38,7 +38,7 @@ public final class SunderShaman extends CardImpl { // Whenever Sunder Shaman deals combat damage to a player, destroy target artifact or enchantment that player controls. Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new DestroyTargetEffect(), false, true); ability.addTarget(new TargetPermanent(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SunderingTitan.java b/Mage.Sets/src/mage/cards/s/SunderingTitan.java index 1dd5d08a327..81f2eabcb13 100644 --- a/Mage.Sets/src/mage/cards/s/SunderingTitan.java +++ b/Mage.Sets/src/mage/cards/s/SunderingTitan.java @@ -14,7 +14,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; -import mage.target.common.TargetLandPermanent; +import mage.target.TargetPermanent; import java.util.HashSet; import java.util.Set; @@ -71,7 +71,7 @@ class SunderingTitanDestroyLandEffect extends OneShotEffect { for (SubType landName : SubType.getBasicLands()) { FilterLandPermanent filter = new FilterLandPermanent(landName + " to destroy"); filter.add(landName.getPredicate()); - Target target = new TargetLandPermanent(1, 1, filter, true); + Target target = new TargetPermanent(1, 1, filter, true); if (target.canChoose(source.getControllerId(), source, game)) { controller.chooseTarget(outcome, target, source, game); lands.add(target.getFirstTarget()); diff --git a/Mage.Sets/src/mage/cards/s/SurpriseDeployment.java b/Mage.Sets/src/mage/cards/s/SurpriseDeployment.java index f82dde223d6..0ccf05b99a0 100644 --- a/Mage.Sets/src/mage/cards/s/SurpriseDeployment.java +++ b/Mage.Sets/src/mage/cards/s/SurpriseDeployment.java @@ -24,6 +24,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInHand; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @@ -85,7 +86,7 @@ class SurpriseDeploymentEffect extends OneShotEffect { Card card = game.getCard(target.getFirstTarget()); if (card != null) { if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { ReturnToHandTargetEffect effect = new ReturnToHandTargetEffect(); effect.setTargetPointer(new FixedTarget(permanent, game)); diff --git a/Mage.Sets/src/mage/cards/s/SurrakDragonclaw.java b/Mage.Sets/src/mage/cards/s/SurrakDragonclaw.java index e800d7898d1..7f17e5ef1c5 100644 --- a/Mage.Sets/src/mage/cards/s/SurrakDragonclaw.java +++ b/Mage.Sets/src/mage/cards/s/SurrakDragonclaw.java @@ -1,7 +1,6 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.CantBeCounteredSourceAbility; import mage.abilities.common.SimpleStaticAbility; @@ -12,15 +11,15 @@ import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; +import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.Zone; import mage.filter.FilterSpell; import mage.filter.StaticFilters; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class SurrakDragonclaw extends CardImpl { @@ -47,7 +46,7 @@ public final class SurrakDragonclaw extends CardImpl { this.addAbility(new CantBeCounteredSourceAbility()); // Creature spells you control can't be countered. - this.addAbility(new SimpleStaticAbility(new CantBeCounteredControlledEffect(filterTarget, null, Duration.WhileOnBattlefield))); + this.addAbility(new SimpleStaticAbility(new CantBeCounteredControlledEffect(filterTarget, Duration.WhileOnBattlefield))); // Other creatures you control have trample. this.addAbility(new SimpleStaticAbility( diff --git a/Mage.Sets/src/mage/cards/s/SurtrFieryJotun.java b/Mage.Sets/src/mage/cards/s/SurtrFieryJotun.java index c775f00f854..1f8f6eec934 100644 --- a/Mage.Sets/src/mage/cards/s/SurtrFieryJotun.java +++ b/Mage.Sets/src/mage/cards/s/SurtrFieryJotun.java @@ -10,8 +10,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.filter.FilterSpell; -import mage.filter.common.FilterHistoricSpell; +import mage.filter.StaticFilters; import mage.target.common.TargetAnyTarget; import java.util.UUID; @@ -21,8 +20,6 @@ import java.util.UUID; */ public final class SurtrFieryJotun extends CardImpl { - private static final FilterSpell filter = new FilterHistoricSpell(); - public SurtrFieryJotun(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}"); @@ -37,7 +34,7 @@ public final class SurtrFieryJotun extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // Whenever you cast a historic spell, Surtr, Fiery Jotun deals 3 damage to any target. - Ability ability = new SpellCastControllerTriggeredAbility(new DamageTargetEffect(3), filter, false); + Ability ability = new SpellCastControllerTriggeredAbility(new DamageTargetEffect(3), StaticFilters.FILTER_SPELL_HISTORIC, false); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SwallowedByLeviathan.java b/Mage.Sets/src/mage/cards/s/SwallowedByLeviathan.java new file mode 100644 index 00000000000..3be8232bb19 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SwallowedByLeviathan.java @@ -0,0 +1,42 @@ +package mage.cards.s; + +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.common.CounterUnlessPaysEffect; +import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.keyword.SurveilEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.TargetSpell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SwallowedByLeviathan extends CardImpl { + + private static final DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD); + + public SwallowedByLeviathan(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}"); + + // Choose target spell. Surveil 2, then counter the chosen spell unless its controller pays {1} for each card in your graveyard. + this.getSpellAbility().addEffect(new InfoEffect("Choose target spell")); + this.getSpellAbility().addEffect(new SurveilEffect(2, false)); + this.getSpellAbility().addEffect(new CounterUnlessPaysEffect(xValue) + .setText(", then counter the chosen spell unless its controller pays {1} for each card in your graveyard")); + this.getSpellAbility().addTarget(new TargetSpell()); + } + + private SwallowedByLeviathan(final SwallowedByLeviathan card) { + super(card); + } + + @Override + public SwallowedByLeviathan copy() { + return new SwallowedByLeviathan(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SwiftWarkite.java b/Mage.Sets/src/mage/cards/s/SwiftWarkite.java index 0f7192f7cb3..84aace1205c 100644 --- a/Mage.Sets/src/mage/cards/s/SwiftWarkite.java +++ b/Mage.Sets/src/mage/cards/s/SwiftWarkite.java @@ -17,9 +17,11 @@ import mage.constants.*; import mage.filter.FilterCard; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetCard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -99,15 +101,18 @@ class SwiftWarkiteEffect extends OneShotEffect { target.withNotTarget(true); controller.choose(outcome, cards, target, source, game); Card card = game.getCard(target.getFirstTarget()); - if (card != null) { - controller.moveCards(card, Zone.BATTLEFIELD, source, game); - + if (card == null) { + return true; + } + controller.moveCards(card, Zone.BATTLEFIELD, source, game); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); + if (permanent != null) { ContinuousEffect gainHasteEffect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); - gainHasteEffect.setTargetPointer(new FixedTarget(card, game)); + gainHasteEffect.setTargetPointer(new FixedTarget(permanent, game)); game.addEffect(gainHasteEffect, source); Effect returnToHandEffect = new ReturnToHandTargetEffect(); - returnToHandEffect.setTargetPointer(new FixedTarget(card, game)); + returnToHandEffect.setTargetPointer(new FixedTarget(permanent, game)); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(returnToHandEffect); game.addDelayedTriggeredAbility(delayedAbility, source); } diff --git a/Mage.Sets/src/mage/cards/s/SylvanPrimordial.java b/Mage.Sets/src/mage/cards/s/SylvanPrimordial.java index 44133a8abd3..91b61dff522 100644 --- a/Mage.Sets/src/mage/cards/s/SylvanPrimordial.java +++ b/Mage.Sets/src/mage/cards/s/SylvanPrimordial.java @@ -20,7 +20,7 @@ import mage.game.permanent.Permanent; import mage.target.Target; import mage.target.TargetPermanent; import mage.target.common.TargetCardInLibrary; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import java.util.UUID; @@ -47,7 +47,7 @@ public final class SylvanPrimordial extends CardImpl { // When Sylvan Primordial enters the battlefield, for each opponent, destroy target noncreature permanent that player controls. For each permanent destroyed this way, search your library for a Forest card and put that card onto the battlefield tapped. Then shuffle your library. Ability ability = new EntersBattlefieldTriggeredAbility(new SylvanPrimordialEffect(), false); ability.addTarget(new TargetPermanent(filter)); - ability.setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/TaigamOjutaiMaster.java b/Mage.Sets/src/mage/cards/t/TaigamOjutaiMaster.java index 0dfd4b2c825..68cf65183b4 100644 --- a/Mage.Sets/src/mage/cards/t/TaigamOjutaiMaster.java +++ b/Mage.Sets/src/mage/cards/t/TaigamOjutaiMaster.java @@ -1,6 +1,5 @@ package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; @@ -22,7 +21,8 @@ import mage.game.events.GameEvent; import mage.game.stack.Spell; import mage.players.Player; import mage.target.targetpointer.FixedTarget; -import mage.watchers.common.AttackedThisTurnWatcher; + +import java.util.UUID; /** * @author spjspj @@ -51,7 +51,7 @@ public final class TaigamOjutaiMaster extends CardImpl { this.toughness = new MageInt(4); // Instant, sorcery, and Dragon spells you control can't be countered. - this.addAbility(new SimpleStaticAbility(new CantBeCounteredControlledEffect(filter, null, Duration.WhileOnBattlefield))); + this.addAbility(new SimpleStaticAbility(new CantBeCounteredControlledEffect(filter, Duration.WhileOnBattlefield))); // Whenever you cast an instant or sorcery spell from your hand, if Taigam, Ojutai Master attacked this turn, that spell gains rebound. Ability ability = new ConditionalInterveningIfTriggeredAbility(new TaigamOjutaiMasterTriggeredAbility(), diff --git a/Mage.Sets/src/mage/cards/t/Tallowisp.java b/Mage.Sets/src/mage/cards/t/Tallowisp.java index f56734c047d..bf8df5cd6e2 100644 --- a/Mage.Sets/src/mage/cards/t/Tallowisp.java +++ b/Mage.Sets/src/mage/cards/t/Tallowisp.java @@ -1,10 +1,7 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.MageObject; -import mage.abilities.Abilities; import mage.abilities.Ability; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; @@ -19,6 +16,8 @@ import mage.filter.predicate.Predicate; import mage.game.Game; import mage.target.common.TargetCardInLibrary; +import java.util.UUID; + /** * * @author LevelX2 @@ -30,7 +29,7 @@ public final class Tallowisp extends CardImpl { static { filterAura.add(CardType.ENCHANTMENT.getPredicate()); filterAura.add(SubType.AURA.getPredicate()); - filterAura.add(new TallowispAbilityPredicate()); + filterAura.add(TallowispAbilityPredicate.instance); } public Tallowisp(UUID ownerId, CardSetInfo setInfo) { @@ -41,7 +40,7 @@ public final class Tallowisp extends CardImpl { this.toughness = new MageInt(3); // Whenever you cast a Spirit or Arcane spell, you may search your library for an Aura card with enchant creature, reveal it, and put it into your hand. If you do, shuffle your library. - this.addAbility(new SpellCastControllerTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filterAura), true), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filterAura), true), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, true)); } private Tallowisp(final Tallowisp card) { @@ -54,20 +53,14 @@ public final class Tallowisp extends CardImpl { } } -class TallowispAbilityPredicate implements Predicate { - - public TallowispAbilityPredicate() { - } +enum TallowispAbilityPredicate implements Predicate { + instance; @Override public boolean apply(MageObject input, Game game) { - Abilities abilities = input.getAbilities(); - for (int i = 0; i < abilities.size(); i++) { - if (abilities.get(i) instanceof EnchantAbility) { - String enchantText = abilities.get(i).getRule(); - if (enchantText.contentEquals("Enchant creature")) { - return true; - } + for (Ability ability : input.getAbilities()) { + if (ability instanceof EnchantAbility && ability.getRule().contentEquals("Enchant creature")) { + return true; } } return false; diff --git a/Mage.Sets/src/mage/cards/t/TamiyoFieldResearcher.java b/Mage.Sets/src/mage/cards/t/TamiyoFieldResearcher.java index d938346820c..3fd3f65fb23 100644 --- a/Mage.Sets/src/mage/cards/t/TamiyoFieldResearcher.java +++ b/Mage.Sets/src/mage/cards/t/TamiyoFieldResearcher.java @@ -13,9 +13,7 @@ import mage.abilities.effects.common.TapTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.command.emblems.TamiyoFieldResearcherEmblem; import mage.game.events.DamagedBatchBySourceEvent; @@ -35,12 +33,6 @@ import java.util.UUID; */ public final class TamiyoFieldResearcher extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("nonland permanent"); - - static { - filter.add(Predicates.not(CardType.LAND.getPredicate())); - } - public TamiyoFieldResearcher(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{1}{G}{W}{U}"); this.supertype.add(SuperType.LEGENDARY); @@ -55,7 +47,7 @@ public final class TamiyoFieldResearcher extends CardImpl { // -2: Tap up to two target nonland permanents. They don't untap during their controller's next untap step. ability = new LoyaltyAbility(new TapTargetEffect(), -2); - ability.addTarget(new TargetPermanent(0, 2, filter, false)); + ability.addTarget(new TargetPermanent(0, 2, StaticFilters.FILTER_PERMANENTS_NON_LAND, false)); ability.addEffect(new DontUntapInControllersNextUntapStepTargetEffect("They")); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/t/TangleGolem.java b/Mage.Sets/src/mage/cards/t/TangleGolem.java index 2a82ff5e0b6..92529012d8b 100644 --- a/Mage.Sets/src/mage/cards/t/TangleGolem.java +++ b/Mage.Sets/src/mage/cards/t/TangleGolem.java @@ -1,28 +1,28 @@ - package mage.cards.t; -import java.util.UUID; import mage.MageInt; -import mage.abilities.keyword.AffinityForLandTypeAbility; +import mage.abilities.keyword.AffinityAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.AffinityType; import mage.constants.CardType; import mage.constants.SubType; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class TangleGolem extends CardImpl { public TangleGolem(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{7}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{7}"); this.subtype.add(SubType.GOLEM); this.power = new MageInt(5); this.toughness = new MageInt(4); // Affinity for Forests - this.addAbility(new AffinityForLandTypeAbility(SubType.FOREST, "Forests")); + this.addAbility(new AffinityAbility(AffinityType.FORESTS)); } private TangleGolem(final TangleGolem card) { diff --git a/Mage.Sets/src/mage/cards/t/TashaTheWitchQueen.java b/Mage.Sets/src/mage/cards/t/TashaTheWitchQueen.java index 997fde41b3b..41142ca3a90 100644 --- a/Mage.Sets/src/mage/cards/t/TashaTheWitchQueen.java +++ b/Mage.Sets/src/mage/cards/t/TashaTheWitchQueen.java @@ -18,7 +18,7 @@ import mage.game.Game; import mage.game.permanent.token.Demon33Token; import mage.players.Player; import mage.target.common.TargetCardInOpponentsGraveyard; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; import mage.util.CardUtil; @@ -50,7 +50,7 @@ public final class TashaTheWitchQueen extends CardImpl { Ability ability = new LoyaltyAbility(new DrawCardSourceControllerEffect(1), 1); ability.addEffect(new TashaTheWitchQueenExileEffect()); ability.addTarget(new TargetCardInOpponentsGraveyard(0, 1, filterCard)); - ability.setTargetAdjuster(new ForEachOpponentTargetsAdjuster(true)); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(true, true)); this.addAbility(ability); // −3: You may cast a spell from among cards in exile with page counters on them without paying its mana cost. diff --git a/Mage.Sets/src/mage/cards/t/TataruTaru.java b/Mage.Sets/src/mage/cards/t/TataruTaru.java index e9b72ed0ea7..5d7301f0277 100644 --- a/Mage.Sets/src/mage/cards/t/TataruTaru.java +++ b/Mage.Sets/src/mage/cards/t/TataruTaru.java @@ -37,7 +37,7 @@ public final class TataruTaru extends CardImpl { this.toughness = new MageInt(3); // When Tataru Taru enters, you draw a card and target opponent may draw a card. - Ability ability = new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1)); + Ability ability = new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1, true)); ability.addEffect(new TataruTaruEffect()); ability.addTarget(new TargetOpponent()); this.addAbility(ability); @@ -68,6 +68,11 @@ enum TataruTaruCondition implements Condition { .map(game::isActivePlayer) .orElse(true); } + + @Override + public String toString() { + return "it isn't that player's turn"; + } } class TataruTaruEffect extends OneShotEffect { diff --git a/Mage.Sets/src/mage/cards/t/TawnossCoffin.java b/Mage.Sets/src/mage/cards/t/TawnossCoffin.java index 94133d3d2bd..aad1e1c9166 100644 --- a/Mage.Sets/src/mage/cards/t/TawnossCoffin.java +++ b/Mage.Sets/src/mage/cards/t/TawnossCoffin.java @@ -221,7 +221,7 @@ class TawnossCoffinReturnEffect extends OneShotEffect { continue; } controller.moveCards(creatureCard, Zone.BATTLEFIELD, source, game, false, false, true, null); - Permanent newPermanent = game.getPermanent(creatureCard.getId()); + Permanent newPermanent = CardUtil.getPermanentFromCardPutToBattlefield(creatureCard, game); if (newPermanent == null) { continue; } diff --git a/Mage.Sets/src/mage/cards/t/TeferisTimeTwist.java b/Mage.Sets/src/mage/cards/t/TeferisTimeTwist.java index 6997269a619..79f88bf5e5d 100644 --- a/Mage.Sets/src/mage/cards/t/TeferisTimeTwist.java +++ b/Mage.Sets/src/mage/cards/t/TeferisTimeTwist.java @@ -16,6 +16,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetControlledPermanent; +import mage.util.CardUtil; import java.util.UUID; @@ -111,7 +112,7 @@ class TeferisTimeTwistReturnEffect extends OneShotEffect { if (!player.moveCards(card, Zone.BATTLEFIELD, source, game)) { return true; } - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null && permanent.isCreature(game)) { // TODO: This is technically wrong as it should enter with the counters, // however there's currently no way to know that for sure @@ -120,4 +121,4 @@ class TeferisTimeTwistReturnEffect extends OneShotEffect { } return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/t/TellahGreatSage.java b/Mage.Sets/src/mage/cards/t/TellahGreatSage.java new file mode 100644 index 00000000000..56f7345aeef --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TellahGreatSage.java @@ -0,0 +1,120 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.costs.mana.ManaCost; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.token.HeroToken; +import mage.game.stack.Spell; +import mage.util.CardUtil; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TellahGreatSage extends CardImpl { + + public TellahGreatSage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Whenever you cast a noncreature spell, create a 1/1 colorless Hero creature token. If four or more mana was spent to cast that spell, draw two cards. If eight or more mana was spent to cast that spell, sacrifice Tellah and it deals that much damage to each opponent. + Ability ability = new SpellCastControllerTriggeredAbility( + new CreateTokenEffect(new HeroToken()), StaticFilters.FILTER_SPELL_A_NON_CREATURE, false + ); + ability.addEffect(new ConditionalOneShotEffect( + new DrawCardSourceControllerEffect(2), TellahGreatSageCondition.FOUR, + "If four or more mana was spent to cast that spell, draw two cards" + )); + ability.addEffect(new ConditionalOneShotEffect( + new SacrificeSourceEffect(), TellahGreatSageCondition.EIGHT, + "If eight or more mana was spent to cast that spell, " + + "sacrifice {this} and it deals that much damage to each opponent" + ).addEffect(new DamagePlayersEffect(TellahGreatSageValue.instance, TargetController.OPPONENT))); + this.addAbility(ability); + } + + private TellahGreatSage(final TellahGreatSage card) { + super(card); + } + + @Override + public TellahGreatSage copy() { + return new TellahGreatSage(this); + } +} + +enum TellahGreatSageCondition implements Condition { + FOUR(4), + EIGHT(8); + private final int amount; + + TellahGreatSageCondition(int amount) { + this.amount = amount; + } + + @Override + public boolean apply(Game game, Ability source) { + return CardUtil + .getEffectValueFromAbility(source, "spellCast", Spell.class) + .map(Spell::getStackAbility) + .map(Ability::getManaCostsToPay) + .map(ManaCost::getUsedManaToPay) + .map(Mana::count) + .orElse(0) >= amount; + } +} + +enum TellahGreatSageValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return Optional + .ofNullable((Spell) effect.getValue("spellCast")) + .map(Spell::getStackAbility) + .map(Ability::getManaCostsToPay) + .map(ManaCost::getUsedManaToPay) + .map(Mana::count) + .orElse(0); + } + + @Override + public TellahGreatSageValue copy() { + return this; + } + + @Override + public String getMessage() { + return ""; + } + + @Override + public String toString() { + return "1"; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TellerOfTales.java b/Mage.Sets/src/mage/cards/t/TellerOfTales.java index 3a7f5768061..f4c3b6670ba 100644 --- a/Mage.Sets/src/mage/cards/t/TellerOfTales.java +++ b/Mage.Sets/src/mage/cards/t/TellerOfTales.java @@ -30,7 +30,7 @@ public final class TellerOfTales extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // Whenever you cast a Spirit or Arcane spell, you may tap or untap target creature. - Ability ability = new SpellCastControllerTriggeredAbility(new MayTapOrUntapTargetEffect(), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true); + Ability ability = new SpellCastControllerTriggeredAbility(new MayTapOrUntapTargetEffect(), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, true); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/TempleOfCyclicalTime.java b/Mage.Sets/src/mage/cards/t/TempleOfCyclicalTime.java index e01c99d833d..b5639028634 100644 --- a/Mage.Sets/src/mage/cards/t/TempleOfCyclicalTime.java +++ b/Mage.Sets/src/mage/cards/t/TempleOfCyclicalTime.java @@ -2,6 +2,7 @@ package mage.cards.t; import mage.abilities.Ability; import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; @@ -11,6 +12,7 @@ import mage.abilities.mana.BlueManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.TimingRule; import mage.constants.Zone; import mage.counters.CounterType; @@ -22,6 +24,8 @@ import java.util.UUID; */ public final class TempleOfCyclicalTime extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.TIME, ComparisonType.EQUAL_TO, 0); + public TempleOfCyclicalTime(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); this.nightCard = true; @@ -35,11 +39,8 @@ public final class TempleOfCyclicalTime extends CardImpl { // {2}{U}, {T}: Transform Temple of Cyclical Time. Activate only if it has no time counters on it and only as a sorcery. ability = new ActivateIfConditionActivatedAbility( - Zone.BATTLEFIELD, - new TransformSourceEffect(), - new ManaCostsImpl<>("{2}{U}"), - new SourceHasCounterCondition(CounterType.TIME, 0, 0), - TimingRule.SORCERY + Zone.BATTLEFIELD, new TransformSourceEffect(), + new ManaCostsImpl<>("{2}{U}"), condition, TimingRule.SORCERY ); ability.addCost(new TapSourceCost()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/t/TemporalCleansing.java b/Mage.Sets/src/mage/cards/t/TemporalCleansing.java index cf3e97c9e1e..250e304cc9b 100644 --- a/Mage.Sets/src/mage/cards/t/TemporalCleansing.java +++ b/Mage.Sets/src/mage/cards/t/TemporalCleansing.java @@ -72,6 +72,6 @@ class TemporalCleansingEffect extends OneShotEffect { )) { return player.putCardOnTopXOfLibrary(permanent, game, source, 2, true); } - return player.putCardsOnBottomOfLibrary(permanent, game, source, false); + return player.putCardsOnBottomOfLibrary(permanent, game, source); } } diff --git a/Mage.Sets/src/mage/cards/t/TemporalFirestorm.java b/Mage.Sets/src/mage/cards/t/TemporalFirestorm.java index ac5d7f964e6..62e1cec5d6b 100644 --- a/Mage.Sets/src/mage/cards/t/TemporalFirestorm.java +++ b/Mage.Sets/src/mage/cards/t/TemporalFirestorm.java @@ -1,6 +1,7 @@ package mage.cards.t; import mage.abilities.dynamicvalue.common.MultikickerCount; +import mage.abilities.effects.OneShotNonTargetEffect; import mage.abilities.effects.common.DamageAllEffect; import mage.abilities.effects.common.PhaseOutTargetEffect; import mage.abilities.keyword.KickerAbility; @@ -35,10 +36,10 @@ public final class TemporalFirestorm extends CardImpl { this.addAbility(kickerAbility); // Choose up to X creatures and/or planeswalkers you control, where X is the number of times this spell was kicked. Those permanents phase out. - this.getSpellAbility().addEffect(new PhaseOutTargetEffect().setText("choose up to X creatures and/or " + - "planeswalkers you control, where X is the number of times this spell was kicked. Those permanents phase out")); - this.getSpellAbility().addTarget(new TargetPermanent(0, 1, filter, true)); - this.getSpellAbility().setTargetAdjuster(new TargetsCountAdjuster(MultikickerCount.instance)); + this.getSpellAbility().addEffect(new OneShotNonTargetEffect(new PhaseOutTargetEffect().setText("choose up to X creatures and/or " + + "planeswalkers you control, where X is the number of times this spell was kicked. Those permanents phase out"), + new TargetPermanent(0, 1, filter, true), new TargetsCountAdjuster(MultikickerCount.instance) + )); // Temporal Firestorm deals 5 damage to each creature and each planeswalker. this.getSpellAbility().addEffect(new DamageAllEffect( diff --git a/Mage.Sets/src/mage/cards/t/TemporaryInsanity.java b/Mage.Sets/src/mage/cards/t/TemporaryInsanity.java index 35acfcaff16..514db387a07 100644 --- a/Mage.Sets/src/mage/cards/t/TemporaryInsanity.java +++ b/Mage.Sets/src/mage/cards/t/TemporaryInsanity.java @@ -32,7 +32,7 @@ public final class TemporaryInsanity extends CardImpl { // and gain control of it until end of turn. Effect effect = new GainControlTargetEffect(Duration.EndOfTurn); - effect.setText("and gain control of it until end of turn. "); + effect.setText("and gain control of it until end of turn"); this.getSpellAbility().addEffect(effect); // That creature gains haste until end of turn. diff --git a/Mage.Sets/src/mage/cards/t/TemptWithBunnies.java b/Mage.Sets/src/mage/cards/t/TemptWithBunnies.java index 99c1d414e3e..f25af1112eb 100644 --- a/Mage.Sets/src/mage/cards/t/TemptWithBunnies.java +++ b/Mage.Sets/src/mage/cards/t/TemptWithBunnies.java @@ -1,7 +1,5 @@ package mage.cards.t; -import java.util.UUID; - import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; @@ -13,8 +11,9 @@ import mage.game.permanent.token.RabbitToken; import mage.game.permanent.token.Token; import mage.players.Player; +import java.util.UUID; + /** - * * @author Grath */ public final class TemptWithBunnies extends CardImpl { @@ -26,6 +25,7 @@ public final class TemptWithBunnies extends CardImpl { // card and create a 1/1 white Rabbit creature token. For each opponent who does, you draw a card and you // create a 1/1 white Rabbit creature token. this.getSpellAbility().addEffect(new TemptWithBunniesEffect()); + this.getSpellAbility().withFlavorWord("Tempting Offer"); } private TemptWithBunnies(final TemptWithBunnies card) { @@ -42,7 +42,7 @@ class TemptWithBunniesEffect extends OneShotEffect { TemptWithBunniesEffect() { super(Outcome.PutLandInPlay); - this.staticText = "Tempting offer — Draw a card and create a 1/1 white Rabbit creature token. Then each opponent may draw a card and create a 1/1 white Rabbit creature token. For each opponent who does, you draw a card and you create a 1/1 white Rabbit creature token."; + this.staticText = "draw a card and create a 1/1 white Rabbit creature token. Then each opponent may draw a card and create a 1/1 white Rabbit creature token. For each opponent who does, you draw a card and you create a 1/1 white Rabbit creature token."; } private TemptWithBunniesEffect(final TemptWithBunniesEffect effect) { @@ -83,4 +83,4 @@ class TemptWithBunniesEffect extends OneShotEffect { return false; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/t/TemptedByTheOriq.java b/Mage.Sets/src/mage/cards/t/TemptedByTheOriq.java index e662a20696d..ada5ea439b4 100644 --- a/Mage.Sets/src/mage/cards/t/TemptedByTheOriq.java +++ b/Mage.Sets/src/mage/cards/t/TemptedByTheOriq.java @@ -9,7 +9,7 @@ import mage.constants.Duration; import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetPermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; import java.util.UUID; @@ -32,7 +32,7 @@ public final class TemptedByTheOriq extends CardImpl { .setText("for each opponent, gain control of up to one target creature " + "or planeswalker that player controls with mana value 3 or less")); this.getSpellAbility().addTarget(new TargetPermanent(0, 1, filter)); - this.getSpellAbility().setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + this.getSpellAbility().setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); } private TemptedByTheOriq(final TemptedByTheOriq card) { diff --git a/Mage.Sets/src/mage/cards/t/TenuousTruce.java b/Mage.Sets/src/mage/cards/t/TenuousTruce.java index cff82aa3cef..88eccd4c5e8 100644 --- a/Mage.Sets/src/mage/cards/t/TenuousTruce.java +++ b/Mage.Sets/src/mage/cards/t/TenuousTruce.java @@ -2,10 +2,13 @@ package mage.cards.t; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.*; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.DrawCardTargetEffect; +import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.keyword.EnchantAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -36,8 +39,8 @@ public class TenuousTruce extends CardImpl { // At the beginning of enchanted opponent’s end step, you and that player each draw a card. Ability drawAbility = new BeginningOfEndStepTriggeredAbility( - TargetController.ENCHANTED, new DrawCardSourceControllerEffect(1).setText("you "), - false); + TargetController.ENCHANTED, new DrawCardSourceControllerEffect(1).setText("you"), false + ).setTriggerPhrase("At the beginning of enchanted opponent's end step, "); Effect enchantedPlayerDrawEffect = new DrawCardTargetEffect(1); enchantedPlayerDrawEffect.concatBy("and").setText("that player each draw a card"); drawAbility.addEffect(enchantedPlayerDrawEffect); diff --git a/Mage.Sets/src/mage/cards/t/TerraMagicalAdept.java b/Mage.Sets/src/mage/cards/t/TerraMagicalAdept.java index 34864b9ec3e..2b3d125cd26 100644 --- a/Mage.Sets/src/mage/cards/t/TerraMagicalAdept.java +++ b/Mage.Sets/src/mage/cards/t/TerraMagicalAdept.java @@ -38,7 +38,7 @@ public final class TerraMagicalAdept extends CardImpl { // When Terra enters, mill five cards. Put up to one enchantment milled this this way into your hand. this.addAbility(new EntersBattlefieldTriggeredAbility(new MillThenPutInHandEffect( 5, StaticFilters.FILTER_CARD_ENCHANTMENT, true - ))); + ).setText("mill five cards. Put up to one enchantment card milled this way into your hand"))); // Trance -- {4}{R}{G}, {T}: Exile Terra, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery. this.addAbility(new TransformAbility()); diff --git a/Mage.Sets/src/mage/cards/t/TesharAncestorsApostle.java b/Mage.Sets/src/mage/cards/t/TesharAncestorsApostle.java index 10463db3189..5cf8715a3ce 100644 --- a/Mage.Sets/src/mage/cards/t/TesharAncestorsApostle.java +++ b/Mage.Sets/src/mage/cards/t/TesharAncestorsApostle.java @@ -1,7 +1,6 @@ package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SpellCastControllerTriggeredAbility; @@ -14,11 +13,13 @@ import mage.constants.ComparisonType; import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreatureCard; -import mage.filter.common.FilterHistoricSpell; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInYourGraveyard; +import java.util.UUID; + /** * * @author L_J @@ -46,14 +47,14 @@ public final class TesharAncestorsApostle extends CardImpl { // Whenever you cast a historic spell, return target creature card with converted mana cost 3 or less from your graveyard to the battlefield. Ability ability = new SpellCastControllerTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect() .setText("return target creature card with mana value 3 or less from your graveyard to the battlefield. " - + "(Artifacts, legendaries, and Sagas are historic.)"), new FilterHistoricSpell(), false); + + "(Artifacts, legendaries, and Sagas are historic.)"), StaticFilters.FILTER_SPELL_HISTORIC, false); ability.addTarget(new TargetCardInYourGraveyard(filter)); this.addAbility(ability); } - private TesharAncestorsApostle(final TesharAncestorsApostle TesharAncestorsApostle) { - super(TesharAncestorsApostle); + private TesharAncestorsApostle(final TesharAncestorsApostle card) { + super(card); } public TesharAncestorsApostle copy() { diff --git a/Mage.Sets/src/mage/cards/t/TetsuoImperialChampion.java b/Mage.Sets/src/mage/cards/t/TetsuoImperialChampion.java index 721bfb1e7a6..5452d742939 100644 --- a/Mage.Sets/src/mage/cards/t/TetsuoImperialChampion.java +++ b/Mage.Sets/src/mage/cards/t/TetsuoImperialChampion.java @@ -1,18 +1,15 @@ package mage.cards.t; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.condition.common.EquippedSourceCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.cost.CastFromHandForFreeEffect; import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -20,14 +17,14 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.FilterCard; +import mage.filter.FilterPermanent; import mage.filter.common.FilterInstantOrSorceryCard; import mage.filter.predicate.ObjectSourcePlayer; import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.filter.predicate.permanent.AttachedToSourcePredicate; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.common.TargetAnyTarget; -import java.util.Objects; import java.util.UUID; /** @@ -35,9 +32,15 @@ import java.util.UUID; */ public final class TetsuoImperialChampion extends CardImpl { - private static final Hint hint = new ValueHint( - "Greatest mana value of attached equipment", TetsuoImperialChampionValue.instance - ); + private static final FilterPermanent filterEquipment = new FilterPermanent("Equipment attached to {this}"); + + static { + filterEquipment.add(SubType.EQUIPMENT.getPredicate()); + filterEquipment.add(AttachedToSourcePredicate.instance); + } + + static final GreatestAmongPermanentsValue xValue = new GreatestAmongPermanentsValue(GreatestAmongPermanentsValue.Quality.ManaValue, filterEquipment); + private static final Hint hint = xValue.getHint(); private static final FilterCard filter = new FilterInstantOrSorceryCard( "an instant or sorcery spell from your hand with mana value " + "less than or equal to the highest mana value among Equipment attached to {this}" @@ -57,18 +60,18 @@ public final class TetsuoImperialChampion extends CardImpl { this.toughness = new MageInt(3); // Whenever Tetsuo, Imperial Champion attacks, if it's equipped, choose one -- - // * Tetsuo deals damage equal to the highest mana value among Equipment attached to it to any target. + // * Tetsuo deals damage equal to the greatest mana value among Equipment attached to it to any target. Ability ability = new ConditionalInterveningIfTriggeredAbility( new AttacksTriggeredAbility(new DamageTargetEffect( - TetsuoImperialChampionValue.instance, "it" - ).setText("{this} deals damage equal to the highest mana value " + + xValue, "it" + ).setText("{this} deals damage equal to the greatest mana value " + "among Equipment attached to it to any target") ).setTriggerPhrase("Whenever {this} attacks, if it's equipped, "), EquippedSourceCondition.instance, null ); ability.addTarget(new TargetAnyTarget()); - // * You may cast an instant or sorcery spell from your hand with mana value less than or equal to the highest mana value among Equipment attached to Tetsuo without paying its mana cost. + // * You may cast an instant or sorcery spell from your hand with mana value less than or equal to the greatest mana value among Equipment attached to Tetsuo without paying its mana cost. ability.addMode(new Mode(new CastFromHandForFreeEffect(filter))); this.addAbility(ability); } @@ -88,44 +91,7 @@ enum TetsuoImperialChampionPredicate implements ObjectSourcePlayerPredicate input, Game game) { - return input - .getObject() - .getManaValue() - <= TetsuoImperialChampionValue - .instance - .calculate(game, input.getSource(), null); + int value = TetsuoImperialChampion.xValue.calculate(game, input.getSource(), null); + return input.getObject().getManaValue() <= value; } -} - -enum TetsuoImperialChampionValue implements DynamicValue { - instance; - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - Permanent permanent = sourceAbility.getSourcePermanentOrLKI(game); - return permanent == null ? 0 : permanent - .getAttachments() - .stream() - .map(game::getPermanent) - .filter(Objects::nonNull) - .filter(p -> p.hasSubtype(SubType.EQUIPMENT, game)) - .mapToInt(MageObject::getManaValue) - .max() - .orElse(0); - } - - @Override - public TetsuoImperialChampionValue copy() { - return this; - } - - @Override - public String getMessage() { - return ""; - } - - @Override - public String toString() { - return "1"; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/t/TheAetherspark.java b/Mage.Sets/src/mage/cards/t/TheAetherspark.java index 6d1b78e4e24..f38f9434e56 100644 --- a/Mage.Sets/src/mage/cards/t/TheAetherspark.java +++ b/Mage.Sets/src/mage/cards/t/TheAetherspark.java @@ -8,7 +8,6 @@ import mage.abilities.condition.Condition; import mage.abilities.condition.common.AttachedToMatchesFilterCondition; import mage.abilities.condition.common.MyTurnCondition; import mage.abilities.decorator.ConditionalContinuousEffect; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.dynamicvalue.common.SavedDamageValue; import mage.abilities.effects.RestrictionEffect; import mage.abilities.effects.common.AttachEffect; @@ -35,7 +34,6 @@ import java.util.UUID; public final class TheAetherspark extends CardImpl { private static final Condition condition = new AttachedToMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_CREATURE); - private static final String rule = "Whenever equipped creature deals combat damage during your turn, put that many loyalty counters on {this}."; public TheAetherspark(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.PLANESWALKER}, "{4}"); @@ -47,12 +45,10 @@ public final class TheAetherspark extends CardImpl { // As long as The Aetherspark is attached to a creature, The Aetherspark can't be attacked and has "Whenever equipped creature deals combat damage during your turn, put that many loyalty counters on The Aetherspark." Ability ability = new SimpleStaticAbility(new TheAethersparkEffect()); ability.addEffect(new ConditionalContinuousEffect(new GainAbilitySourceEffect( - new ConditionalTriggeredAbility( - new DealsCombatDamageEquippedTriggeredAbility(new AddCountersSourceEffect( - CounterType.LOYALTY.createInstance(0), SavedDamageValue.MANY - )), MyTurnCondition.instance, rule - ) - ), condition, "and has \"" + rule + "\"")); + new DealsCombatDamageEquippedTriggeredAbility(new AddCountersSourceEffect( + CounterType.LOYALTY.createInstance(0), SavedDamageValue.MANY + )).withTriggerCondition(MyTurnCondition.instance) + ), condition, "and has \"Whenever equipped creature deals combat damage during your turn, put that many loyalty counters on {this}.\"")); this.addAbility(ability); // +1: Attach The Aetherspark to up to one target creature you control. Put a +1/+1 counter on that creature. diff --git a/Mage.Sets/src/mage/cards/t/TheBalrogOfMoria.java b/Mage.Sets/src/mage/cards/t/TheBalrogOfMoria.java index 67d713aed58..55adff1e9cd 100644 --- a/Mage.Sets/src/mage/cards/t/TheBalrogOfMoria.java +++ b/Mage.Sets/src/mage/cards/t/TheBalrogOfMoria.java @@ -19,7 +19,7 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.game.permanent.token.TreasureToken; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; import java.util.UUID; @@ -52,7 +52,7 @@ public final class TheBalrogOfMoria extends CardImpl { false ); reflexiveAbility.addTarget(new TargetCreaturePermanent(0,1)); - reflexiveAbility.setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + reflexiveAbility.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); this.addAbility(new DiesSourceTriggeredAbility( new DoWhenCostPaid( diff --git a/Mage.Sets/src/mage/cards/t/TheBeamtownBullies.java b/Mage.Sets/src/mage/cards/t/TheBeamtownBullies.java index 010311874c6..23b12fb7432 100644 --- a/Mage.Sets/src/mage/cards/t/TheBeamtownBullies.java +++ b/Mage.Sets/src/mage/cards/t/TheBeamtownBullies.java @@ -29,6 +29,7 @@ import mage.players.Player; import mage.target.TargetPlayer; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -104,7 +105,7 @@ class TheBeamtownBulliesEffect extends OneShotEffect { } // Add continuous effects - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/t/TheCircleOfLoyalty.java b/Mage.Sets/src/mage/cards/t/TheCircleOfLoyalty.java index a8b6ed085b2..7f4c805b7c0 100644 --- a/Mage.Sets/src/mage/cards/t/TheCircleOfLoyalty.java +++ b/Mage.Sets/src/mage/cards/t/TheCircleOfLoyalty.java @@ -6,17 +6,16 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.common.AffinityEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.AffinityAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.AffinityType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SuperType; import mage.filter.FilterSpell; -import mage.filter.common.FilterControlledPermanent; import mage.game.permanent.token.KnightToken; import java.util.UUID; @@ -26,22 +25,19 @@ import java.util.UUID; */ public final class TheCircleOfLoyalty extends CardImpl { - private static final FilterControlledPermanent filterKnight = new FilterControlledPermanent(SubType.KNIGHT, "Knights"); private static final FilterSpell filterLegendary = new FilterSpell("a legendary spell"); static { filterLegendary.add(SuperType.LEGENDARY.getPredicate()); } - private static final Hint hint = new ValueHint("Knights you control", new PermanentsOnBattlefieldCount(filterKnight)); - public TheCircleOfLoyalty(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}{W}{W}"); this.supertype.add(SuperType.LEGENDARY); // This spell costs {1} less to cast for each Knight you control. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filterKnight)).addHint(hint)); + this.addAbility(new AffinityAbility(AffinityType.KNIGHTS)); // Creatures you control get +1/+1. this.addAbility(new SimpleStaticAbility( diff --git a/Mage.Sets/src/mage/cards/t/TheCurseOfFenric.java b/Mage.Sets/src/mage/cards/t/TheCurseOfFenric.java new file mode 100644 index 00000000000..e17de685187 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheCurseOfFenric.java @@ -0,0 +1,162 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.FightTargetsEffect; +import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.NamePredicate; +import mage.filter.predicate.other.AnotherTargetPredicate; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.Mutant33DeathtouchToken; +import mage.game.permanent.token.Token; +import mage.game.permanent.token.TokenImpl; +import mage.target.Target; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; +import mage.target.targetpointer.EachTargetPointer; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * @author padfoothelix + */ +public final class TheCurseOfFenric extends CardImpl { + + private static final FilterCreaturePermanent nontokenFilter = new FilterCreaturePermanent("nontoken creature"); + private static final FilterPermanent mutantFilter = new FilterPermanent(SubType.MUTANT, "mutant"); + private static final FilterCreaturePermanent fenricFilter = new FilterCreaturePermanent("another target creature named Fenric"); + + static { + nontokenFilter.add(TokenPredicate.FALSE); + fenricFilter.add(new NamePredicate("Fenric")); + fenricFilter.add(new AnotherTargetPredicate(2)); + } + + public TheCurseOfFenric(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}{W}"); + + this.subtype.add(SubType.SAGA); + + // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I -- For each player, destroy up to one target creature that player controls. For each creature destroyed this way, its controller creates a 3/3 green Mutant creature token with deathtouch. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_I, + ability -> { + ability.addEffect(new TheCurseOfFenricDestroyEffect() + .setText("for each player, destroy up to one target creature " + + "that player controls. For each creature destroyed " + + "this way, its controller creates a 3/3 green Mutant " + + "creature with deathtouch") + .setTargetPointer(new EachTargetPointer()) + ); + ability.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_PERMANENT_CREATURE)); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, false)); + } + ); + + + // II -- Target nontoken creature becomes a 6/6 legendary Horror creature named Fenric and loses all abilities. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_II, + new BecomesCreatureTargetEffect( + new TheCurseOfFenricHorrorToken(), + true, false, Duration.EndOfGame, true + ), + new TargetCreaturePermanent(nontokenFilter) + ); + + // III -- Target Mutant fights another target creature named Fenric. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_III, + ability -> { + ability.addEffect(new FightTargetsEffect().setText( + "Target Mutant fights another target creature named Fenric" + )); + ability.addTarget(new TargetPermanent(mutantFilter).setTargetTag(1)); + ability.addTarget(new TargetCreaturePermanent(fenricFilter).setTargetTag(2)); + } + ); + + this.addAbility(sagaAbility); + } + + private TheCurseOfFenric(final TheCurseOfFenric card) { + super(card); + } + + @Override + public TheCurseOfFenric copy() { + return new TheCurseOfFenric(this); + } +} + +class TheCurseOfFenricDestroyEffect extends OneShotEffect { + + TheCurseOfFenricDestroyEffect() { + super(Outcome.DestroyPermanent); + } + + private TheCurseOfFenricDestroyEffect(final TheCurseOfFenricDestroyEffect effect) { + super(effect); + } + + @Override + public TheCurseOfFenricDestroyEffect copy() { + return new TheCurseOfFenricDestroyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Token mutantToken = new Mutant33DeathtouchToken(); + List playersToCreateToken = new ArrayList<>(); + for (Target target : source.getTargets()) { + Permanent targetCreature = game.getPermanent(target.getFirstTarget()); + if (targetCreature != null) { + UUID controllerId = targetCreature.getControllerId(); + if (targetCreature.destroy(source, game, false)) { + playersToCreateToken.add(controllerId); + } + } + } + game.processAction(); + for (UUID controllerId : playersToCreateToken) { + mutantToken.putOntoBattlefield(1, game, source, controllerId); + } + return true; + } +} + +class TheCurseOfFenricHorrorToken extends TokenImpl { + + TheCurseOfFenricHorrorToken() { + super("Fenric", "6/6 legendary Horror creature named Fenric"); + this.supertype.add(SuperType.LEGENDARY); + this.cardType.add(CardType.CREATURE); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + } + + private TheCurseOfFenricHorrorToken(final TheCurseOfFenricHorrorToken token) { + super(token); + } + + public TheCurseOfFenricHorrorToken copy() { + return new TheCurseOfFenricHorrorToken(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheDalekEmperor.java b/Mage.Sets/src/mage/cards/t/TheDalekEmperor.java index a60b3904f57..ea94067bddc 100644 --- a/Mage.Sets/src/mage/cards/t/TheDalekEmperor.java +++ b/Mage.Sets/src/mage/cards/t/TheDalekEmperor.java @@ -3,12 +3,9 @@ package mage.cards.t; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.common.AffinityEffect; import mage.abilities.effects.common.FaceVillainousChoiceOpponentsEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.AffinityAbility; import mage.abilities.keyword.HasteAbility; import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; @@ -32,7 +29,6 @@ import java.util.UUID; public final class TheDalekEmperor extends CardImpl { private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.DALEK, "Daleks"); - private static final Hint hint = new ValueHint("Daleks you control", new PermanentsOnBattlefieldCount(filter)); private static final FaceVillainousChoice choice = new FaceVillainousChoice( Outcome.Sacrifice, new TheDalekEmperorFirstChoice(), new TheDalekEmperorSecondChoice() ); @@ -46,7 +42,7 @@ public final class TheDalekEmperor extends CardImpl { this.toughness = new MageInt(6); // Affinity for Daleks - this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filter)).addHint(hint)); + this.addAbility(new AffinityAbility(AffinityType.DALEKS)); // Other Daleks you control have haste. this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( diff --git a/Mage.Sets/src/mage/cards/t/TheDarknessCrystal.java b/Mage.Sets/src/mage/cards/t/TheDarknessCrystal.java new file mode 100644 index 00000000000..baa92ca3fcc --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheDarknessCrystal.java @@ -0,0 +1,174 @@ +package mage.cards.t; + +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.counters.Counters; +import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.players.Player; +import mage.target.common.TargetCardInExile; +import mage.util.CardUtil; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheDarknessCrystal extends CardImpl { + + private static final FilterCard filter = new FilterCard("black spells"); + private static final FilterCard filter2 = new FilterCreatureCard("exiled with this"); + + static { + filter.add(new ColorPredicate(ObjectColor.BLACK)); + filter2.add(TheDarknessCrystalPredicate.instance); + } + + public TheDarknessCrystal(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{B}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + + // Black spells you cast cost {1} less to cast. + this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 1))); + + // If a nontoken creature an opponent controls would die, instead exile it and you gain 2 life. + this.addAbility(new SimpleStaticAbility(new TheDarknessCrystalExileEffect())); + + // {4}{B}{B}, {T}: Put target creature card exiled with The Darkness Crystal onto the battlefield tapped under your control with two additional +1/+1 counters on it. + Ability ability = new SimpleActivatedAbility( + new TheDarknessCrystalReturnEffect(), new ManaCostsImpl<>("{4}{B}{B}") + ); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetCardInExile(filter2)); + this.addAbility(ability); + } + + private TheDarknessCrystal(final TheDarknessCrystal card) { + super(card); + } + + @Override + public TheDarknessCrystal copy() { + return new TheDarknessCrystal(this); + } +} + +enum TheDarknessCrystalPredicate implements ObjectSourcePlayerPredicate { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + return Optional + .ofNullable(CardUtil.getExileZoneId( + game, input.getSourceId(), + game.getState().getZoneChangeCounter(input.getSourceId()) + )) + .map(game.getExile()::getExileZone) + .map(e -> e.contains(input.getObject().getId())) + .orElse(false); + } +} + +class TheDarknessCrystalExileEffect extends ReplacementEffectImpl { + + TheDarknessCrystalExileEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "if a nontoken creature an opponent controls would die, instead exile it and you gain 2 life"; + } + + private TheDarknessCrystalExileEffect(final TheDarknessCrystalExileEffect effect) { + super(effect); + } + + @Override + public TheDarknessCrystalExileEffect copy() { + return new TheDarknessCrystalExileEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player player = game.getPlayer(source.getControllerId()); + Card card = Optional + .ofNullable(event) + .map(ZoneChangeEvent.class::cast) + .map(ZoneChangeEvent::getTarget) + .map(Card.class::cast) + .orElseGet(() -> game.getCard(event.getTargetId())); + if (player == null || card == null) { + return false; + } + player.moveCardsToExile( + card, source, game, true, + CardUtil.getExileZoneId( + game, source.getSourceId(), + game.getState().getZoneChangeCounter(source.getSourceId()) + ), + CardUtil.getSourceName(game, source) + ); + player.gainLife(2, game, source); + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + return zEvent.isDiesEvent() + && zEvent.getTarget().isCreature(game) + && game.getOpponents(source.getControllerId()).contains(zEvent.getTarget().getControllerId()); + } + +} + +class TheDarknessCrystalReturnEffect extends OneShotEffect { + + TheDarknessCrystalReturnEffect() { + super(Outcome.Benefit); + staticText = "put target creature card exiled with {this} onto the battlefield tapped " + + "under your control with two additional +1/+1 counters on it"; + } + + private TheDarknessCrystalReturnEffect(final TheDarknessCrystalReturnEffect effect) { + super(effect); + } + + @Override + public TheDarknessCrystalReturnEffect copy() { + return new TheDarknessCrystalReturnEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(getTargetPointer().getFirst(game, source)); + if (player == null || card == null) { + return false; + } + game.setEnterWithCounters(card.getId(), new Counters(CounterType.P1P1.createInstance(2))); + return player.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheDayOfTheDoctor.java b/Mage.Sets/src/mage/cards/t/TheDayOfTheDoctor.java index 27e978726b8..3df16434a2b 100644 --- a/Mage.Sets/src/mage/cards/t/TheDayOfTheDoctor.java +++ b/Mage.Sets/src/mage/cards/t/TheDayOfTheDoctor.java @@ -126,7 +126,7 @@ class TheDayOfTheDoctorChooseEffect extends OneShotEffect { game.getBattlefield() .getActivePermanents( StaticFilters.FILTER_PERMANENT_CREATURE, - source.getSourceId(), source, game + source.getControllerId(), source, game ) .stream() .filter(permanent -> !target.getTargets() diff --git a/Mage.Sets/src/mage/cards/t/TheDeckOfManyThings.java b/Mage.Sets/src/mage/cards/t/TheDeckOfManyThings.java index f07f860e66b..6145636776a 100644 --- a/Mage.Sets/src/mage/cards/t/TheDeckOfManyThings.java +++ b/Mage.Sets/src/mage/cards/t/TheDeckOfManyThings.java @@ -23,6 +23,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -122,7 +123,7 @@ class TheDeckOfManyThingsReturnEffect extends OneShotEffect { return false; } player.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/t/TheEarthCrystal.java b/Mage.Sets/src/mage/cards/t/TheEarthCrystal.java new file mode 100644 index 00000000000..ce2491df81b --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheEarthCrystal.java @@ -0,0 +1,117 @@ +package mage.cards.t; + +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; +import mage.abilities.effects.common.counter.DistributeCountersEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanentAmount; +import mage.util.CardUtil; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheEarthCrystal extends CardImpl { + + private static final FilterCard filter = new FilterCard("green spells"); + + static { + filter.add(new ColorPredicate(ObjectColor.GREEN)); + } + + public TheEarthCrystal(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{G}{G}"); + + this.supertype.add(SuperType.LEGENDARY); + + // Green spells you cast cost {1} less to cast. + this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 1))); + + // If one or more +1/+1 counters would be put on a creature you control, twice that many +1/+1 counters are put on that creature instead. + this.addAbility(new SimpleStaticAbility(new TheEarthCrystalEffect())); + + // {4}{G}{G}, {T}: Distribute two +1/+1 counters among one or two target creatures you control. + Ability ability = new SimpleActivatedAbility( + new DistributeCountersEffect(CounterType.P1P1), new ManaCostsImpl<>("{4}{G}{G}") + ); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetCreaturePermanentAmount( + 2, 1, 2, + StaticFilters.FILTER_CONTROLLED_CREATURES + )); + this.addAbility(ability); + } + + private TheEarthCrystal(final TheEarthCrystal card) { + super(card); + } + + @Override + public TheEarthCrystal copy() { + return new TheEarthCrystal(this); + } +} + +class TheEarthCrystalEffect extends ReplacementEffectImpl { + + TheEarthCrystalEffect() { + super(Duration.WhileOnBattlefield, Outcome.BoostCreature, false); + staticText = "if one or more +1/+1 counters would be put on a creature you control, " + + "twice that many +1/+1 counters are put on that creature instead"; + } + + private TheEarthCrystalEffect(final TheEarthCrystalEffect effect) { + super(effect); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + event.setAmountForCounters(CardUtil.overflowMultiply(event.getAmount(), 2), true); + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ADD_COUNTERS; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (!CounterType.P1P1.getName().equals(event.getData()) || event.getAmount() < 1) { + return false; + } + Permanent permanent = Optional + .ofNullable(event) + .map(GameEvent::getTargetId) + .map(game::getPermanent) + .orElseGet(() -> game.getPermanentEntering(event.getTargetId())); + return permanent != null + && permanent.isControlledBy(source.getControllerId()) + && permanent.isCreature(game); + } + + @Override + public TheEarthCrystalEffect copy() { + return new TheEarthCrystalEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheFiligreeSylex.java b/Mage.Sets/src/mage/cards/t/TheFiligreeSylex.java index e2a3fadefa1..db110f38f74 100644 --- a/Mage.Sets/src/mage/cards/t/TheFiligreeSylex.java +++ b/Mage.Sets/src/mage/cards/t/TheFiligreeSylex.java @@ -17,15 +17,10 @@ import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterNonlandPermanent; -import mage.filter.predicate.ObjectSourcePlayer; -import mage.filter.predicate.ObjectSourcePlayerPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.filter.predicate.mageobject.ManaValueEqualToCountersSourceCountPredicate; import mage.target.TargetPermanent; import mage.target.common.TargetAnyTarget; -import java.util.Objects; -import java.util.Optional; import java.util.UUID; /** @@ -33,10 +28,12 @@ import java.util.UUID; */ public final class TheFiligreeSylex extends CardImpl { - private static final FilterPermanent filter = new FilterNonlandPermanent(); + private static final FilterPermanent filter = new FilterNonlandPermanent( + "each nonland permanent with mana value equal to the number of oil counters on {this}" + ); static { - filter.add(TheFiligreeSylexPredicate.instance); + filter.add(new ManaValueEqualToCountersSourceCountPredicate(CounterType.OIL)); } public TheFiligreeSylex(UUID ownerId, CardSetInfo setInfo) { @@ -50,12 +47,7 @@ public final class TheFiligreeSylex extends CardImpl { )); // {T}, Sacrifice The Filigree Sylex: Destroy each nonland permanent with mana value equal to the number of oil counters on The Filigree Sylex. - Ability ability = new SimpleActivatedAbility( - new DestroyAllEffect(filter) - .setText("destroy each nonland permanent with mana value " + - "equal to the number of oil counters on {this}"), - new TapSourceCost() - ); + Ability ability = new SimpleActivatedAbility(new DestroyAllEffect(filter), new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); @@ -81,18 +73,3 @@ public final class TheFiligreeSylex extends CardImpl { return new TheFiligreeSylex(this); } } - -enum TheFiligreeSylexPredicate implements ObjectSourcePlayerPredicate { - instance; - - @Override - public boolean apply(ObjectSourcePlayer input, Game game) { - return Optional - .ofNullable(input.getSource().getSourcePermanentOrLKI(game)) - .filter(Objects::nonNull) - .map(permanent -> permanent.getCounters(game)) - .map(counters -> counters.getCount(CounterType.OIL)) - .orElse(-1) - .equals(input.getObject().getManaValue()); - } -} diff --git a/Mage.Sets/src/mage/cards/t/TheFinalDays.java b/Mage.Sets/src/mage/cards/t/TheFinalDays.java new file mode 100644 index 00000000000..9aee7221cfb --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheFinalDays.java @@ -0,0 +1,52 @@ +package mage.cards.t; + +import mage.abilities.condition.common.CastFromGraveyardSourceCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.FlashbackAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.game.permanent.token.Horror3Token; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheFinalDays extends CardImpl { + + private static final DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_CREATURE); + private static final Hint hint = new ValueHint("Creature cards in your graveyard", xValue); + + public TheFinalDays(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}{B}"); + + // Create two tapped 2/2 black Horror creature tokens. If this spell was cast from a graveyard, instead create X of those tokens, where X is the number of creature cards in your graveyard. + this.getSpellAbility().addEffect(new ConditionalOneShotEffect( + new CreateTokenEffect(new Horror3Token(), xValue), new CreateTokenEffect(new Horror3Token(), 2), + CastFromGraveyardSourceCondition.instance, "create two tapped 2/2 black Horror creature tokens. " + + "If this spell was cast from a graveyard, instead create X of those tokens, " + + "where X is the number of creature cards in your graveyard" + )); + this.getSpellAbility().addHint(hint); + + // Flashback {4}{B}{B} + this.addAbility(new FlashbackAbility(this, new ManaCostsImpl<>("{4}{B}{B}"))); + } + + private TheFinalDays(final TheFinalDays card) { + super(card); + } + + @Override + public TheFinalDays copy() { + return new TheFinalDays(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheFireCrystal.java b/Mage.Sets/src/mage/cards/t/TheFireCrystal.java new file mode 100644 index 00000000000..b91eca491bc --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheFireCrystal.java @@ -0,0 +1,97 @@ +package mage.cards.t; + +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SuperType; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheFireCrystal extends CardImpl { + + private static final FilterCard filter = new FilterCard("red spells"); + + static { + filter.add(new ColorPredicate(ObjectColor.RED)); + } + + public TheFireCrystal(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{R}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + + // Red spells you cast cost {1} less to cast. + this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 1))); + + // Creatures you control have haste. + this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + HasteAbility.getInstance(), Duration.WhileOnBattlefield, + StaticFilters.FILTER_PERMANENT_CREATURES + ))); + + // {4}{R}{R}, {T}: Create a token that's a copy of target creature you control. Sacrifice it at the beginning of the next end step. + Ability ability = new SimpleActivatedAbility(new TheFireCrystalEffect(), new ManaCostsImpl<>("{4}{R}{R}")); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + private TheFireCrystal(final TheFireCrystal card) { + super(card); + } + + @Override + public TheFireCrystal copy() { + return new TheFireCrystal(this); + } +} + +class TheFireCrystalEffect extends OneShotEffect { + + TheFireCrystalEffect() { + super(Outcome.Benefit); + staticText = "create a token that's a copy of target creature you control. " + + "Sacrifice it at the beginning of the next end step"; + } + + private TheFireCrystalEffect(final TheFireCrystalEffect effect) { + super(effect); + } + + @Override + public TheFireCrystalEffect copy() { + return new TheFireCrystalEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Optional.ofNullable(getTargetPointer().getFirst(game, source)) + .map(game::getPermanent) + .map(new CreateTokenCopyTargetEffect()::setSavedPermanent) + .filter(effect -> effect.apply(game, source)) + .ifPresent(effect -> effect.sacrificeTokensCreatedAtNextEndStep(game, source)); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheGoldSaucer.java b/Mage.Sets/src/mage/cards/t/TheGoldSaucer.java new file mode 100644 index 00000000000..9ccf8468d27 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheGoldSaucer.java @@ -0,0 +1,56 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.FlipCoinEffect; +import mage.abilities.mana.ColorlessManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.game.permanent.token.TreasureToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheGoldSaucer extends CardImpl { + + public TheGoldSaucer(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.subtype.add(SubType.TOWN); + + // {T}: Add {C}. + this.addAbility(new ColorlessManaAbility()); + + // {2}, {T}: Flip a coin. If you win the flip, create a Treasure token. + Ability ability = new SimpleActivatedAbility( + new FlipCoinEffect(new CreateTokenEffect(new TreasureToken())), new GenericManaCost(2) + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + + // {3}, {T}, Sacrifice two artifacts: Draw a card. + ability = new SimpleActivatedAbility(new DrawCardSourceControllerEffect(1), new GenericManaCost(3)); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeTargetCost(2, StaticFilters.FILTER_PERMANENT_ARTIFACTS)); + this.addAbility(ability); + } + + private TheGoldSaucer(final TheGoldSaucer card) { + super(card); + } + + @Override + public TheGoldSaucer copy() { + return new TheGoldSaucer(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheGreatHenge.java b/Mage.Sets/src/mage/cards/t/TheGreatHenge.java index 78dc0a12051..2b9626453b5 100644 --- a/Mage.Sets/src/mage/cards/t/TheGreatHenge.java +++ b/Mage.Sets/src/mage/cards/t/TheGreatHenge.java @@ -1,12 +1,12 @@ package mage.cards.t; -import mage.MageInt; import mage.Mana; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.cost.CostModificationEffectImpl; @@ -18,7 +18,6 @@ import mage.constants.*; import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.util.CardUtil; import java.util.UUID; @@ -34,7 +33,8 @@ public final class TheGreatHenge extends CardImpl { this.supertype.add(SuperType.LEGENDARY); // This spell costs {X} less to cast, where X is the greatest power among creatures you control. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new TheGreatHengeCostReductionEffect())); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new TheGreatHengeCostReductionEffect()) + .addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint())); // {T}: Add {G}{G}. You gain 2 life. Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, Mana.GreenMana(2), new TapSourceCost()); @@ -73,14 +73,8 @@ class TheGreatHengeCostReductionEffect extends CostModificationEffectImpl { @Override public boolean apply(Game game, Ability source, Ability abilityToModify) { - int reductionAmount = game.getBattlefield() - .getAllActivePermanents( - StaticFilters.FILTER_PERMANENT_CREATURE, abilityToModify.getControllerId(), game - ).stream() - .map(Permanent::getPower) - .mapToInt(MageInt::getValue) - .max() - .orElse(0); + // TODO: that abilityToModify should be source, but there is currently a bug with that #11166 + int reductionAmount = GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.calculate(game, abilityToModify, this); CardUtil.reduceCost(abilityToModify, Math.max(0, reductionAmount)); return true; } diff --git a/Mage.Sets/src/mage/cards/t/TheGrimCaptain.java b/Mage.Sets/src/mage/cards/t/TheGrimCaptain.java index e8ca7add470..3ec8f196b3f 100644 --- a/Mage.Sets/src/mage/cards/t/TheGrimCaptain.java +++ b/Mage.Sets/src/mage/cards/t/TheGrimCaptain.java @@ -102,7 +102,7 @@ class TheGrimCaptainEffect extends OneShotEffect { return false; } player.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/t/TheHorusHeresy.java b/Mage.Sets/src/mage/cards/t/TheHorusHeresy.java index f8d782a878c..1cd1dcdadf8 100644 --- a/Mage.Sets/src/mage/cards/t/TheHorusHeresy.java +++ b/Mage.Sets/src/mage/cards/t/TheHorusHeresy.java @@ -22,7 +22,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; import java.util.HashSet; @@ -58,7 +58,7 @@ public final class TheHorusHeresy extends CardImpl { sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, ability -> { ability.addEffect(new TheHorusHeresyControlEffect()); ability.addTarget(new TargetPermanent(0, 1, filterNonlegendary)); - ability.setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); }); // II -- Draw a card for each creature you control but don't own. diff --git a/Mage.Sets/src/mage/cards/t/TheHungerTideRises.java b/Mage.Sets/src/mage/cards/t/TheHungerTideRises.java index 176dd6923cd..5e2d921ff6d 100644 --- a/Mage.Sets/src/mage/cards/t/TheHungerTideRises.java +++ b/Mage.Sets/src/mage/cards/t/TheHungerTideRises.java @@ -9,11 +9,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterCard; -import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; @@ -57,12 +54,6 @@ public final class TheHungerTideRises extends CardImpl { //Based on Scapeshift class HungerTideSacrificeSearchEffect extends OneShotEffect { - private static final FilterPermanent filter = new FilterControlledPermanent("other permanents you control"); - - static { - filter.add(AnotherPredicate.instance); - } - HungerTideSacrificeSearchEffect() { super(Outcome.Benefit); staticText = "Sacrifice any number of creatures. Search your library and/or graveyard for a creature card " diff --git a/Mage.Sets/src/mage/cards/t/TheLunarWhale.java b/Mage.Sets/src/mage/cards/t/TheLunarWhale.java new file mode 100644 index 00000000000..f34466379e4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheLunarWhale.java @@ -0,0 +1,55 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.AttackedThisTurnSourceCondition; +import mage.abilities.decorator.ConditionalAsThoughEffect; +import mage.abilities.effects.common.continuous.LookAtTopCardOfLibraryAnyTimeEffect; +import mage.abilities.effects.common.continuous.PlayFromTopOfLibraryEffect; +import mage.abilities.keyword.CrewAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheLunarWhale extends CardImpl { + + public TheLunarWhale(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(3); + this.toughness = new MageInt(5); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // You may look at the top card of your library any time. + this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect())); + + // As long as The Lunar Whale attacked this turn, you may play the top card of your library. + this.addAbility(new SimpleStaticAbility(new ConditionalAsThoughEffect( + new PlayFromTopOfLibraryEffect(), AttackedThisTurnSourceCondition.instance + ).setText("as long as {this} attacked this turn, you may play the top card of your library"))); + + // Crew 1 + this.addAbility(new CrewAbility(1)); + } + + private TheLunarWhale(final TheLunarWhale card) { + super(card); + } + + @Override + public TheLunarWhale copy() { + return new TheLunarWhale(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheMasamune.java b/Mage.Sets/src/mage/cards/t/TheMasamune.java new file mode 100644 index 00000000000..7237e07ad1e --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheMasamune.java @@ -0,0 +1,119 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.AttachedAttackingCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.decorator.ConditionalRequirementEffect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.combat.MustBeBlockedByAtLeastOneAttachedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.command.Emblem; +import mage.game.events.GameEvent; +import mage.game.events.NumberOfTriggersEvent; +import mage.game.events.ZoneChangeEvent; +import mage.util.CardUtil; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheMasamune extends CardImpl { + + public TheMasamune(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.EQUIPMENT); + + // As long as equipped creature is attacking, it has first strike and must be blocked if able. + Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilityAttachedEffect(FirstStrikeAbility.getInstance(), AttachmentType.EQUIPMENT), + AttachedAttackingCondition.instance, "as long as equipped creature is attacking, it has first strike" + )); + ability.addEffect(new ConditionalRequirementEffect( + new MustBeBlockedByAtLeastOneAttachedEffect(), + AttachedAttackingCondition.instance, "and must be blocked if able" + )); + this.addAbility(ability); + + // Equipped creature has "If a creature dying causes a triggered ability of this creature or an emblem you own to trigger, that ability triggers an additional time." + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( + new SimpleStaticAbility(new TheMasamuneEffect()), AttachmentType.EQUIPMENT + ))); + + // Equip {2} + this.addAbility(new EquipAbility(2)); + } + + private TheMasamune(final TheMasamune card) { + super(card); + } + + @Override + public TheMasamune copy() { + return new TheMasamune(this); + } +} + +class TheMasamuneEffect extends ReplacementEffectImpl { + + TheMasamuneEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "If a creature dying causes a triggered ability of this creature " + + "or an emblem you own to trigger, that ability triggers an additional time."; + } + + private TheMasamuneEffect(final TheMasamuneEffect effect) { + super(effect); + } + + @Override + public TheMasamuneEffect copy() { + return new TheMasamuneEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.NUMBER_OF_TRIGGERS; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return Optional + .ofNullable(event) + .filter(NumberOfTriggersEvent.class::isInstance) + .map(NumberOfTriggersEvent.class::cast) + .map(NumberOfTriggersEvent::getSourceEvent) + .filter(ZoneChangeEvent.class::isInstance) + .map(ZoneChangeEvent.class::cast) + .filter(ZoneChangeEvent::isDiesEvent) + .map(ZoneChangeEvent::getTarget) + .map(permanent -> permanent.isCreature(game)) + .orElse(false) + && source.isControlledBy(event.getPlayerId()) + && (source.getSourceId().equals(event.getSourceId()) + || Optional + .ofNullable(event) + .map(GameEvent::getSourceId) + .map(game::getEmblem) + .map(Emblem.class::cast) + .map(Emblem::getControllerOrOwnerId) + .map(source::isControlledBy) + .orElse(false)); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + event.setAmount(CardUtil.overflowInc(event.getAmount(), 1)); + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheNightOfTheDoctor.java b/Mage.Sets/src/mage/cards/t/TheNightOfTheDoctor.java index 0a3d98cceb3..104facba54a 100644 --- a/Mage.Sets/src/mage/cards/t/TheNightOfTheDoctor.java +++ b/Mage.Sets/src/mage/cards/t/TheNightOfTheDoctor.java @@ -18,6 +18,7 @@ import mage.filter.common.FilterCreatureCard; import mage.filter.StaticFilters; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; +import mage.util.CardUtil; import java.util.*; /** @@ -90,7 +91,7 @@ class TheNightOfTheDoctorEffect extends OneShotEffect { if (card == null || !controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { return false; } - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/t/TheNinthDoctor.java b/Mage.Sets/src/mage/cards/t/TheNinthDoctor.java index 0f514f54004..9b791bfdeb7 100644 --- a/Mage.Sets/src/mage/cards/t/TheNinthDoctor.java +++ b/Mage.Sets/src/mage/cards/t/TheNinthDoctor.java @@ -2,9 +2,8 @@ package mage.cards.t; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.IsStepCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.InspiredAbility; @@ -22,6 +21,8 @@ import java.util.UUID; */ public final class TheNinthDoctor extends CardImpl { + private static final Condition condition = new IsStepCondition(PhaseStep.UNTAP); + public TheNinthDoctor(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{R}"); this.supertype.add(SuperType.LEGENDARY); @@ -33,9 +34,9 @@ public final class TheNinthDoctor extends CardImpl { this.addAbility(HasteAbility.getInstance()); // Into the TARDIS — Whenever The Ninth Doctor becomes untapped during your untap step, you get an additional upkeep step after this step. - TriggeredAbilityImpl ability = new InspiredAbility(new TheNinthDoctorEffect(), false, false).setTriggerPhrase("Whenever {this} becomes untapped during your untap step, "); - ability.withFlavorWord("Into the TARDIS"); - this.addAbility(new ConditionalTriggeredAbility(ability, new IsStepCondition(PhaseStep.UNTAP), "")); + this.addAbility(new InspiredAbility(new TheNinthDoctorEffect(), false, false) + .withTriggerCondition(condition) + .withFlavorWord("Into the TARDIS")); } private TheNinthDoctor(final TheNinthDoctor card) { diff --git a/Mage.Sets/src/mage/cards/t/ThePartingOfTheWays.java b/Mage.Sets/src/mage/cards/t/ThePartingOfTheWays.java index c65cefa54da..e3db2478e5e 100644 --- a/Mage.Sets/src/mage/cards/t/ThePartingOfTheWays.java +++ b/Mage.Sets/src/mage/cards/t/ThePartingOfTheWays.java @@ -12,7 +12,7 @@ import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetArtifactPermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import java.util.UUID; @@ -47,7 +47,7 @@ public final class ThePartingOfTheWays extends CardImpl { ability.addEffect(new DestroyTargetEffect() .setText("For each opponent, destroy up to one target artifact that player controls")); ability.addTarget(new TargetArtifactPermanent(0, 1)); - ability.setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); } ); this.addAbility(sagaAbility); diff --git a/Mage.Sets/src/mage/cards/t/TheRegalia.java b/Mage.Sets/src/mage/cards/t/TheRegalia.java new file mode 100644 index 00000000000..44e1db3b123 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheRegalia.java @@ -0,0 +1,51 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.RevealCardsFromLibraryUntilEffect; +import mage.abilities.keyword.CrewAbility; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.PutCards; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheRegalia extends CardImpl { + + public TheRegalia(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Whenever The Regalia attacks, reveal cards from the top of your library until you reveal a land card. Put that card onto the battlefield tapped and the rest on the bottom of your library in a random order. + this.addAbility(new AttacksTriggeredAbility(new RevealCardsFromLibraryUntilEffect( + StaticFilters.FILTER_CARD_LAND_A, PutCards.BATTLEFIELD_TAPPED, PutCards.BOTTOM_RANDOM + ))); + + // Crew 1 + this.addAbility(new CrewAbility(1)); + } + + private TheRegalia(final TheRegalia card) { + super(card); + } + + @Override + public TheRegalia copy() { + return new TheRegalia(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheScarabGod.java b/Mage.Sets/src/mage/cards/t/TheScarabGod.java index 8e2ec515794..500c819a2ec 100644 --- a/Mage.Sets/src/mage/cards/t/TheScarabGod.java +++ b/Mage.Sets/src/mage/cards/t/TheScarabGod.java @@ -4,6 +4,8 @@ import mage.MageInt; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.abilities.effects.keyword.ScryEffect; import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; @@ -31,7 +33,6 @@ import mage.target.targetpointer.FixedTarget; import java.util.UUID; /** - * * @author spjspj */ public final class TheScarabGod extends CardImpl { @@ -51,15 +52,17 @@ public final class TheScarabGod extends CardImpl { this.toughness = new MageInt(5); // At the beginning of your upkeep, each opponent loses X life and you scry X, where X is the number of Zombies you control. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new TheScarabGodEffect(xValue)).addHint(hint)); + Ability ability = new BeginningOfUpkeepTriggeredAbility(new LoseLifeOpponentsEffect(xValue).setText("each opponent loses X life")); + ability.addEffect(new ScryEffect(xValue).concatBy("and you")); + this.addAbility(ability.addHint(hint)); // {2}{U}{B}: Exile target creature card from a graveyard. Create a token that's a copy of it, except it's a 4/4 black Zombie. - Ability ability = new SimpleActivatedAbility(new TheScarabGodEffect2(), new ManaCostsImpl<>("{2}{U}{B}")); + ability = new SimpleActivatedAbility(new TheScarabGodExileEffect(), new ManaCostsImpl<>("{2}{U}{B}")); ability.addTarget(new TargetCardInGraveyard(StaticFilters.FILTER_CARD_CREATURE_A_GRAVEYARD)); this.addAbility(ability); // When The Scarab God dies, return it to its owner's hand at the beginning of the next end step. - this.addAbility(new DiesSourceTriggeredAbility(new TheScarabGodEffect3())); + this.addAbility(new DiesSourceTriggeredAbility(new TheScarabGodEffectDieEffect())); } private TheScarabGod(final TheScarabGod card) { @@ -72,61 +75,20 @@ public final class TheScarabGod extends CardImpl { } } -class TheScarabGodEffect extends OneShotEffect { +class TheScarabGodExileEffect extends OneShotEffect { - private final DynamicValue numOfZombies; - - public TheScarabGodEffect(DynamicValue numOfZombies) { - super(Outcome.Benefit); - this.numOfZombies = numOfZombies; - staticText = "each opponent loses X life and you scry X, where X is the number of Zombies you control"; - } - - private TheScarabGodEffect(final TheScarabGodEffect effect) { - super(effect); - this.numOfZombies = effect.numOfZombies; - } - - @Override - public TheScarabGodEffect copy() { - return new TheScarabGodEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - int numZombies = numOfZombies.calculate(game, source, this); - if (numZombies > 0) { - for (UUID playerId : game.getOpponents(source.getControllerId())) { - Player opponent = game.getPlayer(playerId); - if (opponent != null) { - opponent.loseLife(numZombies, game, source, false); - } - } - controller.scry(numZombies, source, game); - } - - return true; - } - return false; - } -} - -class TheScarabGodEffect2 extends OneShotEffect { - - public TheScarabGodEffect2() { + public TheScarabGodExileEffect() { super(Outcome.PutCreatureInPlay); this.staticText = "Exile target creature card from a graveyard. Create a token that's a copy of it, except it's a 4/4 black Zombie."; } - private TheScarabGodEffect2(final TheScarabGodEffect2 effect) { + private TheScarabGodExileEffect(final TheScarabGodExileEffect effect) { super(effect); } @Override - public TheScarabGodEffect2 copy() { - return new TheScarabGodEffect2(this); + public TheScarabGodExileEffect copy() { + return new TheScarabGodExileEffect(this); } @Override @@ -147,16 +109,16 @@ class TheScarabGodEffect2 extends OneShotEffect { } } -class TheScarabGodEffect3 extends OneShotEffect { +class TheScarabGodEffectDieEffect extends OneShotEffect { private static final String effectText = "return it to its owner's hand at the beginning of the next end step"; - TheScarabGodEffect3() { + TheScarabGodEffectDieEffect() { super(Outcome.Benefit); staticText = effectText; } - private TheScarabGodEffect3(final TheScarabGodEffect3 effect) { + private TheScarabGodEffectDieEffect(final TheScarabGodEffectDieEffect effect) { super(effect); } @@ -172,7 +134,7 @@ class TheScarabGodEffect3 extends OneShotEffect { } @Override - public TheScarabGodEffect3 copy() { - return new TheScarabGodEffect3(this); + public TheScarabGodEffectDieEffect copy() { + return new TheScarabGodEffectDieEffect(this); } } diff --git a/Mage.Sets/src/mage/cards/t/TheSkullsporeNexus.java b/Mage.Sets/src/mage/cards/t/TheSkullsporeNexus.java index ff220b1df5a..eac1facb4da 100644 --- a/Mage.Sets/src/mage/cards/t/TheSkullsporeNexus.java +++ b/Mage.Sets/src/mage/cards/t/TheSkullsporeNexus.java @@ -1,6 +1,5 @@ package mage.cards.t; -import mage.MageInt; import mage.MageItem; import mage.MageObject; import mage.abilities.Ability; @@ -10,6 +9,7 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; @@ -19,7 +19,6 @@ import mage.abilities.effects.common.cost.CostModificationEffectImpl; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeGroupEvent; @@ -45,7 +44,8 @@ public final class TheSkullsporeNexus extends CardImpl { this.supertype.add(SuperType.LEGENDARY); // This spell costs {X} less to cast, where X is the greatest power among creatures you control. - this.addAbility(new SimpleStaticAbility(Zone.ALL, new TheSkullsporeNexusReductionEffect())); + this.addAbility(new SimpleStaticAbility(Zone.ALL, new TheSkullsporeNexusReductionEffect()) + .addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint())); // Whenever one or more nontoken creatures you control die, create a green Fungus Dinosaur creature token with base power and toughness each equal to the total power of those creatures. this.addAbility(new TheSkullsporeNexusTrigger()); @@ -83,14 +83,8 @@ class TheSkullsporeNexusReductionEffect extends CostModificationEffectImpl { @Override public boolean apply(Game game, Ability source, Ability abilityToModify) { - int reductionAmount = game.getBattlefield() - .getAllActivePermanents( - StaticFilters.FILTER_PERMANENT_CREATURE, abilityToModify.getControllerId(), game - ).stream() - .map(Permanent::getPower) - .mapToInt(MageInt::getValue) - .max() - .orElse(0); + // TODO: that abilityToModify should be source, but there is currently a bug with that #11166 + int reductionAmount = GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.calculate(game, abilityToModify, this); CardUtil.reduceCost(abilityToModify, Math.max(0, reductionAmount)); return true; } diff --git a/Mage.Sets/src/mage/cards/t/TheTrueScriptures.java b/Mage.Sets/src/mage/cards/t/TheTrueScriptures.java index 862f72bcdcc..d29aba1a894 100644 --- a/Mage.Sets/src/mage/cards/t/TheTrueScriptures.java +++ b/Mage.Sets/src/mage/cards/t/TheTrueScriptures.java @@ -17,7 +17,7 @@ import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCreatureOrPlaneswalker; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; import java.util.Collection; @@ -47,7 +47,7 @@ public final class TheTrueScriptures extends CardImpl { ability.addEffect(new DestroyTargetEffect().setTargetPointer(new EachTargetPointer()) .setText("for each opponent, destroy up to one target creature or planeswalker that player controls")); ability.addTarget(new TargetCreatureOrPlaneswalker(0,1)); - ability.setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); } ); diff --git a/Mage.Sets/src/mage/cards/t/TheWanderer.java b/Mage.Sets/src/mage/cards/t/TheWanderer.java index 52bfacaca3d..62a8ce6d306 100644 --- a/Mage.Sets/src/mage/cards/t/TheWanderer.java +++ b/Mage.Sets/src/mage/cards/t/TheWanderer.java @@ -12,10 +12,9 @@ import mage.constants.ComparisonType; import mage.constants.Duration; import mage.constants.SuperType; import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledPermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; -import mage.filter.predicate.mageobject.AnotherPredicate; import mage.target.TargetPermanent; import java.util.UUID; @@ -26,13 +25,10 @@ import java.util.UUID; public final class TheWanderer extends CardImpl { private static final FilterPermanent filter - = new FilterControlledPermanent("other permanents you control"); - private static final FilterPermanent filter2 = new FilterCreaturePermanent("creature with power 4 or greater"); static { - filter.add(AnotherPredicate.instance); - filter2.add(new PowerPredicate(ComparisonType.MORE_THAN, 3)); + filter.add(new PowerPredicate(ComparisonType.MORE_THAN, 3)); } public TheWanderer(UUID ownerId, CardSetInfo setInfo) { @@ -43,12 +39,12 @@ public final class TheWanderer extends CardImpl { // Prevent all noncombat damage that would be dealt to you and other permanents you control. this.addAbility(new SimpleStaticAbility(new PreventAllNonCombatDamageToAllEffect( - Duration.WhileOnBattlefield, filter, true + Duration.WhileOnBattlefield, StaticFilters.FILTER_OTHER_CONTROLLED_PERMANENTS, true ))); // -2: Exile target creature with power 4 or greater. Ability ability = new LoyaltyAbility(new ExileTargetEffect(), -2); - ability.addTarget(new TargetPermanent(filter2)); + ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/TheWanderingMinstrel.java b/Mage.Sets/src/mage/cards/t/TheWanderingMinstrel.java new file mode 100644 index 00000000000..9f4f17c1382 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheWanderingMinstrel.java @@ -0,0 +1,74 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.EnterUntappedAllEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.game.permanent.token.ElementalAllColorsToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheWanderingMinstrel extends CardImpl { + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + new FilterControlledPermanent(SubType.TOWN, "you control five or more Towns"), + ComparisonType.MORE_THAN, 4 + ); + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount( + new FilterControlledPermanent(SubType.TOWN, "Towns you control"), null + ); + private static final Hint hint = new ValueHint("Towns you control", xValue); + + public TheWanderingMinstrel(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.BARD); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Lands you control enter untapped. + this.addAbility(new SimpleStaticAbility(new EnterUntappedAllEffect(StaticFilters.FILTER_CONTROLLED_PERMANENT_LANDS))); + + // The Minstrel's Ballad -- At the beginning of combat on your turn, if you control five or more Towns, create a 2/2 Elemental creature token that's all colors. + this.addAbility(new BeginningOfCombatTriggeredAbility( + new CreateTokenEffect(new ElementalAllColorsToken()) + ).withInterveningIf(condition).withFlavorWord("The Minstrel's Ballad")); + + // {3}{W}{U}{B}{R}{G}: Other creatures you control get +X/+X until end of turn, where X is the number of Towns you control. + this.addAbility(new SimpleActivatedAbility( + new BoostControlledEffect( + xValue, xValue, Duration.EndOfTurn, + StaticFilters.FILTER_PERMANENT_CREATURES, true + ), new ManaCostsImpl<>("{3}{W}{U}{B}{R}{G}") + ).addHint(hint)); + } + + private TheWanderingMinstrel(final TheWanderingMinstrel card) { + super(card); + } + + @Override + public TheWanderingMinstrel copy() { + return new TheWanderingMinstrel(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheWarInHeaven.java b/Mage.Sets/src/mage/cards/t/TheWarInHeaven.java index 43898175052..b8e1ebd88f9 100644 --- a/Mage.Sets/src/mage/cards/t/TheWarInHeaven.java +++ b/Mage.Sets/src/mage/cards/t/TheWarInHeaven.java @@ -97,7 +97,7 @@ class TheWarInHeavenEffect extends OneShotEffect { Card card = game.getCard(targetId); if (card != null) { card.moveToZone(Zone.BATTLEFIELD, source, game, false); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { permanent.addCounters(CounterType.NECRODERMIS.createInstance(), source, game); game.addEffect(new AddCardTypeTargetEffect(Duration.Custom, CardType.ARTIFACT) diff --git a/Mage.Sets/src/mage/cards/t/TheWatcherInTheWater.java b/Mage.Sets/src/mage/cards/t/TheWatcherInTheWater.java index 8e323ecd537..149db334210 100644 --- a/Mage.Sets/src/mage/cards/t/TheWatcherInTheWater.java +++ b/Mage.Sets/src/mage/cards/t/TheWatcherInTheWater.java @@ -6,7 +6,6 @@ import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.common.DrawCardControllerTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.condition.common.OpponentsTurnCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.TapSourceEffect; import mage.abilities.effects.common.UntapTargetEffect; @@ -52,12 +51,9 @@ public final class TheWatcherInTheWater extends CardImpl { this.addAbility(ability); // Whenever you draw a card during an opponent's turn, create a 1/1 blue Tentacle creature token. - this.addAbility(new ConditionalTriggeredAbility( - new DrawCardControllerTriggeredAbility( - new CreateTokenEffect(new TentacleToken()), false - ), OpponentsTurnCondition.instance, "Whenever you draw a card " + - "during an opponent's turn, create a 1/1 blue Tentacle creature token." - )); + this.addAbility(new DrawCardControllerTriggeredAbility( + new CreateTokenEffect(new TentacleToken()), false + ).withTriggerCondition(OpponentsTurnCondition.instance)); // Whenever a Tentacle you control dies, untap up to one target Kraken and put a stun counter on up to one target nonland permanent. ability = new DiesCreatureTriggeredAbility(new UntapTargetEffect(), false, filter); diff --git a/Mage.Sets/src/mage/cards/t/TheWaterCrystal.java b/Mage.Sets/src/mage/cards/t/TheWaterCrystal.java new file mode 100644 index 00000000000..8b8fa07f12e --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheWaterCrystal.java @@ -0,0 +1,97 @@ +package mage.cards.t; + +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.CardsInControllerHandCount; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.MillCardsEachPlayerEffect; +import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheWaterCrystal extends CardImpl { + + private static final FilterCard filter = new FilterCard("blue spells"); + + static { + filter.add(new ColorPredicate(ObjectColor.BLUE)); + } + + public TheWaterCrystal(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{U}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + + // Blue spells you cast cost {1} less to cast. + this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 1))); + + // If an opponent would mill one or more cards, they mill that many cards plus four instead. + this.addAbility(new SimpleStaticAbility(new TheWaterCrystalEffect())); + + // {4}{U}{U}, {T}: Each opponent mills cards equal to the number of cards in your hand. + Ability ability = new SimpleActivatedAbility( + new MillCardsEachPlayerEffect(CardsInControllerHandCount.ANY, TargetController.OPPONENT) + .setText("each opponent mills cards equal to the number of cards in your hand"), + new ManaCostsImpl<>("{4}{U}{U}") + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private TheWaterCrystal(final TheWaterCrystal card) { + super(card); + } + + @Override + public TheWaterCrystal copy() { + return new TheWaterCrystal(this); + } +} + +class TheWaterCrystalEffect extends ReplacementEffectImpl { + + TheWaterCrystalEffect() { + super(Duration.WhileOnBattlefield, Outcome.Neutral); + staticText = "if an opponent would mill one or more cards, they mill that many cards plus four instead"; + } + + private TheWaterCrystalEffect(final TheWaterCrystalEffect effect) { + super(effect); + } + + @Override + public TheWaterCrystalEffect copy() { + return new TheWaterCrystalEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.MILL_CARDS; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return game.getOpponents(source.getControllerId()).contains(event.getPlayerId()); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + event.setAmount(CardUtil.overflowInc(event.getAmount(), 4)); + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheWindCrystal.java b/Mage.Sets/src/mage/cards/t/TheWindCrystal.java new file mode 100644 index 00000000000..0dcfa19b7ed --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheWindCrystal.java @@ -0,0 +1,67 @@ +package mage.cards.t; + +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; +import mage.abilities.effects.common.replacement.GainDoubleLifeReplacementEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SuperType; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.predicate.mageobject.ColorPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheWindCrystal extends CardImpl { + + private static final FilterCard filter = new FilterCard("white spells"); + + static { + filter.add(new ColorPredicate(ObjectColor.WHITE)); + } + + public TheWindCrystal(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{W}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + + // White spells you cast cost {1} less to cast. + this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 1))); + + // If you would gain life, you gain twice that much life instead. + this.addAbility(new SimpleStaticAbility(new GainDoubleLifeReplacementEffect())); + + // {4}{W}{W}, {T}: Creatures you control gain flying and lifelink until end of turn. + Ability ability = new SimpleActivatedAbility(new GainAbilityControlledEffect( + FlyingAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_PERMANENT_CREATURE + ).setText("creatures you control gain flying"), new ManaCostsImpl<>("{4}{W}{W}")); + ability.addCost(new TapSourceCost()); + ability.addEffect(new GainAbilityControlledEffect( + FlyingAbility.getInstance(), Duration.EndOfTurn, + StaticFilters.FILTER_PERMANENT_CREATURE + ).setText("and lifelink until end of turn")); + this.addAbility(ability); + } + + private TheWindCrystal(final TheWindCrystal card) { + super(card); + } + + @Override + public TheWindCrystal copy() { + return new TheWindCrystal(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/ThiefOfHope.java b/Mage.Sets/src/mage/cards/t/ThiefOfHope.java index 9f1eda77e5a..32baeef0f98 100644 --- a/Mage.Sets/src/mage/cards/t/ThiefOfHope.java +++ b/Mage.Sets/src/mage/cards/t/ThiefOfHope.java @@ -27,7 +27,7 @@ public final class ThiefOfHope extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - Ability ability = new SpellCastControllerTriggeredAbility(new LoseLifeTargetEffect(1), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, false); + Ability ability = new SpellCastControllerTriggeredAbility(new LoseLifeTargetEffect(1), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, false); ability.addEffect(new GainLifeEffect(1).concatBy("and")); ability.addTarget(new TargetOpponent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/t/ThiefsKnife.java b/Mage.Sets/src/mage/cards/t/ThiefsKnife.java new file mode 100644 index 00000000000..4c3987037c9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThiefsKnife.java @@ -0,0 +1,57 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.AddCardSubtypeAttachedEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.abilities.keyword.JobSelectAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ThiefsKnife extends CardImpl { + + public ThiefsKnife(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{U}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Job select + this.addAbility(new JobSelectAbility()); + + // Equipped creature gets +1/+1, has "Whenever this creature deals combat damage to a player, draw a card," and is a Rogue in addition to its other types. + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(1, 1)); + ability.addEffect(new GainAbilityAttachedEffect( + new DealsCombatDamageToAPlayerTriggeredAbility( + new DrawCardSourceControllerEffect(1) + ), AttachmentType.EQUIPMENT + ).setText(", has \"Whenever this creature deals combat damage to a player, draw a card,\"")); + ability.addEffect(new AddCardSubtypeAttachedEffect( + SubType.ROGUE, AttachmentType.EQUIPMENT + ).setText("and is a Rogue in addition to its other types")); + this.addAbility(ability); + + // Equip {4} + this.addAbility(new EquipAbility(4)); + } + + private ThiefsKnife(final ThiefsKnife card) { + super(card); + } + + @Override + public ThiefsKnife copy() { + return new ThiefsKnife(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/ThingInTheIce.java b/Mage.Sets/src/mage/cards/t/ThingInTheIce.java index fea3d82c6b2..149beca63ac 100644 --- a/Mage.Sets/src/mage/cards/t/ThingInTheIce.java +++ b/Mage.Sets/src/mage/cards/t/ThingInTheIce.java @@ -1,15 +1,12 @@ - package mage.cards.t; -import java.util.UUID; - import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.decorator.ConditionalOneShotEffect; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.TransformSourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; @@ -18,11 +15,14 @@ import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.FilterSpell; import mage.filter.predicate.Predicates; +import java.util.UUID; + /** * @author fireshoes */ @@ -36,6 +36,8 @@ public final class ThingInTheIce extends CardImpl { CardType.SORCERY.getPredicate())); } + private static final Condition condition = new SourceHasCounterCondition(CounterType.ICE, ComparisonType.EQUAL_TO, 0); + public ThingInTheIce(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.HORROR); @@ -48,20 +50,20 @@ public final class ThingInTheIce extends CardImpl { this.addAbility(DefenderAbility.getInstance()); // Thing in the Ice enters the battlefield with four ice counters on it. - Effect effect = new AddCountersSourceEffect(CounterType.ICE.createInstance(4)); - effect.setText("with four ice counters on it"); - this.addAbility(new EntersBattlefieldAbility(effect)); + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.ICE.createInstance(4)).setText("with four ice counters on it") + )); // Whenever you cast an instant or sorcery spell, remove an ice counter from Thing in the Ice. Then if it has no ice counters on it, transform it. this.addAbility(new TransformAbility()); - effect = new RemoveCounterSourceEffect(CounterType.ICE.createInstance(1)); - effect.setText("remove an ice counter from {this}"); - Ability ability = new SpellCastControllerTriggeredAbility(effect, filter, false); - effect = new ConditionalOneShotEffect(new TransformSourceEffect(), new SourceHasCounterCondition(CounterType.ICE, 0, 0), - "Then if it has no ice counters on it, transform it"); - ability.addEffect(effect); + Ability ability = new SpellCastControllerTriggeredAbility( + new RemoveCounterSourceEffect(CounterType.ICE.createInstance(1)), filter, false + ); + ability.addEffect(new ConditionalOneShotEffect( + new TransformSourceEffect(), condition, + "Then if it has no ice counters on it, transform it" + )); this.addAbility(ability); - } private ThingInTheIce(final ThingInTheIce card) { diff --git a/Mage.Sets/src/mage/cards/t/ThornplateIntimidator.java b/Mage.Sets/src/mage/cards/t/ThornplateIntimidator.java index fd2921883d5..43f1b367d22 100644 --- a/Mage.Sets/src/mage/cards/t/ThornplateIntimidator.java +++ b/Mage.Sets/src/mage/cards/t/ThornplateIntimidator.java @@ -42,7 +42,7 @@ public final class ThornplateIntimidator extends CardImpl { new SacrificeTargetCost(StaticFilters.FILTER_PERMANENT_NON_LAND), new DiscardCardCost() ), "Sacrifice a nonland permanent or discard a card to prevent losing 3 life?" - )); + ).setText("target opponent loses 3 life unless they sacrifice a nonland permanent of their choice or discard a card")); ability.addTarget(new TargetOpponent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/ThoughtsOfRuin.java b/Mage.Sets/src/mage/cards/t/ThoughtsOfRuin.java index 8a5b23ab5fd..f816e6f73fa 100644 --- a/Mage.Sets/src/mage/cards/t/ThoughtsOfRuin.java +++ b/Mage.Sets/src/mage/cards/t/ThoughtsOfRuin.java @@ -1,9 +1,6 @@ package mage.cards.t; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; @@ -16,16 +13,19 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; -import mage.target.common.TargetLandPermanent; +import mage.target.TargetPermanent; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; /** - * * @author LevelX2 */ public final class ThoughtsOfRuin extends CardImpl { public ThoughtsOfRuin(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{R}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}{R}"); // Each player sacrifices a land for each card in your hand. @@ -78,7 +78,7 @@ class ThoughtsOfRuinEffect extends OneShotEffect { } else { FilterLandPermanent playerFilter = filter.copy(); playerFilter.add(new ControllerIdPredicate(playerId)); - Target target = new TargetLandPermanent(amount, amount, playerFilter, true); + Target target = new TargetPermanent(amount, amount, playerFilter, true); player.choose(outcome, target, source, game); for (UUID landId : target.getTargets()) { Permanent permanent = game.getPermanent(landId); @@ -91,7 +91,7 @@ class ThoughtsOfRuinEffect extends OneShotEffect { } } // sacrifice all lands - for (Permanent permanent :permanentsToSacrifice) { + for (Permanent permanent : permanentsToSacrifice) { permanent.sacrifice(source, game); } } diff --git a/Mage.Sets/src/mage/cards/t/ThrastaTempestsRoar.java b/Mage.Sets/src/mage/cards/t/ThrastaTempestsRoar.java index b81047801d1..077aaeeeda1 100644 --- a/Mage.Sets/src/mage/cards/t/ThrastaTempestsRoar.java +++ b/Mage.Sets/src/mage/cards/t/ThrastaTempestsRoar.java @@ -49,7 +49,7 @@ public final class ThrastaTempestsRoar extends CardImpl { // Thrasta has hexproof as long as it entered the battlefield this turn. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( new GainAbilitySourceEffect(HexproofAbility.getInstance(), Duration.WhileOnBattlefield), - SourceEnteredThisTurnCondition.instance, "{this} has hexproof as long as it entered the battlefield this turn" + SourceEnteredThisTurnCondition.DID, "{this} has hexproof as long as it entered the battlefield this turn" ))); } diff --git a/Mage.Sets/src/mage/cards/t/ThreeTreeScribe.java b/Mage.Sets/src/mage/cards/t/ThreeTreeScribe.java index b443b915419..bb42901c663 100644 --- a/Mage.Sets/src/mage/cards/t/ThreeTreeScribe.java +++ b/Mage.Sets/src/mage/cards/t/ThreeTreeScribe.java @@ -75,7 +75,12 @@ class ThreeTreeScribeTriggeredAbility extends TriggeredAbilityImpl { return false; } Permanent permanent = zEvent.getTarget(); - return (permanent != null || !permanent.isControlledBy(getControllerId())) - && (permanent.getId().equals(getSourceId()) || permanent.isCreature(game)); + if (permanent == null) { + return false; + } + if (permanent.getId().equals(getSourceId())) { + return true; // {this} or another + } + return permanent.isCreature(game) && permanent.isControlledBy(getControllerId()); } } diff --git a/Mage.Sets/src/mage/cards/t/ThroatSlitter.java b/Mage.Sets/src/mage/cards/t/ThroatSlitter.java index f887b058182..15c49b945eb 100644 --- a/Mage.Sets/src/mage/cards/t/ThroatSlitter.java +++ b/Mage.Sets/src/mage/cards/t/ThroatSlitter.java @@ -15,7 +15,7 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; import mage.target.TargetPermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -45,7 +45,7 @@ public final class ThroatSlitter extends CardImpl { // Whenever Throat Slitter deals combat damage to a player, destroy target nonblack creature that player controls. Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new DestroyTargetEffect(), false, true); ability.addTarget(new TargetPermanent(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/ThroughTheBreach.java b/Mage.Sets/src/mage/cards/t/ThroughTheBreach.java index 9b141d2c3ae..c24a2f21d88 100644 --- a/Mage.Sets/src/mage/cards/t/ThroughTheBreach.java +++ b/Mage.Sets/src/mage/cards/t/ThroughTheBreach.java @@ -1,6 +1,5 @@ package mage.cards.t; -import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.PutCardIntoPlayWithHasteAndSacrificeEffect; import mage.abilities.keyword.SpliceAbility; import mage.cards.CardImpl; @@ -27,7 +26,7 @@ public final class ThroughTheBreach extends CardImpl { )); // Splice onto Arcane {2}{R}{R} - this.addAbility(new SpliceAbility(SpliceAbility.ARCANE, new ManaCostsImpl<>("{2}{R}{R}"))); + this.addAbility(new SpliceAbility(SpliceAbility.ARCANE, "{2}{R}{R}")); } private ThroughTheBreach(final ThroughTheBreach card) { diff --git a/Mage.Sets/src/mage/cards/t/ThryxTheSuddenStorm.java b/Mage.Sets/src/mage/cards/t/ThryxTheSuddenStorm.java index d93b05c56a2..2f2eeddca6a 100644 --- a/Mage.Sets/src/mage/cards/t/ThryxTheSuddenStorm.java +++ b/Mage.Sets/src/mage/cards/t/ThryxTheSuddenStorm.java @@ -1,6 +1,5 @@ package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; @@ -16,6 +15,8 @@ import mage.filter.FilterSpell; import mage.filter.predicate.Predicate; import mage.filter.predicate.mageobject.ManaValuePredicate; +import java.util.UUID; + /** * @author TheElk801 */ @@ -48,7 +49,7 @@ public final class ThryxTheSuddenStorm extends CardImpl { // Spells you cast with converted mana cost 5 or greater cost {1} less to cast and can't be countered. Ability ability = new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 1)); ability.addEffect(new CantBeCounteredControlledEffect( - filter2, null, Duration.WhileOnBattlefield + filter2, Duration.WhileOnBattlefield ).setText("and can't be countered")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/ThunderMagic.java b/Mage.Sets/src/mage/cards/t/ThunderMagic.java new file mode 100644 index 00000000000..243b4961570 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/ThunderMagic.java @@ -0,0 +1,53 @@ +package mage.cards.t; + +import mage.abilities.Mode; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.keyword.TieredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ThunderMagic extends CardImpl { + + public ThunderMagic(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}"); + + // Tiered + this.addAbility(new TieredAbility(this)); + + // * Thunder -- {0} -- Thunder Magic deals 2 damage to target creature. + this.getSpellAbility().addEffect(new DamageTargetEffect(2)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().withFirstModeFlavorWord("Thunder"); + this.getSpellAbility().withFirstModeCost(new GenericManaCost(0)); + + // * Thundara -- {3} -- Thunder Magic deals 4 damage to target creature. + this.getSpellAbility().addMode(new Mode(new DamageTargetEffect(4)) + .addTarget(new TargetCreaturePermanent()) + .withFlavorWord("Thundara") + .withCost(new GenericManaCost(3))); + + // * Thundaga -- {5}{R} -- Thunder Magic deals 8 damage to target creature. + this.getSpellAbility().addMode(new Mode(new DamageTargetEffect(8)) + .addTarget(new TargetCreaturePermanent()) + .withFlavorWord("Thundaga") + .withCost(new ManaCostsImpl<>("{5}{R}"))); + } + + private ThunderMagic(final ThunderMagic card) { + super(card); + } + + @Override + public ThunderMagic copy() { + return new ThunderMagic(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/ThunderfootBaloth.java b/Mage.Sets/src/mage/cards/t/ThunderfootBaloth.java index 3f76d6069d9..8b18562b249 100644 --- a/Mage.Sets/src/mage/cards/t/ThunderfootBaloth.java +++ b/Mage.Sets/src/mage/cards/t/ThunderfootBaloth.java @@ -33,7 +33,7 @@ public final class ThunderfootBaloth extends CardImpl { // Lieutenant - As long as you control your commander, Thunderfoot Baloth gets +2/+2 and other creatures you control get +2/+2 and have trample. this.addAbility(new LieutenantAbility(new BoostControlledEffect( 2, 2, Duration.WhileOnBattlefield, true - ), "and other creature you control get +2/+2").addLieutenantEffect(new GainAbilityAllEffect( + ), "and other creatures you control get +2/+2").addLieutenantEffect(new GainAbilityAllEffect( TrampleAbility.getInstance(), Duration.WhileOnBattlefield, StaticFilters.FILTER_CONTROLLED_CREATURES, true ), "and have trample")); diff --git a/Mage.Sets/src/mage/cards/t/TidalInfluence.java b/Mage.Sets/src/mage/cards/t/TidalInfluence.java index e6b62405116..6debbfd6c5f 100644 --- a/Mage.Sets/src/mage/cards/t/TidalInfluence.java +++ b/Mage.Sets/src/mage/cards/t/TidalInfluence.java @@ -2,7 +2,6 @@ package mage.cards.t; import mage.ObjectColor; import mage.abilities.StateTriggeredAbility; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.CastOnlyIfConditionIsTrueAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; @@ -14,9 +13,13 @@ import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.effects.common.RemoveAllCountersSourceEffect; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Duration; +import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; @@ -42,8 +45,8 @@ public final class TidalInfluence extends CardImpl { private static final Condition conditionCast = new PermanentsOnTheBattlefieldCondition( filterName, ComparisonType.EQUAL_TO, 0, false); - private static final Condition condition1 = new SourceHasCounterCondition(CounterType.TIDE, 1, 1); - private static final Condition condition3 = new SourceHasCounterCondition(CounterType.TIDE, 3, 3); + private static final Condition condition1 = new SourceHasCounterCondition(CounterType.TIDE, ComparisonType.EQUAL_TO, 1); + private static final Condition condition3 = new SourceHasCounterCondition(CounterType.TIDE, ComparisonType.EQUAL_TO, 3); public TidalInfluence(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); @@ -62,14 +65,14 @@ public final class TidalInfluence extends CardImpl { // As long as there is exactly one tide counter on Tidal Influence, all blue creatures get -2/-0. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( new BoostAllEffect(-2, -0, Duration.WhileOnBattlefield, filterBlue, false), - condition1, - "As long as there is exactly one tide counter on {this}, all blue creatures get -2/-0."))); + condition1, "As long as there is exactly one tide counter on {this}, all blue creatures get -2/-0." + ))); // As long as there are exactly three tide counters on Tidal Influence, all blue creatures get +2/+0. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( new BoostAllEffect(2, 0, Duration.WhileOnBattlefield, filterBlue, false), - condition3, - "As long as there are exactly three tide counter on {this}, all blue creatures get +2/+0."))); + condition3, "As long as there are exactly three tide counters on {this}, all blue creatures get +2/+0." + ))); // Whenever there are four tide counters on Tidal Influence, remove all tide counters from it. this.addAbility(new TidalInfluenceTriggeredAbility()); @@ -88,8 +91,8 @@ public final class TidalInfluence extends CardImpl { class TidalInfluenceTriggeredAbility extends StateTriggeredAbility { public TidalInfluenceTriggeredAbility() { - super(Zone.BATTLEFIELD, new RemoveAllCountersSourceEffect(CounterType.TIDE)); - setTriggerPhrase("Whenever there are four tide counters on {this}, "); + super(Zone.BATTLEFIELD, new RemoveAllCountersSourceEffect(CounterType.TIDE).setText("remove all tide counters from it")); + setTriggerPhrase("Whenever there are four or more tide counters on {this}, "); } private TidalInfluenceTriggeredAbility(final TidalInfluenceTriggeredAbility ability) { @@ -103,6 +106,6 @@ class TidalInfluenceTriggeredAbility extends StateTriggeredAbility { @Override public boolean checkTrigger(GameEvent event, Game game) { - return new CountersSourceCount(CounterType.TIDE).calculate(game, this, null) == 4; + return new CountersSourceCount(CounterType.TIDE).calculate(game, this, null) >= 4; } } diff --git a/Mage.Sets/src/mage/cards/t/TidusBlitzballStar.java b/Mage.Sets/src/mage/cards/t/TidusBlitzballStar.java new file mode 100644 index 00000000000..bf62f29b99e --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TidusBlitzballStar.java @@ -0,0 +1,54 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TidusBlitzballStar extends CardImpl { + + public TidusBlitzballStar(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Whenever an artifact you control enters, put a +1/+1 counter on Tidus. + this.addAbility(new EntersBattlefieldAllTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), + StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT + )); + + // Whenever Tidus attacks, tap target creature an opponent controls. + Ability ability = new AttacksTriggeredAbility(new TapTargetEffect()); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + this.addAbility(ability); + } + + private TidusBlitzballStar(final TidusBlitzballStar card) { + super(card); + } + + @Override + public TidusBlitzballStar copy() { + return new TidusBlitzballStar(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TidusYunasGuardian.java b/Mage.Sets/src/mage/cards/t/TidusYunasGuardian.java new file mode 100644 index 00000000000..204b9350c26 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TidusYunasGuardian.java @@ -0,0 +1,71 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.OneOrMoreCombatDamagePlayerTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.counter.MoveCounterTargetsEffect; +import mage.abilities.effects.common.counter.ProliferateEffect; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SetTargetPointer; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.other.AnotherTargetPredicate; +import mage.filter.predicate.permanent.CounterAnyPredicate; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TidusYunasGuardian extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("a second target creature you control"); + private static final FilterPermanent filter2 + = new FilterControlledCreaturePermanent("creatures you control with counters on them"); + + static { + filter.add(new AnotherTargetPredicate(2)); + filter2.add(CounterAnyPredicate.instance); + } + + public TidusYunasGuardian(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{W}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // At the beginning of combat on your turn, you may move a counter from target creature you control onto a second target creature you control. + Ability ability = new BeginningOfCombatTriggeredAbility(new MoveCounterTargetsEffect(), true); + ability.addTarget(new TargetControlledCreaturePermanent().withChooseHint("to take a counter from").setTargetTag(1)); + ability.addTarget(new TargetPermanent(filter).withChooseHint("to move a counter to").setTargetTag(2)); + this.addAbility(ability); + + // Cheer - Whenever one or more creatures you control with counters on them deal combat damage to a player, you may draw a card and proliferate. Do this only once each turn. + ability = new OneOrMoreCombatDamagePlayerTriggeredAbility( + new DrawCardSourceControllerEffect(1), SetTargetPointer.NONE, filter2, true + ).setDoOnlyOnceEachTurn(true); + ability.addEffect(new ProliferateEffect(false).concatBy("and")); + this.addAbility(ability.withFlavorWord("Cheer")); + } + + private TidusYunasGuardian(final TidusYunasGuardian card) { + super(card); + } + + @Override + public TidusYunasGuardian copy() { + return new TidusYunasGuardian(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TifaLockhart.java b/Mage.Sets/src/mage/cards/t/TifaLockhart.java index 0ef792afaf2..f9be1fc6bdb 100644 --- a/Mage.Sets/src/mage/cards/t/TifaLockhart.java +++ b/Mage.Sets/src/mage/cards/t/TifaLockhart.java @@ -36,7 +36,7 @@ public final class TifaLockhart extends CardImpl { this.addAbility(new LandfallAbility(new BoostSourceEffect( SourcePermanentPowerValue.ALLOW_NEGATIVE, StaticValue.get(0), Duration.EndOfTurn - ).setText(" double {this}'s power until end of turn"))); + ).setText("double {this}'s power until end of turn"))); } private TifaLockhart(final TifaLockhart card) { diff --git a/Mage.Sets/src/mage/cards/t/TifasLimitBreak.java b/Mage.Sets/src/mage/cards/t/TifasLimitBreak.java new file mode 100644 index 00000000000..e8cb50e8c86 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TifasLimitBreak.java @@ -0,0 +1,104 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.keyword.TieredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TifasLimitBreak extends CardImpl { + + public TifasLimitBreak(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}"); + + // Tiered + this.addAbility(new TieredAbility(this)); + + // * Somersault -- {0} -- Target creature gets +2/+2 until end of turn. + this.getSpellAbility().addEffect(new BoostTargetEffect(2, 2)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().withFirstModeCost(new GenericManaCost(0)); + this.getSpellAbility().withFirstModeFlavorWord("Somersault"); + + // * Meteor Strikes -- {2} -- Double target creature's power and toughness until end of turn. + this.getSpellAbility().addMode(new Mode(new TifasLimitBreakEffect(2)) + .addTarget(new TargetCreaturePermanent()) + .withCost(new GenericManaCost(2)) + .withFlavorWord("Meteor Strikes")); + + // * Final Heaven -- {6}{G} -- Triple target creature's power and toughness until end of turn. + this.getSpellAbility().addMode(new Mode(new TifasLimitBreakEffect(3)) + .addTarget(new TargetCreaturePermanent()) + .withCost(new ManaCostsImpl<>("{6}{G}")) + .withFlavorWord("Final Heaven")); + } + + private TifasLimitBreak(final TifasLimitBreak card) { + super(card); + } + + @Override + public TifasLimitBreak copy() { + return new TifasLimitBreak(this); + } +} + +class TifasLimitBreakEffect extends OneShotEffect { + + private final int multiplier; + + TifasLimitBreakEffect(int multiplier) { + super(Outcome.Benefit); + staticText = makeWord(multiplier) + " target creature's power and toughness until end of turn"; + this.multiplier = multiplier; + } + + private TifasLimitBreakEffect(final TifasLimitBreakEffect effect) { + super(effect); + this.multiplier = effect.multiplier; + } + + @Override + public TifasLimitBreakEffect copy() { + return new TifasLimitBreakEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (permanent == null) { + return false; + } + game.addEffect(new BoostTargetEffect( + (multiplier - 1) * permanent.getPower().getValue(), + (multiplier - 1) * permanent.getToughness().getValue() + ).setTargetPointer(new FixedTarget(permanent, game)), source); + return true; + } + + private static String makeWord(int multiplier) { + switch (multiplier) { + case 2: + return "double"; + case 3: + return "triple"; + default: + throw new UnsupportedOperationException("no idea how we got here"); + } + } +} diff --git a/Mage.Sets/src/mage/cards/t/TimberlineRidge.java b/Mage.Sets/src/mage/cards/t/TimberlineRidge.java index ebad3e35930..24b5023b754 100644 --- a/Mage.Sets/src/mage/cards/t/TimberlineRidge.java +++ b/Mage.Sets/src/mage/cards/t/TimberlineRidge.java @@ -1,50 +1,50 @@ - package mage.cards.t; -import java.util.UUID; -import mage.Mana; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; -import mage.abilities.costs.common.TapSourceCost; import mage.abilities.decorator.ConditionalContinuousRuleModifyingEffect; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DontUntapInControllersUntapStepSourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; -import mage.abilities.mana.SimpleManaAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.abilities.mana.RedManaAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; import mage.counters.CounterType; +import java.util.UUID; + /** - * * @author Luna Skyrise */ public final class TimberlineRidge extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.DEPLETION); + public TimberlineRidge(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // Timberline Ridge doesn't untap during your untap step if it has a depletion counter on it. - Effect effect = new ConditionalContinuousRuleModifyingEffect(new DontUntapInControllersUntapStepSourceEffect(false, true), - new SourceHasCounterCondition(CounterType.DEPLETION, 1, Integer.MAX_VALUE)); - effect.setText("{this} doesn't untap during your untap step if it has a depletion counter on it"); - Ability ability = new SimpleStaticAbility(effect); - this.addAbility(ability); + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousRuleModifyingEffect( + new DontUntapInControllersUntapStepSourceEffect(false, true), condition + ).setText("{this} doesn't untap during your untap step if it has a depletion counter on it"))); + // At the beginning of your upkeep, remove a depletion counter from Timberline Ridge. - Ability ability2 = new BeginningOfUpkeepTriggeredAbility(new RemoveCounterSourceEffect(CounterType.DEPLETION.createInstance())); - this.addAbility(ability2); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new RemoveCounterSourceEffect(CounterType.DEPLETION.createInstance()) + )); + // {tap}: Add {R} or {G}. Put a depletion counter on Timberline Ridge. - Ability ability3 = new SimpleManaAbility(Zone.BATTLEFIELD, Mana.RedMana(1), new TapSourceCost()); - ability3.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance())); - this.addAbility(ability3); - Ability ability4 = new SimpleManaAbility(Zone.BATTLEFIELD, Mana.GreenMana(1), new TapSourceCost()); - ability4.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance())); - this.addAbility(ability4); + Ability ability = new RedManaAbility(); + ability.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance())); + this.addAbility(ability); + Ability ability2 = new GreenManaAbility(); + ability2.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance())); + this.addAbility(ability2); } private TimberlineRidge(final TimberlineRidge card) { diff --git a/Mage.Sets/src/mage/cards/t/TimeReaper.java b/Mage.Sets/src/mage/cards/t/TimeReaper.java index d6a55ec7bcb..3d055d4bbef 100644 --- a/Mage.Sets/src/mage/cards/t/TimeReaper.java +++ b/Mage.Sets/src/mage/cards/t/TimeReaper.java @@ -15,7 +15,7 @@ import mage.filter.FilterCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.card.FaceDownPredicate; import mage.target.common.TargetCardInExile; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -49,7 +49,7 @@ public final class TimeReaper extends CardImpl { Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new PutOnLibraryTargetEffect(false), false, true); ability.addEffect(new GainLifeEffect(3).concatBy("If you do,")); //I don't think the move can fail? If there's no target then the trigger won't happen ability.addTarget(new TargetCardInExile(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster(true)); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster(true)); ability.withFlavorWord("Consume Anomaly"); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/TimelyWard.java b/Mage.Sets/src/mage/cards/t/TimelyWard.java index 0e9d9ef01eb..6d52ca741ea 100644 --- a/Mage.Sets/src/mage/cards/t/TimelyWard.java +++ b/Mage.Sets/src/mage/cards/t/TimelyWard.java @@ -11,7 +11,10 @@ import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.IndestructibleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.predicate.mageobject.CommanderPredicate; import mage.target.TargetPermanent; @@ -38,8 +41,8 @@ public final class TimelyWard extends CardImpl { this.subtype.add(SubType.AURA); // You may cast this spell as though it had flash if it targets a commander. - this.addAbility(new CastAsThoughItHadFlashIfConditionAbility(condition, - "You may cast {this} as though it had flash if it targets a commander." + this.addAbility(new CastAsThoughItHadFlashIfConditionAbility( + condition, "You may cast this spell as though it had flash if it targets a commander." )); // Enchant creature diff --git a/Mage.Sets/src/mage/cards/t/Timesifter.java b/Mage.Sets/src/mage/cards/t/Timesifter.java index 5c98cdb79e8..2ef423ca5de 100644 --- a/Mage.Sets/src/mage/cards/t/Timesifter.java +++ b/Mage.Sets/src/mage/cards/t/Timesifter.java @@ -46,7 +46,7 @@ class TimesifterEffect extends OneShotEffect { TimesifterEffect() { super(Outcome.ExtraTurn); - this.staticText = "each player exiles the top card of their library. The player who exiled the card with the highest mana value takes an extra turn after this one. If two or more players' cards are tied for highest, the tied players repeat this process until the tie is broken"; + this.staticText = "each player exiles the top card of their library. The player who exiled the card with the greatest mana value takes an extra turn after this one. If two or more players' cards are tied for greatest, the tied players repeat this process until the tie is broken"; } private TimesifterEffect(final TimesifterEffect effect) { diff --git a/Mage.Sets/src/mage/cards/t/TinybonesThePickpocket.java b/Mage.Sets/src/mage/cards/t/TinybonesThePickpocket.java index 3a48422bda3..789c75a5332 100644 --- a/Mage.Sets/src/mage/cards/t/TinybonesThePickpocket.java +++ b/Mage.Sets/src/mage/cards/t/TinybonesThePickpocket.java @@ -16,7 +16,7 @@ import mage.filter.FilterCard; import mage.filter.common.FilterPermanentCard; import mage.filter.predicate.Predicates; import mage.target.common.TargetCardInGraveyard; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -48,7 +48,7 @@ public final class TinybonesThePickpocket extends CardImpl { OneShotEffect effect = new MayCastTargetCardEffect(CastManaAdjustment.AS_THOUGH_ANY_MANA_TYPE, false); Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(effect, false, true); ability.addTarget(new TargetCardInGraveyard(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster(true)); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster(true)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/TolarianContempt.java b/Mage.Sets/src/mage/cards/t/TolarianContempt.java index f3263351279..c4c17fc05cb 100644 --- a/Mage.Sets/src/mage/cards/t/TolarianContempt.java +++ b/Mage.Sets/src/mage/cards/t/TolarianContempt.java @@ -17,7 +17,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; import java.util.UUID; @@ -47,7 +47,7 @@ public final class TolarianContempt extends CardImpl { // At the beginning of your end step, for each opponent, choose up to one target creature they control with a rejection counter on it. That creature's owner puts it on the top or bottom of their library. Ability ability = new BeginningOfEndStepTriggeredAbility(new TolarianContemptEffect()); ability.addTarget(new TargetPermanent(0,1, filterRejection)); - ability.setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); this.addAbility(ability); } @@ -93,7 +93,7 @@ class TolarianContemptEffect extends OneShotEffect { )) { owner.putCardsOnTopOfLibrary(permanent, game, source, false); } else { - owner.putCardsOnBottomOfLibrary(permanent, game, source, false); + owner.putCardsOnBottomOfLibrary(permanent, game, source); } } return true; diff --git a/Mage.Sets/src/mage/cards/t/TomikWielderOfLaw.java b/Mage.Sets/src/mage/cards/t/TomikWielderOfLaw.java index b33d524311c..008d455a408 100644 --- a/Mage.Sets/src/mage/cards/t/TomikWielderOfLaw.java +++ b/Mage.Sets/src/mage/cards/t/TomikWielderOfLaw.java @@ -1,39 +1,27 @@ package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.common.AffinityEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.LoseLifeTargetEffect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; -import mage.constants.SubType; -import mage.constants.SuperType; +import mage.abilities.keyword.AffinityAbility; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Zone; -import mage.filter.common.FilterControlledPermanent; -import mage.filter.common.FilterControlledPlaneswalkerPermanent; +import mage.constants.*; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author DominionSpy */ public final class TomikWielderOfLaw extends CardImpl { - private static final FilterControlledPermanent filter = new FilterControlledPlaneswalkerPermanent("planeswalkers"); - private static final Hint hint = new ValueHint("planeswalkers you control", new PermanentsOnBattlefieldCount(filter)); - public TomikWielderOfLaw(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{B}"); @@ -44,8 +32,7 @@ public final class TomikWielderOfLaw extends CardImpl { this.toughness = new MageInt(4); // Affinity for planeswalkers - this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filter)) - .addHint(hint)); + this.addAbility(new AffinityAbility(AffinityType.PLANESWALKERS)); // Flying this.addAbility(FlyingAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/t/TooGreedilyTooDeep.java b/Mage.Sets/src/mage/cards/t/TooGreedilyTooDeep.java index bc68de63da7..2ce19c286c7 100644 --- a/Mage.Sets/src/mage/cards/t/TooGreedilyTooDeep.java +++ b/Mage.Sets/src/mage/cards/t/TooGreedilyTooDeep.java @@ -8,13 +8,12 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.FilterCard; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreatureCard; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInGraveyard; +import mage.util.CardUtil; import java.util.UUID; @@ -66,7 +65,7 @@ class TooGreedilyTooDeepEffect extends OneShotEffect { } controller.moveCards(card, Zone.BATTLEFIELD, source, game); game.processAction(); - Permanent returnedCreature = game.getPermanent(card.getId()); + Permanent returnedCreature = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (returnedCreature != null && returnedCreature.getPower().getValue() > 0) { for (Permanent creature : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), source, game)) { if (!creature.getId().equals(returnedCreature.getId())) { diff --git a/Mage.Sets/src/mage/cards/t/TorgalAFineHound.java b/Mage.Sets/src/mage/cards/t/TorgalAFineHound.java new file mode 100644 index 00000000000..af8aac838ca --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TorgalAFineHound.java @@ -0,0 +1,180 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.mana.AnyColorManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.FilterSpell; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterCreatureSpell; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.util.CardUtil; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TorgalAFineHound extends CardImpl { + + private static final FilterSpell filter = new FilterCreatureSpell("your first Human creature spell each turn"); + + static { + filter.add(SubType.HUMAN.getPredicate()); + } + + public TorgalAFineHound(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.WOLF); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever you cast your first Human creature spell each turn, that creature enters with an additional +1/+1 counter on it for each Dog and/or Wolf you control. + this.addAbility(new SpellCastControllerTriggeredAbility(new TorgalAFineHoundEffect(), filter, false) + .withTriggerCondition(TorgalAFineHoundCondition.instance) + .addHint(TorgalAFineHoundEffect.getHint()), new TorgalAFineHoundWatcher()); + + // {T}: Add one mana of any color. + this.addAbility(new AnyColorManaAbility()); + } + + private TorgalAFineHound(final TorgalAFineHound card) { + super(card); + } + + @Override + public TorgalAFineHound copy() { + return new TorgalAFineHound(this); + } +} + +enum TorgalAFineHoundCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + return TorgalAFineHoundWatcher.check(game, source); + } + + @Override + public String toString() { + return ""; + } +} + +class TorgalAFineHoundEffect extends ReplacementEffectImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(); + + static { + filter.add(Predicates.or( + SubType.DOG.getPredicate(), + SubType.WOLF.getPredicate() + )); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + private static final Hint hint = new ValueHint("Dogs and Wolves you control", xValue); + + public static Hint getHint() { + return hint; + } + + TorgalAFineHoundEffect() { + super(Duration.EndOfStep, Outcome.BoostCreature); + staticText = "that creature enters with an additional +1/+1 counter on it for each Dog and/or Wolf you control"; + } + + private TorgalAFineHoundEffect(final TorgalAFineHoundEffect effect) { + super(effect); + } + + @Override + public TorgalAFineHoundEffect copy() { + return new TorgalAFineHoundEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Spell spell = (Spell) getValue("spellCast"); + return spell != null && event.getTargetId().equals(spell.getCard().getId()); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget(); + int count = xValue.calculate(game, source, this); + if (creature == null || count < 1) { + return false; + } + creature.addCounters( + CounterType.P1P1.createInstance(count), source.getControllerId(), + source, game, event.getAppliedEffects() + ); + discard(); + return false; + } +} + +class TorgalAFineHoundWatcher extends Watcher { + + private final Map map = new HashMap<>(); + + TorgalAFineHoundWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.SPELL_CAST) { + return; + } + Optional.ofNullable(event) + .map(GameEvent::getTargetId) + .map(game::getSpell) + .filter(spell -> spell.isCreature(game) && spell.hasSubtype(SubType.HUMAN, game)) + .map(Spell::getControllerId) + .ifPresent(playerId -> map.compute(playerId, CardUtil::setOrIncrementValue)); + } + + @Override + public void reset() { + super.reset(); + map.clear(); + } + + static boolean check(Game game, Ability source) { + return game + .getState() + .getWatcher(TorgalAFineHoundWatcher.class) + .map + .getOrDefault(source.getControllerId(), 0) < 2; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TorrentOfFire.java b/Mage.Sets/src/mage/cards/t/TorrentOfFire.java index ad3183cc199..5a697d956e1 100644 --- a/Mage.Sets/src/mage/cards/t/TorrentOfFire.java +++ b/Mage.Sets/src/mage/cards/t/TorrentOfFire.java @@ -1,16 +1,16 @@ package mage.cards.t; -import java.util.UUID; -import mage.abilities.dynamicvalue.common.HighestManaValueCount; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.target.common.TargetAnyTarget; +import java.util.UUID; + /** - * * @author nigelzor */ public final class TorrentOfFire extends CardImpl { @@ -18,10 +18,11 @@ public final class TorrentOfFire extends CardImpl { public TorrentOfFire(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}{R}"); - // Torrent of Fire deals damage equal to the highest converted mana cost among permanents you control to any target. - this.getSpellAbility().addEffect(new DamageTargetEffect(new HighestManaValueCount()) - .setText("{this} deals damage to any target equal to the highest mana value among permanents you control.") + // Torrent of Fire deals damage equal to the greatest converted mana cost among permanents you control to any target. + this.getSpellAbility().addEffect(new DamageTargetEffect(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_PERMANENTS) + .setText("{this} deals damage to any target equal to the greatest mana value among permanents you control.") ); + this.getSpellAbility().addHint(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_PERMANENTS.getHint()); this.getSpellAbility().addTarget(new TargetAnyTarget()); } diff --git a/Mage.Sets/src/mage/cards/t/Touchstone.java b/Mage.Sets/src/mage/cards/t/Touchstone.java index 2b2157e6116..2d109a7ceaa 100644 --- a/Mage.Sets/src/mage/cards/t/Touchstone.java +++ b/Mage.Sets/src/mage/cards/t/Touchstone.java @@ -1,7 +1,6 @@ package mage.cards.t; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; @@ -10,12 +9,12 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.TargetController; -import mage.constants.Zone; import mage.filter.common.FilterArtifactPermanent; -import mage.target.common.TargetArtifactPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author LoneFox */ public final class Touchstone extends CardImpl { @@ -27,11 +26,11 @@ public final class Touchstone extends CardImpl { } public Touchstone(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); // {tap}: Tap target artifact you don't control. Ability ability = new SimpleActivatedAbility(new TapTargetEffect(), new TapSourceCost()); - ability.addTarget(new TargetArtifactPermanent(filter)); + ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/ToughCookie.java b/Mage.Sets/src/mage/cards/t/ToughCookie.java index e7ef120c870..3acbd41f706 100644 --- a/Mage.Sets/src/mage/cards/t/ToughCookie.java +++ b/Mage.Sets/src/mage/cards/t/ToughCookie.java @@ -48,10 +48,10 @@ public final class ToughCookie extends CardImpl { // {2}{G}: Target noncreature artifact you control becomes a 4/4 artifact creature until end of turn. Ability ability = new SimpleActivatedAbility(new AddCardTypeTargetEffect( Duration.EndOfTurn, CardType.ARTIFACT, CardType.CREATURE - ).setText("target noncreature artifact you control becomes"), new ManaCostsImpl<>("{2}{G}")); + ).setText("until end of turn, target noncreature artifact you control becomes"), new ManaCostsImpl<>("{2}{G}")); ability.addEffect(new SetBasePowerToughnessTargetEffect( 4, 4, Duration.EndOfTurn - ).setText(" a 4/4 artifact creature until end of turn")); + ).setText(" a 4/4 artifact creature")); ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/t/ToweringGibbon.java b/Mage.Sets/src/mage/cards/t/ToweringGibbon.java index 0ac8f2ab14c..d6082560c48 100644 --- a/Mage.Sets/src/mage/cards/t/ToweringGibbon.java +++ b/Mage.Sets/src/mage/cards/t/ToweringGibbon.java @@ -1,11 +1,8 @@ package mage.cards.t; import mage.MageInt; -import mage.MageObject; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.continuous.SetBasePowerSourceEffect; import mage.abilities.keyword.ReachAbility; import mage.cards.CardImpl; @@ -13,8 +10,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.StaticFilters; -import mage.game.Game; import java.util.UUID; @@ -36,9 +31,8 @@ public final class ToweringGibbon extends CardImpl { // Towering Gibbon's power is equal to the greatest mana value among creatures you control. this.addAbility(new SimpleStaticAbility( Zone.ALL, - new SetBasePowerSourceEffect( - ToweringGibbonValue.instance - ).setText("{this}'s power is equal to the greatest mana value among creatures you control") + new SetBasePowerSourceEffect(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_CREATURES) + .setText("{this}'s power is equal to the greatest mana value among creatures you control") )); } @@ -50,36 +44,4 @@ public final class ToweringGibbon extends CardImpl { public ToweringGibbon copy() { return new ToweringGibbon(this); } -} - -enum ToweringGibbonValue implements DynamicValue { - instance; - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - return game - .getBattlefield() - .getActivePermanents( - StaticFilters.FILTER_CONTROLLED_CREATURE, - sourceAbility.getControllerId(), sourceAbility, game - ).stream() - .mapToInt(MageObject::getManaValue) - .max() - .orElse(0); - } - - @Override - public ToweringGibbonValue copy() { - return this; - } - - @Override - public String getMessage() { - return ""; - } - - @Override - public String toString() { - return "X"; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/t/TragicArrogance.java b/Mage.Sets/src/mage/cards/t/TragicArrogance.java index 2a7322518e5..bb92d2fa3ca 100644 --- a/Mage.Sets/src/mage/cards/t/TragicArrogance.java +++ b/Mage.Sets/src/mage/cards/t/TragicArrogance.java @@ -1,8 +1,5 @@ package mage.cards.t; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; @@ -20,10 +17,12 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; import mage.target.TargetPermanent; -import mage.target.common.TargetArtifactPermanent; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; /** - * * @author LevelX2 */ public final class TragicArrogance extends CardImpl { @@ -71,7 +70,7 @@ class TragicArroganceEffect extends OneShotEffect { if (player != null) { FilterArtifactPermanent filterArtifactPermanent = new FilterArtifactPermanent("an artifact of " + player.getName()); filterArtifactPermanent.add(new ControllerIdPredicate(playerId)); - Target target1 = new TargetArtifactPermanent(1, 1, filterArtifactPermanent, true); + Target target1 = new TargetPermanent(1, 1, filterArtifactPermanent, true); FilterCreaturePermanent filterCreaturePermanent = new FilterCreaturePermanent("a creature of " + player.getName()); filterCreaturePermanent.add(new ControllerIdPredicate(playerId)); diff --git a/Mage.Sets/src/mage/cards/t/TravelTheOverworld.java b/Mage.Sets/src/mage/cards/t/TravelTheOverworld.java new file mode 100644 index 00000000000..0d2115a9333 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TravelTheOverworld.java @@ -0,0 +1,35 @@ +package mage.cards.t; + +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.AffinityAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AffinityType; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TravelTheOverworld extends CardImpl { + + public TravelTheOverworld(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{U}{U}"); + + // Affinity for Towns + this.addAbility(new AffinityAbility(AffinityType.TOWNS)); + + // Draw four cards. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(4)); + } + + private TravelTheOverworld(final TravelTheOverworld card) { + super(card); + } + + @Override + public TravelTheOverworld copy() { + return new TravelTheOverworld(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TravelingChocobo.java b/Mage.Sets/src/mage/cards/t/TravelingChocobo.java index f66dead4ac6..679c1602ac8 100644 --- a/Mage.Sets/src/mage/cards/t/TravelingChocobo.java +++ b/Mage.Sets/src/mage/cards/t/TravelingChocobo.java @@ -66,7 +66,7 @@ class TravelingChocoboEffect extends ReplacementEffectImpl { TravelingChocoboEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "if land or Bird entering the battlefield causes a triggered ability " + + staticText = "if a land or Bird you control entering the battlefield causes a triggered ability " + "of a permanent you control to trigger, that ability triggers an additional time"; } @@ -102,6 +102,7 @@ class TravelingChocoboEffect extends ReplacementEffectImpl { EntersTheBattlefieldEvent entersTheBattlefieldEvent = (EntersTheBattlefieldEvent) sourceEvent; return (entersTheBattlefieldEvent.getTarget().isLand(game) || entersTheBattlefieldEvent.getTarget().hasSubtype(SubType.BIRD, game)) + && entersTheBattlefieldEvent.getTarget().isControlledBy(source.getControllerId()) && game.getPermanent(numberOfTriggersEvent.getSourceId()) != null; } diff --git a/Mage.Sets/src/mage/cards/t/TraverseEternity.java b/Mage.Sets/src/mage/cards/t/TraverseEternity.java index e0b832bb37d..e6b068fc161 100644 --- a/Mage.Sets/src/mage/cards/t/TraverseEternity.java +++ b/Mage.Sets/src/mage/cards/t/TraverseEternity.java @@ -1,17 +1,13 @@ package mage.cards.t; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.StaticFilters; -import mage.game.Game; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.mageobject.HistoricPredicate; import java.util.UUID; @@ -20,13 +16,22 @@ import java.util.UUID; */ public final class TraverseEternity extends CardImpl { + private static final FilterControlledPermanent filter = new FilterControlledPermanent("historic permanents you control"); + + static { + filter.add(HistoricPredicate.instance); + } + + private static final GreatestAmongPermanentsValue xValue = new GreatestAmongPermanentsValue(GreatestAmongPermanentsValue.Quality.ManaValue, filter); + private static final Hint hint = xValue.getHint(); + public TraverseEternity(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}{U}"); - // Draw cards equal to the highest mana value among historic permanents you control. - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(TraverseEternityValue.instance) + // Draw cards equal to the greatest mana value among historic permanents you control. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(xValue) .setText("draw cards equal to the greatest mana value among historic permanents you control")); - this.getSpellAbility().addHint(TraverseEternityValue.getHint()); + this.getSpellAbility().addHint(hint); } private TraverseEternity(final TraverseEternity card) { @@ -37,42 +42,4 @@ public final class TraverseEternity extends CardImpl { public TraverseEternity copy() { return new TraverseEternity(this); } -} - -enum TraverseEternityValue implements DynamicValue { - instance; - private static final Hint hint = new ValueHint("Greatest mana value among your historic permanents", instance); - - public static Hint getHint() { - return hint; - } - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - return game - .getBattlefield() - .getActivePermanents( - StaticFilters.FILTER_CONTROLLED_PERMANENT, - sourceAbility.getControllerId(), sourceAbility, game - ).stream() - .filter(permanent -> permanent.isHistoric(game)) - .mapToInt(MageObject::getManaValue) - .max() - .orElse(0); - } - - @Override - public TraverseEternityValue copy() { - return this; - } - - @Override - public String getMessage() { - return ""; - } - - @Override - public String toString() { - return "1"; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/t/TraxosScourgeOfKroog.java b/Mage.Sets/src/mage/cards/t/TraxosScourgeOfKroog.java index 11b6b09c7bc..87c266ddf40 100644 --- a/Mage.Sets/src/mage/cards/t/TraxosScourgeOfKroog.java +++ b/Mage.Sets/src/mage/cards/t/TraxosScourgeOfKroog.java @@ -14,7 +14,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.filter.common.FilterHistoricSpell; +import mage.filter.StaticFilters; /** * @@ -39,7 +39,7 @@ public final class TraxosScourgeOfKroog extends CardImpl { ability.addEffect(new DontUntapInControllersUntapStepSourceEffect()); this.addAbility(ability); // Whenever you cast a historic spell untap Traxos. - this.addAbility(new SpellCastControllerTriggeredAbility(new UntapSourceEffect(), new FilterHistoricSpell(), false)); + this.addAbility(new SpellCastControllerTriggeredAbility(new UntapSourceEffect(), StaticFilters.FILTER_SPELL_HISTORIC, false)); } private TraxosScourgeOfKroog(final TraxosScourgeOfKroog card) { diff --git a/Mage.Sets/src/mage/cards/t/TreacherousUrge.java b/Mage.Sets/src/mage/cards/t/TreacherousUrge.java index dcb297c2e0c..2b60523a9df 100644 --- a/Mage.Sets/src/mage/cards/t/TreacherousUrge.java +++ b/Mage.Sets/src/mage/cards/t/TreacherousUrge.java @@ -24,6 +24,7 @@ import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetOpponent; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @@ -81,7 +82,7 @@ class TreacherousUrgeEffect extends OneShotEffect { card = opponent.getHand().get(target.getFirstTarget(), game); if (card != null) { if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); effect.setTargetPointer(new FixedTarget(permanent, game)); diff --git a/Mage.Sets/src/mage/cards/t/TrenoDarkCity.java b/Mage.Sets/src/mage/cards/t/TrenoDarkCity.java new file mode 100644 index 00000000000..f79ba8b109d --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TrenoDarkCity.java @@ -0,0 +1,39 @@ +package mage.cards.t; + +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.BlueManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TrenoDarkCity extends CardImpl { + + public TrenoDarkCity(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.subtype.add(SubType.TOWN); + + // This land enters tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {U} or {B}. + this.addAbility(new BlueManaAbility()); + this.addAbility(new BlackManaAbility()); + } + + private TrenoDarkCity(final TrenoDarkCity card) { + super(card); + } + + @Override + public TrenoDarkCity copy() { + return new TrenoDarkCity(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TriassicEgg.java b/Mage.Sets/src/mage/cards/t/TriassicEgg.java index fac59e4d996..efb88ddb034 100644 --- a/Mage.Sets/src/mage/cards/t/TriassicEgg.java +++ b/Mage.Sets/src/mage/cards/t/TriassicEgg.java @@ -1,10 +1,9 @@ - package mage.cards.t; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; @@ -16,39 +15,39 @@ import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.StaticFilters; -import mage.target.Target; import mage.target.common.TargetCardInYourGraveyard; +import java.util.UUID; + /** - * * @author fireshoes */ public final class TriassicEgg extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.HATCHLING, 2); + public TriassicEgg(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); // {3}, {tap}: Put a hatchling counter on Triassic Egg. Ability ability = new SimpleActivatedAbility( - new AddCountersSourceEffect(CounterType.HATCHLING.createInstance(), true), - new GenericManaCost(3)); + new AddCountersSourceEffect(CounterType.HATCHLING.createInstance()), new GenericManaCost(3) + ); ability.addCost(new TapSourceCost()); this.addAbility(ability); // Sacrifice Triassic Egg: Choose one - You may put a creature card from your hand onto the battlefield; - ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, + ability = new ConditionalActivatedAbility( new PutCardFromHandOntoBattlefieldEffect(StaticFilters.FILTER_CARD_CREATURE_A), - new SacrificeSourceCost(), - new SourceHasCounterCondition(CounterType.HATCHLING, 2, Integer.MAX_VALUE)); + new SacrificeSourceCost(), condition + ).hideCondition(); + ability.getModes().setChooseText("Choose one. Activate only if there are two or more hatchling counters on {this}."); // or return target creature card from your graveyard to the battlefield. Activate this ability only if two or more hatchling counters are on Triassic Egg. - Mode mode = new Mode(new ReturnFromGraveyardToBattlefieldTargetEffect()); - Target target = new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD); - mode.addTarget(target); - ability.addMode(mode); + ability.addMode(new Mode(new ReturnFromGraveyardToBattlefieldTargetEffect()) + .addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/TriumphantChomp.java b/Mage.Sets/src/mage/cards/t/TriumphantChomp.java index a64e670092c..4161ca74378 100644 --- a/Mage.Sets/src/mage/cards/t/TriumphantChomp.java +++ b/Mage.Sets/src/mage/cards/t/TriumphantChomp.java @@ -2,7 +2,7 @@ package mage.cards.t; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.MaximumDynamicValue; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.hint.ValueHint; @@ -23,7 +23,7 @@ public final class TriumphantChomp extends CardImpl { private static final FilterPermanent filter = new FilterControlledPermanent(SubType.DINOSAUR); private static final DynamicValue xValue = new MaximumDynamicValue( StaticValue.get(2), - new PermanentsOnBattlefieldCount(filter) + new GreatestAmongPermanentsValue(GreatestAmongPermanentsValue.Quality.Power, filter) ); public TriumphantChomp(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/t/TromellSeymoursButler.java b/Mage.Sets/src/mage/cards/t/TromellSeymoursButler.java index 53baa22c379..3194755eb18 100644 --- a/Mage.Sets/src/mage/cards/t/TromellSeymoursButler.java +++ b/Mage.Sets/src/mage/cards/t/TromellSeymoursButler.java @@ -21,7 +21,6 @@ import mage.constants.SubType; import mage.constants.SuperType; import mage.counters.CounterType; import mage.filter.FilterPermanent; -import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.permanent.EnteredThisTurnPredicate; import mage.filter.predicate.permanent.TokenPredicate; @@ -34,6 +33,12 @@ import java.util.UUID; */ public final class TromellSeymoursButler extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent("nontoken creature"); + + static { + filter.add(TokenPredicate.FALSE); + } + public TromellSeymoursButler(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); @@ -45,8 +50,7 @@ public final class TromellSeymoursButler extends CardImpl { // Each other nontoken creature you control enters with an additional +1/+1 counter on it. this.addAbility(new SimpleStaticAbility(new EntersWithCountersControlledEffect( - StaticFilters.FILTER_CONTROLLED_CREATURE_NON_TOKEN, - CounterType.P1P1.createInstance(), true + filter, CounterType.P1P1.createInstance(), true ))); // {1}, {T}: Proliferate X times, where X is the number of nontoken creatures you control that entered this turn. diff --git a/Mage.Sets/src/mage/cards/t/TrygonPredator.java b/Mage.Sets/src/mage/cards/t/TrygonPredator.java index c3d22b61ee4..40cff196c1c 100644 --- a/Mage.Sets/src/mage/cards/t/TrygonPredator.java +++ b/Mage.Sets/src/mage/cards/t/TrygonPredator.java @@ -12,7 +12,7 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -36,7 +36,7 @@ public final class TrygonPredator extends CardImpl { // Whenever Trygon Predator deals combat damage to a player, you may destroy target artifact or enchantment that player controls. Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new DestroyTargetEffect(), true, true); ability.addTarget(new TargetPermanent(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/t/TumbleweedRising.java b/Mage.Sets/src/mage/cards/t/TumbleweedRising.java index fa0239749b6..caacadd5cce 100644 --- a/Mage.Sets/src/mage/cards/t/TumbleweedRising.java +++ b/Mage.Sets/src/mage/cards/t/TumbleweedRising.java @@ -1,7 +1,7 @@ package mage.cards.t; import mage.abilities.Ability; -import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.PlotAbility; @@ -24,7 +24,7 @@ public final class TumbleweedRising extends CardImpl { // Create an X/X green Elemental creature token, where X is the greatest power among creatures you control. this.getSpellAbility().addEffect(new TumbleweedRisingEffect()); - this.getSpellAbility().addHint(GreatestPowerAmongControlledCreaturesValue.getHint()); + this.getSpellAbility().addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint()); // Plot {2}{G} this.addAbility(new PlotAbility("{2}{G}")); @@ -60,7 +60,7 @@ class TumbleweedRisingEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - int xvalue = GreatestPowerAmongControlledCreaturesValue.instance.calculate(game, source, this); + int xvalue = GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.calculate(game, source, this); return new CreateTokenEffect(new ElementalXXGreenToken(xvalue)).apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/t/TurfWar.java b/Mage.Sets/src/mage/cards/t/TurfWar.java index 629b0fe2f7c..79f4248f2a1 100644 --- a/Mage.Sets/src/mage/cards/t/TurfWar.java +++ b/Mage.Sets/src/mage/cards/t/TurfWar.java @@ -1,16 +1,17 @@ package mage.cards.t; -import java.util.UUID; - -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.filter.common.FilterLandPermanent; @@ -22,13 +23,15 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.players.PlayerList; -import mage.target.Target; +import mage.target.TargetPermanent; import mage.target.common.TargetLandPermanent; -import mage.target.targetadjustment.TargetAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; +import mage.target.targetpointer.EachTargetPointer; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author weirddan455 */ public final class TurfWar extends CardImpl { @@ -37,8 +40,12 @@ public final class TurfWar extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{R}"); // When Turf War enters the battlefield, for each player, put a contested counter on target land that player controls. - this.addAbility(new EntersBattlefieldTriggeredAbility(new TurfWarCounterEffect()) - .setTargetAdjuster(TurfWarAdjuster.instance)); + Ability ability = new EntersBattlefieldTriggeredAbility( + new AddCountersTargetEffect(CounterType.CONTESTED.createInstance()).setTargetPointer(new EachTargetPointer()) + .setText("for each player, put a contested counter on target land that player controls")); + ability.addTarget(new TargetLandPermanent()); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, false)); + this.addAbility(ability); // Whenever a creature deals combat damage to a player, if that player controls one or more lands with contested counters on them, that creature's controller gains control of one of those lands of their choice and untaps it. this.addAbility(new TurfWarTriggeredAbility()); @@ -54,62 +61,6 @@ public final class TurfWar extends CardImpl { } } -class TurfWarCounterEffect extends OneShotEffect { - - TurfWarCounterEffect() { - super(Outcome.Benefit); - this.staticText = "for each player, put a contested counter on target land that player controls"; - } - - private TurfWarCounterEffect(final TurfWarCounterEffect effect) { - super(effect); - } - - @Override - public TurfWarCounterEffect copy() { - return new TurfWarCounterEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source); - if (controller == null || sourceObject == null) { - return false; - } - boolean success = false; - for (Target target : source.getTargets()) { - for (UUID uuid : target.getTargets()) { - Permanent permanent = game.getPermanent(uuid); - if (permanent != null && permanent.addCounters(CounterType.CONTESTED.createInstance(), source, game)) { - game.informPlayers(sourceObject.getLogName() + ": " + controller.getLogName() - + " puts a contested counter on " + permanent.getLogName()); - success = true; - } - } - } - return success; - } -} - -enum TurfWarAdjuster implements TargetAdjuster { - instance; - - @Override - public void adjustTargets(Ability ability, Game game) { - ability.getTargets().clear(); - for (UUID playerId : game.getState().getPlayersInRange(ability.getControllerId(), game)) { - Player player = game.getPlayer(playerId); - if (player == null) { - continue; - } - FilterLandPermanent filter = new FilterLandPermanent("land controlled by " + player.getName()); - filter.add(new ControllerIdPredicate(playerId)); - ability.addTarget(new TargetLandPermanent(filter)); - } - } -} - class TurfWarTriggeredAbility extends TriggeredAbilityImpl { public TurfWarTriggeredAbility() { @@ -191,7 +142,7 @@ class TurfWarControlEffect extends OneShotEffect { FilterLandPermanent filter = new FilterLandPermanent("land with a contested counter controlled by " + damagedPlayer.getName()); filter.add(new ControllerIdPredicate(damagedPlayer.getId())); filter.add(TurfWarPredicate.instance); - TargetLandPermanent target = new TargetLandPermanent(1, 1, filter, true); + TargetPermanent target = new TargetPermanent(1, 1, filter, true); if (!target.canChoose(creatureController.getId(), source, game)) { return false; } diff --git a/Mage.Sets/src/mage/cards/t/TurnToDust.java b/Mage.Sets/src/mage/cards/t/TurnToDust.java index 1fe7e7014db..760d0e10f1c 100644 --- a/Mage.Sets/src/mage/cards/t/TurnToDust.java +++ b/Mage.Sets/src/mage/cards/t/TurnToDust.java @@ -1,34 +1,27 @@ package mage.cards.t; -import java.util.UUID; import mage.Mana; -import mage.abilities.effects.mana.BasicManaEffect; import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.mana.BasicManaEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; -import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author Loki */ public final class TurnToDust extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("Equipment"); - - static { - filter.add(SubType.EQUIPMENT.getPredicate()); - } - public TurnToDust(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{G}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{G}"); this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_EQUIPMENT)); this.getSpellAbility().addEffect(new BasicManaEffect(Mana.GreenMana(1))); } diff --git a/Mage.Sets/src/mage/cards/t/TuyaBearclaw.java b/Mage.Sets/src/mage/cards/t/TuyaBearclaw.java index 1ba1baa17ab..906182f308d 100644 --- a/Mage.Sets/src/mage/cards/t/TuyaBearclaw.java +++ b/Mage.Sets/src/mage/cards/t/TuyaBearclaw.java @@ -1,11 +1,8 @@ package mage.cards.t; import mage.MageInt; -import mage.MageObject; -import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -13,10 +10,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.constants.SuperType; -import mage.filter.StaticFilters; -import mage.game.Game; -import java.util.Objects; import java.util.UUID; /** @@ -35,10 +29,10 @@ public final class TuyaBearclaw extends CardImpl { // Whenever Tuya Bearclaw attacks, it gets +X/+X until end of turn, where X is the greatest power among other creatures you control. this.addAbility(new AttacksTriggeredAbility(new BoostSourceEffect( - TuyaBearclawValue.instance, - TuyaBearclawValue.instance, + GreatestAmongPermanentsValue.POWER_OTHER_CONTROLLED_CREATURES, + GreatestAmongPermanentsValue.POWER_OTHER_CONTROLLED_CREATURES, Duration.EndOfTurn - ), false)); + ), false).addHint(GreatestAmongPermanentsValue.POWER_OTHER_CONTROLLED_CREATURES.getHint())); } private TuyaBearclaw(final TuyaBearclaw card) { @@ -49,37 +43,4 @@ public final class TuyaBearclaw extends CardImpl { public TuyaBearclaw copy() { return new TuyaBearclaw(this); } -} - -enum TuyaBearclawValue implements DynamicValue { - instance; - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - return game.getBattlefield() - .getActivePermanents( - StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE, - sourceAbility.getControllerId(), sourceAbility, game - ).stream() - .filter(Objects::nonNull) - .map(MageObject::getPower) - .mapToInt(MageInt::getValue) - .max() - .orElse(0); - } - - @Override - public TuyaBearclawValue copy() { - return instance; - } - - @Override - public String getMessage() { - return "the greatest power among other creatures you control"; - } - - @Override - public String toString() { - return "X"; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/t/TwoHeadedGiant.java b/Mage.Sets/src/mage/cards/t/TwoHeadedGiant.java index fa65ee2a703..d7dbb6eb88a 100644 --- a/Mage.Sets/src/mage/cards/t/TwoHeadedGiant.java +++ b/Mage.Sets/src/mage/cards/t/TwoHeadedGiant.java @@ -1,7 +1,6 @@ package mage.cards.t; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; @@ -9,17 +8,19 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.keyword.DoubleStrikeAbility; import mage.abilities.keyword.MenaceAbility; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.constants.SubType; import mage.game.Game; import mage.players.Player; +import java.util.List; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class TwoHeadedGiant extends CardImpl { @@ -69,14 +70,11 @@ class TwoHeadedGiantEffect extends OneShotEffect { if (player == null) { return false; } - boolean head1 = player.flipCoin(source, game, false); - boolean head2 = player.flipCoin(source, game, false); - if (head1 == head2) { - if (head1) { - game.addEffect(new GainAbilitySourceEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn), source); - } else { - game.addEffect(new GainAbilitySourceEffect(new MenaceAbility(), Duration.EndOfTurn), source); - } + List flips = player.flipCoins(source, game, 2, false); + if (flips.get(0) == flips.get(1)) { + game.addEffect(new GainAbilitySourceEffect( + flips.get(0) ? DoubleStrikeAbility.getInstance() : new MenaceAbility(), Duration.EndOfTurn + ), source); } return true; } diff --git a/Mage.Sets/src/mage/cards/t/TymaretCallsTheDead.java b/Mage.Sets/src/mage/cards/t/TymaretCallsTheDead.java index 6226f9e23aa..083bc824aef 100644 --- a/Mage.Sets/src/mage/cards/t/TymaretCallsTheDead.java +++ b/Mage.Sets/src/mage/cards/t/TymaretCallsTheDead.java @@ -2,15 +2,22 @@ package mage.cards.t; import mage.abilities.Ability; import mage.abilities.common.SagaAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.MillCardsControllerEffect; +import mage.abilities.effects.keyword.ScryEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterCard; import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.permanent.token.ZombieToken; @@ -25,6 +32,11 @@ import java.util.UUID; */ public final class TymaretCallsTheDead extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent(SubType.ZOMBIE, "Zombies you control"); + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, null); + private static final Hint hint = new ValueHint("Number of Rats you control", xValue); + public TymaretCallsTheDead(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); @@ -39,7 +51,10 @@ public final class TymaretCallsTheDead extends CardImpl { ); // III — You gain X life and scry X, where X is the number of Zombies you control. - sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new TymaretCallsTheDeadLastEffect()); + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, + new GainLifeEffect(xValue).setText("You gain X life"), + new ScryEffect(xValue).concatBy("and")); + sagaAbility.addHint(hint); this.addAbility(sagaAbility); } @@ -101,38 +116,4 @@ class TymaretCallsTheDeadFirstEffect extends OneShotEffect { return player.moveCards(game.getCard(target.getFirstTarget()), Zone.EXILED, source, game) && tokenEffect.apply(game, source); } -} - -class TymaretCallsTheDeadLastEffect extends OneShotEffect { - - private static final FilterPermanent filter = new FilterPermanent(SubType.ZOMBIE, ""); - - TymaretCallsTheDeadLastEffect() { - super(Outcome.Benefit); - staticText = "You gain X life and scry X, where X is the number of Zombies you control."; - } - - private TymaretCallsTheDeadLastEffect(final TymaretCallsTheDeadLastEffect effect) { - super(effect); - } - - @Override - public TymaretCallsTheDeadLastEffect copy() { - return new TymaretCallsTheDeadLastEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player == null) { - return false; - } - int zombieCount = game.getBattlefield().countAll(filter, source.getControllerId(), game); - if (zombieCount <= 0) { - return true; - } - player.gainLife(zombieCount, game, source); - player.scry(zombieCount, source, game); - return true; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/t/TyvarThePummeler.java b/Mage.Sets/src/mage/cards/t/TyvarThePummeler.java index 0c8e8b9f6f0..1ab5528b2cc 100644 --- a/Mage.Sets/src/mage/cards/t/TyvarThePummeler.java +++ b/Mage.Sets/src/mage/cards/t/TyvarThePummeler.java @@ -5,7 +5,7 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapTargetCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.TapSourceEffect; import mage.abilities.effects.common.continuous.BoostControlledEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; @@ -56,9 +56,10 @@ public final class TyvarThePummeler extends CardImpl { // {3}{G}{G}: Creatures you control get +X/+X until end of turn, where X is the greatest power among creatures you control. this.addAbility(new SimpleActivatedAbility(new BoostControlledEffect( - GreatestPowerAmongControlledCreaturesValue.instance, - GreatestPowerAmongControlledCreaturesValue.instance, Duration.EndOfTurn - ), new ManaCostsImpl<>("{3}{G}{G}")).addHint(GreatestPowerAmongControlledCreaturesValue.getHint())); + GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES, + GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES, + Duration.EndOfTurn + ), new ManaCostsImpl<>("{3}{G}{G}")).addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint())); } private TyvarThePummeler(final TyvarThePummeler card) { diff --git a/Mage.Sets/src/mage/cards/u/UginsInsight.java b/Mage.Sets/src/mage/cards/u/UginsInsight.java index 1458669d91b..010080317b1 100644 --- a/Mage.Sets/src/mage/cards/u/UginsInsight.java +++ b/Mage.Sets/src/mage/cards/u/UginsInsight.java @@ -1,28 +1,29 @@ package mage.cards.u; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.dynamicvalue.common.HighestManaValueCount; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.keyword.ScryEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.game.Game; -import mage.players.Player; + +import java.util.UUID; /** - * * @author fireshoes */ public final class UginsInsight extends CardImpl { public UginsInsight(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{U}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}{U}"); - // Scry X, where X is the highest converted mana cost among permanents you control, then draw three cards. - this.getSpellAbility().addEffect(new UginsInsightEffect()); + // Scry X, where X is the greatest mana value among permanents you control, then draw three cards. + this.getSpellAbility().addEffect( + new ScryEffect(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_PERMANENTS) + ); + this.getSpellAbility().addHint(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_PERMANENTS.getHint()); + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(3).concatBy(", then")); } private UginsInsight(final UginsInsight card) { @@ -33,35 +34,4 @@ public final class UginsInsight extends CardImpl { public UginsInsight copy() { return new UginsInsight(this); } -} - -class UginsInsightEffect extends OneShotEffect { - - UginsInsightEffect() { - super(Outcome.DrawCard); - this.staticText = "Scry X, where X is the highest mana value among permanents you control, then draw three cards"; - } - - private UginsInsightEffect(final UginsInsightEffect effect) { - super(effect); - } - - @Override - public UginsInsightEffect copy() { - return new UginsInsightEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - int highCMC = new HighestManaValueCount().calculate(game, source, this); - if (highCMC > 0) { - controller.scry(highCMC, source, game); - } - controller.drawCards(3, source, game); - return true; - } - return false; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/u/Ultima.java b/Mage.Sets/src/mage/cards/u/Ultima.java new file mode 100644 index 00000000000..9430e643c88 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/Ultima.java @@ -0,0 +1,43 @@ +package mage.cards.u; + +import mage.abilities.effects.common.DestroyAllEffect; +import mage.abilities.effects.common.EndTurnEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Ultima extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("artifacts and creatures"); + + static { + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.CREATURE.getPredicate() + )); + } + + public Ultima(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{W}{W}"); + + // Destroy all artifacts and creatures. End the turn. + this.getSpellAbility().addEffect(new DestroyAllEffect(filter)); + this.getSpellAbility().addEffect(new EndTurnEffect()); + } + + private Ultima(final Ultima card) { + super(card); + } + + @Override + public Ultima copy() { + return new Ultima(this); + } +} diff --git a/Mage.Sets/src/mage/cards/u/UltimaWeapon.java b/Mage.Sets/src/mage/cards/u/UltimaWeapon.java index 380e021f546..5e6da88ac2e 100644 --- a/Mage.Sets/src/mage/cards/u/UltimaWeapon.java +++ b/Mage.Sets/src/mage/cards/u/UltimaWeapon.java @@ -1,7 +1,7 @@ package mage.cards.u; import mage.abilities.Ability; -import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.AttacksAttachedTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.continuous.BoostEquippedEffect; @@ -27,7 +27,7 @@ public final class UltimaWeapon extends CardImpl { this.subtype.add(SubType.EQUIPMENT); // Whenever equipped creature attacks, destroy target creature an opponent controls. - Ability ability = new AttacksTriggeredAbility(new DestroyTargetEffect()); + Ability ability = new AttacksAttachedTriggeredAbility(new DestroyTargetEffect()); ability.addTarget(new TargetOpponentsCreaturePermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/u/UltimateMagicMeteor.java b/Mage.Sets/src/mage/cards/u/UltimateMagicMeteor.java new file mode 100644 index 00000000000..25baff669e7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UltimateMagicMeteor.java @@ -0,0 +1,117 @@ +package mage.cards.u; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageAllEffect; +import mage.abilities.keyword.ForetellAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.TargetPermanent; + +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UltimateMagicMeteor extends CardImpl { + + public UltimateMagicMeteor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{R}"); + + // Ultimate Magic: Meteor deals 7 damage to each creature. If this spell was cast from exile, for each opponent, choose an artifact or land that player controls. Destroy the chosen permanents. + this.getSpellAbility().addEffect(new DamageAllEffect(7, StaticFilters.FILTER_PERMANENT_CREATURE)); + this.getSpellAbility().addEffect(new UltimateMagicMeteorEffect()); + + // Foretell {5}{R} + this.addAbility(new ForetellAbility(this, "{5}{R}")); + } + + private UltimateMagicMeteor(final UltimateMagicMeteor card) { + super(card); + } + + @Override + public UltimateMagicMeteor copy() { + return new UltimateMagicMeteor(this); + } +} + +class UltimateMagicMeteorEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterPermanent("artifact or land"); + + static { + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.LAND.getPredicate() + )); + } + + UltimateMagicMeteorEffect() { + super(Outcome.Benefit); + staticText = "If this spell was cast from exile, for each opponent, " + + "choose an artifact or land that player controls. Destroy the chosen permanents"; + } + + private UltimateMagicMeteorEffect(final UltimateMagicMeteorEffect effect) { + super(effect); + } + + @Override + public UltimateMagicMeteorEffect copy() { + return new UltimateMagicMeteorEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (!Optional + .ofNullable(source) + .map(Ability::getSourceId) + .map(game::getSpell) + .map(Spell::getFromZone) + .map(Zone.EXILED::match) + .orElse(false)) { + return false; + } + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Set permanents = new HashSet<>(); + for (UUID playerId : game.getOpponents(source.getControllerId())) { + FilterPermanent filter = this.filter.copy(); + Optional.ofNullable(playerId) + .map(game::getPlayer) + .map(Player::getName) + .ifPresent(s -> filter.setMessage("artifact or land controlled by " + s)); + filter.add(new ControllerIdPredicate(playerId)); + if (!game.getBattlefield().contains(filter, source.getControllerId(), source, game, 1)) { + continue; + } + TargetPermanent target = new TargetPermanent(filter); + target.withNotTarget(true); + player.choose(outcome, target, source, game); + permanents.add(game.getPermanent(target.getFirstTarget())); + } + for (Permanent permanent : permanents) { + if (permanent != null) { + permanent.destroy(source, game); + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/u/UltraMagnusTactician.java b/Mage.Sets/src/mage/cards/u/UltraMagnusTactician.java index 34637d112ad..88775f8e8b6 100644 --- a/Mage.Sets/src/mage/cards/u/UltraMagnusTactician.java +++ b/Mage.Sets/src/mage/cards/u/UltraMagnusTactician.java @@ -20,6 +20,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetCardInHand; +import mage.util.CardUtil; import java.util.UUID; @@ -97,7 +98,7 @@ class UltraMagnusTacticianEffect extends OneShotEffect { card, Zone.BATTLEFIELD, source, game, true, false, false, null ); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return true; } diff --git a/Mage.Sets/src/mage/cards/u/UltrosObnoxiousOctopus.java b/Mage.Sets/src/mage/cards/u/UltrosObnoxiousOctopus.java new file mode 100644 index 00000000000..7d9c9ea0559 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UltrosObnoxiousOctopus.java @@ -0,0 +1,68 @@ +package mage.cards.u; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.FilterSpell; +import mage.filter.StaticFilters; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ManaSpentToCastPredicate; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UltrosObnoxiousOctopus extends CardImpl { + + private static final FilterSpell filter + = new FilterSpell("a noncreature spell, if at least eight mana was spent to cast it"); + + static { + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); + filter.add(new ManaSpentToCastPredicate(ComparisonType.MORE_THAN, 7)); + } + + public UltrosObnoxiousOctopus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.OCTOPUS); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Whenever you cast a noncreature spell, if at least four mana was spent to cast it, tap target creature an opponent controls and put a stun counter on it. + Ability ability = new SpellCastControllerTriggeredAbility( + new TapTargetEffect(), StaticFilters.FILTER_NONCREATURE_SPELL_FOUR_MANA_SPENT, false + ); + ability.addEffect(new AddCountersTargetEffect(CounterType.STUN.createInstance()) + .setText("and put a stun counter on it")); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + this.addAbility(ability); + + // Whenever you cast a noncreature spell, if at least eight mana was spent to cast it, put eight +1/+1 counters on Ultros. + this.addAbility(new SpellCastControllerTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance(8)), filter, false + )); + } + + private UltrosObnoxiousOctopus(final UltrosObnoxiousOctopus card) { + super(card); + } + + @Override + public UltrosObnoxiousOctopus copy() { + return new UltrosObnoxiousOctopus(this); + } +} diff --git a/Mage.Sets/src/mage/cards/u/UmaroRagingYeti.java b/Mage.Sets/src/mage/cards/u/UmaroRagingYeti.java index 65802796d48..1fe45cf8bfd 100644 --- a/Mage.Sets/src/mage/cards/u/UmaroRagingYeti.java +++ b/Mage.Sets/src/mage/cards/u/UmaroRagingYeti.java @@ -42,7 +42,7 @@ public final class UmaroRagingYeti extends CardImpl { // * Other creatures you control get +3/+0 and gain trample until end of turn. Ability ability = new BeginningOfCombatTriggeredAbility(new BoostControlledEffect( 3, 0, Duration.EndOfTurn, true - )); + ).setText("other creatures you control get +3/+0")); ability.addEffect(new GainAbilityControlledEffect( TrampleAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURE, true diff --git a/Mage.Sets/src/mage/cards/u/UncontrolledInfestation.java b/Mage.Sets/src/mage/cards/u/UncontrolledInfestation.java index 738326beb54..72ef6de0c17 100644 --- a/Mage.Sets/src/mage/cards/u/UncontrolledInfestation.java +++ b/Mage.Sets/src/mage/cards/u/UncontrolledInfestation.java @@ -31,7 +31,8 @@ public final class UncontrolledInfestation extends CardImpl { Ability ability = new EnchantAbility(auraTarget); this.addAbility(ability); // When enchanted land becomes tapped, destroy it. - this.addAbility(new BecomesTappedAttachedTriggeredAbility(new DestroyAttachedToEffect("it"), "enchanted land")); + this.addAbility(new BecomesTappedAttachedTriggeredAbility(new DestroyAttachedToEffect("it"), "enchanted land") + .setTriggerPhrase("When enchanted land becomes tapped, ")); } private UncontrolledInfestation(final UncontrolledInfestation card) { diff --git a/Mage.Sets/src/mage/cards/u/UnexpectedRequest.java b/Mage.Sets/src/mage/cards/u/UnexpectedRequest.java new file mode 100644 index 00000000000..b0a04004b7d --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UnexpectedRequest.java @@ -0,0 +1,119 @@ +package mage.cards.u; + +import mage.abilities.Ability; +import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.continuous.GainControlTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UnexpectedRequest extends CardImpl { + + public UnexpectedRequest(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}"); + + // Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. You may attach an Equipment you control to that creature. If you do, unattach it at the beginning of the next end step. + this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.EndOfTurn)); + this.getSpellAbility().addEffect(new UntapTargetEffect("Untap that creature")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + HasteAbility.getInstance(), Duration.EndOfTurn + ).setText("It gains haste until end of turn.")); + this.getSpellAbility().addEffect(new UnexpectedRequestAttachEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private UnexpectedRequest(final UnexpectedRequest card) { + super(card); + } + + @Override + public UnexpectedRequest copy() { + return new UnexpectedRequest(this); + } +} + +class UnexpectedRequestAttachEffect extends OneShotEffect { + + UnexpectedRequestAttachEffect() { + super(Outcome.Benefit); + staticText = "You may attach an Equipment you control to that creature. " + + "If you do, unattach it at the beginning of the next end step"; + } + + private UnexpectedRequestAttachEffect(final UnexpectedRequestAttachEffect effect) { + super(effect); + } + + @Override + public UnexpectedRequestAttachEffect copy() { + return new UnexpectedRequestAttachEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (player == null || permanent == null || !game.getBattlefield().contains( + StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT, + source.getControllerId(), source, game, 1 + )) { + return false; + } + TargetPermanent target = new TargetPermanent( + 0, 1, StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT, true + ); + player.choose(outcome, target, source, game); + Permanent equipment = game.getPermanent(target.getFirstTarget()); + if (equipment == null || !permanent.addAttachment(equipment.getId(), source, game)) { + return false; + } + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility( + new UnexpectedRequestUnattachEffect(equipment, game) + ), source); + return true; + } +} + +class UnexpectedRequestUnattachEffect extends OneShotEffect { + + UnexpectedRequestUnattachEffect(Permanent permanent, Game game) { + super(Outcome.Benefit); + staticText = "unattach that equipment"; + this.setTargetPointer(new FixedTarget(permanent, game)); + } + + private UnexpectedRequestUnattachEffect(final UnexpectedRequestUnattachEffect effect) { + super(effect); + } + + @Override + public UnexpectedRequestUnattachEffect copy() { + return new UnexpectedRequestUnattachEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Optional.ofNullable(game.getPermanent(getTargetPointer().getFirst(game, source))) + .ifPresent(permanent -> permanent.unattach(game)); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/u/Unforge.java b/Mage.Sets/src/mage/cards/u/Unforge.java index f3dcd5e4d40..444f57b7710 100644 --- a/Mage.Sets/src/mage/cards/u/Unforge.java +++ b/Mage.Sets/src/mage/cards/u/Unforge.java @@ -1,6 +1,5 @@ package mage.cards.u; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect; @@ -8,29 +7,23 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.SubType; -import mage.filter.common.FilterArtifactPermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.target.common.TargetArtifactPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author wetterlicht */ public final class Unforge extends CardImpl { - - private static final FilterArtifactPermanent filter = new FilterArtifactPermanent("Equipment"); - - static { - filter.add(SubType.EQUIPMENT.getPredicate()); - } public Unforge(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); // Destroy target Equipment. If that Equipment was attached to a creature, Unforge deals 2 damage to that creature. - getSpellAbility().addTarget(new TargetArtifactPermanent(filter)); + getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_EQUIPMENT)); getSpellAbility().addEffect(new DestroyTargetEffect()); getSpellAbility().addEffect(new UnforgeEffect()); } @@ -43,17 +36,17 @@ public final class Unforge extends CardImpl { public Unforge copy() { return new Unforge(this); } - + } -class UnforgeEffect extends OneShotEffect{ - - UnforgeEffect(){ - super(Outcome.Damage); - staticText = "If that Equipment was attached to a creature, {this} deals 2 damage to that creature."; +class UnforgeEffect extends OneShotEffect { + + UnforgeEffect() { + super(Outcome.Damage); + staticText = "If that Equipment was attached to a creature, {this} deals 2 damage to that creature."; } - - private UnforgeEffect(final UnforgeEffect effect){ + + private UnforgeEffect(final UnforgeEffect effect) { super(effect); } @@ -63,8 +56,8 @@ class UnforgeEffect extends OneShotEffect{ if (equipment != null) { Permanent creature = game.getPermanent(equipment.getAttachedTo()); if (creature != null) { - creature.damage(2, source.getSourceId(), source, game, false, true); - return true; + creature.damage(2, source.getSourceId(), source, game, false, true); + return true; } } return false; @@ -74,6 +67,6 @@ class UnforgeEffect extends OneShotEffect{ public UnforgeEffect copy() { return new UnforgeEffect(this); } - + } diff --git a/Mage.Sets/src/mage/cards/u/UnsparingBoltcaster.java b/Mage.Sets/src/mage/cards/u/UnsparingBoltcaster.java index b9bfc685171..ac77afc870b 100644 --- a/Mage.Sets/src/mage/cards/u/UnsparingBoltcaster.java +++ b/Mage.Sets/src/mage/cards/u/UnsparingBoltcaster.java @@ -8,9 +8,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterOpponentsCreaturePermanent; -import mage.filter.predicate.permanent.WasDealtDamageThisTurnPredicate; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import java.util.UUID; @@ -20,13 +18,6 @@ import java.util.UUID; */ public final class UnsparingBoltcaster extends CardImpl { - private static final FilterPermanent filter - = new FilterOpponentsCreaturePermanent("creature an opponent controls that was dealt damage this turn"); - - static { - filter.add(WasDealtDamageThisTurnPredicate.instance); - } - public UnsparingBoltcaster(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); @@ -37,7 +28,7 @@ public final class UnsparingBoltcaster extends CardImpl { // When this creature enters, it deals 5 damage to target creature an opponent controls that was dealt damage this turn. Ability ability = new EntersBattlefieldTriggeredAbility(new DamageTargetEffect(5)); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_OPPONENTS_CREATURE_DAMAGED_THIS_TURN)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/u/UnyieldingGatekeeper.java b/Mage.Sets/src/mage/cards/u/UnyieldingGatekeeper.java index 6cff9a562a6..44d2bd40835 100644 --- a/Mage.Sets/src/mage/cards/u/UnyieldingGatekeeper.java +++ b/Mage.Sets/src/mage/cards/u/UnyieldingGatekeeper.java @@ -1,35 +1,35 @@ package mage.cards.u; -import java.util.UUID; - import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenControllerTargetEffect; import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.effects.common.ExileThenReturnTargetEffect; -import mage.constants.Outcome; -import mage.constants.PutCards; -import mage.constants.SubType; -import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.keyword.DisguiseAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.PutCards; +import mage.constants.SubType; import mage.filter.common.FilterNonlandPermanent; import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.DetectiveToken; -import mage.target.common.TargetNonlandPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** * @author Cguy7777 */ public final class UnyieldingGatekeeper extends CardImpl { - private static final FilterNonlandPermanent filter = new FilterNonlandPermanent(); + private static final FilterNonlandPermanent filter = new FilterNonlandPermanent("another target nonland permanent"); static { filter.add(AnotherPredicate.instance); @@ -50,7 +50,7 @@ public final class UnyieldingGatekeeper extends CardImpl { // If you controlled it, return it to the battlefield tapped. // Otherwise, its controller creates a 2/2 white and blue Detective creature token. Ability ability = new TurnedFaceUpSourceTriggeredAbility(new UnyieldingGatekeeperEffect()); - ability.addTarget(new TargetNonlandPermanent(filter)); + ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/u/UriangerAugurelt.java b/Mage.Sets/src/mage/cards/u/UriangerAugurelt.java new file mode 100644 index 00000000000..a72e0d4c91d --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UriangerAugurelt.java @@ -0,0 +1,155 @@ +package mage.cards.u; + +import mage.MageIdentifier; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.PlayLandOrCastSpellFromExileTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.asthought.MayLookAtTargetCardEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Controllable; +import mage.game.Game; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UriangerAugurelt extends CardImpl { + + public UriangerAugurelt(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.ELF); + this.subtype.add(SubType.ADVISOR); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Whenever you play a land from exile or cast a spell from exile, you gain 2 life. + this.addAbility(new PlayLandOrCastSpellFromExileTriggeredAbility(new GainLifeEffect(2))); + + // Draw Arcanum -- {T}: Look at the top card of your library. You may exile it face down. + this.addAbility(new SimpleActivatedAbility( + new UriangerAugureltExileEffect(), new TapSourceCost() + ).withFlavorWord("Draw Arcanum")); + + // Play Arcanum -- {T}: Until end of turn, you may play cards exiled with Urianger Augurelt. Spells you cast this way cost {2} less to cast. + this.addAbility(new SimpleActivatedAbility( + new UriangerAugureltPlayEffect(), new TapSourceCost() + ).setIdentifier(MageIdentifier.UriangerAugureltAlternateCast).withFlavorWord("Play Arcanum")); + } + + private UriangerAugurelt(final UriangerAugurelt card) { + super(card); + } + + @Override + public UriangerAugurelt copy() { + return new UriangerAugurelt(this); + } +} + +class UriangerAugureltExileEffect extends OneShotEffect { + + UriangerAugureltExileEffect() { + super(Outcome.Benefit); + staticText = "look at the top card of your library. You may exile it face down"; + } + + private UriangerAugureltExileEffect(final UriangerAugureltExileEffect effect) { + super(effect); + } + + @Override + public UriangerAugureltExileEffect copy() { + return new UriangerAugureltExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Card card = player.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + player.lookAtCards("Top card of library", card, game); + if (!player.chooseUse(Outcome.DrawCard, "Exile " + card.getLogName() + " face down?", source, game)) { + return false; + } + player.moveCardsToExile( + card, source, game, false, + CardUtil.getExileZoneId(game, source), + CardUtil.getSourceName(game, source) + ); + card.setFaceDown(true, game); + game.addEffect(new MayLookAtTargetCardEffect(source.getControllerId()) + .setTargetPointer(new FixedTarget(card, game)), source); + return true; + } +} + +class UriangerAugureltPlayEffect extends AsThoughEffectImpl { + + UriangerAugureltPlayEffect() { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, + Duration.EndOfTurn, Outcome.AIDontUseIt); + staticText = "until end of turn, you may play cards exiled " + + "with {this}. Spells you cast this way cost {2} less to cast"; + } + + private UriangerAugureltPlayEffect(final UriangerAugureltPlayEffect effect) { + super(effect); + } + + @Override + public UriangerAugureltPlayEffect copy() { + return new UriangerAugureltPlayEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + if (!source.isControlledBy(affectedControllerId)) { + return false; + } + Card card = game.getCard(objectId); + if (card == null || !Optional + .ofNullable(CardUtil.getExileZoneId(game, source)) + .map(game.getState().getExile()::getExileZone) + .filter(e -> e.contains(objectId)) + .isPresent()) { + return false; + } + if (card.isLand(game)) { + return true; + } + // TODO: This should ideally apply the reduction while the spell is being cast because effects that increase the cost apply first + Optional.ofNullable(source) + .map(Controllable::getControllerId) + .map(game::getPlayer) + .ifPresent(player -> player.setCastSourceIdWithAlternateMana( + card.getId(), CardUtil.reduceCost(card.getManaCost(), 2), + null, MageIdentifier.UriangerAugureltAlternateCast + )); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/u/UrzaChiefArtificer.java b/Mage.Sets/src/mage/cards/u/UrzaChiefArtificer.java index 471d5c8fba2..7962bb9fd6d 100644 --- a/Mage.Sets/src/mage/cards/u/UrzaChiefArtificer.java +++ b/Mage.Sets/src/mage/cards/u/UrzaChiefArtificer.java @@ -1,15 +1,12 @@ package mage.cards.u; import mage.MageInt; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.common.AffinityEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.AffinityAbility; import mage.abilities.keyword.MenaceAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -27,14 +24,6 @@ public final class UrzaChiefArtificer extends CardImpl { private static final FilterControlledPermanent filter = new FilterControlledCreaturePermanent("artifact creatures"); - static { - filter.add(CardType.ARTIFACT.getPredicate()); - } - - private static final Hint hint = new ValueHint( - "Artifact creatures you control", new PermanentsOnBattlefieldCount(filter) - ); - public UrzaChiefArtificer(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{U}{B}"); @@ -45,7 +34,7 @@ public final class UrzaChiefArtificer extends CardImpl { this.toughness = new MageInt(5); // Affinity for artifact creatures - this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filter)).addHint(hint)); + this.addAbility(new AffinityAbility(AffinityType.ARTIFACT_CREATURES)); // Artifact creatures you control have menace. this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( @@ -53,9 +42,7 @@ public final class UrzaChiefArtificer extends CardImpl { )); // At the beginning of your end step, create a 0/0 colorless Construct artifact creature token with "This creature gets +1/+1 for each artifact you control." - this.addAbility(new BeginningOfEndStepTriggeredAbility( - new CreateTokenEffect(new KarnConstructToken()) - )); + this.addAbility(new BeginningOfEndStepTriggeredAbility(new CreateTokenEffect(new KarnConstructToken()))); } private UrzaChiefArtificer(final UrzaChiefArtificer card) { diff --git a/Mage.Sets/src/mage/cards/u/UrzasTome.java b/Mage.Sets/src/mage/cards/u/UrzasTome.java index 7d33e6178d9..919ae0dd24a 100644 --- a/Mage.Sets/src/mage/cards/u/UrzasTome.java +++ b/Mage.Sets/src/mage/cards/u/UrzasTome.java @@ -12,8 +12,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.common.FilterHistoricCard; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.HistoricPredicate; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; @@ -25,11 +25,17 @@ import java.util.UUID; */ public final class UrzasTome extends CardImpl { + private static final FilterCard filter = new FilterCard("historic card"); + static { + filter.add(HistoricPredicate.instance); + } + public UrzasTome(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); // {3}, {T}: Draw a card. Then discard a card unless you exile a historic card from your graveyard. - Ability ability = new SimpleActivatedAbility(new UrzasTomeEffect(), new GenericManaCost(3)); + Ability ability = new SimpleActivatedAbility(new DrawCardSourceControllerEffect(1), new GenericManaCost(3)); + ability.addEffect(new UrzasTomeEffect()); ability.addCost(new TapSourceCost()); this.addAbility(ability); } @@ -46,9 +52,14 @@ public final class UrzasTome extends CardImpl { class UrzasTomeEffect extends OneShotEffect { + private static final FilterCard filter = new FilterCard("a historic card"); + static { + filter.add(HistoricPredicate.instance); + } + UrzasTomeEffect() { super(Outcome.Discard); - staticText = "Draw a card. Then discard a card unless you exile a historic card from your graveyard"; + staticText = "Then discard a card unless you exile a historic card from your graveyard"; } private UrzasTomeEffect(final UrzasTomeEffect effect) { @@ -63,20 +74,17 @@ class UrzasTomeEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - new DrawCardSourceControllerEffect(1).apply(game, source); - if (controller != null - && controller.chooseUse(Outcome.Exile, "Exile a historic card from your graveyard?", source, game)) { - Cost cost = new ExileFromGraveCost(new TargetCardInYourGraveyard(new FilterHistoricCard())); - if (cost.canPay(source, source, controller.getId(), game)) { - if (cost.pay(source, game, source, controller.getId(), false, null)) { - return true; - } + if (controller == null) { + return false; + } + if (controller.chooseUse(Outcome.Exile, "Exile a historic card from your graveyard?", source, game)) { + Cost cost = new ExileFromGraveCost(new TargetCardInYourGraveyard(filter)); + if (cost.canPay(source, source, controller.getId(), game) + && cost.pay(source, game, source, controller.getId(), false, null)) { + return true; } } - if (controller != null) { - controller.discard(1, false, false, source, game); - return true; - } - return false; + controller.discard(1, false, false, source, game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/u/UtopiaSprawl.java b/Mage.Sets/src/mage/cards/u/UtopiaSprawl.java index 0739c77e012..4b4edce1ec0 100644 --- a/Mage.Sets/src/mage/cards/u/UtopiaSprawl.java +++ b/Mage.Sets/src/mage/cards/u/UtopiaSprawl.java @@ -9,18 +9,17 @@ import mage.abilities.effects.common.ChooseColorEffect; import mage.abilities.effects.mana.ManaEffect; import mage.abilities.keyword.EnchantAbility; import mage.abilities.mana.EnchantedTappedTriggeredManaAbility; -import mage.abilities.mana.TriggeredManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.common.FilterLandPermanent; +import mage.constants.CardType; +import mage.constants.ColoredManaSymbol; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterPermanent; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; -import mage.target.common.TargetLandPermanent; import java.util.UUID; @@ -29,24 +28,23 @@ import java.util.UUID; */ public final class UtopiaSprawl extends CardImpl { - private static final FilterLandPermanent filter = new FilterLandPermanent(SubType.FOREST, "Forest"); + private static final FilterPermanent filter = new FilterPermanent(SubType.FOREST, "Forest"); public UtopiaSprawl(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{G}"); this.subtype.add(SubType.AURA); // Enchant Forest - TargetPermanent auraTarget = new TargetLandPermanent(filter); + TargetPermanent auraTarget = new TargetPermanent(filter); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.AddAbility)); - Ability ability = new EnchantAbility(auraTarget); - this.addAbility(ability); + this.addAbility(new EnchantAbility(auraTarget)); // As Utopia Sprawl enters the battlefield, choose a color. this.addAbility(new AsEntersBattlefieldAbility(new ChooseColorEffect(Outcome.Detriment))); // Whenever enchanted Forest is tapped for mana, its controller adds one mana of the chosen color. - this.addAbility(new EnchantedTappedTriggeredManaAbility(new UtopiaSprawlEffect(),"Forest")); + this.addAbility(new EnchantedTappedTriggeredManaAbility(new UtopiaSprawlEffect(), "Forest")); } private UtopiaSprawl(final UtopiaSprawl card) { @@ -58,9 +56,10 @@ public final class UtopiaSprawl extends CardImpl { return new UtopiaSprawl(this); } } + class UtopiaSprawlEffect extends ManaEffect { - UtopiaSprawlEffect() { + UtopiaSprawlEffect() { super(); staticText = "its controller adds an additional one mana of the chosen color"; } diff --git a/Mage.Sets/src/mage/cards/v/VaanStreetThief.java b/Mage.Sets/src/mage/cards/v/VaanStreetThief.java new file mode 100644 index 00000000000..720852f21b2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VaanStreetThief.java @@ -0,0 +1,109 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.OneOrMoreCombatDamagePlayerTriggeredAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.FilterSpell; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.permanent.token.TreasureToken; +import mage.players.Player; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VaanStreetThief extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent("Scouts, Pirates, and/or Rogues you control"); + private static final FilterSpell filter2 = new FilterSpell("a spell you don't own"); + + static { + filter.add(Predicates.or( + SubType.SCOUT.getPredicate(), + SubType.PIRATE.getPredicate(), + SubType.ROGUE.getPredicate() + )); + filter2.add(TargetController.NOT_YOU.getOwnerPredicate()); + } + + public VaanStreetThief(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SCOUT); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever one or more Scouts, Pirates, and/or Rogues you control deal combat damage to a player, exile the top card of that player's library. You may cast it. If you don't, create a Treasure token. + this.addAbility(new OneOrMoreCombatDamagePlayerTriggeredAbility( + new VaanStreetThiefEffect(), SetTargetPointer.PLAYER, filter, false + )); + + // Whenever you cast a spell you don't own, put a +1/+1 counter on each Scout, Pirate, and Rogue you control. + this.addAbility(new SpellCastControllerTriggeredAbility( + new AddCountersAllEffect(CounterType.P1P1.createInstance(), filter) + .setText("put a +1/+1 counter on each Scout, Pirate, and Rogue you control"), + filter2, false + )); + } + + private VaanStreetThief(final VaanStreetThief card) { + super(card); + } + + @Override + public VaanStreetThief copy() { + return new VaanStreetThief(this); + } +} + +class VaanStreetThiefEffect extends OneShotEffect { + + VaanStreetThiefEffect() { + super(Outcome.Benefit); + staticText = "exile the top card of that player's library. " + + "You may cast it. If you don't, create a Treasure token"; + } + + private VaanStreetThiefEffect(final VaanStreetThiefEffect effect) { + super(effect); + } + + @Override + public VaanStreetThiefEffect copy() { + return new VaanStreetThiefEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); + if (controller == null || player == null) { + return false; + } + Card card = player.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + controller.moveCards(card, Zone.EXILED, source, game); + if (!controller.chooseUse(Outcome.DrawCard, "Cast " + card.getIdName() + '?', source, game) + || !CardUtil.castSingle(controller, source, game, card)) { + new TreasureToken().putOntoBattlefield(1, game, source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/v/VadrokApexOfThunder.java b/Mage.Sets/src/mage/cards/v/VadrokApexOfThunder.java index 8983c563218..cfc26c54b96 100644 --- a/Mage.Sets/src/mage/cards/v/VadrokApexOfThunder.java +++ b/Mage.Sets/src/mage/cards/v/VadrokApexOfThunder.java @@ -11,7 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterCard; -import mage.filter.common.FilterNoncreatureCard; +import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInYourGraveyard; @@ -22,11 +22,12 @@ import java.util.UUID; */ public final class VadrokApexOfThunder extends CardImpl { - private static final FilterCard filter = new FilterNoncreatureCard( + private static final FilterCard filter = new FilterCard( "noncreature card with mana value 3 or less from your graveyard" ); static { + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } diff --git a/Mage.Sets/src/mage/cards/v/VaevictisAsmadiTheDire.java b/Mage.Sets/src/mage/cards/v/VaevictisAsmadiTheDire.java index b84030a6822..ca061a66904 100644 --- a/Mage.Sets/src/mage/cards/v/VaevictisAsmadiTheDire.java +++ b/Mage.Sets/src/mage/cards/v/VaevictisAsmadiTheDire.java @@ -1,30 +1,25 @@ package mage.cards.v; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.constants.SubType; -import mage.constants.SuperType; import mage.abilities.keyword.FlyingAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.cards.CardsImpl; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.constants.*; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; import mage.target.TargetPermanent; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; /** * @@ -45,7 +40,10 @@ public final class VaevictisAsmadiTheDire extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever Vaevictis Asmadi, the Dire attacks, for each player, choose target permanent that player controls. Those players sacrifice those permanents. Each player who sacrificed a permanent this way reveals the top card of their library, then puts it onto the battlefield if it's a permanent card. - this.addAbility(new VaevictisAsmadiTheDireTriggeredAbility()); + Ability ability = new AttacksTriggeredAbility(new VaevictisAsmadiTheDireEffect()); + ability.addTarget(new TargetPermanent()); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, false)); + this.addAbility(ability); } private VaevictisAsmadiTheDire(final VaevictisAsmadiTheDire card) { @@ -58,48 +56,6 @@ public final class VaevictisAsmadiTheDire extends CardImpl { } } -class VaevictisAsmadiTheDireTriggeredAbility extends TriggeredAbilityImpl { - - VaevictisAsmadiTheDireTriggeredAbility() { - super(Zone.BATTLEFIELD, new VaevictisAsmadiTheDireEffect(), false); - setTriggerPhrase("Whenever {this} attacks, "); - } - - private VaevictisAsmadiTheDireTriggeredAbility(final VaevictisAsmadiTheDireTriggeredAbility ability) { - super(ability); - } - - @Override - public VaevictisAsmadiTheDireTriggeredAbility copy() { - return new VaevictisAsmadiTheDireTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DECLARED_ATTACKERS; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (!game.getCombat().getAttackers().contains(this.getSourceId())) { - return false; - } - this.getTargets().clear(); - for (UUID playerId : game.getState().getPlayerList(this.getControllerId())) { - Player player = game.getPlayer(playerId); - if (player == null) { - continue; - } - FilterPermanent filter = new FilterPermanent("permanent controlled by " + player.getName()); - filter.add(new ControllerIdPredicate(playerId)); - TargetPermanent target = new TargetPermanent(filter); - this.addTarget(target); - } - return true; - } - -} - class VaevictisAsmadiTheDireEffect extends OneShotEffect { VaevictisAsmadiTheDireEffect() { diff --git a/Mage.Sets/src/mage/cards/v/ValleyFlamecaller.java b/Mage.Sets/src/mage/cards/v/ValleyFlamecaller.java index 93ab7796f03..f2d8c395f87 100644 --- a/Mage.Sets/src/mage/cards/v/ValleyFlamecaller.java +++ b/Mage.Sets/src/mage/cards/v/ValleyFlamecaller.java @@ -76,7 +76,7 @@ class ValleyFlamecallerEffect extends ReplacementEffectImpl { public boolean applies(GameEvent event, Ability source, Game game) { Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId()); return permanent != null - && permanent.isControlledBy(permanent.getControllerId()) + && permanent.isControlledBy(source.getControllerId()) && (permanent.hasSubtype(SubType.LIZARD, game) || permanent.hasSubtype(SubType.MOUSE, game) || permanent.hasSubtype(SubType.OTTER, game) diff --git a/Mage.Sets/src/mage/cards/v/ValleyFloodcaller.java b/Mage.Sets/src/mage/cards/v/ValleyFloodcaller.java index 093df79bfed..c29f61e1303 100644 --- a/Mage.Sets/src/mage/cards/v/ValleyFloodcaller.java +++ b/Mage.Sets/src/mage/cards/v/ValleyFloodcaller.java @@ -18,7 +18,6 @@ import mage.constants.TargetController; import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.common.FilterNoncreatureCard; import mage.filter.predicate.Predicates; /** @@ -27,12 +26,13 @@ import mage.filter.predicate.Predicates; */ public final class ValleyFloodcaller extends CardImpl { - private static final FilterCard nonCreatureFilter = new FilterNoncreatureCard("noncreature spells"); + private static final FilterCard nonCreatureFilter = new FilterCard("noncreature spells"); private static final FilterCreaturePermanent creatureFilter = new FilterCreaturePermanent("Birds, Frogs, Otters, and Rats"); static { + nonCreatureFilter.add(Predicates.not(CardType.CREATURE.getPredicate())); creatureFilter.add(TargetController.YOU.getControllerPredicate()); creatureFilter.add(Predicates.or( SubType.BIRD.getPredicate(), diff --git a/Mage.Sets/src/mage/cards/v/Valleymaker.java b/Mage.Sets/src/mage/cards/v/Valleymaker.java index a10e061391d..d1338338f0f 100644 --- a/Mage.Sets/src/mage/cards/v/Valleymaker.java +++ b/Mage.Sets/src/mage/cards/v/Valleymaker.java @@ -1,26 +1,29 @@ package mage.cards.v; -import java.util.UUID; import mage.MageInt; import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.mana.AddManaToManaPoolTargetControllerEffect; import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.mana.ManaEffect; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.players.Player; import mage.target.TargetPlayer; -import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** * * @author jeffwadsworth @@ -48,10 +51,9 @@ public final class Valleymaker extends CardImpl { this.addAbility(ability); // {tap}, Sacrifice a Forest: Choose a player. That player adds {G}{G}{G}. - Ability ability2 = new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaToManaPoolTargetControllerEffect(Mana.GreenMana(3), "chosen player") - .setText("choose a player. That player adds {G}{G}{G}"), new TapSourceCost()); + Ability ability2 = new SimpleManaAbility(Zone.BATTLEFIELD, new ValleymakerManaEffect() + .setText("That player adds {G}{G}{G}"), new TapSourceCost()); ability2.addCost(new SacrificeTargetCost(filter2)); - ability2.addTarget(new TargetPlayer(1, 1, true)); this.addAbility(ability2); } @@ -64,3 +66,37 @@ public final class Valleymaker extends CardImpl { return new Valleymaker(this); } } + +//Based on Spectral Searchlight +class ValleymakerManaEffect extends ManaEffect { + + ValleymakerManaEffect() { + super(); + this.staticText = "Choose a player. That player adds {G}{G}{G}."; + } + + private ValleymakerManaEffect(final ValleymakerManaEffect effect) { + super(effect); + } + + @Override + public Player getPlayer(Game game, Ability source) { + if (!game.inCheckPlayableState()) { + TargetPlayer target = new TargetPlayer(1, 1, true); + if (target.choose(Outcome.PutManaInPool, source.getControllerId(), source, game)) { + return game.getPlayer(target.getFirstTarget()); + } + } + return game.getPlayer(source.getControllerId()); // Count as controller's potential mana for card playability + } + + @Override + public Mana produceMana(Game game, Ability source) { + return Mana.GreenMana(3); + } + + @Override + public ValleymakerManaEffect copy() { + return new ValleymakerManaEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/Valor.java b/Mage.Sets/src/mage/cards/v/Valor.java index f14382a7c04..b1bd2301ab4 100644 --- a/Mage.Sets/src/mage/cards/v/Valor.java +++ b/Mage.Sets/src/mage/cards/v/Valor.java @@ -24,7 +24,7 @@ import mage.filter.common.FilterCreaturePermanent; */ public final class Valor extends CardImpl { - private static final String ruleText = "As long as Valor is in your graveyard and you control a Plains, creatures you control have first strike"; + private static final String ruleText = "As long as this card is in your graveyard and you control a Plains, creatures you control have first strike"; private static final FilterControlledPermanent filter = new FilterControlledPermanent("Plains"); diff --git a/Mage.Sets/src/mage/cards/v/VampireScrivener.java b/Mage.Sets/src/mage/cards/v/VampireScrivener.java index 582fd6b96a2..71ab807e70a 100644 --- a/Mage.Sets/src/mage/cards/v/VampireScrivener.java +++ b/Mage.Sets/src/mage/cards/v/VampireScrivener.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.common.GainLifeControllerTriggeredAbility; import mage.abilities.common.LoseLifeTriggeredAbility; import mage.abilities.condition.common.MyTurnCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; @@ -12,8 +11,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; -import mage.game.Game; -import mage.game.events.GameEvent; import java.util.UUID; @@ -34,13 +31,14 @@ public final class VampireScrivener extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever you gain life during your turn, put a +1/+1 counter on Vampire Scrivener. - this.addAbility(new ConditionalTriggeredAbility(new GainLifeControllerTriggeredAbility( - new AddCountersSourceEffect(CounterType.P1P1.createInstance())), MyTurnCondition.instance, - "Whenever you gain life during your turn, put a +1/+1 counter on {this}." - )); + this.addAbility(new GainLifeControllerTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()) + ).withTriggerCondition(MyTurnCondition.instance)); // Whenever you lose life during your turn, put a +1/+1 counter on Vampire Scrivener. - this.addAbility(new VampireScrivenerTriggeredAbility()); + this.addAbility(new LoseLifeTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()) + ).withTriggerCondition(MyTurnCondition.instance)); } private VampireScrivener(final VampireScrivener card) { @@ -52,26 +50,3 @@ public final class VampireScrivener extends CardImpl { return new VampireScrivener(this); } } - -class VampireScrivenerTriggeredAbility extends LoseLifeTriggeredAbility { - - VampireScrivenerTriggeredAbility() { - super(new AddCountersSourceEffect(CounterType.P1P1.createInstance())); - setTriggerPhrase("Whenever you lose life during your turn, "); - } - - private VampireScrivenerTriggeredAbility(final VampireScrivenerTriggeredAbility ability) { - super(ability); - } - - @Override - public VampireScrivenerTriggeredAbility copy() { - return new VampireScrivenerTriggeredAbility(this); - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return game.isActivePlayer(getControllerId()) && super.checkTrigger(event, game); - } - -} diff --git a/Mage.Sets/src/mage/cards/v/Vandalblast.java b/Mage.Sets/src/mage/cards/v/Vandalblast.java index 580d56d1bd4..ec07ccff7b4 100644 --- a/Mage.Sets/src/mage/cards/v/Vandalblast.java +++ b/Mage.Sets/src/mage/cards/v/Vandalblast.java @@ -9,7 +9,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.TargetController; import mage.filter.common.FilterArtifactPermanent; -import mage.target.common.TargetArtifactPermanent; +import mage.target.TargetPermanent; import java.util.UUID; @@ -28,7 +28,7 @@ public final class Vandalblast extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}"); // Destroy target artifact you don't control. - this.getSpellAbility().addTarget(new TargetArtifactPermanent(FILTER)); + this.getSpellAbility().addTarget(new TargetPermanent(FILTER)); this.getSpellAbility().addEffect(new DestroyTargetEffect()); // Overload {4}{R} (You may cast this spell for its overload cost. If you do, change its text by replacing all instances of "target" with "each.") diff --git a/Mage.Sets/src/mage/cards/v/Vault101BirthdayParty.java b/Mage.Sets/src/mage/cards/v/Vault101BirthdayParty.java index 782e74392f4..39b51284dea 100644 --- a/Mage.Sets/src/mage/cards/v/Vault101BirthdayParty.java +++ b/Mage.Sets/src/mage/cards/v/Vault101BirthdayParty.java @@ -18,6 +18,7 @@ import mage.players.Player; import mage.target.TargetCard; import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; +import mage.util.CardUtil; /** * @@ -103,7 +104,7 @@ class Vault101BirthdayPartyEffect extends OneShotEffect { return false; } player.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent equipment = game.getPermanent(card.getId()); + Permanent equipment = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (equipment == null || !equipment.hasSubtype(SubType.EQUIPMENT, game)) { return true; } diff --git a/Mage.Sets/src/mage/cards/v/Vault13DwellersJourney.java b/Mage.Sets/src/mage/cards/v/Vault13DwellersJourney.java index 84ccfe61af4..a33f3d93ae7 100644 --- a/Mage.Sets/src/mage/cards/v/Vault13DwellersJourney.java +++ b/Mage.Sets/src/mage/cards/v/Vault13DwellersJourney.java @@ -2,9 +2,11 @@ package mage.cards.v; import mage.abilities.Ability; import mage.abilities.common.SagaAbility; +import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileUntilSourceLeavesEffect; import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.PutOnLibraryTargetEffect; import mage.abilities.effects.keyword.ScryEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -20,7 +22,9 @@ import mage.players.Player; import mage.target.TargetCard; import mage.target.TargetPermanent; import mage.target.common.TargetCardInExile; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; +import mage.target.targetpointer.EachTargetPointer; +import mage.target.targetpointer.FixedTargets; import mage.util.CardUtil; import java.util.Optional; @@ -55,9 +59,10 @@ public final class Vault13DwellersJourney extends CardImpl { triggeredAbility -> { triggeredAbility.addEffect(new ExileUntilSourceLeavesEffect() .setText("for each player, exile up to one other target enchantment or " + - "creature that player controls until {this} leaves the battlefield")); - triggeredAbility.addTarget(new TargetPermanent(filter)); - triggeredAbility.setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + "creature that player controls until {this} leaves the battlefield") + .setTargetPointer(new EachTargetPointer())); + triggeredAbility.addTarget(new TargetPermanent(0, 1, filter)); + triggeredAbility.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, false)); } ); @@ -126,7 +131,11 @@ class Vault13DwellersJourneyEffect extends OneShotEffect { ); cards.retainZone(Zone.EXILED, game); } - player.putCardsOnBottomOfLibrary(cards, game, source, true); + if (!cards.isEmpty()) { + Effect e = new PutOnLibraryTargetEffect(false); + e.setTargetPointer(new FixedTargets(cards, game)); + e.apply(game, source); + } return true; } } diff --git a/Mage.Sets/src/mage/cards/v/Vault87ForcedEvolution.java b/Mage.Sets/src/mage/cards/v/Vault87ForcedEvolution.java index c5dada35a52..d5346befc1f 100644 --- a/Mage.Sets/src/mage/cards/v/Vault87ForcedEvolution.java +++ b/Mage.Sets/src/mage/cards/v/Vault87ForcedEvolution.java @@ -1,46 +1,48 @@ package mage.cards.v; -import java.util.UUID; - -import mage.MageInt; -import mage.MageObject; -import mage.abilities.Ability; import mage.abilities.common.SagaAbility; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.Effects; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.AddCardSubTypeTargetEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; -import mage.constants.Duration; -import mage.constants.SagaChapter; -import mage.constants.SubType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SagaChapter; +import mage.constants.SubType; import mage.counters.CounterType; +import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.game.Game; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** * @author Cguy7777 */ public final class Vault87ForcedEvolution extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("non-Mutant creature"); - private static final Hint hint = new ValueHint( - "Highest power among Mutants you control", Vault87ForcedEvolutionValue.instance); + private static final FilterCreaturePermanent filterNonMutant = new FilterCreaturePermanent("non-Mutant creature"); static { - filter.add(Predicates.not(SubType.MUTANT.getPredicate())); + filterNonMutant.add(Predicates.not(SubType.MUTANT.getPredicate())); } + private static final FilterControlledPermanent filterMutant = new FilterControlledPermanent("Mutants you control"); + + static { + filterMutant.add(SubType.MUTANT.getPredicate()); + } + + private static final GreatestAmongPermanentsValue xValue = new GreatestAmongPermanentsValue(GreatestAmongPermanentsValue.Quality.Power, filterMutant); + private static final Hint hint = xValue.getHint(); + public Vault87ForcedEvolution(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}{U}"); @@ -54,7 +56,7 @@ public final class Vault87ForcedEvolution extends CardImpl { this, SagaChapter.CHAPTER_I, new GainControlTargetEffect(Duration.WhileControlled), - new TargetCreaturePermanent(filter)); + new TargetCreaturePermanent(filterNonMutant)); // II -- Put a +1/+1 counter on target creature you control. It becomes a Mutant in addition to its other types. sagaAbility.addChapterEffect( @@ -70,7 +72,7 @@ public final class Vault87ForcedEvolution extends CardImpl { sagaAbility.addChapterEffect( this, SagaChapter.CHAPTER_III, - new DrawCardSourceControllerEffect(Vault87ForcedEvolutionValue.instance) + new DrawCardSourceControllerEffect(xValue) .setText("draw cards equal to the greatest power among Mutants you control")); this.addAbility(sagaAbility.addHint(hint)); } @@ -83,31 +85,4 @@ public final class Vault87ForcedEvolution extends CardImpl { public Vault87ForcedEvolution copy() { return new Vault87ForcedEvolution(this); } -} - -enum Vault87ForcedEvolutionValue implements DynamicValue { - instance; - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - return game.getBattlefield() - .getAllActivePermanents(sourceAbility.getControllerId()) - .stream() - .filter(permanent1 -> permanent1.isCreature(game)) - .filter(permanent -> permanent.hasSubtype(SubType.MUTANT, game)) - .map(MageObject::getPower) - .mapToInt(MageInt::getValue) - .max() - .orElse(0); - } - - @Override - public DynamicValue copy() { - return instance; - } - - @Override - public String getMessage() { - return ""; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/v/VaynesTreachery.java b/Mage.Sets/src/mage/cards/v/VaynesTreachery.java new file mode 100644 index 00000000000..36e9e783eb8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VaynesTreachery.java @@ -0,0 +1,46 @@ +package mage.cards.v; + +import mage.abilities.condition.common.KickedCondition; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.AddContinuousEffectToGame; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.keyword.KickerAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VaynesTreachery extends CardImpl { + + public VaynesTreachery(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{B}"); + + // Kicker--Sacrifice an artifact or creature. + this.addAbility(new KickerAbility(new SacrificeTargetCost(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_CREATURE))); + + // Target creature gets -2/-2 until end of turn. If this spell was kicked, that creature gets -6/-6 until end of turn instead. + this.getSpellAbility().addEffect(new ConditionalOneShotEffect( + new AddContinuousEffectToGame(new BoostTargetEffect(-6, -6)), + new AddContinuousEffectToGame(new BoostTargetEffect(-2, -2)), + KickedCondition.ONCE, "target creature gets -2/-2 until end of turn. " + + "If this spell was kicked, that creature gets -6/-6 until end of turn instead" + )); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private VaynesTreachery(final VaynesTreachery card) { + super(card); + } + + @Override + public VaynesTreachery copy() { + return new VaynesTreachery(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VectorImperialCapital.java b/Mage.Sets/src/mage/cards/v/VectorImperialCapital.java new file mode 100644 index 00000000000..38315f32c84 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VectorImperialCapital.java @@ -0,0 +1,39 @@ +package mage.cards.v; + +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.RedManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VectorImperialCapital extends CardImpl { + + public VectorImperialCapital(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.subtype.add(SubType.TOWN); + + // This land enters tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {B} or {R}. + this.addAbility(new BlackManaAbility()); + this.addAbility(new RedManaAbility()); + } + + private VectorImperialCapital(final VectorImperialCapital card) { + super(card); + } + + @Override + public VectorImperialCapital copy() { + return new VectorImperialCapital(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/Veldt.java b/Mage.Sets/src/mage/cards/v/Veldt.java index 14b9ec804e3..d12518abd5c 100644 --- a/Mage.Sets/src/mage/cards/v/Veldt.java +++ b/Mage.Sets/src/mage/cards/v/Veldt.java @@ -1,50 +1,50 @@ - package mage.cards.v; -import java.util.UUID; -import mage.Mana; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; -import mage.abilities.costs.common.TapSourceCost; import mage.abilities.decorator.ConditionalContinuousRuleModifyingEffect; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DontUntapInControllersUntapStepSourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; -import mage.abilities.mana.SimpleManaAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; import mage.counters.CounterType; +import java.util.UUID; + /** - * * @author Luna Skyrise */ public final class Veldt extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.DEPLETION); + public Veldt(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // Veldt doesn't untap during your untap step if it has a depletion counter on it. - Effect effect = new ConditionalContinuousRuleModifyingEffect(new DontUntapInControllersUntapStepSourceEffect(false, true), - new SourceHasCounterCondition(CounterType.DEPLETION, 1, Integer.MAX_VALUE)); - effect.setText("{this} doesn't untap during your untap step if it has a depletion counter on it"); - Ability ability = new SimpleStaticAbility(effect); - this.addAbility(ability); + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousRuleModifyingEffect( + new DontUntapInControllersUntapStepSourceEffect(false, true), condition + ).setText("{this} doesn't untap during your untap step if it has a depletion counter on it"))); + // At the beginning of your upkeep, remove a depletion counter from Veldt. - Ability ability2 = new BeginningOfUpkeepTriggeredAbility(new RemoveCounterSourceEffect(CounterType.DEPLETION.createInstance())); - this.addAbility(ability2); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new RemoveCounterSourceEffect(CounterType.DEPLETION.createInstance()) + )); + // {tap}: Add {G} or {W}. Put a depletion counter on Veldt. - Ability ability3 = new SimpleManaAbility(Zone.BATTLEFIELD, Mana.GreenMana(1), new TapSourceCost()); - ability3.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance())); - this.addAbility(ability3); - Ability ability4 = new SimpleManaAbility(Zone.BATTLEFIELD, Mana.WhiteMana(1), new TapSourceCost()); - ability4.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance())); - this.addAbility(ability4); + Ability ability = new GreenManaAbility(); + ability.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance())); + this.addAbility(ability); + Ability ability2 = new WhiteManaAbility(); + ability2.addEffect(new AddCountersSourceEffect(CounterType.DEPLETION.createInstance())); + this.addAbility(ability2); } private Veldt(final Veldt card) { diff --git a/Mage.Sets/src/mage/cards/v/VenatHeartOfHydaelyn.java b/Mage.Sets/src/mage/cards/v/VenatHeartOfHydaelyn.java new file mode 100644 index 00000000000..217d13738c9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VenatHeartOfHydaelyn.java @@ -0,0 +1,66 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterSpell; +import mage.target.common.TargetNonlandPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VenatHeartOfHydaelyn extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("a legendary spell"); + + static { + filter.add(SuperType.LEGENDARY.getPredicate()); + } + + public VenatHeartOfHydaelyn(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.ELDER); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + this.secondSideCardClazz = mage.cards.h.HydaelynTheMothercrystal.class; + + // Whenever you cast a legendary spell, draw a card. This ability triggers only once each turn. + this.addAbility(new SpellCastControllerTriggeredAbility( + new DrawCardSourceControllerEffect(1), filter, false + ).setTriggersLimitEachTurn(1)); + + // Hero's Sundering -- {7}, {T}: Exile target nonland permanent. Transform Venat. Activate only as a sorcery. + this.addAbility(new TransformAbility()); + Ability ability = new ActivateAsSorceryActivatedAbility(new ExileTargetEffect(), new GenericManaCost(7)); + ability.addCost(new TapSourceCost()); + ability.addEffect(new TransformSourceEffect()); + ability.addTarget(new TargetNonlandPermanent()); + this.addAbility(ability.withFlavorWord("Hero's Sundering")); + } + + private VenatHeartOfHydaelyn(final VenatHeartOfHydaelyn card) { + super(card); + } + + @Override + public VenatHeartOfHydaelyn copy() { + return new VenatHeartOfHydaelyn(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VentifactBottle.java b/Mage.Sets/src/mage/cards/v/VentifactBottle.java index 6c7c1114d21..8e21b814cdc 100644 --- a/Mage.Sets/src/mage/cards/v/VentifactBottle.java +++ b/Mage.Sets/src/mage/cards/v/VentifactBottle.java @@ -1,23 +1,20 @@ - package mage.cards.v; import mage.Mana; import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; import mage.abilities.common.ActivateAsSorceryActivatedAbility; -import mage.abilities.triggers.BeginningOfFirstMainTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.dynamicvalue.common.GetXValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.triggers.BeginningOfFirstMainTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; @@ -30,22 +27,20 @@ import java.util.UUID; */ public final class VentifactBottle extends CardImpl { + private static final Condition condition = new SourceHasCounterCondition(CounterType.CHARGE); + public VentifactBottle(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); // {X}{1}, {tap}: Put X charge counters on Ventifact Bottle. Activate this ability only any time you could cast a sorcery. - Ability ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, - new AddCountersSourceEffect(CounterType.CHARGE.createInstance(), GetXValue.instance, true), - new ManaCostsImpl<>("{X}{1}")); + Ability ability = new ActivateAsSorceryActivatedAbility(new AddCountersSourceEffect( + CounterType.CHARGE.createInstance(), GetXValue.instance, true + ), new ManaCostsImpl<>("{X}{1}")); ability.addCost(new TapSourceCost()); this.addAbility(ability); + // At the beginning of your precombat main phase, if Ventifact Bottle has a charge counter on it, tap it and remove all charge counters from it. Add {C} for each charge counter removed this way. - TriggeredAbility ability2 = new BeginningOfFirstMainTriggeredAbility(new VentifactBottleEffect()); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability2, - new SourceHasCounterCondition(CounterType.CHARGE, 1, Integer.MAX_VALUE), - "At the beginning of your first main phase, " - + "if {this} has a charge counter on it, tap it and remove all charge counters from it. " - + "Add {C} for each charge counter removed this way.")); + this.addAbility(new BeginningOfFirstMainTriggeredAbility(new VentifactBottleEffect()).withInterveningIf(condition)); } private VentifactBottle(final VentifactBottle card) { @@ -62,6 +57,7 @@ class VentifactBottleEffect extends OneShotEffect { VentifactBottleEffect() { super(Outcome.Benefit); + staticText = "tap it and remove all charge counters from it. Add {C} for each charge counter removed this way"; } private VentifactBottleEffect(final VentifactBottleEffect effect) { @@ -75,16 +71,16 @@ class VentifactBottleEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); Player player = game.getPlayer(source.getControllerId()); - if (sourcePermanent != null && player != null) { - int amountRemoved = sourcePermanent.removeAllCounters(CounterType.CHARGE.getName(), source, game); - sourcePermanent.tap(source, game); - Mana mana = new Mana(); - mana.setColorless(amountRemoved); - player.getManaPool().addMana(mana, game, source); - return true; + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); + if (player == null || sourcePermanent == null) { + return false; } - return false; + sourcePermanent.tap(source, game); + int amountRemoved = sourcePermanent.removeAllCounters(CounterType.CHARGE.getName(), source, game); + if (amountRemoved > 0) { + player.getManaPool().addMana(Mana.ColorlessMana(amountRemoved), game, source); + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/v/VictoryChimes.java b/Mage.Sets/src/mage/cards/v/VictoryChimes.java index 4cf0e7d3df8..c02c16c7726 100644 --- a/Mage.Sets/src/mage/cards/v/VictoryChimes.java +++ b/Mage.Sets/src/mage/cards/v/VictoryChimes.java @@ -4,9 +4,8 @@ import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.common.ChoosePlayerEffect; -import mage.abilities.effects.mana.ManaEffect; import mage.abilities.effects.common.continuous.UntapSourceDuringEachOtherPlayersUntapStepEffect; +import mage.abilities.effects.mana.ManaEffect; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -15,6 +14,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; import mage.players.Player; +import mage.target.TargetPlayer; import java.util.UUID; @@ -33,8 +33,6 @@ public final class VictoryChimes extends CardImpl { ManaEffect effect = new VictoryChimesManaEffect("chosen player"); effect.setText("a player of your choice adds {C}"); Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); - // choosing player as first effect, before adding mana effect - ability.getEffects().add(0, new ChoosePlayerEffect(Outcome.PutManaInPool).setText("")); this.addAbility(ability); } @@ -61,7 +59,13 @@ class VictoryChimesManaEffect extends ManaEffect { @Override public Player getPlayer(Game game, Ability source) { - return game.getPlayer((UUID) game.getState().getValue(source.getSourceId() + "_player")); + if (!game.inCheckPlayableState()) { + TargetPlayer target = new TargetPlayer(1, 1, true); + if (target.choose(Outcome.PutManaInPool, source.getControllerId(), source, game)) { + return game.getPlayer(target.getFirstTarget()); + } + } + return game.getPlayer(source.getControllerId()); // Count as controller's potential mana for card playability } @Override diff --git a/Mage.Sets/src/mage/cards/v/VincentVengefulAtoner.java b/Mage.Sets/src/mage/cards/v/VincentVengefulAtoner.java index 9667d82c503..40dbccab205 100644 --- a/Mage.Sets/src/mage/cards/v/VincentVengefulAtoner.java +++ b/Mage.Sets/src/mage/cards/v/VincentVengefulAtoner.java @@ -44,7 +44,7 @@ public final class VincentVengefulAtoner extends CardImpl { // Chaos -- Whenever Vincent deals combat damage to an opponent, it deals that much damage to each other opponent if Vincent's power is 7 or greater. this.addAbility(new DealsDamageToOpponentTriggeredAbility( new VincentVengefulAtonerEffect(), false, true, true - )); + ).withFlavorWord("Chaos")); } private VincentVengefulAtoner(final VincentVengefulAtoner card) { diff --git a/Mage.Sets/src/mage/cards/v/VincentsLimitBreak.java b/Mage.Sets/src/mage/cards/v/VincentsLimitBreak.java new file mode 100644 index 00000000000..af68f437d12 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VincentsLimitBreak.java @@ -0,0 +1,106 @@ +package mage.cards.v; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.continuous.SetBasePowerToughnessTargetEffect; +import mage.abilities.keyword.TieredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VincentsLimitBreak extends CardImpl { + + public VincentsLimitBreak(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{B}"); + + // Tiered + this.addAbility(new TieredAbility(this)); + + // Until end of turn, target creature you control gains "When this creature dies, return it to the battlefield tapped under its owner's control" and has the chosen base power and toughness. + this.getSpellAbility().getModes().setChooseText("Tiered (Choose one additional cost.)
    " + + "Until end of turn, target creature you control gains \"When this creature dies, return it " + + "to the battlefield tapped under its owner's control\" and has the chosen base power and toughness."); + + // * Galian Beast -- {0} -- 3/2. + this.getSpellAbility().addEffect(new VincentsLimitBreakEffect(3, 2)); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + this.getSpellAbility().withFirstModeFlavorWord("Galian Beast"); + this.getSpellAbility().withFirstModeCost(new GenericManaCost(0)); + + // * Death Gigas -- {1} -- 5/2. + this.getSpellAbility().addMode(new Mode(new VincentsLimitBreakEffect(5, 2)) + .addTarget(new TargetControlledCreaturePermanent()) + .withFlavorWord("Death Gigas") + .withCost(new GenericManaCost(1))); + + // * Hellmasker -- {3} -- 7/2 + this.getSpellAbility().addMode(new Mode(new VincentsLimitBreakEffect(7, 2)) + .addTarget(new TargetControlledCreaturePermanent()) + .withFlavorWord("Hellmasker") + .withCost(new GenericManaCost(3))); + } + + private VincentsLimitBreak(final VincentsLimitBreak card) { + super(card); + } + + @Override + public VincentsLimitBreak copy() { + return new VincentsLimitBreak(this); + } +} + +class VincentsLimitBreakEffect extends OneShotEffect { + + private final int power; + private final int toughness; + + VincentsLimitBreakEffect(int power, int toughness) { + super(Outcome.Benefit); + staticText = power + "/" + toughness + '.'; + this.power = power; + this.toughness = toughness; + } + + private VincentsLimitBreakEffect(final VincentsLimitBreakEffect effect) { + super(effect); + this.power = effect.power; + this.toughness = effect.toughness; + } + + @Override + public VincentsLimitBreakEffect copy() { + return new VincentsLimitBreakEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (permanent == null) { + return false; + } + game.addEffect(new GainAbilityTargetEffect(new DiesSourceTriggeredAbility( + new ReturnSourceFromGraveyardToBattlefieldEffect(true, true), false + )).setTargetPointer(new FixedTarget(permanent, game)), source); + game.addEffect(new SetBasePowerToughnessTargetEffect( + power, toughness, Duration.EndOfTurn + ).setTargetPointer(new FixedTarget(permanent, game)), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/v/ViridianJoiner.java b/Mage.Sets/src/mage/cards/v/ViridianJoiner.java index 2a72625ec06..399bf05854a 100644 --- a/Mage.Sets/src/mage/cards/v/ViridianJoiner.java +++ b/Mage.Sets/src/mage/cards/v/ViridianJoiner.java @@ -26,7 +26,10 @@ public final class ViridianJoiner extends CardImpl { this.toughness = new MageInt(2); // {T}: Add an amount of {G} equal to Viridian Joiner's power. - this.addAbility(new DynamicManaAbility(Mana.GreenMana(1), SourcePermanentPowerValue.NOT_NEGATIVE)); + this.addAbility(new DynamicManaAbility(Mana.GreenMana(1), + SourcePermanentPowerValue.NOT_NEGATIVE, + "Add an amount of {G} equal to {this}'s power" + )); } private ViridianJoiner(final ViridianJoiner card) { diff --git a/Mage.Sets/src/mage/cards/v/ViviensInvocation.java b/Mage.Sets/src/mage/cards/v/ViviensInvocation.java index 037f26f73c9..5eb5c4a09a3 100644 --- a/Mage.Sets/src/mage/cards/v/ViviensInvocation.java +++ b/Mage.Sets/src/mage/cards/v/ViviensInvocation.java @@ -15,6 +15,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetOpponentsCreaturePermanent; +import mage.util.CardUtil; import java.util.UUID; @@ -78,7 +79,7 @@ class ViviensInvocationEffect extends OneShotEffect { if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { cards.remove(card); } - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { controller.putCardsOnBottomOfLibrary(cards, game, source, false); return true; diff --git a/Mage.Sets/src/mage/cards/v/VogarNecropolisTyrant.java b/Mage.Sets/src/mage/cards/v/VogarNecropolisTyrant.java index 35fa7b78a68..6c5bb74545f 100644 --- a/Mage.Sets/src/mage/cards/v/VogarNecropolisTyrant.java +++ b/Mage.Sets/src/mage/cards/v/VogarNecropolisTyrant.java @@ -4,11 +4,9 @@ import mage.MageInt; import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.condition.common.MyTurnCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.abilities.hint.common.MyTurnHint; import mage.abilities.keyword.MenaceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -35,10 +33,9 @@ public final class VogarNecropolisTyrant extends CardImpl { this.addAbility(new MenaceAbility(false)); // Whenever another creature dies during your turn, put a +1/+1 counter on Vogar, Necropolis Tyrant. - this.addAbility(new ConditionalTriggeredAbility( - new DiesCreatureTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false, true), - MyTurnCondition.instance, "Whenever another creature dies during your turn, put a +1/+1 counter on {this}." - ).addHint(MyTurnHint.instance)); + this.addAbility(new DiesCreatureTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false, true + ).withTriggerCondition(MyTurnCondition.instance)); // When Vogar dies, draw a card for each +1/+1 counter on it. this.addAbility(new DiesSourceTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/v/VoiceOfResurgence.java b/Mage.Sets/src/mage/cards/v/VoiceOfResurgence.java index 1097c4d4bec..1a9ffc011b6 100644 --- a/Mage.Sets/src/mage/cards/v/VoiceOfResurgence.java +++ b/Mage.Sets/src/mage/cards/v/VoiceOfResurgence.java @@ -4,7 +4,6 @@ import mage.MageInt; import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.SpellCastOpponentTriggeredAbility; import mage.abilities.condition.common.MyTurnCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.hint.common.CreaturesYouControlHint; import mage.abilities.hint.common.MyTurnHint; @@ -14,7 +13,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.FilterSpell; import mage.filter.StaticFilters; import mage.game.permanent.token.VoiceOfResurgenceToken; @@ -33,17 +31,14 @@ public final class VoiceOfResurgence extends CardImpl { this.toughness = new MageInt(2); // Whenever an opponent casts a spell during your turn or when Voice of Resurgence dies, create a green and white Elemental creature token with "This creature's power and toughness are each equal to the number of creatures you control." - OrTriggeredAbility ability = new OrTriggeredAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new VoiceOfResurgenceToken()), - new ConditionalTriggeredAbility( - new SpellCastOpponentTriggeredAbility(null, StaticFilters.FILTER_SPELL_A, false), - MyTurnCondition.instance, - "Whenever an opponent casts a spell during your turn, "), - new DiesSourceTriggeredAbility(null, false)); + OrTriggeredAbility ability = new OrTriggeredAbility( + Zone.BATTLEFIELD, new CreateTokenEffect(new VoiceOfResurgenceToken()), + new SpellCastOpponentTriggeredAbility(null, StaticFilters.FILTER_SPELL_A, false) + .withTriggerCondition(MyTurnCondition.instance), + new DiesSourceTriggeredAbility(null, false) + ); ability.setLeavesTheBattlefieldTrigger(true); - ability.addHint(MyTurnHint.instance); - ability.addHint(CreaturesYouControlHint.instance); - this.addAbility(ability); - + this.addAbility(ability.addHint(MyTurnHint.instance).addHint(CreaturesYouControlHint.instance)); } private VoiceOfResurgence(final VoiceOfResurgence card) { @@ -56,4 +51,3 @@ public final class VoiceOfResurgence extends CardImpl { } } - diff --git a/Mage.Sets/src/mage/cards/v/VolcanicEruption.java b/Mage.Sets/src/mage/cards/v/VolcanicEruption.java index 6b84d3b68e8..9fca4c45e45 100644 --- a/Mage.Sets/src/mage/cards/v/VolcanicEruption.java +++ b/Mage.Sets/src/mage/cards/v/VolcanicEruption.java @@ -8,12 +8,13 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; +import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterLandPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.common.TargetLandPermanent; +import mage.target.TargetPermanent; import mage.target.targetadjustment.XTargetsCountAdjuster; import java.util.List; @@ -23,15 +24,15 @@ import java.util.UUID; * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ public final class VolcanicEruption extends CardImpl { - private static final FilterLandPermanent filter - = new FilterLandPermanent(SubType.MOUNTAIN, "Mountain"); + + private static final FilterPermanent filter = new FilterLandPermanent(SubType.MOUNTAIN, "Mountain"); public VolcanicEruption(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{U}{U}{U}"); // Destroy X target Mountains. Volcanic Eruption deals damage to each creature and each player equal to the number of Mountains put into a graveyard this way. this.getSpellAbility().addEffect(new VolcanicEruptionEffect()); - this.getSpellAbility().addTarget(new TargetLandPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); this.getSpellAbility().setTargetAdjuster(new XTargetsCountAdjuster()); } diff --git a/Mage.Sets/src/mage/cards/v/VolcanicSpite.java b/Mage.Sets/src/mage/cards/v/VolcanicSpite.java index b7822816836..c54533d162c 100644 --- a/Mage.Sets/src/mage/cards/v/VolcanicSpite.java +++ b/Mage.Sets/src/mage/cards/v/VolcanicSpite.java @@ -85,7 +85,7 @@ class VolcanicSpiteEffect extends OneShotEffect { if (card == null) { return false; } - if (player.putCardsOnBottomOfLibrary(card, game, source, false)) { + if (player.putCardsOnBottomOfLibrary(card, game, source)) { player.drawCards(1, source, game); } return true; diff --git a/Mage.Sets/src/mage/cards/v/VoyagerGlidecar.java b/Mage.Sets/src/mage/cards/v/VoyagerGlidecar.java index 5131996f87e..36386420cea 100644 --- a/Mage.Sets/src/mage/cards/v/VoyagerGlidecar.java +++ b/Mage.Sets/src/mage/cards/v/VoyagerGlidecar.java @@ -17,7 +17,10 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.counters.CounterType; -import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.predicate.permanent.TappedPredicate; import mage.target.common.TargetControlledPermanent; import java.util.UUID; @@ -27,6 +30,13 @@ import java.util.UUID; */ public final class VoyagerGlidecar extends CardImpl { + private static final FilterControlledPermanent filter = new FilterControlledCreaturePermanent("other untapped creatures you control"); + + static { + filter.add(AnotherPredicate.instance); + filter.add(TappedPredicate.UNTAPPED); + } + public VoyagerGlidecar(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{W}"); @@ -42,9 +52,7 @@ public final class VoyagerGlidecar extends CardImpl { new AddCardTypeSourceEffect( Duration.EndOfTurn, CardType.ARTIFACT, CardType.CREATURE ).setText("until end of turn, this Vehicle becomes an artifact creature"), - new TapTargetCost(new TargetControlledPermanent( - 3, StaticFilters.FILTER_CONTROLLED_UNTAPPED_CREATURES - )) + new TapTargetCost(new TargetControlledPermanent(3, filter)) ); ability.addEffect(new GainAbilitySourceEffect( FlyingAbility.getInstance(), Duration.EndOfTurn diff --git a/Mage.Sets/src/mage/cards/v/VrenTheRelentless.java b/Mage.Sets/src/mage/cards/v/VrenTheRelentless.java index 90ca31c8068..081b584c775 100644 --- a/Mage.Sets/src/mage/cards/v/VrenTheRelentless.java +++ b/Mage.Sets/src/mage/cards/v/VrenTheRelentless.java @@ -2,15 +2,16 @@ package mage.cards.v; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.replacement.CreaturesAreExiledOnDeathReplacementEffect; +import mage.abilities.hint.Hint; import mage.abilities.hint.ValueHint; import mage.abilities.keyword.WardAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -31,6 +32,10 @@ import java.util.UUID; */ public final class VrenTheRelentless extends CardImpl { + private static final Hint hint = new ValueHint( + "Creatures exiled under opponents' control this turn", VrenTheRelentlessCount.instance + ); + public VrenTheRelentless(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{B}"); this.supertype.add(SuperType.LEGENDARY); @@ -49,11 +54,8 @@ public final class VrenTheRelentless extends CardImpl { // At the beginning of each end step, create X 1/1 black Rat creature tokens with "This creature gets +1/+1 for each // other Rat you control," where X is the number of creatures your opponents controlled that were exiled this turn. this.addAbility(new BeginningOfEndStepTriggeredAbility( - TargetController.ANY, new CreateTokenEffect(new VrenRatToken(), VrenTheRelentlessCount.instance) - .setText("create X 1/1 black Rat creature tokens with \"This creature gets +1/+1 for each other Rat you " + - "control,\" where X is the number of creatures your opponents controlled that were exiled this turn"), - false - ).addHint(new ValueHint("Creatures exiled under opponents' control this turn", VrenTheRelentlessCount.instance)), new VrenTheRelentlessWatcher()); + TargetController.ANY, new CreateTokenEffect(new VrenRatToken(), VrenTheRelentlessCount.instance), false + ).addHint(hint), new VrenTheRelentlessWatcher()); } private VrenTheRelentless(final VrenTheRelentless card) { @@ -72,7 +74,7 @@ enum VrenTheRelentlessCount implements DynamicValue { @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { VrenTheRelentlessWatcher watcher = game.getState().getWatcher(VrenTheRelentlessWatcher.class); - return watcher == null ? 0 : watcher.getCount(sourceAbility.getControllerId()); + return watcher == null ? 0 : watcher.getCount(sourceAbility.getControllerId(), game); } @Override @@ -80,9 +82,14 @@ enum VrenTheRelentlessCount implements DynamicValue { return instance; } + @Override + public String toString() { + return "X"; + } + @Override public String getMessage() { - return ""; + return "the number of creatures that were exiled under your opponents' control this turn"; } } @@ -100,7 +107,9 @@ class VrenTheRelentlessWatcher extends Watcher { return; } ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if ((zEvent.getToZone() == Zone.EXILED && zEvent.getFromZone() == Zone.BATTLEFIELD) && zEvent.getTarget().isCreature(game)) { + if (zEvent.getToZone() == Zone.EXILED + && zEvent.getFromZone() == Zone.BATTLEFIELD + && zEvent.getTarget().isCreature(game)) { playerMap.compute(zEvent.getTarget().getControllerId(), CardUtil::setOrIncrementValue); } } @@ -111,10 +120,11 @@ class VrenTheRelentlessWatcher extends Watcher { super.reset(); } - int getCount(UUID playerId) { - return playerMap.entrySet().stream() - .filter(entry -> !entry.getKey().equals(playerId)) - .mapToInt(Map.Entry::getValue) + int getCount(UUID playerId, Game game) { + return game + .getOpponents(playerId) + .stream() + .mapToInt(uuid -> playerMap.getOrDefault(uuid, 0)) .sum(); } } diff --git a/Mage.Sets/src/mage/cards/v/VronosMaskedInquisitor.java b/Mage.Sets/src/mage/cards/v/VronosMaskedInquisitor.java index be711c0156b..565c36ce27e 100644 --- a/Mage.Sets/src/mage/cards/v/VronosMaskedInquisitor.java +++ b/Mage.Sets/src/mage/cards/v/VronosMaskedInquisitor.java @@ -24,7 +24,7 @@ import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.permanent.token.custom.CreatureToken; import mage.target.TargetPermanent; import mage.target.common.TargetNonlandPermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; import java.util.UUID; @@ -57,7 +57,7 @@ public final class VronosMaskedInquisitor extends CardImpl { LoyaltyAbility ability2 = new LoyaltyAbility(new ReturnToHandTargetEffect().setTargetPointer(new EachTargetPointer()) .setText("for each opponent, return up to one target nonland permanent that player controls to its owner's hand"), -2); ability2.addTarget(new TargetNonlandPermanent(0,1)); - ability2.setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + ability2.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); this.addAbility(ability2); // −7: Target artifact you control becomes a 9/9 Construct artifact creature and gains vigilance, indestructible, and "This creature can't be blocked." diff --git a/Mage.Sets/src/mage/cards/v/VulshokBattlemaster.java b/Mage.Sets/src/mage/cards/v/VulshokBattlemaster.java index 7d6ccd31a0b..54621ce5e6b 100644 --- a/Mage.Sets/src/mage/cards/v/VulshokBattlemaster.java +++ b/Mage.Sets/src/mage/cards/v/VulshokBattlemaster.java @@ -10,7 +10,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; @@ -30,6 +30,7 @@ public final class VulshokBattlemaster extends CardImpl { // Haste this.addAbility(HasteAbility.getInstance()); + // When Vulshok Battlemaster enters the battlefield, attach all Equipment on the battlefield to it. this.addAbility(new EntersBattlefieldTriggeredAbility(new VulshokBattlemasterEffect())); } @@ -43,39 +44,35 @@ public final class VulshokBattlemaster extends CardImpl { return new VulshokBattlemaster(this); } - static class VulshokBattlemasterEffect extends OneShotEffect { +} - public VulshokBattlemasterEffect() { - super(Outcome.Benefit); - this.staticText = "attach all Equipment on the battlefield to it"; - } +class VulshokBattlemasterEffect extends OneShotEffect { - private VulshokBattlemasterEffect(final VulshokBattlemasterEffect effect) { - super(effect); - } + VulshokBattlemasterEffect() { + super(Outcome.Benefit); + this.staticText = "attach all Equipment on the battlefield to it"; + } - @Override - public VulshokBattlemasterEffect copy() { - return new VulshokBattlemasterEffect(this); - } + private VulshokBattlemasterEffect(final VulshokBattlemasterEffect effect) { + super(effect); + } - @Override - public boolean apply(Game game, Ability source) { - Permanent battlemaster = game.getPermanent(source.getSourceId()); - if (battlemaster != null) { - FilterPermanent filter = new FilterPermanent(); - filter.add(SubType.EQUIPMENT.getPredicate()); - for (Permanent equipment : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source, game)) { - if (equipment != null) { - //If an Equipment can't equip Vulshok Battlemaster, it isn't attached to the Battlemaster, and it doesn't become unattached (if it's attached to a creature). (https://gatherer.wizards.com/Pages/Card/Details.aspx?multiverseid=48125) - if (!battlemaster.cantBeAttachedBy(equipment, source, game, false)) { - battlemaster.addAttachment(equipment.getId(), source, game); - } - } - } - return true; - } + @Override + public VulshokBattlemasterEffect copy() { + return new VulshokBattlemasterEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent == null) { return false; } + for (Permanent equipment : game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_PERMANENT_EQUIPMENT, source.getControllerId(), source, game + )) { + permanent.addAttachment(equipment.getId(), source, game); + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/v/VulshokGauntlets.java b/Mage.Sets/src/mage/cards/v/VulshokGauntlets.java index 89226e8ac9f..84720726be1 100644 --- a/Mage.Sets/src/mage/cards/v/VulshokGauntlets.java +++ b/Mage.Sets/src/mage/cards/v/VulshokGauntlets.java @@ -36,7 +36,7 @@ public final class VulshokGauntlets extends CardImpl { effect.setText("Equipped creature gets +4/+2"); Ability ability = new SimpleStaticAbility(effect); effect = new VulshokGauntletsEffect(); - effect.setText("and has doesn't untap during its controller's untap step"); + effect.setText("and doesn't untap during its controller's untap step"); ability.addEffect(effect); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/w/WakkaDevotedGuardian.java b/Mage.Sets/src/mage/cards/w/WakkaDevotedGuardian.java new file mode 100644 index 00000000000..c76978d4db2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WakkaDevotedGuardian.java @@ -0,0 +1,141 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.ReachAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.WatcherScope; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterArtifactPermanent; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.util.CardUtil; +import mage.watchers.Watcher; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WakkaDevotedGuardian extends CardImpl { + + private static final FilterPermanent filter = new FilterArtifactPermanent("artifact that player controls"); + + static { + filter.add(WakkaDevotedGuardianPredicate.instance); + } + + public WakkaDevotedGuardian(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Whenever Wakka deals combat damage to a player, destroy up to one target artifact that player controls and put a +1/+1 counter on Wakka. + Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new DestroyTargetEffect()); + ability.addEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance()).concatBy("and")); + ability.addTarget(new TargetPermanent(0, 1, filter)); + this.addAbility(ability); + + // Blitzball Captain -- At the beginning of your end step, if a counter was put on Wakka this turn, put a +1/+1 counter on each other creature you control. + this.addAbility(new BeginningOfEndStepTriggeredAbility(new AddCountersAllEffect( + CounterType.P1P1.createInstance(), StaticFilters.FILTER_OTHER_CONTROLLED_CREATURE + )).withInterveningIf(WakkaDevotedGuardianCondition.instance).withFlavorWord("Blitzball Captain"), new WakkaDevotedGuardianWatcher()); + } + + private WakkaDevotedGuardian(final WakkaDevotedGuardian card) { + super(card); + } + + @Override + public WakkaDevotedGuardian copy() { + return new WakkaDevotedGuardian(this); + } +} + +enum WakkaDevotedGuardianPredicate implements ObjectSourcePlayerPredicate { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + return CardUtil + .getEffectValueFromAbility( + input.getSource(), "damagedPlayer", UUID.class + ) + .map(input.getObject()::isControlledBy) + .orElse(false); + } +} + +enum WakkaDevotedGuardianCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public String toString() { + return "a counter was put on {this} this turn"; + } +} + +class WakkaDevotedGuardianWatcher extends Watcher { + + private final Set set = new HashSet<>(); + + WakkaDevotedGuardianWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.COUNTER_ADDED) { + set.add(new MageObjectReference(event.getTargetId(), game)); + } + } + + @Override + public void reset() { + super.reset(); + set.clear(); + } + + static boolean checkPermanent(Game game, Ability source) { + return game + .getState() + .getWatcher(WakkaDevotedGuardianWatcher.class) + .set + .contains(new MageObjectReference(source.getSourceId(), game)); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WanderingMage.java b/Mage.Sets/src/mage/cards/w/WanderingMage.java index ff860723176..af772345bdd 100644 --- a/Mage.Sets/src/mage/cards/w/WanderingMage.java +++ b/Mage.Sets/src/mage/cards/w/WanderingMage.java @@ -1,13 +1,11 @@ package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.costs.common.PutCountersTargetCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.PreventDamageToTargetEffect; import mage.cards.CardImpl; @@ -15,17 +13,14 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.target.Target; -import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreaturePermanent; import mage.target.common.TargetPlayerOrPlaneswalker; +import java.util.UUID; + /** * * @author fireshoes @@ -64,11 +59,8 @@ public final class WanderingMage extends CardImpl { // {B}, Put a -1/-1 counter on a creature you control: Prevent the next 2 damage that would be dealt to target player this turn. ability = new SimpleActivatedAbility( new PreventDamageToTargetEffect(Duration.EndOfTurn, 2), new ManaCostsImpl<>("{B}")); - ability.addCost(new WanderingMageCost()); + ability.addCost(new PutCountersTargetCost(CounterType.M1M1.createInstance())); ability.addTarget(new TargetPlayerOrPlaneswalker()); - Target target = new TargetControlledCreaturePermanent(); - target.withNotTarget(true); - ability.addTarget(target); this.addAbility(ability); } @@ -81,34 +73,3 @@ public final class WanderingMage extends CardImpl { return new WanderingMage(this); } } - -class WanderingMageCost extends CostImpl { - - public WanderingMageCost() { - this.text = "Put a -1/-1 counter on a creature you control"; - } - - private WanderingMageCost(final WanderingMageCost cost) { - super(cost); - } - - @Override - public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { - return true; - } - - @Override - public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - Permanent permanent = game.getPermanent(ability.getTargets().get(1).getFirstTarget()); - if (permanent != null) { - permanent.addCounters(CounterType.M1M1.createInstance(), controllerId, ability, game); - this.paid = true; - } - return paid; - } - - @Override - public WanderingMageCost copy() { - return new WanderingMageCost(this); - } -} diff --git a/Mage.Sets/src/mage/cards/w/WarElemental.java b/Mage.Sets/src/mage/cards/w/WarElemental.java index 51147cd3b3d..557d791b3fb 100644 --- a/Mage.Sets/src/mage/cards/w/WarElemental.java +++ b/Mage.Sets/src/mage/cards/w/WarElemental.java @@ -97,6 +97,6 @@ enum WarElementalCondition implements Condition { @Override public String toString() { - return "if an opponent was dealt damage this turn"; + return "an opponent was dealt damage this turn"; } } diff --git a/Mage.Sets/src/mage/cards/w/WarriorsSword.java b/Mage.Sets/src/mage/cards/w/WarriorsSword.java index 02b90a950d2..a0649f7b9c1 100644 --- a/Mage.Sets/src/mage/cards/w/WarriorsSword.java +++ b/Mage.Sets/src/mage/cards/w/WarriorsSword.java @@ -31,7 +31,7 @@ public final class WarriorsSword extends CardImpl { Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(3, 2)); ability.addEffect(new AddCardSubtypeAttachedEffect( SubType.WARRIOR, AttachmentType.EQUIPMENT - ).setText(", and is a Warrior in addition to its other types")); + ).setText("and is a Warrior in addition to its other types")); this.addAbility(ability); // Equip {5} diff --git a/Mage.Sets/src/mage/cards/w/WaveOfTerror.java b/Mage.Sets/src/mage/cards/w/WaveOfTerror.java index 259d2af6b9c..41c08f6421c 100644 --- a/Mage.Sets/src/mage/cards/w/WaveOfTerror.java +++ b/Mage.Sets/src/mage/cards/w/WaveOfTerror.java @@ -1,24 +1,18 @@ - package mage.cards.w; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfDrawTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyAllEffect; import mage.abilities.keyword.CumulativeUpkeepAbility; +import mage.abilities.triggers.BeginningOfDrawTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.ComparisonType; -import mage.constants.Outcome; -import mage.constants.TargetController; import mage.counters.CounterType; +import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.mageobject.ManaValuePredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.filter.predicate.mageobject.ManaValueEqualToCountersSourceCountPredicate; + +import java.util.UUID; /** * @@ -26,6 +20,13 @@ import mage.game.permanent.Permanent; */ public final class WaveOfTerror extends CardImpl { + private static final FilterPermanent filter = new FilterCreaturePermanent( + "each creature with mana value equal to the number of age counters on {this}" + ); + static { + filter.add(new ManaValueEqualToCountersSourceCountPredicate(CounterType.AGE)); + } + public WaveOfTerror(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); @@ -33,7 +34,7 @@ public final class WaveOfTerror extends CardImpl { this.addAbility(new CumulativeUpkeepAbility(new ManaCostsImpl<>("{1}"))); // At the beginning of your draw step, destroy each creature with converted mana cost equal to the number of age counters on Wave of Terror. They can't be regenerated. - this.addAbility(new BeginningOfDrawTriggeredAbility(new WaveOfTerrorEffect(), false)); + this.addAbility(new BeginningOfDrawTriggeredAbility(new DestroyAllEffect(filter, true), false)); } private WaveOfTerror(final WaveOfTerror card) { @@ -45,34 +46,3 @@ public final class WaveOfTerror extends CardImpl { return new WaveOfTerror(this); } } - -class WaveOfTerrorEffect extends OneShotEffect { - - WaveOfTerrorEffect() { - super(Outcome.DestroyPermanent); - this.staticText = "destroy each creature with mana value equal to the number of age counters on {this}. They can't be regenerated."; - } - - private WaveOfTerrorEffect(final WaveOfTerrorEffect effect) { - super(effect); - } - - @Override - public WaveOfTerrorEffect copy() { - return new WaveOfTerrorEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (permanent == null) { - return false; - } - FilterCreaturePermanent filter = new FilterCreaturePermanent(); - filter.add(new ManaValuePredicate( - ComparisonType.EQUAL_TO, - permanent.getCounters(game).getCount(CounterType.AGE) - )); - return new DestroyAllEffect(filter, true).apply(game, source); - } -} diff --git a/Mage.Sets/src/mage/cards/w/WaxmaneBaku.java b/Mage.Sets/src/mage/cards/w/WaxmaneBaku.java index a1b9525baf1..ee721ad93cf 100644 --- a/Mage.Sets/src/mage/cards/w/WaxmaneBaku.java +++ b/Mage.Sets/src/mage/cards/w/WaxmaneBaku.java @@ -33,7 +33,7 @@ public final class WaxmaneBaku extends CardImpl { this.toughness = new MageInt(2); // Whenever you cast a Spirit or Arcane spell, you may put a ki counter on Waxmane Baku. - this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.KI.createInstance()), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, true)); // {1}, Remove X ki counters from Waxmane Baku: Tap X target creatures. Ability ability = new SimpleActivatedAbility(new TapTargetEffect("tap X target creatures"), new GenericManaCost(1)); diff --git a/Mage.Sets/src/mage/cards/w/WaywardAngel.java b/Mage.Sets/src/mage/cards/w/WaywardAngel.java index 7c0af9bde37..0bbfb4e6509 100644 --- a/Mage.Sets/src/mage/cards/w/WaywardAngel.java +++ b/Mage.Sets/src/mage/cards/w/WaywardAngel.java @@ -56,7 +56,7 @@ public final class WaywardAngel extends CardImpl { ); ability.addEffect(new ConditionalContinuousEffect( new GainAbilitySourceEffect(gainedAbility), ThresholdCondition.instance, - "and has \"At the beginning of your upkeep, sacrifice a creature.\"" + ", and has \"At the beginning of your upkeep, sacrifice a creature.\"" )); ability.setAbilityWord(AbilityWord.THRESHOLD); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/w/WeaponsTrainer.java b/Mage.Sets/src/mage/cards/w/WeaponsTrainer.java index a4425d78e96..fd2e9697947 100644 --- a/Mage.Sets/src/mage/cards/w/WeaponsTrainer.java +++ b/Mage.Sets/src/mage/cards/w/WeaponsTrainer.java @@ -1,7 +1,5 @@ - package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; @@ -12,24 +10,19 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterControlledPermanent; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author fireshoes */ public final class WeaponsTrainer extends CardImpl { private static final String rule = "Other creatures you control get +1/+0 as long as you control an Equipment."; - private static final FilterControlledPermanent filter = new FilterControlledPermanent("an Equipment"); - - static { - filter.add(SubType.EQUIPMENT.getPredicate()); - } public WeaponsTrainer(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{R}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.SOLDIER); this.subtype.add(SubType.ALLY); @@ -38,7 +31,7 @@ public final class WeaponsTrainer extends CardImpl { // Other creatures you control get +1/+0 as long as you control an Equipment. ConditionalContinuousEffect effect = new ConditionalContinuousEffect(new BoostControlledEffect(1, 0, Duration.WhileOnBattlefield, true), - new PermanentsOnTheBattlefieldCondition(filter), rule); + new PermanentsOnTheBattlefieldCondition(StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT), rule); this.addAbility(new SimpleStaticAbility(effect)); } diff --git a/Mage.Sets/src/mage/cards/w/WeaponsVendor.java b/Mage.Sets/src/mage/cards/w/WeaponsVendor.java new file mode 100644 index 00000000000..553251f9a9f --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WeaponsVendor.java @@ -0,0 +1,65 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.AttachTargetToTargetEffect; +import mage.abilities.effects.common.DoWhenCostPaid; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WeaponsVendor extends CardImpl { + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + new FilterControlledPermanent(SubType.EQUIPMENT, "you control an Equipment") + ); + private static final Hint hint = new ConditionHint(condition); + + public WeaponsVendor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ARTIFICER); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // When this creature enters, draw a card. + this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1))); + + // At the beginning of combat on your turn, if you control an Equipment, you may pay {1}. When you do, attach target Equipment you control to target creature you control. + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(new AttachTargetToTargetEffect(), false); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT)); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(new BeginningOfCombatTriggeredAbility(new DoWhenCostPaid( + ability, new GenericManaCost(1), + "Pay {1} to attach an equipment to a creature you control?" + )).withInterveningIf(condition).addHint(hint)); + } + + private WeaponsVendor(final WeaponsVendor card) { + super(card); + } + + @Override + public WeaponsVendor copy() { + return new WeaponsVendor(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WeatherseedTotem.java b/Mage.Sets/src/mage/cards/w/WeatherseedTotem.java index ff2009b7210..7a7a03a5c22 100644 --- a/Mage.Sets/src/mage/cards/w/WeatherseedTotem.java +++ b/Mage.Sets/src/mage/cards/w/WeatherseedTotem.java @@ -17,8 +17,8 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.game.Game; import mage.game.permanent.token.custom.CreatureToken; +import mage.util.CardUtil; -import java.util.Objects; import java.util.UUID; /** @@ -65,11 +65,8 @@ enum WeatherseedTotemCondition implements Condition { @Override public boolean apply(Game game, Ability source) { - return source - .getEffects() - .stream() - .map(effect -> effect.getValue("permanentWasCreature")) - .filter(Objects::nonNull) - .anyMatch(Boolean.class::cast); + return CardUtil + .getEffectValueFromAbility(source, "permanentWasCreature", Boolean.class) + .orElse(false); } } diff --git a/Mage.Sets/src/mage/cards/w/WelcomeTo.java b/Mage.Sets/src/mage/cards/w/WelcomeTo.java index 9de18a0816d..42cde76dfbd 100644 --- a/Mage.Sets/src/mage/cards/w/WelcomeTo.java +++ b/Mage.Sets/src/mage/cards/w/WelcomeTo.java @@ -21,7 +21,7 @@ import mage.game.Game; import mage.game.permanent.token.DinosaurToken; import mage.game.permanent.token.custom.CreatureToken; import mage.target.TargetPermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; import mage.target.targetpointer.FixedTarget; @@ -64,7 +64,7 @@ public final class WelcomeTo extends CardImpl { "a 0/4 Wall artifact creature with defender for as long as you control this Saga.")); ability.getEffects().setTargetPointer(new EachTargetPointer()); ability.addTarget(new TargetPermanent(0, 1, filterNoncreatureArtifact)); - ability.setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + ability.setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); }); // II -- Create a 3/3 green Dinosaur creature token with trample. It gains haste until end of turn. diff --git a/Mage.Sets/src/mage/cards/w/WhipOfErebos.java b/Mage.Sets/src/mage/cards/w/WhipOfErebos.java index ef7df36a913..24f82bd8460 100644 --- a/Mage.Sets/src/mage/cards/w/WhipOfErebos.java +++ b/Mage.Sets/src/mage/cards/w/WhipOfErebos.java @@ -25,6 +25,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -88,7 +89,7 @@ class WhipOfErebosEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null && card != null) { if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { - Permanent creature = game.getPermanent(card.getId()); + Permanent creature = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (creature != null) { // gains haste ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); diff --git a/Mage.Sets/src/mage/cards/w/WhirlingDervish.java b/Mage.Sets/src/mage/cards/w/WhirlingDervish.java index 23e4d3c8769..04625a17c03 100644 --- a/Mage.Sets/src/mage/cards/w/WhirlingDervish.java +++ b/Mage.Sets/src/mage/cards/w/WhirlingDervish.java @@ -38,7 +38,7 @@ public final class WhirlingDervish extends CardImpl { this.addAbility(ProtectionAbility.from(ObjectColor.BLACK)); // At the beginning of each end step, if Whirling Dervish dealt damage to an opponent this turn, put a +1/+1 counter on it. TriggeredAbility triggered = new OnEventTriggeredAbility(GameEvent.EventType.END_TURN_STEP_PRE, "beginning of each end step", true, new AddCountersSourceEffect(CounterType.P1P1.createInstance())); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(triggered, new DealtDamageToAnOpponent(), ruleText)); + this.addAbility(new ConditionalInterveningIfTriggeredAbility(triggered, DealtDamageToAnOpponent.instance, ruleText)); } private WhirlingDervish(final WhirlingDervish card) { diff --git a/Mage.Sets/src/mage/cards/w/WindgracesJudgment.java b/Mage.Sets/src/mage/cards/w/WindgracesJudgment.java index 06da5d4268d..dd0c2fa8e3b 100644 --- a/Mage.Sets/src/mage/cards/w/WindgracesJudgment.java +++ b/Mage.Sets/src/mage/cards/w/WindgracesJudgment.java @@ -5,7 +5,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.target.common.TargetNonlandPermanent; -import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; import mage.target.targetpointer.EachTargetPointer; import java.util.UUID; @@ -24,7 +24,7 @@ public final class WindgracesJudgment extends CardImpl { .setText("For any number of opponents, destroy target nonland permanent that player controls") ); this.getSpellAbility().addTarget(new TargetNonlandPermanent(0, 1)); - this.getSpellAbility().setTargetAdjuster(new ForEachOpponentTargetsAdjuster()); + this.getSpellAbility().setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, true)); } private WindgracesJudgment(final WindgracesJudgment card) { diff --git a/Mage.Sets/src/mage/cards/w/WindurstFederationCenter.java b/Mage.Sets/src/mage/cards/w/WindurstFederationCenter.java new file mode 100644 index 00000000000..7534397ca32 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WindurstFederationCenter.java @@ -0,0 +1,39 @@ +package mage.cards.w; + +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WindurstFederationCenter extends CardImpl { + + public WindurstFederationCenter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + this.subtype.add(SubType.TOWN); + + // This land enters tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {G} or {W}. + this.addAbility(new GreenManaAbility()); + this.addAbility(new WhiteManaAbility()); + } + + private WindurstFederationCenter(final WindurstFederationCenter card) { + super(card); + } + + @Override + public WindurstFederationCenter copy() { + return new WindurstFederationCenter(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WinotaJoinerOfForces.java b/Mage.Sets/src/mage/cards/w/WinotaJoinerOfForces.java index b8b6f6427cc..4ed6dfa21c3 100644 --- a/Mage.Sets/src/mage/cards/w/WinotaJoinerOfForces.java +++ b/Mage.Sets/src/mage/cards/w/WinotaJoinerOfForces.java @@ -17,6 +17,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInLibrary; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -98,7 +99,7 @@ class WinotaJoinerOfForcesEffect extends OneShotEffect { )) { return player.putCardsOnBottomOfLibrary(cards, game, source, false); } - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return player.putCardsOnBottomOfLibrary(cards, game, source, false); } @@ -109,4 +110,4 @@ class WinotaJoinerOfForcesEffect extends OneShotEffect { ).setTargetPointer(new FixedTarget(permanent, game)), source); return player.putCardsOnBottomOfLibrary(cards, game, source, false); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/w/WinterCursedRider.java b/Mage.Sets/src/mage/cards/w/WinterCursedRider.java index c2fea8bd0f0..82eeb2ae7c5 100644 --- a/Mage.Sets/src/mage/cards/w/WinterCursedRider.java +++ b/Mage.Sets/src/mage/cards/w/WinterCursedRider.java @@ -24,6 +24,7 @@ import mage.constants.CardType; import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; +import mage.util.CardUtil; /** * @@ -54,7 +55,7 @@ public final class WinterCursedRider extends CardImpl { WardAbility wardAbility = new WardAbility(new PayLifeCost(2)); this.addAbility(new SimpleStaticAbility( new GainAbilityAllEffect(wardAbility, Duration.WhileOnBattlefield, StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACTS) - .setText("Artifacts you control have " + "\"" + wardAbility.getRuleWithoutHint() + "\"") + .setText("Artifacts you control have " + "\"" + CardUtil.getTextWithFirstCharUpperCase(wardAbility.getRuleWithoutHint()) + "\"") )); // Exhaust -- {2}{U}{B}, {T}, Exile X artifact cards from your graveyard: Each other nonartifact creature gets -X/-X until end of turn. Ability ability = new ExhaustAbility(new BoostAllEffect(xValue, xValue, Duration.EndOfTurn, filter, true), diff --git a/Mage.Sets/src/mage/cards/w/WitchsMist.java b/Mage.Sets/src/mage/cards/w/WitchsMist.java index 38310c003ff..dd03c86a886 100644 --- a/Mage.Sets/src/mage/cards/w/WitchsMist.java +++ b/Mage.Sets/src/mage/cards/w/WitchsMist.java @@ -1,7 +1,5 @@ - package mage.cards.w; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; @@ -10,30 +8,22 @@ import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.WasDealtDamageThisTurnPredicate; -import mage.target.common.TargetCreaturePermanent; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author North */ public final class WitchsMist extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature that was dealt damage this turn"); - - static { - filter.add(WasDealtDamageThisTurnPredicate.instance); - } - public WitchsMist(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{B}"); - + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); // {2}{B}, {T}: Destroy target creature that was dealt damage this turn. - Ability ability = new SimpleActivatedAbility(new DestroyTargetEffect(),new ManaCostsImpl<>("{2}{B}")); - ability.addTarget(new TargetCreaturePermanent(filter)); + Ability ability = new SimpleActivatedAbility(new DestroyTargetEffect(), new ManaCostsImpl<>("{2}{B}")); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_DAMAGED_THIS_TURN)); ability.addCost(new TapSourceCost()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/w/WoebringerDemon.java b/Mage.Sets/src/mage/cards/w/WoebringerDemon.java index 8917bdf355a..26dbfd48dea 100644 --- a/Mage.Sets/src/mage/cards/w/WoebringerDemon.java +++ b/Mage.Sets/src/mage/cards/w/WoebringerDemon.java @@ -55,7 +55,7 @@ class WoebringerDemonEffect extends OneShotEffect { WoebringerDemonEffect() { super(Outcome.Detriment); - this.staticText = "that player sacrifices a creature. If the player can't, sacrifice {this}"; + this.staticText = "that player sacrifices a creature of their choice. If the player can't, sacrifice {this}"; } private WoebringerDemonEffect(final WoebringerDemonEffect effect) { diff --git a/Mage.Sets/src/mage/cards/w/WoodlandChampion.java b/Mage.Sets/src/mage/cards/w/WoodlandChampion.java index 7ccea61836b..50f57ff6a06 100644 --- a/Mage.Sets/src/mage/cards/w/WoodlandChampion.java +++ b/Mage.Sets/src/mage/cards/w/WoodlandChampion.java @@ -89,7 +89,7 @@ class WoodlandChampionTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever one or more tokens enter the battlefield under your control, " + + return "Whenever one or more tokens you control enter, " + "put that many +1/+1 counters on {this}."; } } diff --git a/Mage.Sets/src/mage/cards/w/WorldChampionCelestialWeapon.java b/Mage.Sets/src/mage/cards/w/WorldChampionCelestialWeapon.java new file mode 100644 index 00000000000..5b81bc4e926 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WorldChampionCelestialWeapon.java @@ -0,0 +1,50 @@ +package mage.cards.w; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.abilities.keyword.EquipAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WorldChampionCelestialWeapon extends CardImpl { + + public WorldChampionCelestialWeapon(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, ""); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.EQUIPMENT); + this.nightCard = true; + this.color.setRed(true); + + // Double Overdrive -- Equipped creature gets +2/+0 and has double strike. + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(2, 0)); + ability.addEffect(new GainAbilityAttachedEffect( + DoubleStrikeAbility.getInstance(), AttachmentType.EQUIPMENT + ).setText("and has double strike")); + this.addAbility(ability.withFlavorWord("Double Overdrive")); + + // Equip {3} + this.addAbility(new EquipAbility(3)); + } + + private WorldChampionCelestialWeapon(final WorldChampionCelestialWeapon card) { + super(card); + } + + @Override + public WorldChampionCelestialWeapon copy() { + return new WorldChampionCelestialWeapon(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WorldMap.java b/Mage.Sets/src/mage/cards/w/WorldMap.java new file mode 100644 index 00000000000..6272b56cc1f --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WorldMap.java @@ -0,0 +1,50 @@ +package mage.cards.w; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WorldMap extends CardImpl { + + public WorldMap(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); + + // {1}, {T}, Sacrifice this artifact: Search your library for a basic land card, reveal it, put it into your hand, then shuffle. + Ability ability = new SimpleActivatedAbility(new SearchLibraryPutInHandEffect( + new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true + ), new GenericManaCost(1)); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeSourceCost()); + this.addAbility(ability); + + // {3}, {T}, Sacrifice this artifact: Search your library for a land card, reveal it, put it into your hand, then shuffle. + ability = new SimpleActivatedAbility(new SearchLibraryPutInHandEffect( + new TargetCardInLibrary(StaticFilters.FILTER_CARD_LAND), true + ), new GenericManaCost(3)); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeSourceCost()); + this.addAbility(ability); + } + + private WorldMap(final WorldMap card) { + super(card); + } + + @Override + public WorldMap copy() { + return new WorldMap(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WormfangNewt.java b/Mage.Sets/src/mage/cards/w/WormfangNewt.java index 47473a70185..20cc79dfb13 100644 --- a/Mage.Sets/src/mage/cards/w/WormfangNewt.java +++ b/Mage.Sets/src/mage/cards/w/WormfangNewt.java @@ -1,11 +1,11 @@ package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotNonTargetEffect; import mage.abilities.effects.common.ExileTargetForSourceEffect; import mage.abilities.effects.common.ReturnFromExileForSourceEffect; import mage.cards.CardImpl; @@ -14,18 +14,17 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; import mage.filter.StaticFilters; -import mage.filter.common.FilterControlledLandPermanent; -import mage.filter.predicate.mageobject.AnotherPredicate; -import mage.target.Target; import mage.target.TargetPermanent; +import java.util.UUID; + /** * @author tcontis */ public final class WormfangNewt extends CardImpl { public WormfangNewt(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.NIGHTMARE); this.subtype.add(SubType.SALAMANDER); this.subtype.add(SubType.BEAST); @@ -34,10 +33,9 @@ public final class WormfangNewt extends CardImpl { this.toughness = new MageInt(2); // When Wormfang Turtle enters the battlefield, exile a land you control. - Ability ability1 = new EntersBattlefieldTriggeredAbility(new ExileTargetForSourceEffect(), false); - Target target = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_A_LAND).withNotTarget(true); - ability1.addTarget(target); - this.addAbility(ability1); + this.addAbility(new EntersBattlefieldTriggeredAbility(new OneShotNonTargetEffect( + new ExileTargetForSourceEffect().setText("exile a land you control"), + new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_A_LAND)))); // When Wormfang Turtle leaves the battlefield, return the exiled card to the battlefield under its owner's control. Ability ability2 = new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD), false); diff --git a/Mage.Sets/src/mage/cards/w/WormfangTurtle.java b/Mage.Sets/src/mage/cards/w/WormfangTurtle.java index a3ca56d6f58..5705d2e57ac 100644 --- a/Mage.Sets/src/mage/cards/w/WormfangTurtle.java +++ b/Mage.Sets/src/mage/cards/w/WormfangTurtle.java @@ -1,11 +1,11 @@ package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotNonTargetEffect; import mage.abilities.effects.common.ExileTargetForSourceEffect; import mage.abilities.effects.common.ReturnFromExileForSourceEffect; import mage.cards.CardImpl; @@ -14,11 +14,10 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; import mage.filter.StaticFilters; -import mage.filter.common.FilterControlledLandPermanent; -import mage.filter.predicate.mageobject.AnotherPredicate; -import mage.target.Target; import mage.target.TargetPermanent; +import java.util.UUID; + /** * @author tcontis */ @@ -34,10 +33,10 @@ public final class WormfangTurtle extends CardImpl { this.toughness = new MageInt(4); // When Wormfang Turtle enters the battlefield, exile a land you control. - Ability ability1 = new EntersBattlefieldTriggeredAbility(new ExileTargetForSourceEffect(), false); - Target target = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_A_LAND).withNotTarget(true); - ability1.addTarget(target); - this.addAbility(ability1); + this.addAbility(new EntersBattlefieldTriggeredAbility(new OneShotNonTargetEffect( + new ExileTargetForSourceEffect().setText("exile a land you control"), + new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_A_LAND)))); + // When Wormfang Turtle leaves the battlefield, return the exiled card to the battlefield under its owner's control. Ability ability2 = new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD), false); diff --git a/Mage.Sets/src/mage/cards/w/WreckingBallArm.java b/Mage.Sets/src/mage/cards/w/WreckingBallArm.java index 070a0f47d48..54b40be49e1 100644 --- a/Mage.Sets/src/mage/cards/w/WreckingBallArm.java +++ b/Mage.Sets/src/mage/cards/w/WreckingBallArm.java @@ -32,7 +32,7 @@ public final class WreckingBallArm extends CardImpl { )); ability.addEffect(new CantBeBlockedByCreaturesAttachedEffect( Duration.WhileControlled, DauntAbility.getFilter(), AttachmentType.EQUIPMENT - )); + ).setText("and can't be blocked by creatures with power 2 or less")); this.addAbility(ability); // Equip legendary creature {3} diff --git a/Mage.Sets/src/mage/cards/w/WrexialTheRisenDeep.java b/Mage.Sets/src/mage/cards/w/WrexialTheRisenDeep.java index 15a2c841e0b..d4ccc426dc2 100644 --- a/Mage.Sets/src/mage/cards/w/WrexialTheRisenDeep.java +++ b/Mage.Sets/src/mage/cards/w/WrexialTheRisenDeep.java @@ -17,7 +17,7 @@ import mage.constants.SuperType; import mage.filter.FilterCard; import mage.filter.common.FilterInstantOrSorceryCard; import mage.target.common.TargetCardInGraveyard; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -50,7 +50,7 @@ public final class WrexialTheRisenDeep extends CardImpl { + ThatSpellGraveyardExileReplacementEffect.RULE_A); Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(effect, false, true); ability.addTarget(new TargetCardInGraveyard(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster(true)); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster(true)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/w/WriteIntoBeing.java b/Mage.Sets/src/mage/cards/w/WriteIntoBeing.java index 4b6ff09b2d4..46a0e048d20 100644 --- a/Mage.Sets/src/mage/cards/w/WriteIntoBeing.java +++ b/Mage.Sets/src/mage/cards/w/WriteIntoBeing.java @@ -84,7 +84,7 @@ class WriteIntoBeingEffect extends OneShotEffect { if (controller.getLibrary().hasCards()) { Card cardToPutBack = controller.getLibrary().getFromTop(game); if (controller.chooseUse(Outcome.Detriment, "Put " + cardToPutBack.getName() + " on bottom of library?", source, game)) { - controller.putCardsOnBottomOfLibrary(cardToPutBack, game, source, true); + controller.putCardsOnBottomOfLibrary(cardToPutBack, game, source); } else { controller.putCardsOnTopOfLibrary(cardToPutBack, game, source, true); } diff --git a/Mage.Sets/src/mage/cards/w/WyllsReversal.java b/Mage.Sets/src/mage/cards/w/WyllsReversal.java index 3b46ec66e61..6bad2bf3232 100644 --- a/Mage.Sets/src/mage/cards/w/WyllsReversal.java +++ b/Mage.Sets/src/mage/cards/w/WyllsReversal.java @@ -1,7 +1,7 @@ package mage.cards.w; import mage.abilities.Ability; -import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; import mage.abilities.effects.common.InfoEffect; import mage.abilities.effects.common.RollDieWithResultTableEffect; import mage.cards.CardImpl; @@ -38,7 +38,7 @@ public final class WyllsReversal extends CardImpl { // 15+ | You may choose new targets for that spell or ability. Then copy it. You may choose new targets for the copy. this.getSpellAbility().addEffect(new WyllsReversalEffect()); this.getSpellAbility().addTarget(new TargetStackObject()); - this.getSpellAbility().addHint(GreatestPowerAmongControlledCreaturesValue.getHint()); + this.getSpellAbility().addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint()); } private WyllsReversal(final WyllsReversal card) { @@ -94,7 +94,7 @@ class WyllsReversalEffect extends RollDieWithResultTableEffect { return false; } int result = player.rollDice(outcome, source, game, 20) - + GreatestPowerAmongControlledCreaturesValue.instance.calculate(game, source, this); + + GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.calculate(game, source, this); if (result >= 1) { stackObject.chooseNewTargets( game, source.getControllerId(), false, diff --git a/Mage.Sets/src/mage/cards/x/XandeDarkMage.java b/Mage.Sets/src/mage/cards/x/XandeDarkMage.java index 8c287e2c752..744825c5662 100644 --- a/Mage.Sets/src/mage/cards/x/XandeDarkMage.java +++ b/Mage.Sets/src/mage/cards/x/XandeDarkMage.java @@ -22,7 +22,7 @@ import java.util.UUID; */ public final class XandeDarkMage extends CardImpl { - private static final FilterCard filter = new FilterCard("noncreature, nonland card in your graveyard"); + private static final FilterCard filter = new FilterCard("noncreature, nonland card"); static { filter.add(Predicates.not(CardType.CREATURE.getPredicate())); diff --git a/Mage.Sets/src/mage/cards/x/XenicPoltergeist.java b/Mage.Sets/src/mage/cards/x/XenicPoltergeist.java index 90e0f410a77..ba907743169 100644 --- a/Mage.Sets/src/mage/cards/x/XenicPoltergeist.java +++ b/Mage.Sets/src/mage/cards/x/XenicPoltergeist.java @@ -1,7 +1,6 @@ package mage.cards.x; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -14,10 +13,11 @@ import mage.filter.common.FilterArtifactPermanent; import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.target.common.TargetArtifactPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; /** - * * @author MarcoMarin */ public final class XenicPoltergeist extends CardImpl { @@ -36,7 +36,7 @@ public final class XenicPoltergeist extends CardImpl { // {tap}: Until your next upkeep, target noncreature artifact becomes an artifact creature with power and toughness each equal to its converted mana cost. Ability ability = new SimpleActivatedAbility(new XenicPoltergeistEffect(), new TapSourceCost()); - ability.addTarget(new TargetArtifactPermanent(filter)); + ability.addTarget(new TargetPermanent(filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/y/YaroksWavecrasher.java b/Mage.Sets/src/mage/cards/y/YaroksWavecrasher.java index b61f3a44859..27209c5ff6d 100644 --- a/Mage.Sets/src/mage/cards/y/YaroksWavecrasher.java +++ b/Mage.Sets/src/mage/cards/y/YaroksWavecrasher.java @@ -1,8 +1,8 @@ package mage.cards.y; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotNonTargetEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -26,9 +26,9 @@ public final class YaroksWavecrasher extends CardImpl { this.toughness = new MageInt(4); // When Yarok’s Wavecrasher enters the battlefield, return another creature you control to its owner’s hand. - Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect(), false); - ability.addTarget(new TargetControlledPermanent(StaticFilters.FILTER_ANOTHER_CREATURE_YOU_CONTROL).withNotTarget(true)); - this.addAbility(ability); + this.addAbility(new EntersBattlefieldTriggeredAbility(new OneShotNonTargetEffect( + new ReturnToHandTargetEffect().setText("return another creature you control to its owner's hand"), + new TargetControlledPermanent(StaticFilters.FILTER_ANOTHER_CREATURE_YOU_CONTROL)))); } private YaroksWavecrasher(final YaroksWavecrasher card) { diff --git a/Mage.Sets/src/mage/cards/y/YarusRoarOfTheOldGods.java b/Mage.Sets/src/mage/cards/y/YarusRoarOfTheOldGods.java index 9cc4eff08fe..44e33f09b20 100644 --- a/Mage.Sets/src/mage/cards/y/YarusRoarOfTheOldGods.java +++ b/Mage.Sets/src/mage/cards/y/YarusRoarOfTheOldGods.java @@ -28,6 +28,7 @@ import mage.filter.predicate.card.FaceDownPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import mage.util.CardUtil; /** * @@ -109,7 +110,7 @@ class YarusRoarOfTheOldGodsEffect extends OneShotEffect { BecomesFaceDownCreatureEffect.FaceDownType.MANUAL), newSource); controller.moveCards(card, Zone.BATTLEFIELD, source, game, false, true, true, null); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { permanent.turnFaceUp(source, game, source.getControllerId()); } diff --git a/Mage.Sets/src/mage/cards/y/YggdrasilRebirthEngine.java b/Mage.Sets/src/mage/cards/y/YggdrasilRebirthEngine.java index 7d771534a02..1758f7fe54a 100644 --- a/Mage.Sets/src/mage/cards/y/YggdrasilRebirthEngine.java +++ b/Mage.Sets/src/mage/cards/y/YggdrasilRebirthEngine.java @@ -133,7 +133,7 @@ class YggdrasilRebirthEngineReturnCreatureEffect extends OneShotEffect { if (!player.moveCards(card, Zone.BATTLEFIELD, source, game)) { return false; } - Permanent movedCard = game.getPermanent(card.getId()); + Permanent movedCard = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (movedCard == null){ return false; } @@ -141,4 +141,4 @@ class YggdrasilRebirthEngineReturnCreatureEffect extends OneShotEffect { .setTargetPointer(new FixedTarget(movedCard, game)), source); return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/y/YiazmatUltimateMark.java b/Mage.Sets/src/mage/cards/y/YiazmatUltimateMark.java new file mode 100644 index 00000000000..277a6a11108 --- /dev/null +++ b/Mage.Sets/src/mage/cards/y/YiazmatUltimateMark.java @@ -0,0 +1,65 @@ +package mage.cards.y; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.TapSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.AnotherPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class YiazmatUltimateMark extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("another creature or artifact"); + + static { + filter.add(AnotherPredicate.instance); + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + CardType.ARTIFACT.getPredicate() + )); + } + + public YiazmatUltimateMark(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, ""); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.DRAGON); + this.power = new MageInt(5); + this.toughness = new MageInt(6); + this.nightCard = true; + this.color.setBlack(true); + + // {1}{B}, Sacrifice another creature or artifact: Yiazmat gains indestructible until end of turn. Tap it. + Ability ability = new SimpleActivatedAbility( + new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn), new ManaCostsImpl<>("{1}{B}") + ); + ability.addCost(new SacrificeTargetCost(filter)); + ability.addEffect(new TapSourceEffect().setText("tap it")); + this.addAbility(ability); + } + + private YiazmatUltimateMark(final YiazmatUltimateMark card) { + super(card); + } + + @Override + public YiazmatUltimateMark copy() { + return new YiazmatUltimateMark(this); + } +} diff --git a/Mage.Sets/src/mage/cards/y/YidaroWanderingMonster.java b/Mage.Sets/src/mage/cards/y/YidaroWanderingMonster.java index 6706653e9a4..5a71ce63424 100644 --- a/Mage.Sets/src/mage/cards/y/YidaroWanderingMonster.java +++ b/Mage.Sets/src/mage/cards/y/YidaroWanderingMonster.java @@ -109,7 +109,7 @@ class YidaroWanderingMonsterEffect extends OneShotEffect { } YidaroWanderingMonsterWatcher watcher = game.getState().getWatcher(YidaroWanderingMonsterWatcher.class); if (watcher == null || watcher.getYidaroCount(player.getId()) < 4) { - player.putCardsOnBottomOfLibrary(card, game, source, true); + player.putCardsOnBottomOfLibrary(card, game, source); player.shuffleLibrary(source, game); } else { player.moveCards(card, Zone.BATTLEFIELD, source, game); diff --git a/Mage.Sets/src/mage/cards/y/YorionSkyNomad.java b/Mage.Sets/src/mage/cards/y/YorionSkyNomad.java index f85ab9ecd3e..c65013cc80d 100644 --- a/Mage.Sets/src/mage/cards/y/YorionSkyNomad.java +++ b/Mage.Sets/src/mage/cards/y/YorionSkyNomad.java @@ -1,8 +1,8 @@ package mage.cards.y; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotNonTargetEffect; import mage.abilities.effects.common.ExileReturnBattlefieldNextEndStepTargetEffect; import mage.abilities.keyword.CompanionAbility; import mage.abilities.keyword.CompanionCondition; @@ -54,10 +54,9 @@ public final class YorionSkyNomad extends CardImpl { // When Yorion enters the battlefield, exile any number of other nonland permanents you own // and control. Return those cards to the battlefield at the beginning of the next end step. - Ability ability = new EntersBattlefieldTriggeredAbility( - new ExileReturnBattlefieldNextEndStepTargetEffect().setText(ruleText)); - ability.addTarget(new TargetPermanent(0, Integer.MAX_VALUE, filter, true)); - this.addAbility(ability); + this.addAbility(new EntersBattlefieldTriggeredAbility(new OneShotNonTargetEffect( + new ExileReturnBattlefieldNextEndStepTargetEffect().setText(ruleText), + new TargetPermanent(0, Integer.MAX_VALUE, filter, true)))); } private YorionSkyNomad(final YorionSkyNomad card) { diff --git a/Mage.Sets/src/mage/cards/y/YouAreAlreadyDead.java b/Mage.Sets/src/mage/cards/y/YouAreAlreadyDead.java index 450bd7676ee..f6b77a42ec0 100644 --- a/Mage.Sets/src/mage/cards/y/YouAreAlreadyDead.java +++ b/Mage.Sets/src/mage/cards/y/YouAreAlreadyDead.java @@ -5,9 +5,7 @@ import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.WasDealtDamageThisTurnPredicate; +import mage.filter.StaticFilters; import mage.target.TargetPermanent; import java.util.UUID; @@ -17,19 +15,12 @@ import java.util.UUID; */ public final class YouAreAlreadyDead extends CardImpl { - private static final FilterPermanent filter - = new FilterCreaturePermanent("creature that was dealt damage this turn"); - - static { - filter.add(WasDealtDamageThisTurnPredicate.instance); - } - public YouAreAlreadyDead(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}"); // Destroy target creature that was dealt damage this turn. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_DAMAGED_THIS_TURN)); // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
    ")); diff --git a/Mage.Sets/src/mage/cards/y/YshtolaRhul.java b/Mage.Sets/src/mage/cards/y/YshtolaRhul.java index 2083b4ed242..34939081db4 100644 --- a/Mage.Sets/src/mage/cards/y/YshtolaRhul.java +++ b/Mage.Sets/src/mage/cards/y/YshtolaRhul.java @@ -1,21 +1,18 @@ package mage.cards.y; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileThenReturnTargetEffect; import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.constants.TurnPhase; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; import mage.game.Game; import mage.game.turn.TurnMod; import mage.target.common.TargetControlledCreaturePermanent; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Outcome; + +import java.util.UUID; /** * @author balazskristof @@ -24,7 +21,7 @@ public final class YshtolaRhul extends CardImpl { public YshtolaRhul(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}"); - + this.supertype.add(SuperType.LEGENDARY); this.subtype.add(SubType.CAT); this.subtype.add(SubType.DRUID); @@ -32,9 +29,9 @@ public final class YshtolaRhul extends CardImpl { this.toughness = new MageInt(5); // At the beginning of your end step, exile target creature you control, then return it to the battlefield under its owner's control. Then if it's the first end step of the turn, there is an additional end step after this step. - Ability ability = new BeginningOfEndStepTriggeredAbility(new ExileThenReturnTargetEffect(true, false)); + Ability ability = new BeginningOfEndStepTriggeredAbility(new ExileThenReturnTargetEffect(false, false)); ability.addTarget(new TargetControlledCreaturePermanent()); - ability.addEffect(new YshtolaRhulEffect().concatBy("Then")); + ability.addEffect(new YshtolaRhulEffect()); this.addAbility(ability); } @@ -51,7 +48,7 @@ public final class YshtolaRhul extends CardImpl { class YshtolaRhulEffect extends OneShotEffect { public YshtolaRhulEffect() { super(Outcome.Benefit); - staticText = "if it's the first end step of the turn, there is an additional end step after this step"; + staticText = "Then if it's the first end step of the turn, there is an additional end step after this step"; } protected YshtolaRhulEffect(final YshtolaRhulEffect effect) { @@ -65,10 +62,10 @@ class YshtolaRhulEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - if (game.getTurn().getPhase(TurnPhase.END).getCount() == 0) { - TurnMod end = new TurnMod(game.getState().getActivePlayerId()).withExtraPhase(TurnPhase.END); - game.getState().getTurnMods().add(end); + if (game.getTurn().getPhase(TurnPhase.END).getCount() != 0) { + return false; } + game.getState().getTurnMods().add(new TurnMod(game.getState().getActivePlayerId()).withExtraPhase(TurnPhase.END)); return true; } } diff --git a/Mage.Sets/src/mage/cards/y/YukiOnna.java b/Mage.Sets/src/mage/cards/y/YukiOnna.java index cd765b28e73..b47c617e53e 100644 --- a/Mage.Sets/src/mage/cards/y/YukiOnna.java +++ b/Mage.Sets/src/mage/cards/y/YukiOnna.java @@ -32,7 +32,7 @@ public final class YukiOnna extends CardImpl { ability.addTarget(new TargetArtifactPermanent()); this.addAbility(ability); // Whenever you cast a Spirit or Arcane spell, you may return Yuki-Onna to its owner's hand. - this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), StaticFilters.FILTER_SPIRIT_OR_ARCANE_CARD, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), StaticFilters.FILTER_SPELL_SPIRIT_OR_ARCANE, true)); } private YukiOnna(final YukiOnna card) { diff --git a/Mage.Sets/src/mage/cards/y/YunaGrandSummoner.java b/Mage.Sets/src/mage/cards/y/YunaGrandSummoner.java new file mode 100644 index 00000000000..2f12fec8998 --- /dev/null +++ b/Mage.Sets/src/mage/cards/y/YunaGrandSummoner.java @@ -0,0 +1,107 @@ +package mage.cards.y; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.PutIntoGraveFromBattlefieldAllTriggeredAbility; +import mage.abilities.common.delayed.AddCounterNextSpellDelayedTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.mana.AnyColorManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.counters.Counters; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.predicate.permanent.CounterAnyPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class YunaGrandSummoner extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent("another permanent you control"); + + static { + filter.add(AnotherPredicate.instance); + filter.add(CounterAnyPredicate.instance); + } + + public YunaGrandSummoner(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{W}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CLERIC); + this.power = new MageInt(1); + this.toughness = new MageInt(5); + + // Grand Summon -- {T}: Add one mana of any color. When you next cast a creature spell this turn, that creature enters with two additional +1/+1 counters on it. + AnyColorManaAbility manaAbility = new AnyColorManaAbility(); + manaAbility.addEffect(new CreateDelayedTriggeredAbilityEffect( + new AddCounterNextSpellDelayedTriggeredAbility(2, StaticFilters.FILTER_SPELL_A_CREATURE) + )); + manaAbility.setUndoPossible(false); + this.addAbility(manaAbility.withFlavorWord("Grand Summon")); + + // Whenever another permanent you control is put into a graveyard from the battlefield, if it had one or more counters on it, you may put that number of +1/+1 counters on target creature. + Ability ability = new PutIntoGraveFromBattlefieldAllTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance(), YunaGrandSummonerValue.instance) + .setText("if it had one or more counters on it, " + + "you may put that number of +1/+1 counters on target creature"), + true, filter, false + ); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + private YunaGrandSummoner(final YunaGrandSummoner card) { + super(card); + } + + @Override + public YunaGrandSummoner copy() { + return new YunaGrandSummoner(this); + } +} + +enum YunaGrandSummonerValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return Optional + .ofNullable((Permanent) effect.getValue("permanentDied")) + .map(permanent -> permanent.getCounters(game)) + .map(Counters::getTotalCount) + .orElse(0); + } + + @Override + public YunaGrandSummonerValue copy() { + return this; + } + + @Override + public String getMessage() { + return ""; + } + + @Override + public String toString() { + return "1"; + } +} diff --git a/Mage.Sets/src/mage/cards/y/YusriFortunesFlame.java b/Mage.Sets/src/mage/cards/y/YusriFortunesFlame.java index 5b85ce922c9..ea49683557b 100644 --- a/Mage.Sets/src/mage/cards/y/YusriFortunesFlame.java +++ b/Mage.Sets/src/mage/cards/y/YusriFortunesFlame.java @@ -3,7 +3,6 @@ package mage.cards.y; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; -import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.CastFromHandWithoutPayingManaCostEffect; import mage.abilities.keyword.FlyingAbility; @@ -70,21 +69,16 @@ class YusriFortunesFlameEffect extends OneShotEffect { return false; } int flips = player.getAmount(1, 5, "Choose a number between 1 and 5", source, game); - int wins = 0; - int losses = 0; - for (int i = 0; i < flips; i++) { - if (player.flipCoin(source, game, true)) { - wins++; - } else { - losses++; - } - } + int wins = player + .flipCoins(source, game, flips, true) + .stream() + .mapToInt(x -> x ? 1 : 0) + .sum(); + int losses = flips - wins; player.drawCards(wins, source, game); player.damage(2 * losses, source.getSourceId(), source, game); if (wins >= 5) { - ContinuousEffect effect = new CastFromHandWithoutPayingManaCostEffect(); - effect.setDuration(Duration.EndOfTurn); - game.addEffect(effect, source); + game.addEffect(new CastFromHandWithoutPayingManaCostEffect().setDuration(Duration.EndOfTurn), source); } return true; } diff --git a/Mage.Sets/src/mage/cards/z/ZackFair.java b/Mage.Sets/src/mage/cards/z/ZackFair.java new file mode 100644 index 00000000000..96ee7515874 --- /dev/null +++ b/Mage.Sets/src/mage/cards/z/ZackFair.java @@ -0,0 +1,141 @@ +package mage.cards.z; + +import mage.MageInt; +import mage.MageItem; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutSourceCountersOnTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.PermanentIdPredicate; +import mage.game.Controllable; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.util.RandomUtil; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class ZackFair extends CardImpl { + + public ZackFair(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(0); + this.toughness = new MageInt(1); + + // Zack Fair enters with a +1/+1 counter on it. + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), + "with a +1/+1 counter on it" + )); + + // {1}, Sacrifice Zack Fair: Target creature you control gains indestructible until end of turn. Put Zack Fair's counters on that creature and attach an Equipment that was attached to Zack Fair to that creature. + Ability ability = new SimpleActivatedAbility( + new GainAbilityTargetEffect(IndestructibleAbility.getInstance()), new GenericManaCost(1) + ); + ability.addCost(new SacrificeSourceCost()); + ability.addEffect(new PutSourceCountersOnTargetEffect().setText("Put {this}'s counters on that creature")); + ability.addEffect(new ZackFairEffect()); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + private ZackFair(final ZackFair card) { + super(card); + } + + @Override + public ZackFair copy() { + return new ZackFair(this); + } +} + +class ZackFairEffect extends OneShotEffect { + + ZackFairEffect() { + super(Outcome.Benefit); + staticText = "and attach an Equipment that was attached to {this} to that creature"; + } + + private ZackFairEffect(final ZackFairEffect effect) { + super(effect); + } + + @Override + public ZackFairEffect copy() { + return new ZackFairEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentOrLKI(game); + Permanent creature = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (permanent == null || creature == null) { + return false; + } + List permanents = permanent + .getAttachments() + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .filter(p -> p.hasSubtype(SubType.EQUIPMENT, game)) + .collect(Collectors.toList()); + Permanent equipment; + switch (permanents.size()) { + case 0: + return false; + case 1: + equipment = RandomUtil.randomFromCollection(permanents); + break; + default: + FilterPermanent filter = new FilterPermanent( + SubType.EQUIPMENT, "Equipment to attach to " + creature.getIdName() + ); + filter.add(Predicates.or( + permanents + .stream() + .map(MageItem::getId) + .map(PermanentIdPredicate::new) + .collect(Collectors.toList()) + )); + TargetPermanent target = new TargetPermanent(filter); + target.withNotTarget(true); + Optional.ofNullable(source) + .map(Controllable::getControllerId) + .map(game::getPlayer) + .ifPresent(player -> player.choose(outcome, target, source, game)); + equipment = game.getPermanent(target.getFirstTarget()); + } + return Optional + .ofNullable(equipment) + .map(MageItem::getId) + .map(uuid -> creature.addAttachment(uuid, source, game)) + .orElse(false); + } +} diff --git a/Mage.Sets/src/mage/cards/z/ZaraRenegadeRecruiter.java b/Mage.Sets/src/mage/cards/z/ZaraRenegadeRecruiter.java index 2af062d23a0..31d9612fb30 100644 --- a/Mage.Sets/src/mage/cards/z/ZaraRenegadeRecruiter.java +++ b/Mage.Sets/src/mage/cards/z/ZaraRenegadeRecruiter.java @@ -21,6 +21,7 @@ import mage.players.Player; import mage.target.common.TargetCardInHand; import mage.target.common.TargetPlayerOrPlaneswalker; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -94,7 +95,7 @@ class ZaraRenegadeRecruiterEffect extends OneShotEffect { card, Zone.BATTLEFIELD, source, game, true, false, false, null ); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } diff --git a/Mage.Sets/src/mage/cards/z/ZarethSanTheTrickster.java b/Mage.Sets/src/mage/cards/z/ZarethSanTheTrickster.java index a40f80a5084..057fe6af878 100644 --- a/Mage.Sets/src/mage/cards/z/ZarethSanTheTrickster.java +++ b/Mage.Sets/src/mage/cards/z/ZarethSanTheTrickster.java @@ -22,7 +22,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInGraveyard; import mage.target.common.TargetControlledPermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -61,7 +61,7 @@ public final class ZarethSanTheTrickster extends CardImpl { // Whenever Zareth San deals combat damage to a player, you may put target permanent card from that player's graveyard onto the battlefield under your control. Ability ability2 = new DealsCombatDamageToAPlayerTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(), true, true); ability2.addTarget(new TargetCardInGraveyard(filterCardGraveyard)); - ability2.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster(true)); + ability2.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster(true)); this.addAbility(ability2); } diff --git a/Mage.Sets/src/mage/cards/z/ZellDincht.java b/Mage.Sets/src/mage/cards/z/ZellDincht.java index e58ec740387..6fc27784a49 100644 --- a/Mage.Sets/src/mage/cards/z/ZellDincht.java +++ b/Mage.Sets/src/mage/cards/z/ZellDincht.java @@ -45,7 +45,7 @@ public final class ZellDincht extends CardImpl { // At the beginning of your end step, return a land you control to its owner's hand. this.addAbility(new BeginningOfEndStepTriggeredAbility( - new ReturnToHandChosenControlledPermanentEffect(StaticFilters.FILTER_CONTROLLED_PERMANENT_A_LAND) + new ReturnToHandChosenControlledPermanentEffect(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND) )); } diff --git a/Mage.Sets/src/mage/cards/z/ZenosYaeGalvus.java b/Mage.Sets/src/mage/cards/z/ZenosYaeGalvus.java index 5db0ae321ec..d808e24232d 100644 --- a/Mage.Sets/src/mage/cards/z/ZenosYaeGalvus.java +++ b/Mage.Sets/src/mage/cards/z/ZenosYaeGalvus.java @@ -88,10 +88,11 @@ enum ZenosYaeGalvusPredicate implements ObjectSourcePlayerPredicate { @Override public boolean apply(ObjectSourcePlayer input, Game game) { + int zcc = game.getState().getZoneChangeCounter(input.getSource().getSourceId()); return flag == Optional - .ofNullable(CardUtil.getObjectZoneString( + .of(CardUtil.getObjectZoneString( "chosenCreature", input.getSource().getSourceId(), game, - input.getSource().getSourceObjectZoneChangeCounter(), false + zcc, false )) .map(game.getState()::getValue) .filter(MageObjectReference.class::isInstance) diff --git a/Mage.Sets/src/mage/cards/z/ZidaneTantalusThief.java b/Mage.Sets/src/mage/cards/z/ZidaneTantalusThief.java new file mode 100644 index 00000000000..b93220703dd --- /dev/null +++ b/Mage.Sets/src/mage/cards/z/ZidaneTantalusThief.java @@ -0,0 +1,93 @@ +package mage.cards.z; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.continuous.GainControlTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Controllable; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.token.TreasureToken; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ZidaneTantalusThief extends CardImpl { + + public ZidaneTantalusThief(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.MUTANT); + this.subtype.add(SubType.SCOUT); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When Zidane enters, gain control of target creature an opponent controls until end of turn. Untap it. It gains lifelink and haste until end of turn. + Ability ability = new EntersBattlefieldTriggeredAbility(new GainControlTargetEffect(Duration.EndOfTurn)); + ability.addEffect(new UntapTargetEffect("untap it")); + ability.addEffect(new GainAbilityTargetEffect(LifelinkAbility.getInstance()).setText("It gains lifelink")); + ability.addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance()).setText("and haste until end of turn")); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + this.addAbility(ability); + + // Whenever an opponent gains control of a permanent from you, create a Treasure token. + this.addAbility(new ZidaneTantalusThiefTriggeredAbility()); + } + + private ZidaneTantalusThief(final ZidaneTantalusThief card) { + super(card); + } + + @Override + public ZidaneTantalusThief copy() { + return new ZidaneTantalusThief(this); + } +} + +class ZidaneTantalusThiefTriggeredAbility extends TriggeredAbilityImpl { + + ZidaneTantalusThiefTriggeredAbility() { + super(Zone.BATTLEFIELD, new CreateTokenEffect(new TreasureToken())); + setTriggerPhrase("Whenever an opponent gains control of a permanent from you, "); + } + + private ZidaneTantalusThiefTriggeredAbility(final ZidaneTantalusThiefTriggeredAbility ability) { + super(ability); + } + + @Override + public ZidaneTantalusThiefTriggeredAbility copy() { + return new ZidaneTantalusThiefTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.LOST_CONTROL; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return isControlledBy(event.getPlayerId()) + && Optional + .ofNullable(event.getTargetId()) + .map(game::getPermanent) + .map(Controllable::getControllerId) + .map(uuid -> game.getOpponents(getControllerId()).contains(uuid)) + .orElse(false); + } +} diff --git a/Mage.Sets/src/mage/cards/z/ZirilanOfTheClaw.java b/Mage.Sets/src/mage/cards/z/ZirilanOfTheClaw.java index aaefa4c9f1e..f012c8275c9 100644 --- a/Mage.Sets/src/mage/cards/z/ZirilanOfTheClaw.java +++ b/Mage.Sets/src/mage/cards/z/ZirilanOfTheClaw.java @@ -24,6 +24,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInLibrary; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @@ -83,7 +84,7 @@ class ZirilanOfTheClawEffect extends OneShotEffect { Card card = controller.getLibrary().getCard(target.getFirstTarget(), game); if (card != null) { controller.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { // gains haste ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn); diff --git a/Mage.Sets/src/mage/cards/z/ZodiarkUmbralGod.java b/Mage.Sets/src/mage/cards/z/ZodiarkUmbralGod.java new file mode 100644 index 00000000000..e3e13d4075f --- /dev/null +++ b/Mage.Sets/src/mage/cards/z/ZodiarkUmbralGod.java @@ -0,0 +1,126 @@ +package mage.cards.z; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SacrificePermanentTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicate; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.CanBeSacrificedPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.TargetPermanent; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ZodiarkUmbralGod extends CardImpl { + + public ZodiarkUmbralGod(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{B}{B}{B}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.GOD); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Indestructible + this.addAbility(IndestructibleAbility.getInstance()); + + // When Zodiark enters, each player sacrifices half the non-God creatures they control of their choice, rounded down. + this.addAbility(new EntersBattlefieldTriggeredAbility(new ZodiarkUmbralGodEffect())); + + // Whenever a player sacrifices another creature, put a +1/+1 counter on Zodiark. + this.addAbility(new SacrificePermanentTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), + StaticFilters.FILTER_ANOTHER_CREATURE, TargetController.ANY + )); + } + + private ZodiarkUmbralGod(final ZodiarkUmbralGod card) { + super(card); + } + + @Override + public ZodiarkUmbralGod copy() { + return new ZodiarkUmbralGod(this); + } +} + +class ZodiarkUmbralGodEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterControlledPermanent("non-God creatures you control"); + private static final FilterPermanent filter2 = new FilterControlledPermanent("non-God creatures you control"); + private static final Predicate predicate = Predicates.not(SubType.GOD.getPredicate()); + + static { + filter.add(predicate); + filter2.add(predicate); + filter2.add(CanBeSacrificedPredicate.instance); + } + + ZodiarkUmbralGodEffect() { + super(Outcome.Benefit); + staticText = "each player sacrifices half the non-God creatures they control of their choice, rounded down"; + } + + private ZodiarkUmbralGodEffect(final ZodiarkUmbralGodEffect effect) { + super(effect); + } + + @Override + public ZodiarkUmbralGodEffect copy() { + return new ZodiarkUmbralGodEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Set permanents = new HashSet<>(); + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + // need to take into account how many creatures can actually be sacrificed + int count = Math.min( + game.getBattlefield().count(filter, playerId, source, game) / 2, + game.getBattlefield().count(filter2, playerId, source, game) + ); + if (count < 1) { + continue; + } + TargetPermanent target = new TargetPermanent(count, count, filter2, true); + target.withChooseHint("to sacrifice"); + player.choose(outcome, target, source, game); + target.getTargets() + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .forEach(permanents::add); + } + if (permanents.isEmpty()) { + return false; + } + for (Permanent permanent : permanents) { + permanent.sacrifice(source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/z/ZombieCannibal.java b/Mage.Sets/src/mage/cards/z/ZombieCannibal.java index d2bac983887..ad8c8915e03 100644 --- a/Mage.Sets/src/mage/cards/z/ZombieCannibal.java +++ b/Mage.Sets/src/mage/cards/z/ZombieCannibal.java @@ -12,7 +12,7 @@ import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterCard; import mage.target.common.TargetCardInGraveyard; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import java.util.UUID; @@ -34,7 +34,7 @@ public final class ZombieCannibal extends CardImpl { Effect effect = new ExileTargetEffect(null, "", Zone.GRAVEYARD); Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(effect, true, true); ability.addTarget(new TargetCardInGraveyard(filterGraveyardCard)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster(true)); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster(true)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/z/ZurgoAndOjutai.java b/Mage.Sets/src/mage/cards/z/ZurgoAndOjutai.java index 5bd7714092f..0425fd2395e 100644 --- a/Mage.Sets/src/mage/cards/z/ZurgoAndOjutai.java +++ b/Mage.Sets/src/mage/cards/z/ZurgoAndOjutai.java @@ -57,7 +57,7 @@ public final class ZurgoAndOjutai extends CardImpl { // Zurgo and Ojutai has hexproof as long as it entered the battlefield this turn. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( new GainAbilitySourceEffect(HexproofAbility.getInstance(), Duration.WhileOnBattlefield), - SourceEnteredThisTurnCondition.instance, "{this} has hexproof as long as it entered the battlefield this turn" + SourceEnteredThisTurnCondition.DID, "{this} has hexproof as long as it entered the battlefield this turn" ))); // Whenever one or more Dragons you control deal combat damage to a player or battle, look at the top three cards of your library. Put one of them into your hand and the rest on the bottom of your library in any order. You may return one of those Dragons to its owner's hand. diff --git a/Mage.Sets/src/mage/sets/AssassinsCreed.java b/Mage.Sets/src/mage/sets/AssassinsCreed.java index 5925cca943d..56755d60e15 100644 --- a/Mage.Sets/src/mage/sets/AssassinsCreed.java +++ b/Mage.Sets/src/mage/sets/AssassinsCreed.java @@ -237,9 +237,9 @@ public final class AssassinsCreed extends ExpansionSet { cards.add(new SetCardInfo("Poison-Blade Mentor", 288, Rarity.UNCOMMON, mage.cards.p.PoisonBladeMentor.class)); cards.add(new SetCardInfo("Propaganda", 195, Rarity.UNCOMMON, mage.cards.p.Propaganda.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Propaganda", 85, Rarity.UNCOMMON, mage.cards.p.Propaganda.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Ratonhnhake:ton", 150, Rarity.RARE, mage.cards.r.Ratonhnhaketon.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Ratonhnhake:ton", 244, Rarity.RARE, mage.cards.r.Ratonhnhaketon.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Ratonhnhake:ton", 62, Rarity.RARE, mage.cards.r.Ratonhnhakton.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ratonhnhaketon", 150, Rarity.RARE, mage.cards.r.Ratonhnhaketon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ratonhnhaketon", 244, Rarity.RARE, mage.cards.r.Ratonhnhaketon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ratonhnhaketon", 62, Rarity.RARE, mage.cards.r.Ratonhnhaketon.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Raven Clan War-Axe", 297, Rarity.RARE, mage.cards.r.RavenClanWarAxe.class)); cards.add(new SetCardInfo("Reconnaissance", 179, Rarity.UNCOMMON, mage.cards.r.Reconnaissance.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Reconnaissance", 82, Rarity.UNCOMMON, mage.cards.r.Reconnaissance.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage.Sets/src/mage/sets/CommanderLegendsBattleForBaldursGate.java b/Mage.Sets/src/mage/sets/CommanderLegendsBattleForBaldursGate.java index a789aee7419..76a45438046 100644 --- a/Mage.Sets/src/mage/sets/CommanderLegendsBattleForBaldursGate.java +++ b/Mage.Sets/src/mage/sets/CommanderLegendsBattleForBaldursGate.java @@ -206,9 +206,9 @@ public final class CommanderLegendsBattleForBaldursGate extends ExpansionSet { cards.add(new SetCardInfo("Cloudkill", 121, Rarity.UNCOMMON, mage.cards.c.Cloudkill.class)); cards.add(new SetCardInfo("Colossal Badger", 223, Rarity.COMMON, mage.cards.c.ColossalBadger.class)); cards.add(new SetCardInfo("Command Tower", 351, Rarity.COMMON, mage.cards.c.CommandTower.class)); - //cards.add(new SetCardInfo("Commander Liara Portyr", 270, Rarity.UNCOMMON, mage.cards.c.CommanderLiaraPortyr.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Commander Liara Portyr", 418, Rarity.UNCOMMON, mage.cards.c.CommanderLiaraPortyr.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Commander Liara Portyr", 529, Rarity.UNCOMMON, mage.cards.c.CommanderLiaraPortyr.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Commander Liara Portyr", 270, Rarity.UNCOMMON, mage.cards.c.CommanderLiaraPortyr.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Commander Liara Portyr", 418, Rarity.UNCOMMON, mage.cards.c.CommanderLiaraPortyr.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Commander Liara Portyr", 529, Rarity.UNCOMMON, mage.cards.c.CommanderLiaraPortyr.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Compulsive Research", 715, Rarity.COMMON, mage.cards.c.CompulsiveResearch.class)); cards.add(new SetCardInfo("Cone of Cold", 61, Rarity.UNCOMMON, mage.cards.c.ConeOfCold.class)); cards.add(new SetCardInfo("Consuming Aberration", 840, Rarity.RARE, mage.cards.c.ConsumingAberration.class)); @@ -571,8 +571,8 @@ public final class CommanderLegendsBattleForBaldursGate extends ExpansionSet { cards.add(new SetCardInfo("Mindblade Render", 762, Rarity.RARE, mage.cards.m.MindbladeRender.class)); cards.add(new SetCardInfo("Mindcrank", 866, Rarity.UNCOMMON, mage.cards.m.Mindcrank.class)); cards.add(new SetCardInfo("Minimus Containment", 34, Rarity.COMMON, mage.cards.m.MinimusContainment.class)); - //cards.add(new SetCardInfo("Minsc & Boo, Timeless Heroes", 285, Rarity.MYTHIC, mage.cards.m.MinscAndBooTimelessHeroes.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Minsc & Boo, Timeless Heroes", 363, Rarity.MYTHIC, mage.cards.m.MinscAndBooTimelessHeroes.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Minsc & Boo, Timeless Heroes", 285, Rarity.MYTHIC, mage.cards.m.MinscBooTimelessHeroes.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Minsc & Boo, Timeless Heroes", 363, Rarity.MYTHIC, mage.cards.m.MinscBooTimelessHeroes.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Minthara, Merciless Soul", 286, Rarity.UNCOMMON, mage.cards.m.MintharaMercilessSoul.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Minthara, Merciless Soul", 432, Rarity.UNCOMMON, mage.cards.m.MintharaMercilessSoul.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Minthara, Merciless Soul", 543, Rarity.UNCOMMON, mage.cards.m.MintharaMercilessSoul.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage.Sets/src/mage/sets/DoctorWho.java b/Mage.Sets/src/mage/sets/DoctorWho.java index 7e00a80f7e3..e90aca9d94d 100644 --- a/Mage.Sets/src/mage/sets/DoctorWho.java +++ b/Mage.Sets/src/mage/sets/DoctorWho.java @@ -61,10 +61,10 @@ public final class DoctorWho extends ExpansionSet { cards.add(new SetCardInfo("As Foretold", 805, Rarity.RARE, mage.cards.a.AsForetold.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ash Barrens", 257, Rarity.UNCOMMON, mage.cards.a.AshBarrens.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ash Barrens", 848, Rarity.UNCOMMON, mage.cards.a.AshBarrens.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Ashad, the Lone Cyberman", 113, Rarity.RARE, mage.cards.a.AshadTheLoneCyberman.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Ashad, the Lone Cyberman", 402, Rarity.RARE, mage.cards.a.AshadTheLoneCyberman.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Ashad, the Lone Cyberman", 718, Rarity.RARE, mage.cards.a.AshadTheLoneCyberman.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Ashad, the Lone Cyberman", 993, Rarity.RARE, mage.cards.a.AshadTheLoneCyberman.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ashad, the Lone Cyberman", 113, Rarity.RARE, mage.cards.a.AshadTheLoneCyberman.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ashad, the Lone Cyberman", 402, Rarity.RARE, mage.cards.a.AshadTheLoneCyberman.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ashad, the Lone Cyberman", 718, Rarity.RARE, mage.cards.a.AshadTheLoneCyberman.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ashad, the Lone Cyberman", 993, Rarity.RARE, mage.cards.a.AshadTheLoneCyberman.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Astrid Peth", 11, Rarity.RARE, mage.cards.a.AstridPeth.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Astrid Peth", 334, Rarity.RARE, mage.cards.a.AstridPeth.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Astrid Peth", 616, Rarity.RARE, mage.cards.a.AstridPeth.class, NON_FULL_USE_VARIOUS)); @@ -218,10 +218,10 @@ public final class DoctorWho extends ExpansionSet { cards.add(new SetCardInfo("Dan Lewis", 683, Rarity.RARE, mage.cards.d.DanLewis.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dan Lewis", 78, Rarity.RARE, mage.cards.d.DanLewis.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dan Lewis", 971, Rarity.RARE, mage.cards.d.DanLewis.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Danny Pink", 356, Rarity.RARE, mage.cards.d.DannyPink.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Danny Pink", 39, Rarity.RARE, mage.cards.d.DannyPink.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Danny Pink", 644, Rarity.RARE, mage.cards.d.DannyPink.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Danny Pink", 947, Rarity.RARE, mage.cards.d.DannyPink.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Danny Pink", 356, Rarity.RARE, mage.cards.d.DannyPink.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Danny Pink", 39, Rarity.RARE, mage.cards.d.DannyPink.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Danny Pink", 644, Rarity.RARE, mage.cards.d.DannyPink.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Danny Pink", 947, Rarity.RARE, mage.cards.d.DannyPink.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Darkwater Catacombs", 1078, Rarity.RARE, mage.cards.d.DarkwaterCatacombs.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Darkwater Catacombs", 269, Rarity.RARE, mage.cards.d.DarkwaterCatacombs.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Darkwater Catacombs", 487, Rarity.RARE, mage.cards.d.DarkwaterCatacombs.class, NON_FULL_USE_VARIOUS)); @@ -295,10 +295,10 @@ public final class DoctorWho extends ExpansionSet { cards.add(new SetCardInfo("Ensnared by the Mara", 689, Rarity.RARE, mage.cards.e.EnsnaredByTheMara.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ensnared by the Mara", 84, Rarity.RARE, mage.cards.e.EnsnaredByTheMara.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ensnared by the Mara", 975, Rarity.RARE, mage.cards.e.EnsnaredByTheMara.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Everybody Lives!", 18, Rarity.RARE, mage.cards.e.EverybodyLives.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Everybody Lives!", 338, Rarity.RARE, mage.cards.e.EverybodyLives.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Everybody Lives!", 623, Rarity.RARE, mage.cards.e.EverybodyLives.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Everybody Lives!", 929, Rarity.RARE, mage.cards.e.EverybodyLives.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Everybody Lives!", 18, Rarity.RARE, mage.cards.e.EverybodyLives.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Everybody Lives!", 338, Rarity.RARE, mage.cards.e.EverybodyLives.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Everybody Lives!", 623, Rarity.RARE, mage.cards.e.EverybodyLives.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Everybody Lives!", 929, Rarity.RARE, mage.cards.e.EverybodyLives.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Everything Comes to Dust", 19, Rarity.RARE, mage.cards.e.EverythingComesToDust.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Everything Comes to Dust", 339, Rarity.RARE, mage.cards.e.EverythingComesToDust.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Everything Comes to Dust", 624, Rarity.RARE, mage.cards.e.EverythingComesToDust.class, NON_FULL_USE_VARIOUS)); @@ -877,8 +877,8 @@ public final class DoctorWho extends ExpansionSet { cards.add(new SetCardInfo("The Caves of Androzani", 15, Rarity.RARE, mage.cards.t.TheCavesOfAndrozani.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Caves of Androzani", 620, Rarity.RARE, mage.cards.t.TheCavesOfAndrozani.class, NON_FULL_USE_VARIOUS)); //cards.add(new SetCardInfo("The Cheetah Planet", 574, Rarity.COMMON, mage.cards.t.TheCheetahPlanet.class)); - //cards.add(new SetCardInfo("The Curse of Fenric", 118, Rarity.RARE, mage.cards.t.TheCurseOfFenric.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Curse of Fenric", 723, Rarity.RARE, mage.cards.t.TheCurseOfFenric.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Curse of Fenric", 118, Rarity.RARE, mage.cards.t.TheCurseOfFenric.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Curse of Fenric", 723, Rarity.RARE, mage.cards.t.TheCurseOfFenric.class, NON_FULL_USE_VARIOUS)); //cards.add(new SetCardInfo("The Cyber-Controller", 119, Rarity.RARE, mage.cards.t.TheCyberController.class, NON_FULL_USE_VARIOUS)); //cards.add(new SetCardInfo("The Cyber-Controller", 405, Rarity.RARE, mage.cards.t.TheCyberController.class, NON_FULL_USE_VARIOUS)); //cards.add(new SetCardInfo("The Cyber-Controller", 724, Rarity.RARE, mage.cards.t.TheCyberController.class, NON_FULL_USE_VARIOUS)); @@ -931,6 +931,7 @@ public final class DoctorWho extends ExpansionSet { //cards.add(new SetCardInfo("The Five Doctors", 394, Rarity.RARE, mage.cards.t.TheFiveDoctors.class, NON_FULL_USE_VARIOUS)); //cards.add(new SetCardInfo("The Five Doctors", 706, Rarity.RARE, mage.cards.t.TheFiveDoctors.class, NON_FULL_USE_VARIOUS)); //cards.add(new SetCardInfo("The Five Doctors", 985, Rarity.RARE, mage.cards.t.TheFiveDoctors.class, NON_FULL_USE_VARIOUS)); + //cards.add(new SetCardInfo("The Five Doctors", 101, Rarity.RARE, mage.cards.t.TheFiveDoctors.class)); cards.add(new SetCardInfo("The Flood of Mars", 360, Rarity.RARE, mage.cards.t.TheFloodOfMars.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Flood of Mars", 45, Rarity.RARE, mage.cards.t.TheFloodOfMars.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Flood of Mars", 650, Rarity.RARE, mage.cards.t.TheFloodOfMars.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java b/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java index bbcb0411918..18abe25f7d0 100644 --- a/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java +++ b/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java @@ -278,6 +278,9 @@ public final class DuskmournHouseOfHorror extends ExpansionSet { cards.add(new SetCardInfo("Razorkin Needlehead", 153, Rarity.RARE, mage.cards.r.RazorkinNeedlehead.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Razorkin Needlehead", 347, Rarity.RARE, mage.cards.r.RazorkinNeedlehead.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Razortrap Gorge", 267, Rarity.COMMON, mage.cards.r.RazortrapGorge.class)); + cards.add(new SetCardInfo("Reluctant Role Model", 26, Rarity.RARE, mage.cards.r.ReluctantRoleModel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reluctant Role Model", 289, Rarity.RARE, mage.cards.r.ReluctantRoleModel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reluctant Role Model", 303, Rarity.RARE, mage.cards.r.ReluctantRoleModel.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Resurrected Cultist", 115, Rarity.COMMON, mage.cards.r.ResurrectedCultist.class)); cards.add(new SetCardInfo("Rip, Spawn Hunter", 228, Rarity.RARE, mage.cards.r.RipSpawnHunter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rip, Spawn Hunter", 362, Rarity.RARE, mage.cards.r.RipSpawnHunter.class, NON_FULL_USE_VARIOUS)); @@ -366,7 +369,9 @@ public final class DuskmournHouseOfHorror extends ExpansionSet { cards.add(new SetCardInfo("Valgavoth's Lair", 327, Rarity.RARE, mage.cards.v.ValgavothsLair.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Valgavoth's Onslaught", 204, Rarity.RARE, mage.cards.v.ValgavothsOnslaught.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Valgavoth's Onslaught", 324, Rarity.RARE, mage.cards.v.ValgavothsOnslaught.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Valgavoth, Terror Eater", 120, Rarity.MYTHIC, mage.cards.v.ValgavothTerrorEater.class)); + cards.add(new SetCardInfo("Valgavoth, Terror Eater", 120, Rarity.MYTHIC, mage.cards.v.ValgavothTerrorEater.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Valgavoth, Terror Eater", 352, Rarity.MYTHIC, mage.cards.v.ValgavothTerrorEater.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Valgavoth, Terror Eater", 407, Rarity.MYTHIC, mage.cards.v.ValgavothTerrorEater.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Vanish from Sight", 82, Rarity.COMMON, mage.cards.v.VanishFromSight.class)); cards.add(new SetCardInfo("Vengeful Possession", 162, Rarity.UNCOMMON, mage.cards.v.VengefulPossession.class)); cards.add(new SetCardInfo("Veteran Survivor", 40, Rarity.UNCOMMON, mage.cards.v.VeteranSurvivor.class)); diff --git a/Mage.Sets/src/mage/sets/EternalWeekend.java b/Mage.Sets/src/mage/sets/EternalWeekend.java index 093c5bd439c..fd66aed343f 100644 --- a/Mage.Sets/src/mage/sets/EternalWeekend.java +++ b/Mage.Sets/src/mage/sets/EternalWeekend.java @@ -26,6 +26,8 @@ public class EternalWeekend extends ExpansionSet { cards.add(new SetCardInfo("Gush", "2022a", Rarity.RARE, mage.cards.g.Gush.class, RETRO_ART)); cards.add(new SetCardInfo("Mental Misstep", "2023a", Rarity.RARE, mage.cards.m.MentalMisstep.class, RETRO_ART)); cards.add(new SetCardInfo("Ponder", "2022b", Rarity.RARE, mage.cards.p.Ponder.class, RETRO_ART)); + cards.add(new SetCardInfo("Tendrils of Agony", "2025a", Rarity.RARE, mage.cards.t.TendrilsOfAgony.class, RETRO_ART)); cards.add(new SetCardInfo("Tinker", "2024a", Rarity.MYTHIC, mage.cards.t.Tinker.class, RETRO_ART)); + cards.add(new SetCardInfo("Trinisphere", "2025b", Rarity.MYTHIC, mage.cards.t.Trinisphere.class, RETRO_ART)); } } diff --git a/Mage.Sets/src/mage/sets/FinalFantasy.java b/Mage.Sets/src/mage/sets/FinalFantasy.java index eeb23735f87..6d98c247387 100644 --- a/Mage.Sets/src/mage/sets/FinalFantasy.java +++ b/Mage.Sets/src/mage/sets/FinalFantasy.java @@ -20,35 +20,81 @@ public final class FinalFantasy extends ExpansionSet { this.blockName = "Final Fantasy"; // for sorting in GUI this.hasBasicLands = true; + this.enablePlayBooster(Integer.MAX_VALUE); + this.numBoosterDoubleFaced = -1; + cards.add(new SetCardInfo("A Realm Reborn", 196, Rarity.RARE, mage.cards.a.ARealmReborn.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("A Realm Reborn", 344, Rarity.RARE, mage.cards.a.ARealmReborn.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Absolute Virtue", 212, Rarity.MYTHIC, mage.cards.a.AbsoluteVirtue.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Absolute Virtue", 476, Rarity.MYTHIC, mage.cards.a.AbsoluteVirtue.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Adelbert Steiner", 3, Rarity.UNCOMMON, mage.cards.a.AdelbertSteiner.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Adelbert Steiner", 422, Rarity.UNCOMMON, mage.cards.a.AdelbertSteiner.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Adventurer's Airship", 252, Rarity.COMMON, mage.cards.a.AdventurersAirship.class)); cards.add(new SetCardInfo("Adventurer's Inn", 271, Rarity.COMMON, mage.cards.a.AdventurersInn.class)); cards.add(new SetCardInfo("Aerith Gainsborough", 374, Rarity.RARE, mage.cards.a.AerithGainsborough.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Aerith Gainsborough", 4, Rarity.RARE, mage.cards.a.AerithGainsborough.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Aerith Gainsborough", 423, Rarity.RARE, mage.cards.a.AerithGainsborough.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Aerith Gainsborough", 519, Rarity.RARE, mage.cards.a.AerithGainsborough.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Aerith Rescue Mission", 5, Rarity.COMMON, mage.cards.a.AerithRescueMission.class)); + cards.add(new SetCardInfo("Aettir and Priwen", 253, Rarity.MYTHIC, mage.cards.a.AettirAndPriwen.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Aettir and Priwen", 350, Rarity.MYTHIC, mage.cards.a.AettirAndPriwen.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ahriman", 87, Rarity.COMMON, mage.cards.a.Ahriman.class)); + cards.add(new SetCardInfo("Airship Crash", 171, Rarity.COMMON, mage.cards.a.AirshipCrash.class)); cards.add(new SetCardInfo("Al Bhed Salvagers", 88, Rarity.UNCOMMON, mage.cards.a.AlBhedSalvagers.class)); + cards.add(new SetCardInfo("Ambrosia Whiteheart", 325, Rarity.UNCOMMON, mage.cards.a.AmbrosiaWhiteheart.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ambrosia Whiteheart", 424, Rarity.UNCOMMON, mage.cards.a.AmbrosiaWhiteheart.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ambrosia Whiteheart", 6, Rarity.UNCOMMON, mage.cards.a.AmbrosiaWhiteheart.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ancient Adamantoise", 172, Rarity.MYTHIC, mage.cards.a.AncientAdamantoise.class)); cards.add(new SetCardInfo("Ardyn, the Usurper", 315, Rarity.RARE, mage.cards.a.ArdynTheUsurper.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ardyn, the Usurper", 379, Rarity.RARE, mage.cards.a.ArdynTheUsurper.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ardyn, the Usurper", 444, Rarity.RARE, mage.cards.a.ArdynTheUsurper.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ardyn, the Usurper", 524, Rarity.RARE, mage.cards.a.ArdynTheUsurper.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ardyn, the Usurper", 89, Rarity.RARE, mage.cards.a.ArdynTheUsurper.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ashe, Princess of Dalmasca", 425, Rarity.UNCOMMON, mage.cards.a.AshePrincessOfDalmasca.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ashe, Princess of Dalmasca", 7, Rarity.UNCOMMON, mage.cards.a.AshePrincessOfDalmasca.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Astrologian's Planisphere", 46, Rarity.RARE, mage.cards.a.AstrologiansPlanisphere.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Astrologian's Planisphere", 581, Rarity.RARE, mage.cards.a.AstrologiansPlanisphere.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Auron's Inspiration", 8, Rarity.UNCOMMON, mage.cards.a.AuronsInspiration.class)); + cards.add(new SetCardInfo("Bahamut, Warden of Light", 16, Rarity.RARE, mage.cards.b.BahamutWardenOfLight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bahamut, Warden of Light", 376, Rarity.RARE, mage.cards.b.BahamutWardenOfLight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bahamut, Warden of Light", 428, Rarity.RARE, mage.cards.b.BahamutWardenOfLight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bahamut, Warden of Light", 521, Rarity.RARE, mage.cards.b.BahamutWardenOfLight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Balamb Garden, Airborne", 272, Rarity.RARE, mage.cards.b.BalambGardenAirborne.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Balamb Garden, Airborne", 354, Rarity.RARE, mage.cards.b.BalambGardenAirborne.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Balamb Garden, SeeD Academy", 272, Rarity.RARE, mage.cards.b.BalambGardenSeeDAcademy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Balamb Garden, SeeD Academy", 354, Rarity.RARE, mage.cards.b.BalambGardenSeeDAcademy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Balamb T-Rexaur", 173, Rarity.COMMON, mage.cards.b.BalambTRexaur.class)); + cards.add(new SetCardInfo("Balthier and Fran", 213, Rarity.RARE, mage.cards.b.BalthierAndFran.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Balthier and Fran", 319, Rarity.RARE, mage.cards.b.BalthierAndFran.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Balthier and Fran", 393, Rarity.RARE, mage.cards.b.BalthierAndFran.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Balthier and Fran", 477, Rarity.RARE, mage.cards.b.BalthierAndFran.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Balthier and Fran", 538, Rarity.RARE, mage.cards.b.BalthierAndFran.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Bard's Bow", 174, Rarity.COMMON, mage.cards.b.BardsBow.class)); + cards.add(new SetCardInfo("Baron, Airship Kingdom", 273, Rarity.COMMON, mage.cards.b.BaronAirshipKingdom.class)); cards.add(new SetCardInfo("Barret Wallace", 129, Rarity.UNCOMMON, mage.cards.b.BarretWallace.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Barret Wallace", 457, Rarity.UNCOMMON, mage.cards.b.BarretWallace.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Barret Wallace", 584, Rarity.UNCOMMON, mage.cards.b.BarretWallace.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Bartz and Boko", 175, Rarity.RARE, mage.cards.b.BartzAndBoko.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Bartz and Boko", 469, Rarity.RARE, mage.cards.b.BartzAndBoko.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Battle Menu", 9, Rarity.UNCOMMON, mage.cards.b.BattleMenu.class)); cards.add(new SetCardInfo("Beatrix, Loyal General", 426, Rarity.RARE, mage.cards.b.BeatrixLoyalGeneral.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Beatrix, Loyal General", 554, Rarity.RARE, mage.cards.b.BeatrixLoyalGeneral.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Black Chocobo", 201, Rarity.UNCOMMON, mage.cards.b.BlackChocobo.class)); cards.add(new SetCardInfo("Black Mage's Rod", 90, Rarity.COMMON, mage.cards.b.BlackMagesRod.class)); + cards.add(new SetCardInfo("Black Waltz No. 3", 214, Rarity.UNCOMMON, mage.cards.b.BlackWaltzNo3.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Black Waltz No. 3", 478, Rarity.UNCOMMON, mage.cards.b.BlackWaltzNo3.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Blazing Bomb", 130, Rarity.COMMON, mage.cards.b.BlazingBomb.class)); + cards.add(new SetCardInfo("Blitzball Shot", 176, Rarity.COMMON, mage.cards.b.BlitzballShot.class)); + cards.add(new SetCardInfo("Blitzball", 254, Rarity.COMMON, mage.cards.b.Blitzball.class)); cards.add(new SetCardInfo("Braska's Final Aeon", 104, Rarity.RARE, mage.cards.b.BraskasFinalAeon.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Braska's Final Aeon", 363, Rarity.RARE, mage.cards.b.BraskasFinalAeon.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Braska's Final Aeon", 448, Rarity.RARE, mage.cards.b.BraskasFinalAeon.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Buster Sword", 255, Rarity.MYTHIC, mage.cards.b.BusterSword.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Buster Sword", 351, Rarity.MYTHIC, mage.cards.b.BusterSword.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cactuar", 177, Rarity.UNCOMMON, mage.cards.c.Cactuar.class)); + cards.add(new SetCardInfo("Call the Mountain Chocobo", 131, Rarity.COMMON, mage.cards.c.CallTheMountainChocobo.class)); cards.add(new SetCardInfo("Capital City", 274, Rarity.UNCOMMON, mage.cards.c.CapitalCity.class)); + cards.add(new SetCardInfo("Cargo Ship", 47, Rarity.UNCOMMON, mage.cards.c.CargoShip.class)); cards.add(new SetCardInfo("Cecil, Dark Knight", 380, Rarity.RARE, mage.cards.c.CecilDarkKnight.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Cecil, Dark Knight", 445, Rarity.RARE, mage.cards.c.CecilDarkKnight.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Cecil, Dark Knight", 525, Rarity.RARE, mage.cards.c.CecilDarkKnight.class, NON_FULL_USE_VARIOUS)); @@ -61,6 +107,9 @@ public final class FinalFantasy extends ExpansionSet { cards.add(new SetCardInfo("Chaos, the Endless", 486, Rarity.UNCOMMON, mage.cards.c.ChaosTheEndless.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Choco, Seeker of Paradise", 215, Rarity.RARE, mage.cards.c.ChocoSeekerOfParadise.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Choco, Seeker of Paradise", 479, Rarity.RARE, mage.cards.c.ChocoSeekerOfParadise.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Choco-Comet", 132, Rarity.UNCOMMON, mage.cards.c.ChocoComet.class)); + cards.add(new SetCardInfo("Chocobo Kick", 178, Rarity.COMMON, mage.cards.c.ChocoboKick.class)); + cards.add(new SetCardInfo("Chocobo Racetrack", 179, Rarity.UNCOMMON, mage.cards.c.ChocoboRacetrack.class)); cards.add(new SetCardInfo("Cid, Timeless Artificer", 216, Rarity.UNCOMMON, mage.cards.c.CidTimelessArtificer.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Cid, Timeless Artificer", 407, Rarity.UNCOMMON, mage.cards.c.CidTimelessArtificer.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Cid, Timeless Artificer", 408, Rarity.UNCOMMON, mage.cards.c.CidTimelessArtificer.class, NON_FULL_USE_VARIOUS)); @@ -79,11 +128,16 @@ public final class FinalFantasy extends ExpansionSet { cards.add(new SetCardInfo("Cid, Timeless Artificer", 480, Rarity.UNCOMMON, mage.cards.c.CidTimelessArtificer.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Circle of Power", 583, Rarity.UNCOMMON, mage.cards.c.CircleOfPower.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Circle of Power", 92, Rarity.UNCOMMON, mage.cards.c.CircleOfPower.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Clash of the Eikons", 180, Rarity.UNCOMMON, mage.cards.c.ClashOfTheEikons.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Clash of the Eikons", 341, Rarity.UNCOMMON, mage.cards.c.ClashOfTheEikons.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Clive's Hideaway", 275, Rarity.RARE, mage.cards.c.ClivesHideaway.class)); cards.add(new SetCardInfo("Clive, Ifrit's Dominant", 133, Rarity.MYTHIC, mage.cards.c.CliveIfritsDominant.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Clive, Ifrit's Dominant", 318, Rarity.MYTHIC, mage.cards.c.CliveIfritsDominant.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Clive, Ifrit's Dominant", 385, Rarity.MYTHIC, mage.cards.c.CliveIfritsDominant.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Clive, Ifrit's Dominant", 458, Rarity.MYTHIC, mage.cards.c.CliveIfritsDominant.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Clive, Ifrit's Dominant", 530, Rarity.MYTHIC, mage.cards.c.CliveIfritsDominant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cloud of Darkness", 217, Rarity.UNCOMMON, mage.cards.c.CloudOfDarkness.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cloud of Darkness", 481, Rarity.UNCOMMON, mage.cards.c.CloudOfDarkness.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Cloud, Midgar Mercenary", 10, Rarity.MYTHIC, mage.cards.c.CloudMidgarMercenary.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Cloud, Midgar Mercenary", 375, Rarity.MYTHIC, mage.cards.c.CloudMidgarMercenary.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Cloud, Midgar Mercenary", 427, Rarity.MYTHIC, mage.cards.c.CloudMidgarMercenary.class, NON_FULL_USE_VARIOUS)); @@ -92,29 +146,60 @@ public final class FinalFantasy extends ExpansionSet { cards.add(new SetCardInfo("Cloud, Planet's Champion", 552, Rarity.MYTHIC, mage.cards.c.CloudPlanetsChampion.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Cloudbound Moogle", 11, Rarity.COMMON, mage.cards.c.CloudboundMoogle.class)); cards.add(new SetCardInfo("Coeurl", 12, Rarity.COMMON, mage.cards.c.Coeurl.class)); + cards.add(new SetCardInfo("Coliseum Behemoth", 181, Rarity.UNCOMMON, mage.cards.c.ColiseumBehemoth.class)); + cards.add(new SetCardInfo("Combat Tutorial", 48, Rarity.COMMON, mage.cards.c.CombatTutorial.class)); cards.add(new SetCardInfo("Commune with Beavers", 182, Rarity.COMMON, mage.cards.c.CommuneWithBeavers.class)); cards.add(new SetCardInfo("Cooking Campsite", 31, Rarity.UNCOMMON, mage.cards.c.CookingCampsite.class)); + cards.add(new SetCardInfo("Coral Sword", 134, Rarity.UNCOMMON, mage.cards.c.CoralSword.class)); + cards.add(new SetCardInfo("Cornered by Black Mages", 93, Rarity.COMMON, mage.cards.c.CorneredByBlackMages.class)); + cards.add(new SetCardInfo("Crossroads Village", 276, Rarity.COMMON, mage.cards.c.CrossroadsVillage.class)); + cards.add(new SetCardInfo("Crystal Fragments", 13, Rarity.UNCOMMON, mage.cards.c.CrystalFragments.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Crystal Fragments", 357, Rarity.UNCOMMON, mage.cards.c.CrystalFragments.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Crystallized Serah", 240, Rarity.RARE, mage.cards.c.CrystallizedSerah.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Crystallized Serah", 506, Rarity.RARE, mage.cards.c.CrystallizedSerah.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dark Confidant", 334, Rarity.MYTHIC, mage.cards.d.DarkConfidant.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dark Confidant", 94, Rarity.MYTHIC, mage.cards.d.DarkConfidant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dark Knight's Greatsword", 95, Rarity.UNCOMMON, mage.cards.d.DarkKnightsGreatsword.class)); cards.add(new SetCardInfo("Deadly Embrace", 557, Rarity.RARE, mage.cards.d.DeadlyEmbrace.class)); + cards.add(new SetCardInfo("Delivery Moogle", 15, Rarity.UNCOMMON, mage.cards.d.DeliveryMoogle.class)); + cards.add(new SetCardInfo("Demon Wall", 97, Rarity.UNCOMMON, mage.cards.d.DemonWall.class)); + cards.add(new SetCardInfo("Diamond Weapon", 183, Rarity.UNCOMMON, mage.cards.d.DiamondWeapon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Diamond Weapon", 470, Rarity.UNCOMMON, mage.cards.d.DiamondWeapon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dion, Bahamut's Dominant", 16, Rarity.RARE, mage.cards.d.DionBahamutsDominant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dion, Bahamut's Dominant", 376, Rarity.RARE, mage.cards.d.DionBahamutsDominant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dion, Bahamut's Dominant", 428, Rarity.RARE, mage.cards.d.DionBahamutsDominant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dion, Bahamut's Dominant", 521, Rarity.RARE, mage.cards.d.DionBahamutsDominant.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dragoon's Lance", 17, Rarity.UNCOMMON, mage.cards.d.DragoonsLance.class)); cards.add(new SetCardInfo("Dragoon's Wyvern", 49, Rarity.COMMON, mage.cards.d.DragoonsWyvern.class)); cards.add(new SetCardInfo("Dreams of Laguna", 50, Rarity.COMMON, mage.cards.d.DreamsOfLaguna.class)); + cards.add(new SetCardInfo("Dwarven Castle Guard", 18, Rarity.COMMON, mage.cards.d.DwarvenCastleGuard.class)); + cards.add(new SetCardInfo("Eden, Seat of the Sanctum", 277, Rarity.UNCOMMON, mage.cards.e.EdenSeatOfTheSanctum.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eden, Seat of the Sanctum", 355, Rarity.UNCOMMON, mage.cards.e.EdenSeatOfTheSanctum.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Edgar, King of Figaro", 436, Rarity.RARE, mage.cards.e.EdgarKingOfFigaro.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Edgar, King of Figaro", 51, Rarity.RARE, mage.cards.e.EdgarKingOfFigaro.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eject", 52, Rarity.UNCOMMON, mage.cards.e.Eject.class)); + cards.add(new SetCardInfo("Elixir", 256, Rarity.UNCOMMON, mage.cards.e.Elixir.class)); cards.add(new SetCardInfo("Emet-Selch, Unsundered", 218, Rarity.MYTHIC, mage.cards.e.EmetSelchUnsundered.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Emet-Selch, Unsundered", 394, Rarity.MYTHIC, mage.cards.e.EmetSelchUnsundered.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Emet-Selch, Unsundered", 483, Rarity.MYTHIC, mage.cards.e.EmetSelchUnsundered.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Emet-Selch, Unsundered", 539, Rarity.MYTHIC, mage.cards.e.EmetSelchUnsundered.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Esper Origins", 185, Rarity.RARE, mage.cards.e.EsperOrigins.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Esper Origins", 370, Rarity.RARE, mage.cards.e.EsperOrigins.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Esper Terra", 245, Rarity.MYTHIC, mage.cards.e.EsperTerra.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Esper Terra", 323, Rarity.MYTHIC, mage.cards.e.EsperTerra.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Esper Terra", 511, Rarity.MYTHIC, mage.cards.e.EsperTerra.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ether", 53, Rarity.UNCOMMON, mage.cards.e.Ether.class)); cards.add(new SetCardInfo("Evil Reawakened", 98, Rarity.UNCOMMON, mage.cards.e.EvilReawakened.class)); + cards.add(new SetCardInfo("Excalibur II", 257, Rarity.RARE, mage.cards.e.ExcaliburII.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Excalibur II", 352, Rarity.RARE, mage.cards.e.ExcaliburII.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Exdeath, Void Warlock", 220, Rarity.UNCOMMON, mage.cards.e.ExdeathVoidWarlock.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Exdeath, Void Warlock", 485, Rarity.UNCOMMON, mage.cards.e.ExdeathVoidWarlock.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fang, Fearless l'Cie", 381, Rarity.UNCOMMON, mage.cards.f.FangFearlessLCie.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fang, Fearless l'Cie", 446, Rarity.UNCOMMON, mage.cards.f.FangFearlessLCie.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fang, Fearless l'Cie", 526, Rarity.UNCOMMON, mage.cards.f.FangFearlessLCie.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fang, Fearless l'Cie", 99, Rarity.UNCOMMON, mage.cards.f.FangFearlessLCie.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fate of the Sun-Cryst", 19, Rarity.COMMON, mage.cards.f.FateOfTheSunCryst.class)); + cards.add(new SetCardInfo("Fight On!", 100, Rarity.COMMON, mage.cards.f.FightOn.class)); cards.add(new SetCardInfo("Fire Magic", 136, Rarity.UNCOMMON, mage.cards.f.FireMagic.class)); cards.add(new SetCardInfo("Firion, Wild Rose Warrior", 137, Rarity.RARE, mage.cards.f.FirionWildRoseWarrior.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Firion, Wild Rose Warrior", 386, Rarity.RARE, mage.cards.f.FirionWildRoseWarrior.class, NON_FULL_USE_VARIOUS)); @@ -126,6 +211,12 @@ public final class FinalFantasy extends ExpansionSet { cards.add(new SetCardInfo("Forest", 576, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Freya Crescent", 138, Rarity.UNCOMMON, mage.cards.f.FreyaCrescent.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Freya Crescent", 460, Rarity.UNCOMMON, mage.cards.f.FreyaCrescent.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("From Father to Son", 20, Rarity.RARE, mage.cards.f.FromFatherToSon.class)); + cards.add(new SetCardInfo("G'raha Tia", 21, Rarity.UNCOMMON, mage.cards.g.GrahaTia.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("G'raha Tia", 429, Rarity.UNCOMMON, mage.cards.g.GrahaTia.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gaelicat", 22, Rarity.COMMON, mage.cards.g.Gaelicat.class)); + cards.add(new SetCardInfo("Gaius van Baelsar", 102, Rarity.UNCOMMON, mage.cards.g.GaiusVanBaelsar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gaius van Baelsar", 447, Rarity.UNCOMMON, mage.cards.g.GaiusVanBaelsar.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Galian Beast", 125, Rarity.RARE, mage.cards.g.GalianBeast.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Galian Beast", 383, Rarity.RARE, mage.cards.g.GalianBeast.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Galian Beast", 454, Rarity.RARE, mage.cards.g.GalianBeast.class, NON_FULL_USE_VARIOUS)); @@ -133,15 +224,45 @@ public final class FinalFantasy extends ExpansionSet { cards.add(new SetCardInfo("Galuf's Final Act", 186, Rarity.UNCOMMON, mage.cards.g.GalufsFinalAct.class)); cards.add(new SetCardInfo("Garland, Knight of Cornelia", 221, Rarity.UNCOMMON, mage.cards.g.GarlandKnightOfCornelia.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Garland, Knight of Cornelia", 486, Rarity.UNCOMMON, mage.cards.g.GarlandKnightOfCornelia.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Garnet, Princess of Alexandria", 222, Rarity.UNCOMMON, mage.cards.g.GarnetPrincessOfAlexandria.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Garnet, Princess of Alexandria", 487, Rarity.UNCOMMON, mage.cards.g.GarnetPrincessOfAlexandria.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Genji Glove", 258, Rarity.RARE, mage.cards.g.GenjiGlove.class)); + cards.add(new SetCardInfo("Gigantoad", 187, Rarity.COMMON, mage.cards.g.Gigantoad.class)); cards.add(new SetCardInfo("Gilgamesh, Master-at-Arms", 139, Rarity.RARE, mage.cards.g.GilgameshMasterAtArms.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gilgamesh, Master-at-Arms", 338, Rarity.RARE, mage.cards.g.GilgameshMasterAtArms.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gilgamesh, Master-at-Arms", 461, Rarity.RARE, mage.cards.g.GilgameshMasterAtArms.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Giott, King of the Dwarves", 223, Rarity.UNCOMMON, mage.cards.g.GiottKingOfTheDwarves.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Giott, King of the Dwarves", 488, Rarity.UNCOMMON, mage.cards.g.GiottKingOfTheDwarves.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gladiolus Amicitia", 224, Rarity.UNCOMMON, mage.cards.g.GladiolusAmicitia.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gladiolus Amicitia", 489, Rarity.UNCOMMON, mage.cards.g.GladiolusAmicitia.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gogo, Master of Mimicry", 377, Rarity.MYTHIC, mage.cards.g.GogoMasterOfMimicry.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gogo, Master of Mimicry", 437, Rarity.MYTHIC, mage.cards.g.GogoMasterOfMimicry.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gogo, Master of Mimicry", 522, Rarity.MYTHIC, mage.cards.g.GogoMasterOfMimicry.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gogo, Master of Mimicry", 54, Rarity.MYTHIC, mage.cards.g.GogoMasterOfMimicry.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gohn, Town of Ruin", 278, Rarity.COMMON, mage.cards.g.GohnTownOfRuin.class)); + cards.add(new SetCardInfo("Golbez, Crystal Collector", 225, Rarity.RARE, mage.cards.g.GolbezCrystalCollector.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Golbez, Crystal Collector", 395, Rarity.RARE, mage.cards.g.GolbezCrystalCollector.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Golbez, Crystal Collector", 490, Rarity.RARE, mage.cards.g.GolbezCrystalCollector.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Golbez, Crystal Collector", 540, Rarity.RARE, mage.cards.g.GolbezCrystalCollector.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gongaga, Reactor Town", 280, Rarity.COMMON, mage.cards.g.GongagaReactorTown.class)); + cards.add(new SetCardInfo("Goobbue Gardener", 188, Rarity.COMMON, mage.cards.g.GoobbueGardener.class)); + cards.add(new SetCardInfo("Gran Pulse Ochu", 189, Rarity.COMMON, mage.cards.g.GranPulseOchu.class)); + cards.add(new SetCardInfo("Guadosalam, Farplane Gateway", 281, Rarity.COMMON, mage.cards.g.GuadosalamFarplaneGateway.class)); + cards.add(new SetCardInfo("Gysahl Greens", 190, Rarity.COMMON, mage.cards.g.GysahlGreens.class)); cards.add(new SetCardInfo("Hades, Sorcerer of Eld", 218, Rarity.MYTHIC, mage.cards.h.HadesSorcererOfEld.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Hades, Sorcerer of Eld", 394, Rarity.MYTHIC, mage.cards.h.HadesSorcererOfEld.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Hades, Sorcerer of Eld", 483, Rarity.MYTHIC, mage.cards.h.HadesSorcererOfEld.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Hades, Sorcerer of Eld", 539, Rarity.MYTHIC, mage.cards.h.HadesSorcererOfEld.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Haste Magic", 140, Rarity.COMMON, mage.cards.h.HasteMagic.class)); + cards.add(new SetCardInfo("Hecteyes", 103, Rarity.COMMON, mage.cards.h.Hecteyes.class)); + cards.add(new SetCardInfo("Hill Gigas", 141, Rarity.COMMON, mage.cards.h.HillGigas.class)); + cards.add(new SetCardInfo("Hope Estheim", 226, Rarity.RARE, mage.cards.h.HopeEstheim.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hope Estheim", 396, Rarity.RARE, mage.cards.h.HopeEstheim.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hope Estheim", 491, Rarity.RARE, mage.cards.h.HopeEstheim.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hope Estheim", 541, Rarity.RARE, mage.cards.h.HopeEstheim.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hydaelyn, the Mothercrystal", 329, Rarity.RARE, mage.cards.h.HydaelynTheMothercrystal.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hydaelyn, the Mothercrystal", 39, Rarity.RARE, mage.cards.h.HydaelynTheMothercrystal.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hydaelyn, the Mothercrystal", 434, Rarity.RARE, mage.cards.h.HydaelynTheMothercrystal.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ice Flan", 55, Rarity.COMMON, mage.cards.i.IceFlan.class)); cards.add(new SetCardInfo("Ice Magic", 56, Rarity.COMMON, mage.cards.i.IceMagic.class)); cards.add(new SetCardInfo("Ifrit, Warden of Inferno", 133, Rarity.MYTHIC, mage.cards.i.IfritWardenOfInferno.class, NON_FULL_USE_VARIOUS)); @@ -149,7 +270,12 @@ public final class FinalFantasy extends ExpansionSet { cards.add(new SetCardInfo("Ifrit, Warden of Inferno", 385, Rarity.MYTHIC, mage.cards.i.IfritWardenOfInferno.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ifrit, Warden of Inferno", 458, Rarity.MYTHIC, mage.cards.i.IfritWardenOfInferno.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ifrit, Warden of Inferno", 530, Rarity.MYTHIC, mage.cards.i.IfritWardenOfInferno.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ignis Scientia", 227, Rarity.UNCOMMON, mage.cards.i.IgnisScientia.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ignis Scientia", 492, Rarity.UNCOMMON, mage.cards.i.IgnisScientia.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Il Mheg Pixie", 57, Rarity.UNCOMMON, mage.cards.i.IlMhegPixie.class)); + cards.add(new SetCardInfo("Insomnia, Crown City", 282, Rarity.COMMON, mage.cards.i.InsomniaCrownCity.class)); cards.add(new SetCardInfo("Instant Ramen", 259, Rarity.COMMON, mage.cards.i.InstantRamen.class)); + cards.add(new SetCardInfo("Iron Giant", 260, Rarity.COMMON, mage.cards.i.IronGiant.class)); cards.add(new SetCardInfo("Ishgard, the Holy See", 283, Rarity.RARE, mage.cards.i.IshgardTheHolySee.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ishgard, the Holy See", 310, Rarity.RARE, mage.cards.i.IshgardTheHolySee.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 297, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); @@ -160,71 +286,170 @@ public final class FinalFantasy extends ExpansionSet { cards.add(new SetCardInfo("Jecht, Reluctant Guardian", 104, Rarity.RARE, mage.cards.j.JechtReluctantGuardian.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jecht, Reluctant Guardian", 363, Rarity.RARE, mage.cards.j.JechtReluctantGuardian.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jecht, Reluctant Guardian", 448, Rarity.RARE, mage.cards.j.JechtReluctantGuardian.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jenova, Ancient Calamity", 228, Rarity.RARE, mage.cards.j.JenovaAncientCalamity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jenova, Ancient Calamity", 346, Rarity.RARE, mage.cards.j.JenovaAncientCalamity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jenova, Ancient Calamity", 493, Rarity.RARE, mage.cards.j.JenovaAncientCalamity.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jidoor, Aristocratic Capital", 284, Rarity.RARE, mage.cards.j.JidoorAristocraticCapital.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jidoor, Aristocratic Capital", 311, Rarity.RARE, mage.cards.j.JidoorAristocraticCapital.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jill, Shiva's Dominant", 378, Rarity.RARE, mage.cards.j.JillShivasDominant.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jill, Shiva's Dominant", 438, Rarity.RARE, mage.cards.j.JillShivasDominant.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jill, Shiva's Dominant", 523, Rarity.RARE, mage.cards.j.JillShivasDominant.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jill, Shiva's Dominant", 58, Rarity.RARE, mage.cards.j.JillShivasDominant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Joshua, Phoenix's Dominant", 229, Rarity.RARE, mage.cards.j.JoshuaPhoenixsDominant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Joshua, Phoenix's Dominant", 397, Rarity.RARE, mage.cards.j.JoshuaPhoenixsDominant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Joshua, Phoenix's Dominant", 494, Rarity.RARE, mage.cards.j.JoshuaPhoenixsDominant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Joshua, Phoenix's Dominant", 542, Rarity.RARE, mage.cards.j.JoshuaPhoenixsDominant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Judge Magister Gabranth", 230, Rarity.UNCOMMON, mage.cards.j.JudgeMagisterGabranth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Judge Magister Gabranth", 495, Rarity.UNCOMMON, mage.cards.j.JudgeMagisterGabranth.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Judgment Bolt", 559, Rarity.RARE, mage.cards.j.JudgmentBolt.class)); cards.add(new SetCardInfo("Jumbo Cactuar", 191, Rarity.RARE, mage.cards.j.JumboCactuar.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jumbo Cactuar", 343, Rarity.RARE, mage.cards.j.JumboCactuar.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Kain, Traitorous Dragoon", 105, Rarity.RARE, mage.cards.k.KainTraitorousDragoon.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Kain, Traitorous Dragoon", 316, Rarity.RARE, mage.cards.k.KainTraitorousDragoon.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Kain, Traitorous Dragoon", 449, Rarity.RARE, mage.cards.k.KainTraitorousDragoon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kefka, Court Mage", 231, Rarity.MYTHIC, mage.cards.k.KefkaCourtMage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kefka, Court Mage", 322, Rarity.MYTHIC, mage.cards.k.KefkaCourtMage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kefka, Court Mage", 398, Rarity.MYTHIC, mage.cards.k.KefkaCourtMage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kefka, Court Mage", 496, Rarity.MYTHIC, mage.cards.k.KefkaCourtMage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kefka, Court Mage", 543, Rarity.MYTHIC, mage.cards.k.KefkaCourtMage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kefka, Ruler of Ruin", 231, Rarity.MYTHIC, mage.cards.k.KefkaRulerOfRuin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kefka, Ruler of Ruin", 322, Rarity.MYTHIC, mage.cards.k.KefkaRulerOfRuin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kefka, Ruler of Ruin", 398, Rarity.MYTHIC, mage.cards.k.KefkaRulerOfRuin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kefka, Ruler of Ruin", 496, Rarity.MYTHIC, mage.cards.k.KefkaRulerOfRuin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kefka, Ruler of Ruin", 543, Rarity.MYTHIC, mage.cards.k.KefkaRulerOfRuin.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Kuja, Genome Sorcerer", 232, Rarity.RARE, mage.cards.k.KujaGenomeSorcerer.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Kuja, Genome Sorcerer", 399, Rarity.RARE, mage.cards.k.KujaGenomeSorcerer.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Kuja, Genome Sorcerer", 497, Rarity.RARE, mage.cards.k.KujaGenomeSorcerer.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Kuja, Genome Sorcerer", 544, Rarity.RARE, mage.cards.k.KujaGenomeSorcerer.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Laughing Mad", 143, Rarity.COMMON, mage.cards.l.LaughingMad.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Laughing Mad", 585, Rarity.COMMON, mage.cards.l.LaughingMad.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Light of Judgment", 144, Rarity.COMMON, mage.cards.l.LightOfJudgment.class)); + cards.add(new SetCardInfo("Lightning, Army of One", 233, Rarity.MYTHIC, mage.cards.l.LightningArmyOfOne.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lightning, Army of One", 320, Rarity.MYTHIC, mage.cards.l.LightningArmyOfOne.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lightning, Army of One", 400, Rarity.MYTHIC, mage.cards.l.LightningArmyOfOne.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lightning, Army of One", 498, Rarity.MYTHIC, mage.cards.l.LightningArmyOfOne.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lightning, Army of One", 545, Rarity.MYTHIC, mage.cards.l.LightningArmyOfOne.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lightning, Security Sergeant", 462, Rarity.RARE, mage.cards.l.LightningSecuritySergeant.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lightning, Security Sergeant", 560, Rarity.RARE, mage.cards.l.LightningSecuritySergeant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lindblum, Industrial Regency", 285, Rarity.RARE, mage.cards.l.LindblumIndustrialRegency.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lindblum, Industrial Regency", 312, Rarity.RARE, mage.cards.l.LindblumIndustrialRegency.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lion Heart", 261, Rarity.UNCOMMON, mage.cards.l.LionHeart.class)); + cards.add(new SetCardInfo("Locke Cole", 234, Rarity.UNCOMMON, mage.cards.l.LockeCole.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Locke Cole", 499, Rarity.UNCOMMON, mage.cards.l.LockeCole.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Loporrit Scout", 192, Rarity.COMMON, mage.cards.l.LoporritScout.class)); + cards.add(new SetCardInfo("Louisoix's Sacrifice", 59, Rarity.RARE, mage.cards.l.LouisoixsSacrifice.class)); + cards.add(new SetCardInfo("Lunatic Pandora", 262, Rarity.COMMON, mage.cards.l.LunaticPandora.class)); cards.add(new SetCardInfo("Machinist's Arsenal", 23, Rarity.RARE, mage.cards.m.MachinistsArsenal.class)); + cards.add(new SetCardInfo("Magic Damper", 61, Rarity.COMMON, mage.cards.m.MagicDamper.class)); + cards.add(new SetCardInfo("Magic Pot", 263, Rarity.COMMON, mage.cards.m.MagicPot.class)); + cards.add(new SetCardInfo("Magicked Card", 73, Rarity.UNCOMMON, mage.cards.m.MagickedCard.class)); + cards.add(new SetCardInfo("Magitek Armor", 24, Rarity.UNCOMMON, mage.cards.m.MagitekArmor.class)); + cards.add(new SetCardInfo("Magitek Infantry", 25, Rarity.COMMON, mage.cards.m.MagitekInfantry.class)); cards.add(new SetCardInfo("Magitek Scythe", 562, Rarity.RARE, mage.cards.m.MagitekScythe.class)); cards.add(new SetCardInfo("Malboro", 106, Rarity.COMMON, mage.cards.m.Malboro.class)); cards.add(new SetCardInfo("Matoya, Archon Elder", 439, Rarity.RARE, mage.cards.m.MatoyaArchonElder.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Matoya, Archon Elder", 62, Rarity.RARE, mage.cards.m.MatoyaArchonElder.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Memories Returning", 331, Rarity.RARE, mage.cards.m.MemoriesReturning.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Memories Returning", 63, Rarity.RARE, mage.cards.m.MemoriesReturning.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Midgar, City of Mako", 286, Rarity.RARE, mage.cards.m.MidgarCityOfMako.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Midgar, City of Mako", 313, Rarity.RARE, mage.cards.m.MidgarCityOfMako.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Minwu, White Mage", 26, Rarity.RARE, mage.cards.m.MinwuWhiteMage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Minwu, White Mage", 430, Rarity.RARE, mage.cards.m.MinwuWhiteMage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Monk's Fist", 265, Rarity.COMMON, mage.cards.m.MonksFist.class)); cards.add(new SetCardInfo("Moogles' Valor", 27, Rarity.RARE, mage.cards.m.MooglesValor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Moogles' Valor", 326, Rarity.RARE, mage.cards.m.MooglesValor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 303, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Mountain", 304, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Mountain", 305, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Mountain", 575, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Mysidian Elder", 145, Rarity.COMMON, mage.cards.m.MysidianElder.class)); cards.add(new SetCardInfo("Namazu Trader", 107, Rarity.COMMON, mage.cards.n.NamazuTrader.class)); + cards.add(new SetCardInfo("Neo Exdeath, Dimension's End", 220, Rarity.UNCOMMON, mage.cards.n.NeoExdeathDimensionsEnd.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Neo Exdeath, Dimension's End", 485, Rarity.UNCOMMON, mage.cards.n.NeoExdeathDimensionsEnd.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Nibelheim Aflame", 146, Rarity.MYTHIC, mage.cards.n.NibelheimAflame.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Nibelheim Aflame", 339, Rarity.MYTHIC, mage.cards.n.NibelheimAflame.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ninja's Blades", 108, Rarity.RARE, mage.cards.n.NinjasBlades.class)); + cards.add(new SetCardInfo("Noctis, Prince of Lucis", 235, Rarity.RARE, mage.cards.n.NoctisPrinceOfLucis.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Noctis, Prince of Lucis", 401, Rarity.RARE, mage.cards.n.NoctisPrinceOfLucis.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Noctis, Prince of Lucis", 500, Rarity.RARE, mage.cards.n.NoctisPrinceOfLucis.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Noctis, Prince of Lucis", 546, Rarity.RARE, mage.cards.n.NoctisPrinceOfLucis.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Omega, Heartless Evolution", 236, Rarity.UNCOMMON, mage.cards.o.OmegaHeartlessEvolution.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Omega, Heartless Evolution", 347, Rarity.UNCOMMON, mage.cards.o.OmegaHeartlessEvolution.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Omega, Heartless Evolution", 501, Rarity.UNCOMMON, mage.cards.o.OmegaHeartlessEvolution.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Opera Love Song", 147, Rarity.UNCOMMON, mage.cards.o.OperaLoveSong.class)); + cards.add(new SetCardInfo("Overkill", 109, Rarity.UNCOMMON, mage.cards.o.Overkill.class)); cards.add(new SetCardInfo("Paladin's Arms", 28, Rarity.COMMON, mage.cards.p.PaladinsArms.class)); cards.add(new SetCardInfo("Phantom Train", 110, Rarity.UNCOMMON, mage.cards.p.PhantomTrain.class)); cards.add(new SetCardInfo("Phoenix Down", 29, Rarity.UNCOMMON, mage.cards.p.PhoenixDown.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Phoenix Down", 578, Rarity.UNCOMMON, mage.cards.p.PhoenixDown.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Phoenix, Warden of Fire", 229, Rarity.RARE, mage.cards.p.PhoenixWardenOfFire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Phoenix, Warden of Fire", 397, Rarity.RARE, mage.cards.p.PhoenixWardenOfFire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Phoenix, Warden of Fire", 494, Rarity.RARE, mage.cards.p.PhoenixWardenOfFire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Phoenix, Warden of Fire", 542, Rarity.RARE, mage.cards.p.PhoenixWardenOfFire.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 294, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Plains", 295, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Plains", 296, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Plains", 572, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Poison the Waters", 111, Rarity.UNCOMMON, mage.cards.p.PoisonTheWaters.class)); + cards.add(new SetCardInfo("Prishe's Wanderings", 193, Rarity.COMMON, mage.cards.p.PrishesWanderings.class)); + cards.add(new SetCardInfo("Prompto Argentum", 148, Rarity.UNCOMMON, mage.cards.p.PromptoArgentum.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Prompto Argentum", 387, Rarity.UNCOMMON, mage.cards.p.PromptoArgentum.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Prompto Argentum", 463, Rarity.UNCOMMON, mage.cards.p.PromptoArgentum.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Prompto Argentum", 532, Rarity.UNCOMMON, mage.cards.p.PromptoArgentum.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("PuPu UFO", 266, Rarity.UNCOMMON, mage.cards.p.PuPuUFO.class)); + cards.add(new SetCardInfo("Qiqirn Merchant", 65, Rarity.COMMON, mage.cards.q.QiqirnMerchant.class)); + cards.add(new SetCardInfo("Queen Brahne", 149, Rarity.UNCOMMON, mage.cards.q.QueenBrahne.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Queen Brahne", 464, Rarity.UNCOMMON, mage.cards.q.QueenBrahne.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Quina, Qu Gourmet", 194, Rarity.UNCOMMON, mage.cards.q.QuinaQuGourmet.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Quina, Qu Gourmet", 471, Rarity.UNCOMMON, mage.cards.q.QuinaQuGourmet.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Quistis Trepe", 440, Rarity.UNCOMMON, mage.cards.q.QuistisTrepe.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Quistis Trepe", 66, Rarity.UNCOMMON, mage.cards.q.QuistisTrepe.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Qutrub Forayer", 112, Rarity.COMMON, mage.cards.q.QutrubForayer.class)); cards.add(new SetCardInfo("Rabanastre, Royal City", 287, Rarity.COMMON, mage.cards.r.RabanastreRoyalCity.class)); cards.add(new SetCardInfo("Ragnarok, Divine Deliverance", "381b", Rarity.UNCOMMON, mage.cards.r.RagnarokDivineDeliverance.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ragnarok, Divine Deliverance", "446b", Rarity.UNCOMMON, mage.cards.r.RagnarokDivineDeliverance.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ragnarok, Divine Deliverance", "526b", Rarity.UNCOMMON, mage.cards.r.RagnarokDivineDeliverance.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ragnarok, Divine Deliverance", "99b", Rarity.UNCOMMON, mage.cards.r.RagnarokDivineDeliverance.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Random Encounter", 150, Rarity.UNCOMMON, mage.cards.r.RandomEncounter.class)); + cards.add(new SetCardInfo("Raubahn, Bull of Ala Mhigo", 151, Rarity.RARE, mage.cards.r.RaubahnBullOfAlaMhigo.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Raubahn, Bull of Ala Mhigo", 388, Rarity.RARE, mage.cards.r.RaubahnBullOfAlaMhigo.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Raubahn, Bull of Ala Mhigo", 465, Rarity.RARE, mage.cards.r.RaubahnBullOfAlaMhigo.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Raubahn, Bull of Ala Mhigo", 533, Rarity.RARE, mage.cards.r.RaubahnBullOfAlaMhigo.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reach the Horizon", 195, Rarity.UNCOMMON, mage.cards.r.ReachTheHorizon.class)); + cards.add(new SetCardInfo("Red Mage's Rapier", 152, Rarity.COMMON, mage.cards.r.RedMagesRapier.class)); + cards.add(new SetCardInfo("Relentless X-ATM092", 268, Rarity.UNCOMMON, mage.cards.r.RelentlessXATM092.class)); cards.add(new SetCardInfo("Relm's Sketching", 67, Rarity.UNCOMMON, mage.cards.r.RelmsSketching.class)); - cards.add(new SetCardInfo("Reno and Rude", 113, Rarity.UNCOMMON, mage.cards.r.RenoAndRude.class)); + cards.add(new SetCardInfo("Reno and Rude", 113, Rarity.UNCOMMON, mage.cards.r.RenoAndRude.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reno and Rude", 450, Rarity.UNCOMMON, mage.cards.r.RenoAndRude.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Resentful Revelation", 114, Rarity.COMMON, mage.cards.r.ResentfulRevelation.class)); + cards.add(new SetCardInfo("Restoration Magic", 30, Rarity.UNCOMMON, mage.cards.r.RestorationMagic.class)); cards.add(new SetCardInfo("Retrieve the Esper", 68, Rarity.COMMON, mage.cards.r.RetrieveTheEsper.class)); + cards.add(new SetCardInfo("Ride the Shoopuf", 197, Rarity.UNCOMMON, mage.cards.r.RideTheShoopuf.class)); + cards.add(new SetCardInfo("Ring of the Lucii", 269, Rarity.UNCOMMON, mage.cards.r.RingOfTheLucii.class)); cards.add(new SetCardInfo("Rinoa Heartilly", 237, Rarity.UNCOMMON, mage.cards.r.RinoaHeartilly.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rinoa Heartilly", 502, Rarity.UNCOMMON, mage.cards.r.RinoaHeartilly.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rook Turret", 69, Rarity.COMMON, mage.cards.r.RookTurret.class)); cards.add(new SetCardInfo("Rosa, Resolute White Mage", 431, Rarity.RARE, mage.cards.r.RosaResoluteWhiteMage.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rosa, Resolute White Mage", 555, Rarity.RARE, mage.cards.r.RosaResoluteWhiteMage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rufus Shinra", 238, Rarity.UNCOMMON, mage.cards.r.RufusShinra.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rufus Shinra", 503, Rarity.UNCOMMON, mage.cards.r.RufusShinra.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rydia's Return", 198, Rarity.UNCOMMON, mage.cards.r.RydiasReturn.class)); + cards.add(new SetCardInfo("Rydia, Summoner of Mist", 239, Rarity.UNCOMMON, mage.cards.r.RydiaSummonerOfMist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rydia, Summoner of Mist", 504, Rarity.UNCOMMON, mage.cards.r.RydiaSummonerOfMist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sabotender", 153, Rarity.COMMON, mage.cards.s.Sabotender.class)); cards.add(new SetCardInfo("Sage's Nouliths", 582, Rarity.COMMON, mage.cards.s.SagesNouliths.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sage's Nouliths", 70, Rarity.COMMON, mage.cards.s.SagesNouliths.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sahagin", 71, Rarity.COMMON, mage.cards.s.Sahagin.class)); cards.add(new SetCardInfo("Samurai's Katana", 154, Rarity.UNCOMMON, mage.cards.s.SamuraisKatana.class)); + cards.add(new SetCardInfo("Sandworm", 155, Rarity.UNCOMMON, mage.cards.s.Sandworm.class)); cards.add(new SetCardInfo("Sazh Katzroy", 199, Rarity.RARE, mage.cards.s.SazhKatzroy.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sazh Katzroy", 472, Rarity.RARE, mage.cards.s.SazhKatzroy.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sazh's Chocobo", 200, Rarity.UNCOMMON, mage.cards.s.SazhsChocobo.class)); + cards.add(new SetCardInfo("Scorpion Sentinel", 72, Rarity.COMMON, mage.cards.s.ScorpionSentinel.class)); + cards.add(new SetCardInfo("Seifer Almasy", 156, Rarity.RARE, mage.cards.s.SeiferAlmasy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Seifer Almasy", 389, Rarity.RARE, mage.cards.s.SeiferAlmasy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Seifer Almasy", 466, Rarity.RARE, mage.cards.s.SeiferAlmasy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Seifer Almasy", 534, Rarity.RARE, mage.cards.s.SeiferAlmasy.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Self-Destruct", 157, Rarity.UNCOMMON, mage.cards.s.SelfDestruct.class)); cards.add(new SetCardInfo("Sephiroth's Intervention", 116, Rarity.COMMON, mage.cards.s.SephirothsIntervention.class)); cards.add(new SetCardInfo("Sephiroth, Fabled SOLDIER", 115, Rarity.MYTHIC, mage.cards.s.SephirothFabledSOLDIER.class, NON_FULL_USE_VARIOUS)); @@ -246,6 +471,7 @@ public final class FinalFantasy extends ExpansionSet { cards.add(new SetCardInfo("Shambling Cie'th", 117, Rarity.UNCOMMON, mage.cards.s.ShamblingCieth.class)); cards.add(new SetCardInfo("Shantotto, Tactician Magician", 241, Rarity.UNCOMMON, mage.cards.s.ShantottoTacticianMagician.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shantotto, Tactician Magician", 507, Rarity.UNCOMMON, mage.cards.s.ShantottoTacticianMagician.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sharlayan, Nation of Scholars", 288, Rarity.COMMON, mage.cards.s.SharlayanNationOfScholars.class)); cards.add(new SetCardInfo("Shinra Reinforcements", 118, Rarity.COMMON, mage.cards.s.ShinraReinforcements.class)); cards.add(new SetCardInfo("Shinryu, Transcendent Rival", 127, Rarity.RARE, mage.cards.s.ShinryuTranscendentRival.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shinryu, Transcendent Rival", 384, Rarity.RARE, mage.cards.s.ShinryuTranscendentRival.class, NON_FULL_USE_VARIOUS)); @@ -255,10 +481,20 @@ public final class FinalFantasy extends ExpansionSet { cards.add(new SetCardInfo("Shiva, Warden of Ice", 438, Rarity.RARE, mage.cards.s.ShivaWardenOfIce.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shiva, Warden of Ice", 523, Rarity.RARE, mage.cards.s.ShivaWardenOfIce.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shiva, Warden of Ice", 58, Rarity.RARE, mage.cards.s.ShivaWardenOfIce.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sidequest: Card Collection", 73, Rarity.UNCOMMON, mage.cards.s.SidequestCardCollection.class)); cards.add(new SetCardInfo("Sidequest: Catch a Fish", 31, Rarity.UNCOMMON, mage.cards.s.SidequestCatchAFish.class)); + cards.add(new SetCardInfo("Sidequest: Hunt the Mark", 119, Rarity.UNCOMMON, mage.cards.s.SidequestHuntTheMark.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sidequest: Hunt the Mark", 453, Rarity.UNCOMMON, mage.cards.s.SidequestHuntTheMark.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sidequest: Play Blitzball", 158, Rarity.UNCOMMON, mage.cards.s.SidequestPlayBlitzball.class)); + cards.add(new SetCardInfo("Sidequest: Raise a Chocobo", 201, Rarity.UNCOMMON, mage.cards.s.SidequestRaiseAChocobo.class)); cards.add(new SetCardInfo("Sin, Spira's Punishment", 242, Rarity.RARE, mage.cards.s.SinSpirasPunishment.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sin, Spira's Punishment", 348, Rarity.RARE, mage.cards.s.SinSpirasPunishment.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sin, Spira's Punishment", 508, Rarity.RARE, mage.cards.s.SinSpirasPunishment.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Slash of Light", 32, Rarity.COMMON, mage.cards.s.SlashOfLight.class)); + cards.add(new SetCardInfo("Sleep Magic", 74, Rarity.UNCOMMON, mage.cards.s.SleepMagic.class)); + cards.add(new SetCardInfo("Snow Villiers", 33, Rarity.UNCOMMON, mage.cards.s.SnowVilliers.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Snow Villiers", 432, Rarity.UNCOMMON, mage.cards.s.SnowVilliers.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sorceress's Schemes", 159, Rarity.UNCOMMON, mage.cards.s.SorceresssSchemes.class)); cards.add(new SetCardInfo("Squall, SeeD Mercenary", 243, Rarity.RARE, mage.cards.s.SquallSeeDMercenary.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Squall, SeeD Mercenary", 402, Rarity.RARE, mage.cards.s.SquallSeeDMercenary.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Squall, SeeD Mercenary", 509, Rarity.RARE, mage.cards.s.SquallSeeDMercenary.class, NON_FULL_USE_VARIOUS)); @@ -267,12 +503,29 @@ public final class FinalFantasy extends ExpansionSet { cards.add(new SetCardInfo("Stiltzkin, Moogle Merchant", 327, Rarity.RARE, mage.cards.s.StiltzkinMoogleMerchant.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Stiltzkin, Moogle Merchant", 34, Rarity.RARE, mage.cards.s.StiltzkinMoogleMerchant.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Stiltzkin, Moogle Merchant", 433, Rarity.RARE, mage.cards.s.StiltzkinMoogleMerchant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stolen Uniform", 332, Rarity.UNCOMMON, mage.cards.s.StolenUniform.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stolen Uniform", 75, Rarity.UNCOMMON, mage.cards.s.StolenUniform.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Stuck in Summoner's Sanctum", 76, Rarity.COMMON, mage.cards.s.StuckInSummonersSanctum.class)); + cards.add(new SetCardInfo("Summon: Alexander", 13, Rarity.UNCOMMON, mage.cards.s.SummonAlexander.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Summon: Alexander", 357, Rarity.UNCOMMON, mage.cards.s.SummonAlexander.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summon: Anima", 120, Rarity.UNCOMMON, mage.cards.s.SummonAnima.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summon: Anima", 364, Rarity.UNCOMMON, mage.cards.s.SummonAnima.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summon: Bahamut", 1, Rarity.MYTHIC, mage.cards.s.SummonBahamut.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summon: Bahamut", 356, Rarity.MYTHIC, mage.cards.s.SummonBahamut.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Summon: Choco/Mog", 35, Rarity.COMMON, mage.cards.s.SummonChocoMog.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Summon: Choco/Mog", 358, Rarity.COMMON, mage.cards.s.SummonChocoMog.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Summon: Esper Maduin", 185, Rarity.RARE, mage.cards.s.SummonEsperMaduin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Summon: Esper Maduin", 370, Rarity.RARE, mage.cards.s.SummonEsperMaduin.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summon: Esper Ramuh", 161, Rarity.UNCOMMON, mage.cards.s.SummonEsperRamuh.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summon: Esper Ramuh", 367, Rarity.UNCOMMON, mage.cards.s.SummonEsperRamuh.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Summon: Fat Chocobo", 202, Rarity.COMMON, mage.cards.s.SummonFatChocobo.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Summon: Fat Chocobo", 371, Rarity.COMMON, mage.cards.s.SummonFatChocobo.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Summon: Fenrir", 203, Rarity.UNCOMMON, mage.cards.s.SummonFenrir.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Summon: Fenrir", 372, Rarity.UNCOMMON, mage.cards.s.SummonFenrir.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Summon: G.F. Cerberus", 162, Rarity.RARE, mage.cards.s.SummonGFCerberus.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Summon: G.F. Cerberus", 368, Rarity.RARE, mage.cards.s.SummonGFCerberus.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Summon: G.F. Ifrit", 163, Rarity.COMMON, mage.cards.s.SummonGFIfrit.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Summon: G.F. Ifrit", 369, Rarity.COMMON, mage.cards.s.SummonGFIfrit.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summon: Knights of Round", 359, Rarity.MYTHIC, mage.cards.s.SummonKnightsOfRound.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summon: Knights of Round", 36, Rarity.MYTHIC, mage.cards.s.SummonKnightsOfRound.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summon: Leviathan", 361, Rarity.RARE, mage.cards.s.SummonLeviathan.class, NON_FULL_USE_VARIOUS)); @@ -283,31 +536,67 @@ public final class FinalFantasy extends ExpansionSet { cards.add(new SetCardInfo("Summon: Primal Odin", 365, Rarity.RARE, mage.cards.s.SummonPrimalOdin.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summon: Shiva", 362, Rarity.UNCOMMON, mage.cards.s.SummonShiva.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summon: Shiva", 78, Rarity.UNCOMMON, mage.cards.s.SummonShiva.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Summon: Titan", 204, Rarity.RARE, mage.cards.s.SummonTitan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Summon: Titan", 373, Rarity.RARE, mage.cards.s.SummonTitan.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summoner's Grimoire", 205, Rarity.RARE, mage.cards.s.SummonersGrimoire.class)); cards.add(new SetCardInfo("Suplex", 164, Rarity.COMMON, mage.cards.s.Suplex.class)); + cards.add(new SetCardInfo("Swallowed by Leviathan", 79, Rarity.UNCOMMON, mage.cards.s.SwallowedByLeviathan.class)); cards.add(new SetCardInfo("Swamp", 300, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Swamp", 301, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Swamp", 302, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Swamp", 574, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Syncopate", 80, Rarity.COMMON, mage.cards.s.Syncopate.class)); + cards.add(new SetCardInfo("Tellah, Great Sage", 244, Rarity.RARE, mage.cards.t.TellahGreatSage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tellah, Great Sage", 349, Rarity.RARE, mage.cards.t.TellahGreatSage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tellah, Great Sage", 510, Rarity.RARE, mage.cards.t.TellahGreatSage.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Terra, Magical Adept", 245, Rarity.MYTHIC, mage.cards.t.TerraMagicalAdept.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Terra, Magical Adept", 323, Rarity.MYTHIC, mage.cards.t.TerraMagicalAdept.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Terra, Magical Adept", 511, Rarity.MYTHIC, mage.cards.t.TerraMagicalAdept.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Crystal's Chosen", 14, Rarity.UNCOMMON, mage.cards.t.TheCrystalsChosen.class)); + cards.add(new SetCardInfo("The Darkness Crystal", 335, Rarity.RARE, mage.cards.t.TheDarknessCrystal.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Darkness Crystal", 96, Rarity.RARE, mage.cards.t.TheDarknessCrystal.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Earth Crystal", 184, Rarity.RARE, mage.cards.t.TheEarthCrystal.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Earth Crystal", 342, Rarity.RARE, mage.cards.t.TheEarthCrystal.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Emperor of Palamecia", 219, Rarity.UNCOMMON, mage.cards.t.TheEmperorOfPalamecia.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Emperor of Palamecia", 484, Rarity.UNCOMMON, mage.cards.t.TheEmperorOfPalamecia.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Final Days", 101, Rarity.UNCOMMON, mage.cards.t.TheFinalDays.class)); + cards.add(new SetCardInfo("The Fire Crystal", 135, Rarity.RARE, mage.cards.t.TheFireCrystal.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Fire Crystal", 337, Rarity.RARE, mage.cards.t.TheFireCrystal.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Gold Saucer", 279, Rarity.UNCOMMON, mage.cards.t.TheGoldSaucer.class)); cards.add(new SetCardInfo("The Lord Master of Hell", 219, Rarity.UNCOMMON, mage.cards.t.TheLordMasterOfHell.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Lord Master of Hell", 484, Rarity.UNCOMMON, mage.cards.t.TheLordMasterOfHell.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Lunar Whale", 60, Rarity.RARE, mage.cards.t.TheLunarWhale.class)); + cards.add(new SetCardInfo("The Masamune", 264, Rarity.RARE, mage.cards.t.TheMasamune.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Masamune", 353, Rarity.RARE, mage.cards.t.TheMasamune.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Prima Vista", 64, Rarity.UNCOMMON, mage.cards.t.ThePrimaVista.class)); + cards.add(new SetCardInfo("The Regalia", 267, Rarity.RARE, mage.cards.t.TheRegalia.class)); + cards.add(new SetCardInfo("The Wandering Minstrel", 249, Rarity.RARE, mage.cards.t.TheWanderingMinstrel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Wandering Minstrel", 403, Rarity.RARE, mage.cards.t.TheWanderingMinstrel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Wandering Minstrel", 515, Rarity.RARE, mage.cards.t.TheWanderingMinstrel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Wandering Minstrel", 548, Rarity.RARE, mage.cards.t.TheWanderingMinstrel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Water Crystal", 333, Rarity.RARE, mage.cards.t.TheWaterCrystal.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Water Crystal", 85, Rarity.RARE, mage.cards.t.TheWaterCrystal.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Wind Crystal", 330, Rarity.RARE, mage.cards.t.TheWindCrystal.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Wind Crystal", 43, Rarity.RARE, mage.cards.t.TheWindCrystal.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Thief's Knife", 81, Rarity.UNCOMMON, mage.cards.t.ThiefsKnife.class)); + cards.add(new SetCardInfo("Thunder Magic", 165, Rarity.COMMON, mage.cards.t.ThunderMagic.class)); + cards.add(new SetCardInfo("Tidus, Blitzball Star", 246, Rarity.UNCOMMON, mage.cards.t.TidusBlitzballStar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tidus, Blitzball Star", 512, Rarity.UNCOMMON, mage.cards.t.TidusBlitzballStar.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Tifa Lockhart", 206, Rarity.RARE, mage.cards.t.TifaLockhart.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Tifa Lockhart", 391, Rarity.RARE, mage.cards.t.TifaLockhart.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Tifa Lockhart", 473, Rarity.RARE, mage.cards.t.TifaLockhart.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Tifa Lockhart", 536, Rarity.RARE, mage.cards.t.TifaLockhart.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tifa's Limit Break", 207, Rarity.UNCOMMON, mage.cards.t.TifasLimitBreak.class)); cards.add(new SetCardInfo("Tonberry", 122, Rarity.UNCOMMON, mage.cards.t.Tonberry.class)); + cards.add(new SetCardInfo("Torgal, A Fine Hound", 208, Rarity.UNCOMMON, mage.cards.t.TorgalAFineHound.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Torgal, A Fine Hound", 345, Rarity.UNCOMMON, mage.cards.t.TorgalAFineHound.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Torgal, A Fine Hound", 474, Rarity.UNCOMMON, mage.cards.t.TorgalAFineHound.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Town Greeter", 209, Rarity.COMMON, mage.cards.t.TownGreeter.class)); cards.add(new SetCardInfo("Trance Kuja, Fate Defied", 232, Rarity.RARE, mage.cards.t.TranceKujaFateDefied.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Trance Kuja, Fate Defied", 399, Rarity.RARE, mage.cards.t.TranceKujaFateDefied.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Trance Kuja, Fate Defied", 497, Rarity.RARE, mage.cards.t.TranceKujaFateDefied.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Trance Kuja, Fate Defied", 544, Rarity.RARE, mage.cards.t.TranceKujaFateDefied.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Travel the Overworld", 82, Rarity.UNCOMMON, mage.cards.t.TravelTheOverworld.class)); cards.add(new SetCardInfo("Traveling Chocobo", "551a", Rarity.MYTHIC, mage.cards.t.TravelingChocobo.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Traveling Chocobo", "551b", Rarity.MYTHIC, mage.cards.t.TravelingChocobo.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Traveling Chocobo", "551c", Rarity.MYTHIC, mage.cards.t.TravelingChocobo.class, NON_FULL_USE_VARIOUS)); @@ -316,9 +605,12 @@ public final class FinalFantasy extends ExpansionSet { cards.add(new SetCardInfo("Traveling Chocobo", 210, Rarity.MYTHIC, mage.cards.t.TravelingChocobo.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Traveling Chocobo", 406, Rarity.MYTHIC, mage.cards.t.TravelingChocobo.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Traveling Chocobo", 551, Rarity.MYTHIC, mage.cards.t.TravelingChocobo.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Treno, Dark City", 290, Rarity.COMMON, mage.cards.t.TrenoDarkCity.class)); cards.add(new SetCardInfo("Triple Triad", 166, Rarity.RARE, mage.cards.t.TripleTriad.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Triple Triad", 340, Rarity.RARE, mage.cards.t.TripleTriad.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ultima Weapon", 563, Rarity.RARE, mage.cards.u.UltimaWeapon.class)); + cards.add(new SetCardInfo("Ultima", 328, Rarity.RARE, mage.cards.u.Ultima.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ultima", 38, Rarity.RARE, mage.cards.u.Ultima.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ultima, Origin of Oblivion", 2, Rarity.RARE, mage.cards.u.UltimaOriginOfOblivion.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ultima, Origin of Oblivion", 324, Rarity.RARE, mage.cards.u.UltimaOriginOfOblivion.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ultima, Origin of Oblivion", 421, Rarity.RARE, mage.cards.u.UltimaOriginOfOblivion.class, NON_FULL_USE_VARIOUS)); @@ -328,34 +620,56 @@ public final class FinalFantasy extends ExpansionSet { cards.add(new SetCardInfo("Ultimecia, Temporal Threat", 556, Rarity.RARE, mage.cards.u.UltimeciaTemporalThreat.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ultimecia, Time Sorceress", 247, Rarity.UNCOMMON, mage.cards.u.UltimeciaTimeSorceress.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ultimecia, Time Sorceress", 513, Rarity.UNCOMMON, mage.cards.u.UltimeciaTimeSorceress.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ultros, Obnoxious Octopus", 442, Rarity.UNCOMMON, mage.cards.u.UltrosObnoxiousOctopus.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ultros, Obnoxious Octopus", 83, Rarity.UNCOMMON, mage.cards.u.UltrosObnoxiousOctopus.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Undercity Dire Rat", 123, Rarity.COMMON, mage.cards.u.UndercityDireRat.class)); + cards.add(new SetCardInfo("Unexpected Request", 167, Rarity.UNCOMMON, mage.cards.u.UnexpectedRequest.class)); + cards.add(new SetCardInfo("Vaan, Street Thief", 168, Rarity.RARE, mage.cards.v.VaanStreetThief.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vaan, Street Thief", 390, Rarity.RARE, mage.cards.v.VaanStreetThief.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vaan, Street Thief", 467, Rarity.RARE, mage.cards.v.VaanStreetThief.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vaan, Street Thief", 535, Rarity.RARE, mage.cards.v.VaanStreetThief.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Valkyrie Aerial Unit", 84, Rarity.UNCOMMON, mage.cards.v.ValkyrieAerialUnit.class)); cards.add(new SetCardInfo("Vanille, Cheerful l'Cie", 211, Rarity.UNCOMMON, mage.cards.v.VanilleCheerfulLCie.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Vanille, Cheerful l'Cie", 392, Rarity.UNCOMMON, mage.cards.v.VanilleCheerfulLCie.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Vanille, Cheerful l'Cie", 475, Rarity.UNCOMMON, mage.cards.v.VanilleCheerfulLCie.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Vanille, Cheerful l'Cie", 537, Rarity.UNCOMMON, mage.cards.v.VanilleCheerfulLCie.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vayne's Treachery", 124, Rarity.COMMON, mage.cards.v.VaynesTreachery.class)); + cards.add(new SetCardInfo("Vector, Imperial Capital", 291, Rarity.COMMON, mage.cards.v.VectorImperialCapital.class)); + cards.add(new SetCardInfo("Venat, Heart of Hydaelyn", 329, Rarity.RARE, mage.cards.v.VenatHeartOfHydaelyn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Venat, Heart of Hydaelyn", 39, Rarity.RARE, mage.cards.v.VenatHeartOfHydaelyn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Venat, Heart of Hydaelyn", 434, Rarity.RARE, mage.cards.v.VenatHeartOfHydaelyn.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Vincent Valentine", 125, Rarity.RARE, mage.cards.v.VincentValentine.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Vincent Valentine", 383, Rarity.RARE, mage.cards.v.VincentValentine.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Vincent Valentine", 454, Rarity.RARE, mage.cards.v.VincentValentine.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Vincent Valentine", 528, Rarity.RARE, mage.cards.v.VincentValentine.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vincent's Limit Break", 126, Rarity.COMMON, mage.cards.v.VincentsLimitBreak.class)); cards.add(new SetCardInfo("Vivi Ornitier", 248, Rarity.MYTHIC, mage.cards.v.ViviOrnitier.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Vivi Ornitier", 321, Rarity.MYTHIC, mage.cards.v.ViviOrnitier.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Vivi Ornitier", 514, Rarity.MYTHIC, mage.cards.v.ViviOrnitier.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Warrior's Sword", 169, Rarity.COMMON, mage.cards.w.WarriorsSword.class)); cards.add(new SetCardInfo("Wastes", 309, Rarity.COMMON, mage.cards.w.Wastes.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Weapons Vendor", 40, Rarity.COMMON, mage.cards.w.WeaponsVendor.class)); cards.add(new SetCardInfo("White Auracite", 41, Rarity.COMMON, mage.cards.w.WhiteAuracite.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("White Auracite", 579, Rarity.COMMON, mage.cards.w.WhiteAuracite.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("White Mage's Staff", 42, Rarity.COMMON, mage.cards.w.WhiteMagesStaff.class)); + cards.add(new SetCardInfo("Windurst, Federation Center", 292, Rarity.COMMON, mage.cards.w.WindurstFederationCenter.class)); + cards.add(new SetCardInfo("World Champion, Celestial Weapon", 158, Rarity.UNCOMMON, mage.cards.w.WorldChampionCelestialWeapon.class)); + cards.add(new SetCardInfo("World Map", 270, Rarity.COMMON, mage.cards.w.WorldMap.class)); cards.add(new SetCardInfo("Xande, Dark Mage", 516, Rarity.RARE, mage.cards.x.XandeDarkMage.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Xande, Dark Mage", 561, Rarity.RARE, mage.cards.x.XandeDarkMage.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Y'shtola Rhul", 443, Rarity.MYTHIC, mage.cards.y.YshtolaRhul.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Y'shtola Rhul", 577, Rarity.MYTHIC, mage.cards.y.YshtolaRhul.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Y'shtola Rhul", 86, Rarity.MYTHIC, mage.cards.y.YshtolaRhul.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Yiazmat, Ultimate Mark", 119, Rarity.UNCOMMON, mage.cards.y.YiazmatUltimateMark.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Yiazmat, Ultimate Mark", 453, Rarity.UNCOMMON, mage.cards.y.YiazmatUltimateMark.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("You're Not Alone", 44, Rarity.COMMON, mage.cards.y.YoureNotAlone.class)); cards.add(new SetCardInfo("Yuna, Hope of Spira", 250, Rarity.MYTHIC, mage.cards.y.YunaHopeOfSpira.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Yuna, Hope of Spira", 404, Rarity.MYTHIC, mage.cards.y.YunaHopeOfSpira.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Yuna, Hope of Spira", 517, Rarity.MYTHIC, mage.cards.y.YunaHopeOfSpira.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Yuna, Hope of Spira", 549, Rarity.MYTHIC, mage.cards.y.YunaHopeOfSpira.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Zack Fair", 435, Rarity.UNCOMMON, mage.cards.z.ZackFair.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Zack Fair", 45, Rarity.UNCOMMON, mage.cards.z.ZackFair.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Zack Fair", 580, Rarity.UNCOMMON, mage.cards.z.ZackFair.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Zanarkand, Ancient Metropolis", 293, Rarity.RARE, mage.cards.z.ZanarkandAncientMetropolis.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Zanarkand, Ancient Metropolis", 314, Rarity.RARE, mage.cards.z.ZanarkandAncientMetropolis.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Zell Dincht", 170, Rarity.RARE, mage.cards.z.ZellDincht.class, NON_FULL_USE_VARIOUS)); @@ -364,5 +678,12 @@ public final class FinalFantasy extends ExpansionSet { cards.add(new SetCardInfo("Zenos yae Galvus", 384, Rarity.RARE, mage.cards.z.ZenosYaeGalvus.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Zenos yae Galvus", 455, Rarity.RARE, mage.cards.z.ZenosYaeGalvus.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Zenos yae Galvus", 529, Rarity.RARE, mage.cards.z.ZenosYaeGalvus.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Zidane, Tantalus Thief", 251, Rarity.UNCOMMON, mage.cards.z.ZidaneTantalusThief.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Zidane, Tantalus Thief", 405, Rarity.UNCOMMON, mage.cards.z.ZidaneTantalusThief.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Zidane, Tantalus Thief", 518, Rarity.UNCOMMON, mage.cards.z.ZidaneTantalusThief.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Zidane, Tantalus Thief", 550, Rarity.UNCOMMON, mage.cards.z.ZidaneTantalusThief.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Zodiark, Umbral God", 128, Rarity.RARE, mage.cards.z.ZodiarkUmbralGod.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Zodiark, Umbral God", 336, Rarity.RARE, mage.cards.z.ZodiarkUmbralGod.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Zodiark, Umbral God", 456, Rarity.RARE, mage.cards.z.ZodiarkUmbralGod.class, NON_FULL_USE_VARIOUS)); } } diff --git a/Mage.Sets/src/mage/sets/FinalFantasyCommander.java b/Mage.Sets/src/mage/sets/FinalFantasyCommander.java index 3450f4ada24..cf475f0bf0d 100644 --- a/Mage.Sets/src/mage/sets/FinalFantasyCommander.java +++ b/Mage.Sets/src/mage/sets/FinalFantasyCommander.java @@ -49,6 +49,8 @@ public final class FinalFantasyCommander extends ExpansionSet { cards.add(new SetCardInfo("Avalanche of Sector 7", 53, Rarity.RARE, mage.cards.a.AvalancheOfSector7.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Baleful Strix", 318, Rarity.RARE, mage.cards.b.BalefulStrix.class)); cards.add(new SetCardInfo("Bane of Progress", 299, Rarity.RARE, mage.cards.b.BaneOfProgress.class)); + cards.add(new SetCardInfo("Banon, the Returners' Leader", 78, Rarity.RARE, mage.cards.b.BanonTheReturnersLeader.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Banon, the Returners' Leader", 165, Rarity.RARE, mage.cards.b.BanonTheReturnersLeader.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Barret, Avalanche Leader", 166, Rarity.RARE, mage.cards.b.BarretAvalancheLeader.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Barret, Avalanche Leader", 79, Rarity.RARE, mage.cards.b.BarretAvalancheLeader.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Bastion Protector", 233, Rarity.RARE, mage.cards.b.BastionProtector.class)); @@ -57,6 +59,8 @@ public final class FinalFantasyCommander extends ExpansionSet { cards.add(new SetCardInfo("Bedevil", 319, Rarity.RARE, mage.cards.b.Bedevil.class)); cards.add(new SetCardInfo("Behemoth Sledge", 320, Rarity.UNCOMMON, mage.cards.b.BehemothSledge.class)); cards.add(new SetCardInfo("Big Score", 290, Rarity.COMMON, mage.cards.b.BigScore.class)); + cards.add(new SetCardInfo("Blitzball Stadium", 34, Rarity.RARE, mage.cards.b.BlitzballStadium.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Blitzball Stadium", 111, Rarity.RARE, mage.cards.b.BlitzballStadium.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Blue Mage's Cane", 112, Rarity.RARE, mage.cards.b.BlueMagesCane.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Blue Mage's Cane", 35, Rarity.RARE, mage.cards.b.BlueMagesCane.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Bonders' Enclave", 376, Rarity.RARE, mage.cards.b.BondersEnclave.class)); @@ -94,6 +98,8 @@ public final class FinalFantasyCommander extends ExpansionSet { cards.add(new SetCardInfo("Cloud, Ex-SOLDIER", 202, Rarity.MYTHIC, mage.cards.c.CloudExSOLDIER.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Cloud, Ex-SOLDIER", 210, Rarity.MYTHIC, mage.cards.c.CloudExSOLDIER.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Cloud, Ex-SOLDIER", 221, Rarity.MYTHIC, mage.cards.c.CloudExSOLDIER.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Coin of Fate", 15, Rarity.RARE, mage.cards.c.CoinOfFate.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Coin of Fate", 104, Rarity.RARE, mage.cards.c.CoinOfFate.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Collective Effort", 237, Rarity.RARE, mage.cards.c.CollectiveEffort.class)); cards.add(new SetCardInfo("Colossus Hammer", 338, Rarity.UNCOMMON, mage.cards.c.ColossusHammer.class)); cards.add(new SetCardInfo("Combustible Gearhulk", 292, Rarity.MYTHIC, mage.cards.c.CombustibleGearhulk.class)); @@ -102,6 +108,8 @@ public final class FinalFantasyCommander extends ExpansionSet { cards.add(new SetCardInfo("Command Tower", 485, Rarity.COMMON, mage.cards.c.CommandTower.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Command Tower", 486, Rarity.COMMON, mage.cards.c.CommandTower.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Commander's Sphere", 339, Rarity.COMMON, mage.cards.c.CommandersSphere.class)); + cards.add(new SetCardInfo("Conformer Shuriken", 127, Rarity.RARE, mage.cards.c.ConformerShuriken.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Conformer Shuriken", 98, Rarity.RARE, mage.cards.c.ConformerShuriken.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Conqueror's Flail", 340, Rarity.RARE, mage.cards.c.ConquerorsFlail.class)); cards.add(new SetCardInfo("Contaminated Aquifer", 383, Rarity.COMMON, mage.cards.c.ContaminatedAquifer.class)); cards.add(new SetCardInfo("Coveted Jewel", 341, Rarity.RARE, mage.cards.c.CovetedJewel.class)); @@ -112,8 +120,8 @@ public final class FinalFantasyCommander extends ExpansionSet { cards.add(new SetCardInfo("Cyan, Vengeful Samurai", 132, Rarity.RARE, mage.cards.c.CyanVengefulSamurai.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Cyan, Vengeful Samurai", 16, Rarity.RARE, mage.cards.c.CyanVengefulSamurai.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Damning Verdict", 239, Rarity.RARE, mage.cards.d.DamningVerdict.class)); - cards.add(new SetCardInfo("Dancer's Chakrams", 17, Rarity.RARE, mage.cards.d.DancersChakrams.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dancer's Chakrams", 105, Rarity.RARE, mage.cards.d.DancersChakrams.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dancer's Chakrams", 17, Rarity.RARE, mage.cards.d.DancersChakrams.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Darksteel Plate", 342, Rarity.RARE, mage.cards.d.DarksteelPlate.class)); cards.add(new SetCardInfo("Darkwater Catacombs", 384, Rarity.RARE, mage.cards.d.DarkwaterCatacombs.class)); cards.add(new SetCardInfo("Decimate", 323, Rarity.RARE, mage.cards.d.Decimate.class)); @@ -125,11 +133,17 @@ public final class FinalFantasyCommander extends ExpansionSet { cards.add(new SetCardInfo("Dragonskull Summit", 387, Rarity.RARE, mage.cards.d.DragonskullSummit.class)); cards.add(new SetCardInfo("Drowned Catacomb", 388, Rarity.RARE, mage.cards.d.DrownedCatacomb.class)); cards.add(new SetCardInfo("Duskshell Crawler", 301, Rarity.COMMON, mage.cards.d.DuskshellCrawler.class)); + cards.add(new SetCardInfo("Edgar, Master Machinist", 169, Rarity.RARE, mage.cards.e.EdgarMasterMachinist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Edgar, Master Machinist", 80, Rarity.RARE, mage.cards.e.EdgarMasterMachinist.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Elena, Turk Recruit", 133, Rarity.RARE, mage.cards.e.ElenaTurkRecruit.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Elena, Turk Recruit", 18, Rarity.RARE, mage.cards.e.ElenaTurkRecruit.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Emet-Selch of the Third Seat", 170, Rarity.RARE, mage.cards.e.EmetSelchOfTheThirdSeat.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Emet-Selch of the Third Seat", 81, Rarity.RARE, mage.cards.e.EmetSelchOfTheThirdSeat.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Endless Detour", 324, Rarity.RARE, mage.cards.e.EndlessDetour.class)); cards.add(new SetCardInfo("Espers to Magicite", 114, Rarity.RARE, mage.cards.e.EspersToMagicite.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Espers to Magicite", 43, Rarity.RARE, mage.cards.e.EspersToMagicite.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Estinien Varlineau", 171, Rarity.RARE, mage.cards.e.EstinienVarlineau.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Estinien Varlineau", 82, Rarity.RARE, mage.cards.e.EstinienVarlineau.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Everflowing Chalice", 343, Rarity.UNCOMMON, mage.cards.e.EverflowingChalice.class)); cards.add(new SetCardInfo("Evolving Wilds", 389, Rarity.COMMON, mage.cards.e.EvolvingWilds.class)); cards.add(new SetCardInfo("Exotic Orchard", 390, Rarity.RARE, mage.cards.e.ExoticOrchard.class)); @@ -137,6 +151,8 @@ public final class FinalFantasyCommander extends ExpansionSet { cards.add(new SetCardInfo("Exsanguinate", 276, Rarity.UNCOMMON, mage.cards.e.Exsanguinate.class)); cards.add(new SetCardInfo("Eye of Nidhogg", 115, Rarity.RARE, mage.cards.e.EyeOfNidhogg.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Eye of Nidhogg", 44, Rarity.RARE, mage.cards.e.EyeOfNidhogg.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fandaniel, Telophoroi Ascian", 146, Rarity.RARE, mage.cards.f.FandanielTelophoroiAscian.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fandaniel, Telophoroi Ascian", 46, Rarity.RARE, mage.cards.f.FandanielTelophoroiAscian.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Farewell", 242, Rarity.RARE, mage.cards.f.Farewell.class)); cards.add(new SetCardInfo("Farseek", 302, Rarity.COMMON, mage.cards.f.Farseek.class)); cards.add(new SetCardInfo("Fathom Mage", 325, Rarity.RARE, mage.cards.f.FathomMage.class)); @@ -152,7 +168,14 @@ public final class FinalFantasyCommander extends ExpansionSet { cards.add(new SetCardInfo("Fortified Village", 396, Rarity.RARE, mage.cards.f.FortifiedVillage.class)); cards.add(new SetCardInfo("Furious Rise", 294, Rarity.UNCOMMON, mage.cards.f.FuriousRise.class)); cards.add(new SetCardInfo("Furycalm Snarl", 397, Rarity.RARE, mage.cards.f.FurycalmSnarl.class)); + cards.add(new SetCardInfo("G'raha Tia, Scion Reborn", 172, Rarity.MYTHIC, mage.cards.g.GrahaTiaScionReborn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("G'raha Tia, Scion Reborn", 203, Rarity.MYTHIC, mage.cards.g.GrahaTiaScionReborn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("G'raha Tia, Scion Reborn", 211, Rarity.MYTHIC, mage.cards.g.GrahaTiaScionReborn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("G'raha Tia, Scion Reborn", 222, Rarity.MYTHIC, mage.cards.g.GrahaTiaScionReborn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("G'raha Tia, Scion Reborn", 3, Rarity.MYTHIC, mage.cards.g.GrahaTiaScionReborn.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Game Trail", 398, Rarity.RARE, mage.cards.g.GameTrail.class)); + cards.add(new SetCardInfo("Gatta and Luzzu", 134, Rarity.RARE, mage.cards.g.GattaAndLuzzu.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gatta and Luzzu", 19, Rarity.RARE, mage.cards.g.GattaAndLuzzu.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gau, Feral Youth", 152, Rarity.RARE, mage.cards.g.GauFeralYouth.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gau, Feral Youth", 55, Rarity.RARE, mage.cards.g.GauFeralYouth.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("General Leo Cristophe", 135, Rarity.RARE, mage.cards.g.GeneralLeoCristophe.class, NON_FULL_USE_VARIOUS)); @@ -194,6 +217,8 @@ public final class FinalFantasyCommander extends ExpansionSet { cards.add(new SetCardInfo("Into the Story", 266, Rarity.UNCOMMON, mage.cards.i.IntoTheStory.class)); cards.add(new SetCardInfo("Isolated Chapel", 405, Rarity.RARE, mage.cards.i.IsolatedChapel.class)); cards.add(new SetCardInfo("Jungle Shrine", 406, Rarity.UNCOMMON, mage.cards.j.JungleShrine.class)); + cards.add(new SetCardInfo("Kefka, Dancing Mad", 174, Rarity.RARE, mage.cards.k.KefkaDancingMad.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kefka, Dancing Mad", 84, Rarity.RARE, mage.cards.k.KefkaDancingMad.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Key to the City", 348, Rarity.RARE, mage.cards.k.KeyToTheCity.class)); cards.add(new SetCardInfo("Kimahri, Valiant Guardian", 175, Rarity.RARE, mage.cards.k.KimahriValiantGuardian.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Kimahri, Valiant Guardian", 85, Rarity.RARE, mage.cards.k.KimahriValiantGuardian.class, NON_FULL_USE_VARIOUS)); @@ -201,17 +226,27 @@ public final class FinalFantasyCommander extends ExpansionSet { cards.add(new SetCardInfo("Krile Baldesion", 86, Rarity.RARE, mage.cards.k.KrileBaldesion.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Legions to Ashes", 326, Rarity.RARE, mage.cards.l.LegionsToAshes.class)); cards.add(new SetCardInfo("Lethal Scheme", 277, Rarity.RARE, mage.cards.l.LethalScheme.class)); + cards.add(new SetCardInfo("Lifestream's Blessing", 122, Rarity.RARE, mage.cards.l.LifestreamsBlessing.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lifestream's Blessing", 67, Rarity.RARE, mage.cards.l.LifestreamsBlessing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lightning Greaves", 349, Rarity.UNCOMMON, mage.cards.l.LightningGreaves.class)); cards.add(new SetCardInfo("Lingering Souls", 245, Rarity.UNCOMMON, mage.cards.l.LingeringSouls.class)); + cards.add(new SetCardInfo("Locke, Treasure Hunter", 177, Rarity.RARE, mage.cards.l.LockeTreasureHunter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Locke, Treasure Hunter", 87, Rarity.RARE, mage.cards.l.LockeTreasureHunter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lord Jyscal Guado", 137, Rarity.RARE, mage.cards.l.LordJyscalGuado.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lord Jyscal Guado", 23, Rarity.RARE, mage.cards.l.LordJyscalGuado.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lulu, Stern Guardian", 143, Rarity.RARE, mage.cards.l.LuluSternGuardian.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lulu, Stern Guardian", 38, Rarity.RARE, mage.cards.l.LuluSternGuardian.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Luminous Broodmoth", 246, Rarity.MYTHIC, mage.cards.l.LuminousBroodmoth.class)); + cards.add(new SetCardInfo("Lyse Hext", 178, Rarity.RARE, mage.cards.l.LyseHext.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lyse Hext", 88, Rarity.RARE, mage.cards.l.LyseHext.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Maester Seymour", 160, Rarity.RARE, mage.cards.m.MaesterSeymour.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Maester Seymour", 68, Rarity.RARE, mage.cards.m.MaesterSeymour.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mask of Memory", 350, Rarity.UNCOMMON, mage.cards.m.MaskOfMemory.class)); cards.add(new SetCardInfo("Meteor Golem", 351, Rarity.UNCOMMON, mage.cards.m.MeteorGolem.class)); cards.add(new SetCardInfo("Millikin", 352, Rarity.UNCOMMON, mage.cards.m.Millikin.class)); cards.add(new SetCardInfo("Mind Stone", 353, Rarity.UNCOMMON, mage.cards.m.MindStone.class)); + cards.add(new SetCardInfo("Mog, Moogle Warrior", 179, Rarity.RARE, mage.cards.m.MogMoogleWarrior.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mog, Moogle Warrior", 89, Rarity.RARE, mage.cards.m.MogMoogleWarrior.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Morbid Opportunist", 278, Rarity.UNCOMMON, mage.cards.m.MorbidOpportunist.class)); cards.add(new SetCardInfo("Mortify", 327, Rarity.UNCOMMON, mage.cards.m.Mortify.class)); cards.add(new SetCardInfo("Mossfire Valley", 407, Rarity.RARE, mage.cards.m.MossfireValley.class)); @@ -226,6 +261,8 @@ public final class FinalFantasyCommander extends ExpansionSet { cards.add(new SetCardInfo("Observed Stasis", 40, Rarity.RARE, mage.cards.o.ObservedStasis.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Overflowing Basin", 410, Rarity.RARE, mage.cards.o.OverflowingBasin.class)); cards.add(new SetCardInfo("Palace Jailer", 247, Rarity.UNCOMMON, mage.cards.p.PalaceJailer.class)); + cards.add(new SetCardInfo("Papalymo Totolymo", 180, Rarity.RARE, mage.cards.p.PapalymoTotolymo.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Papalymo Totolymo", 90, Rarity.RARE, mage.cards.p.PapalymoTotolymo.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Path of Ancestry", 411, Rarity.COMMON, mage.cards.p.PathOfAncestry.class)); cards.add(new SetCardInfo("Path of Discovery", 312, Rarity.RARE, mage.cards.p.PathOfDiscovery.class)); cards.add(new SetCardInfo("Path to Exile", 248, Rarity.UNCOMMON, mage.cards.p.PathToExile.class)); @@ -234,6 +271,8 @@ public final class FinalFantasyCommander extends ExpansionSet { cards.add(new SetCardInfo("Prairie Stream", 413, Rarity.RARE, mage.cards.p.PrairieStream.class)); cards.add(new SetCardInfo("Priest of Fell Rites", 328, Rarity.RARE, mage.cards.p.PriestOfFellRites.class)); cards.add(new SetCardInfo("Professional Face-Breaker", 296, Rarity.RARE, mage.cards.p.ProfessionalFaceBreaker.class)); + cards.add(new SetCardInfo("Professor Hojo", 161, Rarity.RARE, mage.cards.p.ProfessorHojo.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Professor Hojo", 69, Rarity.RARE, mage.cards.p.ProfessorHojo.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Promise of Loyalty", 249, Rarity.RARE, mage.cards.p.PromiseOfLoyalty.class)); cards.add(new SetCardInfo("Propaganda", 268, Rarity.UNCOMMON, mage.cards.p.Propaganda.class)); cards.add(new SetCardInfo("Protection Magic", 107, Rarity.RARE, mage.cards.p.ProtectionMagic.class, NON_FULL_USE_VARIOUS)); @@ -244,10 +283,16 @@ public final class FinalFantasyCommander extends ExpansionSet { cards.add(new SetCardInfo("Rampant Growth", 313, Rarity.COMMON, mage.cards.r.RampantGrowth.class)); cards.add(new SetCardInfo("Rampant Rejuvenator", 314, Rarity.RARE, mage.cards.r.RampantRejuvenator.class)); cards.add(new SetCardInfo("Reanimate", 282, Rarity.RARE, mage.cards.r.Reanimate.class)); + cards.add(new SetCardInfo("Reaper's Scythe", 117, Rarity.RARE, mage.cards.r.ReapersScythe.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reaper's Scythe", 48, Rarity.RARE, mage.cards.r.ReapersScythe.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Red XIII, Proud Warrior", 181, Rarity.RARE, mage.cards.r.RedXIIIProudWarrior.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Red XIII, Proud Warrior", 91, Rarity.RARE, mage.cards.r.RedXIIIProudWarrior.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rejoin the Fight", 118, Rarity.RARE, mage.cards.r.RejoinTheFight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rejoin the Fight", 49, Rarity.RARE, mage.cards.r.RejoinTheFight.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Relic of Legends", 354, Rarity.UNCOMMON, mage.cards.r.RelicOfLegends.class)); cards.add(new SetCardInfo("Resourceful Defense", 251, Rarity.RARE, mage.cards.r.ResourcefulDefense.class)); + cards.add(new SetCardInfo("Rikku, Resourceful Guardian", 145, Rarity.RARE, mage.cards.r.RikkuResourcefulGuardian.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rikku, Resourceful Guardian", 41, Rarity.RARE, mage.cards.r.RikkuResourcefulGuardian.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rise of the Dark Realms", 283, Rarity.MYTHIC, mage.cards.r.RiseOfTheDarkRealms.class)); cards.add(new SetCardInfo("Rite of Replication", 270, Rarity.RARE, mage.cards.r.RiteOfReplication.class)); cards.add(new SetCardInfo("Rogue's Passage", 415, Rarity.UNCOMMON, mage.cards.r.RoguesPassage.class)); @@ -272,16 +317,22 @@ public final class FinalFantasyCommander extends ExpansionSet { cards.add(new SetCardInfo("Sepulchral Primordial", 284, Rarity.RARE, mage.cards.s.SepulchralPrimordial.class)); cards.add(new SetCardInfo("Setzer, Wandering Gambler", 183, Rarity.RARE, mage.cards.s.SetzerWanderingGambler.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Setzer, Wandering Gambler", 93, Rarity.RARE, mage.cards.s.SetzerWanderingGambler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shadow, Mysterious Assassin", 148, Rarity.RARE, mage.cards.s.ShadowMysteriousAssassin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shadow, Mysterious Assassin", 50, Rarity.RARE, mage.cards.s.ShadowMysteriousAssassin.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shadowblood Ridge", 421, Rarity.RARE, mage.cards.s.ShadowbloodRidge.class)); cards.add(new SetCardInfo("Shelinda, Yevon Acolyte", 184, Rarity.RARE, mage.cards.s.ShelindaYevonAcolyte.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shelinda, Yevon Acolyte", 94, Rarity.RARE, mage.cards.s.ShelindaYevonAcolyte.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shineshadow Snarl", 422, Rarity.RARE, mage.cards.s.ShineshadowSnarl.class)); cards.add(new SetCardInfo("Siegfried, Famed Swordsman", 149, Rarity.RARE, mage.cards.s.SiegfriedFamedSwordsman.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Siegfried, Famed Swordsman", 51, Rarity.RARE, mage.cards.s.SiegfriedFamedSwordsman.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sin, Unending Cataclysm", 185, Rarity.RARE, mage.cards.s.SinUnendingCataclysm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sin, Unending Cataclysm", 95, Rarity.RARE, mage.cards.s.SinUnendingCataclysm.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Skullclamp", 355, Rarity.UNCOMMON, mage.cards.s.Skullclamp.class)); cards.add(new SetCardInfo("Skycloud Expanse", 423, Rarity.RARE, mage.cards.s.SkycloudExpanse.class)); cards.add(new SetCardInfo("Slayers' Stronghold", 424, Rarity.RARE, mage.cards.s.SlayersStronghold.class)); cards.add(new SetCardInfo("Smoldering Marsh", 425, Rarity.RARE, mage.cards.s.SmolderingMarsh.class)); + cards.add(new SetCardInfo("Snort", 120, Rarity.RARE, mage.cards.s.Snort.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Snort", 58, Rarity.RARE, mage.cards.s.Snort.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Snuff Out", 285, Rarity.COMMON, mage.cards.s.SnuffOut.class)); cards.add(new SetCardInfo("Sol Ring", 356, Rarity.UNCOMMON, mage.cards.s.SolRing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sol Ring", 357, Rarity.UNCOMMON, mage.cards.s.SolRing.class, NON_FULL_USE_VARIOUS)); @@ -293,8 +344,12 @@ public final class FinalFantasyCommander extends ExpansionSet { cards.add(new SetCardInfo("Spire of Industry", 426, Rarity.RARE, mage.cards.s.SpireOfIndustry.class)); cards.add(new SetCardInfo("Stitch Together", 286, Rarity.UNCOMMON, mage.cards.s.StitchTogether.class)); cards.add(new SetCardInfo("Stitcher's Supplier", 287, Rarity.UNCOMMON, mage.cards.s.StitchersSupplier.class)); + cards.add(new SetCardInfo("Strago and Relm", 155, Rarity.RARE, mage.cards.s.StragoAndRelm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Strago and Relm", 59, Rarity.RARE, mage.cards.s.StragoAndRelm.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sublime Epiphany", 271, Rarity.RARE, mage.cards.s.SublimeEpiphany.class)); cards.add(new SetCardInfo("Sulfurous Springs", 427, Rarity.RARE, mage.cards.s.SulfurousSprings.class)); + cards.add(new SetCardInfo("Summon: Esper Valigarmanda", 198, Rarity.RARE, mage.cards.s.SummonEsperValigarmanda.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Summon: Esper Valigarmanda", 60, Rarity.RARE, mage.cards.s.SummonEsperValigarmanda.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summon: Good King Mog XII", 194, Rarity.RARE, mage.cards.s.SummonGoodKingMogXII.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summon: Good King Mog XII", 26, Rarity.RARE, mage.cards.s.SummonGoodKingMogXII.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summon: Ixion", 195, Rarity.RARE, mage.cards.s.SummonIxion.class, NON_FULL_USE_VARIOUS)); @@ -303,6 +358,8 @@ public final class FinalFantasyCommander extends ExpansionSet { cards.add(new SetCardInfo("Summon: Kujata", 61, Rarity.RARE, mage.cards.s.SummonKujata.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summon: Magus Sisters", 200, Rarity.RARE, mage.cards.s.SummonMagusSisters.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summon: Magus Sisters", 71, Rarity.RARE, mage.cards.s.SummonMagusSisters.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Summon: Valefor", 197, Rarity.RARE, mage.cards.s.SummonValefor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Summon: Valefor", 42, Rarity.RARE, mage.cards.s.SummonValefor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summon: Yojimbo", 196, Rarity.RARE, mage.cards.s.SummonYojimbo.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summon: Yojimbo", 28, Rarity.RARE, mage.cards.s.SummonYojimbo.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summoner's Sending", 109, Rarity.RARE, mage.cards.s.SummonersSending.class, NON_FULL_USE_VARIOUS)); @@ -346,6 +403,11 @@ public final class FinalFantasyCommander extends ExpansionSet { cards.add(new SetCardInfo("The Warring Triad", 99, Rarity.RARE, mage.cards.t.TheWarringTriad.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Thought Vessel", 368, Rarity.COMMON, mage.cards.t.ThoughtVessel.class)); cards.add(new SetCardInfo("Three Visits", 315, Rarity.UNCOMMON, mage.cards.t.ThreeVisits.class)); + cards.add(new SetCardInfo("Tidus, Yuna's Guardian", 187, Rarity.MYTHIC, mage.cards.t.TidusYunasGuardian.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tidus, Yuna's Guardian", 205, Rarity.MYTHIC, mage.cards.t.TidusYunasGuardian.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tidus, Yuna's Guardian", 213, Rarity.MYTHIC, mage.cards.t.TidusYunasGuardian.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tidus, Yuna's Guardian", 224, Rarity.MYTHIC, mage.cards.t.TidusYunasGuardian.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tidus, Yuna's Guardian", 5, Rarity.MYTHIC, mage.cards.t.TidusYunasGuardian.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Tifa, Martial Artist", 188, Rarity.MYTHIC, mage.cards.t.TifaMartialArtist.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Tifa, Martial Artist", 206, Rarity.MYTHIC, mage.cards.t.TifaMartialArtist.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Tifa, Martial Artist", 214, Rarity.MYTHIC, mage.cards.t.TifaMartialArtist.class, NON_FULL_USE_VARIOUS)); @@ -363,10 +425,14 @@ public final class FinalFantasyCommander extends ExpansionSet { cards.add(new SetCardInfo("Tromell, Seymour's Butler", 73, Rarity.RARE, mage.cards.t.TromellSeymoursButler.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ultimate Magic: Holy", 110, Rarity.RARE, mage.cards.u.UltimateMagicHoly.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ultimate Magic: Holy", 32, Rarity.RARE, mage.cards.u.UltimateMagicHoly.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ultimate Magic: Meteor", 121, Rarity.RARE, mage.cards.u.UltimateMagicMeteor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ultimate Magic: Meteor", 62, Rarity.RARE, mage.cards.u.UltimateMagicMeteor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Umaro, Raging Yeti", 156, Rarity.RARE, mage.cards.u.UmaroRagingYeti.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Umaro, Raging Yeti", 63, Rarity.RARE, mage.cards.u.UmaroRagingYeti.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Underground River", 439, Rarity.RARE, mage.cards.u.UndergroundRiver.class)); cards.add(new SetCardInfo("Unfinished Business", 259, Rarity.RARE, mage.cards.u.UnfinishedBusiness.class)); + cards.add(new SetCardInfo("Urianger Augurelt", 189, Rarity.RARE, mage.cards.u.UriangerAugurelt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urianger Augurelt", 96, Rarity.RARE, mage.cards.u.UriangerAugurelt.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Vandalblast", 298, Rarity.UNCOMMON, mage.cards.v.Vandalblast.class)); cards.add(new SetCardInfo("Vanquish the Horde", 260, Rarity.RARE, mage.cards.v.VanquishTheHorde.class)); cards.add(new SetCardInfo("Vincent, Vengeful Atoner", 157, Rarity.RARE, mage.cards.v.VincentVengefulAtoner.class, NON_FULL_USE_VARIOUS)); @@ -374,6 +440,8 @@ public final class FinalFantasyCommander extends ExpansionSet { cards.add(new SetCardInfo("Vindicate", 330, Rarity.RARE, mage.cards.v.Vindicate.class)); cards.add(new SetCardInfo("Vineglimmer Snarl", 440, Rarity.RARE, mage.cards.v.VineglimmerSnarl.class)); cards.add(new SetCardInfo("Void Rend", 331, Rarity.RARE, mage.cards.v.VoidRend.class)); + cards.add(new SetCardInfo("Wakka, Devoted Guardian", 190, Rarity.RARE, mage.cards.w.WakkaDevotedGuardian.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Wakka, Devoted Guardian", 97, Rarity.RARE, mage.cards.w.WakkaDevotedGuardian.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Walking Ballista", 371, Rarity.RARE, mage.cards.w.WalkingBallista.class)); cards.add(new SetCardInfo("Wayfarer's Bauble", 372, Rarity.COMMON, mage.cards.w.WayfarersBauble.class)); cards.add(new SetCardInfo("Wooded Ridgeline", 441, Rarity.COMMON, mage.cards.w.WoodedRidgeline.class)); @@ -389,5 +457,11 @@ public final class FinalFantasyCommander extends ExpansionSet { cards.add(new SetCardInfo("Yuna's Decision", 125, Rarity.RARE, mage.cards.y.YunasDecision.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Yuna's Decision", 74, Rarity.RARE, mage.cards.y.YunasDecision.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Yuna's Whistle", 126, Rarity.RARE, mage.cards.y.YunasWhistle.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Yuna's Whistle", 75, Rarity.RARE, mage.cards.y.YunasWhistle.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Yuna, Grand Summoner", 192, Rarity.MYTHIC, mage.cards.y.YunaGrandSummoner.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Yuna, Grand Summoner", 208, Rarity.MYTHIC, mage.cards.y.YunaGrandSummoner.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Yuna, Grand Summoner", 216, Rarity.MYTHIC, mage.cards.y.YunaGrandSummoner.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Yuna, Grand Summoner", 227, Rarity.MYTHIC, mage.cards.y.YunaGrandSummoner.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Yuna, Grand Summoner", 8, Rarity.MYTHIC, mage.cards.y.YunaGrandSummoner.class, NON_FULL_USE_VARIOUS)); } } diff --git a/Mage.Sets/src/mage/sets/FinalFantasyThroughTheAges.java b/Mage.Sets/src/mage/sets/FinalFantasyThroughTheAges.java new file mode 100644 index 00000000000..ac726d9ef9b --- /dev/null +++ b/Mage.Sets/src/mage/sets/FinalFantasyThroughTheAges.java @@ -0,0 +1,89 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * @author TheElk801 + */ +public final class FinalFantasyThroughTheAges extends ExpansionSet { + + private static final FinalFantasyThroughTheAges instance = new FinalFantasyThroughTheAges(); + + public static FinalFantasyThroughTheAges getInstance() { + return instance; + } + + private FinalFantasyThroughTheAges() { + super("Final Fantasy: Through the Ages", "FCA", ExpansionSet.buildDate(2025, 6, 13), SetType.SUPPLEMENTAL); + this.hasBoosters = false; + this.hasBasicLands = false; + this.maxCardNumberInBooster = 64; + + cards.add(new SetCardInfo("Adeline, Resplendent Cathar", 1, Rarity.RARE, mage.cards.a.AdelineResplendentCathar.class)); + cards.add(new SetCardInfo("Akroma's Will", 21, Rarity.MYTHIC, mage.cards.a.AkromasWill.class)); + cards.add(new SetCardInfo("Ancient Copper Dragon", 12, Rarity.MYTHIC, mage.cards.a.AncientCopperDragon.class)); + cards.add(new SetCardInfo("Atraxa, Grand Unifier", 49, Rarity.MYTHIC, mage.cards.a.AtraxaGrandUnifier.class)); + cards.add(new SetCardInfo("Azusa, Lost but Seeking", 15, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class)); + cards.add(new SetCardInfo("Bolas's Citadel", 7, Rarity.RARE, mage.cards.b.BolassCitadel.class)); + cards.add(new SetCardInfo("Brainstorm", 28, Rarity.UNCOMMON, mage.cards.b.Brainstorm.class)); + cards.add(new SetCardInfo("Bruse Tarl, Boorish Herder", 50, Rarity.RARE, mage.cards.b.BruseTarlBoorishHerder.class)); + cards.add(new SetCardInfo("Captain Lannery Storm", 38, Rarity.UNCOMMON, mage.cards.c.CaptainLanneryStorm.class)); + cards.add(new SetCardInfo("Carpet of Flowers", 44, Rarity.RARE, mage.cards.c.CarpetOfFlowers.class)); + cards.add(new SetCardInfo("Chromatic Lantern", 61, Rarity.RARE, mage.cards.c.ChromaticLantern.class)); + cards.add(new SetCardInfo("Command Beacon", 64, Rarity.RARE, mage.cards.c.CommandBeacon.class)); + cards.add(new SetCardInfo("Counterspell", 4, Rarity.UNCOMMON, mage.cards.c.Counterspell.class)); + cards.add(new SetCardInfo("Cryptic Command", 29, Rarity.RARE, mage.cards.c.CrypticCommand.class)); + cards.add(new SetCardInfo("Danitha Capashen, Paragon", 22, Rarity.UNCOMMON, mage.cards.d.DanithaCapashenParagon.class)); + cards.add(new SetCardInfo("Dark Ritual", 8, Rarity.RARE, mage.cards.d.DarkRitual.class)); + cards.add(new SetCardInfo("Deadly Dispute", 33, Rarity.UNCOMMON, mage.cards.d.DeadlyDispute.class)); + cards.add(new SetCardInfo("Diabolic Intent", 34, Rarity.RARE, mage.cards.d.DiabolicIntent.class)); + cards.add(new SetCardInfo("Dovin's Veto", 51, Rarity.UNCOMMON, mage.cards.d.DovinsVeto.class)); + cards.add(new SetCardInfo("Farseek", 45, Rarity.UNCOMMON, mage.cards.f.Farseek.class)); + cards.add(new SetCardInfo("Fatal Push", 9, Rarity.UNCOMMON, mage.cards.f.FatalPush.class)); + cards.add(new SetCardInfo("Fynn, the Fangbearer", 46, Rarity.UNCOMMON, mage.cards.f.FynnTheFangbearer.class)); + cards.add(new SetCardInfo("Gix, Yawgmoth Praetor", 35, Rarity.MYTHIC, mage.cards.g.GixYawgmothPraetor.class)); + cards.add(new SetCardInfo("Godo, Bandit Warlord", 13, Rarity.RARE, mage.cards.g.GodoBanditWarlord.class)); + cards.add(new SetCardInfo("Inalla, Archmage Ritualist", 52, Rarity.RARE, mage.cards.i.InallaArchmageRitualist.class)); + cards.add(new SetCardInfo("Ishai, Ojutai Dragonspeaker", 53, Rarity.RARE, mage.cards.i.IshaiOjutaiDragonspeaker.class)); + cards.add(new SetCardInfo("Isshin, Two Heavens as One", 54, Rarity.RARE, mage.cards.i.IsshinTwoHeavensAsOne.class)); + cards.add(new SetCardInfo("Jodah, the Unifier", 17, Rarity.RARE, mage.cards.j.JodahTheUnifier.class)); + cards.add(new SetCardInfo("K'rrik, Son of Yawgmoth", 36, Rarity.RARE, mage.cards.k.KrrikSonOfYawgmoth.class)); + cards.add(new SetCardInfo("Kenrith, the Returned King", 23, Rarity.RARE, mage.cards.k.KenrithTheReturnedKing.class)); + cards.add(new SetCardInfo("Kinnan, Bonder Prodigy", 55, Rarity.MYTHIC, mage.cards.k.KinnanBonderProdigy.class)); + cards.add(new SetCardInfo("Kraum, Ludevic's Opus", 56, Rarity.RARE, mage.cards.k.KraumLudevicsOpus.class)); + cards.add(new SetCardInfo("Laboratory Maniac", 30, Rarity.UNCOMMON, mage.cards.l.LaboratoryManiac.class)); + cards.add(new SetCardInfo("Light Up the Stage", 39, Rarity.UNCOMMON, mage.cards.l.LightUpTheStage.class)); + cards.add(new SetCardInfo("Lightning Bolt", 40, Rarity.UNCOMMON, mage.cards.l.LightningBolt.class)); + cards.add(new SetCardInfo("Loran of the Third Path", 24, Rarity.RARE, mage.cards.l.LoranOfTheThirdPath.class)); + cards.add(new SetCardInfo("Mangara, the Diplomat", 25, Rarity.RARE, mage.cards.m.MangaraTheDiplomat.class)); + cards.add(new SetCardInfo("Mizzix's Mastery", 41, Rarity.RARE, mage.cards.m.MizzixsMastery.class)); + cards.add(new SetCardInfo("Muldrotha, the Gravetide", 57, Rarity.RARE, mage.cards.m.MuldrothaTheGravetide.class)); + cards.add(new SetCardInfo("Najeela, the Blade-Blossom", 42, Rarity.MYTHIC, mage.cards.n.NajeelaTheBladeBlossom.class)); + cards.add(new SetCardInfo("Nature's Claim", 47, Rarity.UNCOMMON, mage.cards.n.NaturesClaim.class)); + cards.add(new SetCardInfo("Nyxbloom Ancient", 16, Rarity.MYTHIC, mage.cards.n.NyxbloomAncient.class)); + cards.add(new SetCardInfo("Primeval Titan", 48, Rarity.RARE, mage.cards.p.PrimevalTitan.class)); + cards.add(new SetCardInfo("Purphoros, God of the Forge", 14, Rarity.MYTHIC, mage.cards.p.PurphorosGodOfTheForge.class)); + cards.add(new SetCardInfo("Ragavan, Nimble Pilferer", 43, Rarity.MYTHIC, mage.cards.r.RagavanNimblePilferer.class)); + cards.add(new SetCardInfo("Ranger-Captain of Eos", 2, Rarity.MYTHIC, mage.cards.r.RangerCaptainOfEos.class)); + cards.add(new SetCardInfo("Rhystic Study", 31, Rarity.MYTHIC, mage.cards.r.RhysticStudy.class)); + cards.add(new SetCardInfo("Smuggler's Copter", 62, Rarity.RARE, mage.cards.s.SmugglersCopter.class)); + cards.add(new SetCardInfo("Sram, Senior Edificer", 3, Rarity.RARE, mage.cards.s.SramSeniorEdificer.class)); + cards.add(new SetCardInfo("Strixhaven Stadium", 63, Rarity.UNCOMMON, mage.cards.s.StrixhavenStadium.class)); + cards.add(new SetCardInfo("Stroke of Midnight", 26, Rarity.UNCOMMON, mage.cards.s.StrokeOfMidnight.class)); + cards.add(new SetCardInfo("Syr Konrad, the Grim", 10, Rarity.UNCOMMON, mage.cards.s.SyrKonradTheGrim.class)); + cards.add(new SetCardInfo("Teferi, Mage of Zhalfir", 32, Rarity.RARE, mage.cards.t.TeferiMageOfZhalfir.class)); + cards.add(new SetCardInfo("Thrasios, Triton Hero", 58, Rarity.MYTHIC, mage.cards.t.ThrasiosTritonHero.class)); + cards.add(new SetCardInfo("Traxos, Scourge of Kroog", 20, Rarity.RARE, mage.cards.t.TraxosScourgeOfKroog.class)); + cards.add(new SetCardInfo("Tymna the Weaver", 18, Rarity.RARE, mage.cards.t.TymnaTheWeaver.class)); + cards.add(new SetCardInfo("Urza, Lord High Artificer", 5, Rarity.MYTHIC, mage.cards.u.UrzaLordHighArtificer.class)); + cards.add(new SetCardInfo("Varragoth, Bloodsky Sire", 37, Rarity.RARE, mage.cards.v.VarragothBloodskySire.class)); + cards.add(new SetCardInfo("Venser, Shaper Savant", 6, Rarity.RARE, mage.cards.v.VenserShaperSavant.class)); + cards.add(new SetCardInfo("Vial Smasher the Fierce", 59, Rarity.MYTHIC, mage.cards.v.VialSmasherTheFierce.class)); + cards.add(new SetCardInfo("Wall of Omens", 27, Rarity.UNCOMMON, mage.cards.w.WallOfOmens.class)); + cards.add(new SetCardInfo("Winota, Joiner of Forces", 19, Rarity.RARE, mage.cards.w.WinotaJoinerOfForces.class)); + cards.add(new SetCardInfo("Yawgmoth, Thran Physician", 11, Rarity.MYTHIC, mage.cards.y.YawgmothThranPhysician.class)); + cards.add(new SetCardInfo("Yuriko, the Tiger's Shadow", 60, Rarity.RARE, mage.cards.y.YurikoTheTigersShadow.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/FoundationsJumpstart.java b/Mage.Sets/src/mage/sets/FoundationsJumpstart.java index 877f0a5c0fa..19c140f0b35 100644 --- a/Mage.Sets/src/mage/sets/FoundationsJumpstart.java +++ b/Mage.Sets/src/mage/sets/FoundationsJumpstart.java @@ -51,7 +51,7 @@ public final class FoundationsJumpstart extends ExpansionSet { cards.add(new SetCardInfo("Angelic Edict", 164, Rarity.COMMON, mage.cards.a.AngelicEdict.class)); cards.add(new SetCardInfo("Angelic Gift", 165, Rarity.COMMON, mage.cards.a.AngelicGift.class)); cards.add(new SetCardInfo("Anje's Ravager", 517, Rarity.RARE, mage.cards.a.AnjesRavager.class)); - //cards.add(new SetCardInfo("Aphelia, Viper Whisperer", 40, Rarity.MYTHIC, mage.cards.a.ApheliaViperWhisperer.class)); + cards.add(new SetCardInfo("Aphelia, Viper Whisperer", 40, Rarity.MYTHIC, mage.cards.a.ApheliaViperWhisperer.class)); cards.add(new SetCardInfo("Arbor Armament", 628, Rarity.COMMON, mage.cards.a.ArborArmament.class)); cards.add(new SetCardInfo("Archaeomancer", 285, Rarity.COMMON, mage.cards.a.Archaeomancer.class)); cards.add(new SetCardInfo("Archfiend's Vessel", 398, Rarity.UNCOMMON, mage.cards.a.ArchfiendsVessel.class)); @@ -538,7 +538,7 @@ public final class FoundationsJumpstart extends ExpansionSet { cards.add(new SetCardInfo("Primeval Herald", 702, Rarity.UNCOMMON, mage.cards.p.PrimevalHerald.class)); cards.add(new SetCardInfo("Prophetic Prism", 758, Rarity.COMMON, mage.cards.p.PropheticPrism.class)); cards.add(new SetCardInfo("Prosperous Thief", 346, Rarity.UNCOMMON, mage.cards.p.ProsperousThief.class)); - //cards.add(new SetCardInfo("Psemilla, Meletian Poet", 31, Rarity.UNCOMMON, mage.cards.p.PsemillaMeletianPoet.class)); + cards.add(new SetCardInfo("Psemilla, Meletian Poet", 31, Rarity.UNCOMMON, mage.cards.p.PsemillaMeletianPoet.class)); cards.add(new SetCardInfo("Purple-Crystal Crab", 347, Rarity.COMMON, mage.cards.p.PurpleCrystalCrab.class)); cards.add(new SetCardInfo("Pyrophobia", 587, Rarity.COMMON, mage.cards.p.Pyrophobia.class)); cards.add(new SetCardInfo("Qala, Ajani's Pridemate", 32, Rarity.UNCOMMON, mage.cards.q.QalaAjanisPridemate.class)); diff --git a/Mage.Sets/src/mage/sets/Mirage.java b/Mage.Sets/src/mage/sets/Mirage.java index 2361973dafb..a35daacfe45 100644 --- a/Mage.Sets/src/mage/sets/Mirage.java +++ b/Mage.Sets/src/mage/sets/Mirage.java @@ -96,7 +96,7 @@ public final class Mirage extends ExpansionSet { cards.add(new SetCardInfo("Decomposition", 212, Rarity.UNCOMMON, mage.cards.d.Decomposition.class, RETRO_ART)); cards.add(new SetCardInfo("Delirium", 260, Rarity.UNCOMMON, mage.cards.d.Delirium.class, RETRO_ART)); cards.add(new SetCardInfo("Dirtwater Wraith", 117, Rarity.COMMON, mage.cards.d.DirtwaterWraith.class, RETRO_ART)); -// cards.add(new SetCardInfo("Discordant Spirit", 261, Rarity.RARE, mage.cards.d.DiscordantSpirit.class, RETRO_ART)); + cards.add(new SetCardInfo("Discordant Spirit", 261, Rarity.RARE, mage.cards.d.DiscordantSpirit.class, RETRO_ART)); cards.add(new SetCardInfo("Disempower", 9, Rarity.COMMON, mage.cards.d.Disempower.class, RETRO_ART)); cards.add(new SetCardInfo("Disenchant", 10, Rarity.COMMON, mage.cards.d.Disenchant.class, RETRO_ART)); cards.add(new SetCardInfo("Dissipate", 61, Rarity.UNCOMMON, mage.cards.d.Dissipate.class, RETRO_ART)); @@ -295,14 +295,14 @@ public final class Mirage extends ExpansionSet { cards.add(new SetCardInfo("Serene Heart", 242, Rarity.COMMON, mage.cards.s.SereneHeart.class, RETRO_ART)); cards.add(new SetCardInfo("Sewer Rats", 139, Rarity.COMMON, mage.cards.s.SewerRats.class, RETRO_ART)); cards.add(new SetCardInfo("Shadow Guildmage", 140, Rarity.COMMON, mage.cards.s.ShadowGuildmage.class, RETRO_ART)); -// cards.add(new SetCardInfo("Shadowbane", 38, Rarity.UNCOMMON, mage.cards.s.Shadowbane.class, RETRO_ART)); + cards.add(new SetCardInfo("Shadowbane", 38, Rarity.UNCOMMON, mage.cards.s.Shadowbane.class, RETRO_ART)); cards.add(new SetCardInfo("Shallow Grave", 141, Rarity.RARE, mage.cards.s.ShallowGrave.class, RETRO_ART)); cards.add(new SetCardInfo("Shaper Guildmage", 91, Rarity.COMMON, mage.cards.s.ShaperGuildmage.class, RETRO_ART)); cards.add(new SetCardInfo("Shauku's Minion", 283, Rarity.UNCOMMON, mage.cards.s.ShaukusMinion.class, RETRO_ART)); cards.add(new SetCardInfo("Shauku, Endbringer", 142, Rarity.RARE, mage.cards.s.ShaukuEndbringer.class, RETRO_ART)); cards.add(new SetCardInfo("Shimmer", 92, Rarity.RARE, mage.cards.s.Shimmer.class, RETRO_ART)); cards.add(new SetCardInfo("Sidar Jabari", 39, Rarity.RARE, mage.cards.s.SidarJabari.class, RETRO_ART)); -// cards.add(new SetCardInfo("Sirocco", 192, Rarity.UNCOMMON, mage.cards.s.Sirocco.class, RETRO_ART)); + cards.add(new SetCardInfo("Sirocco", 192, Rarity.UNCOMMON, mage.cards.s.Sirocco.class, RETRO_ART)); cards.add(new SetCardInfo("Skulking Ghost", 143, Rarity.COMMON, mage.cards.s.SkulkingGhost.class, RETRO_ART)); cards.add(new SetCardInfo("Sky Diamond", 319, Rarity.UNCOMMON, mage.cards.s.SkyDiamond.class, RETRO_ART)); cards.add(new SetCardInfo("Soar", 93, Rarity.COMMON, mage.cards.s.Soar.class, RETRO_ART)); diff --git a/Mage.Sets/src/mage/sets/RavnicaClueEdition.java b/Mage.Sets/src/mage/sets/RavnicaClueEdition.java index 9b71bfb42a0..6111eb31787 100644 --- a/Mage.Sets/src/mage/sets/RavnicaClueEdition.java +++ b/Mage.Sets/src/mage/sets/RavnicaClueEdition.java @@ -43,7 +43,7 @@ public final class RavnicaClueEdition extends ExpansionSet { cards.add(new SetCardInfo("Boros Garrison", 231, Rarity.COMMON, mage.cards.b.BorosGarrison.class)); cards.add(new SetCardInfo("Boros Guildgate", 232, Rarity.COMMON, mage.cards.b.BorosGuildgate.class)); cards.add(new SetCardInfo("Boros Signet", 220, Rarity.COMMON, mage.cards.b.BorosSignet.class)); - //cards.add(new SetCardInfo("Boros Strike-Captain", 25, Rarity.RARE, mage.cards.b.BorosStrikeCaptain.class)); + cards.add(new SetCardInfo("Boros Strike-Captain", 25, Rarity.RARE, mage.cards.b.BorosStrikeCaptain.class)); cards.add(new SetCardInfo("Breeding Pool", 275, Rarity.RARE, mage.cards.b.BreedingPool.class)); cards.add(new SetCardInfo("Candlestick", 8, Rarity.UNCOMMON, mage.cards.c.Candlestick.class)); cards.add(new SetCardInfo("Carnage Interpreter", 26, Rarity.RARE, mage.cards.c.CarnageInterpreter.class)); @@ -64,7 +64,7 @@ public final class RavnicaClueEdition extends ExpansionSet { cards.add(new SetCardInfo("Corpse Churn", 107, Rarity.COMMON, mage.cards.c.CorpseChurn.class)); cards.add(new SetCardInfo("Cosmotronic Wave", 129, Rarity.COMMON, mage.cards.c.CosmotronicWave.class)); cards.add(new SetCardInfo("Council's Judgment", 57, Rarity.RARE, mage.cards.c.CouncilsJudgment.class)); - //cards.add(new SetCardInfo("Covetous Elegy", 29, Rarity.RARE, mage.cards.c.CovetousElegy.class)); + cards.add(new SetCardInfo("Covetous Elegy", 29, Rarity.RARE, mage.cards.c.CovetousElegy.class)); cards.add(new SetCardInfo("Curse of Chains", 183, Rarity.COMMON, mage.cards.c.CurseOfChains.class)); cards.add(new SetCardInfo("Dagger Caster", 130, Rarity.UNCOMMON, mage.cards.d.DaggerCaster.class)); cards.add(new SetCardInfo("Daggerclaw Imp", 108, Rarity.UNCOMMON, mage.cards.d.DaggerclawImp.class)); @@ -107,7 +107,7 @@ public final class RavnicaClueEdition extends ExpansionSet { cards.add(new SetCardInfo("Fresh-Faced Recruit", 192, Rarity.COMMON, mage.cards.f.FreshFacedRecruit.class)); cards.add(new SetCardInfo("Frostburn Weird", 193, Rarity.COMMON, mage.cards.f.FrostburnWeird.class)); cards.add(new SetCardInfo("Fungal Rebirth", 163, Rarity.UNCOMMON, mage.cards.f.FungalRebirth.class)); - //cards.add(new SetCardInfo("Furious Spinesplitter", 33, Rarity.UNCOMMON, mage.cards.f.FuriousSpinesplitter.class)); + cards.add(new SetCardInfo("Furious Spinesplitter", 33, Rarity.UNCOMMON, mage.cards.f.FuriousSpinesplitter.class)); cards.add(new SetCardInfo("Giant Adephage", 164, Rarity.MYTHIC, mage.cards.g.GiantAdephage.class)); cards.add(new SetCardInfo("Gift of Strength", 165, Rarity.COMMON, mage.cards.g.GiftOfStrength.class)); cards.add(new SetCardInfo("Glorifier of Dusk", 62, Rarity.UNCOMMON, mage.cards.g.GlorifierOfDusk.class)); @@ -168,14 +168,14 @@ public final class RavnicaClueEdition extends ExpansionSet { cards.add(new SetCardInfo("Master Biomancer", 200, Rarity.MYTHIC, mage.cards.m.MasterBiomancer.class)); cards.add(new SetCardInfo("Mastermind Plum", 3, Rarity.RARE, mage.cards.m.MastermindPlum.class)); cards.add(new SetCardInfo("Mausoleum Turnkey", 116, Rarity.UNCOMMON, mage.cards.m.MausoleumTurnkey.class)); - //cards.add(new SetCardInfo("Memory Vampire", 38, Rarity.RARE, mage.cards.m.MemoryVampire.class)); + cards.add(new SetCardInfo("Memory Vampire", 38, Rarity.RARE, mage.cards.m.MemoryVampire.class)); cards.add(new SetCardInfo("Mighty Leap", 66, Rarity.COMMON, mage.cards.m.MightyLeap.class)); cards.add(new SetCardInfo("Mountain", 266, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 267, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 268, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 269, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Nissa's Judgment", 170, Rarity.UNCOMMON, mage.cards.n.NissasJudgment.class)); - //cards.add(new SetCardInfo("Ordruun Mentor", 39, Rarity.UNCOMMON, mage.cards.o.OrdruunMentor.class)); + cards.add(new SetCardInfo("Ordruun Mentor", 39, Rarity.UNCOMMON, mage.cards.o.OrdruunMentor.class)); cards.add(new SetCardInfo("Ornery Goblin", 143, Rarity.COMMON, mage.cards.o.OrneryGoblin.class)); cards.add(new SetCardInfo("Orzhov Basilica", 241, Rarity.COMMON, mage.cards.o.OrzhovBasilica.class)); cards.add(new SetCardInfo("Orzhov Guildgate", 242, Rarity.COMMON, mage.cards.o.OrzhovGuildgate.class)); @@ -210,7 +210,7 @@ public final class RavnicaClueEdition extends ExpansionSet { cards.add(new SetCardInfo("Reduce to Ashes", 145, Rarity.COMMON, mage.cards.r.ReduceToAshes.class)); cards.add(new SetCardInfo("Repeal", 94, Rarity.COMMON, mage.cards.r.Repeal.class)); cards.add(new SetCardInfo("Rescuer Sphinx", 95, Rarity.UNCOMMON, mage.cards.r.RescuerSphinx.class)); - //cards.add(new SetCardInfo("Resonance Technician", 41, Rarity.RARE, mage.cards.r.ResonanceTechnician.class)); + cards.add(new SetCardInfo("Resonance Technician", 41, Rarity.RARE, mage.cards.r.ResonanceTechnician.class)); cards.add(new SetCardInfo("Ribbons of Night", 120, Rarity.UNCOMMON, mage.cards.r.RibbonsOfNight.class)); cards.add(new SetCardInfo("Ripscale Predator", 146, Rarity.COMMON, mage.cards.r.RipscalePredator.class)); cards.add(new SetCardInfo("Roaming Ghostlight", 96, Rarity.COMMON, mage.cards.r.RoamingGhostlight.class)); diff --git a/Mage.Sets/src/mage/sets/SecretLairDrop.java b/Mage.Sets/src/mage/sets/SecretLairDrop.java index c6eb9f9e797..6e0085ed7b0 100644 --- a/Mage.Sets/src/mage/sets/SecretLairDrop.java +++ b/Mage.Sets/src/mage/sets/SecretLairDrop.java @@ -38,7 +38,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Reaper King", 9, Rarity.MYTHIC, mage.cards.r.ReaperKing.class)); cards.add(new SetCardInfo("Sliver Overlord", 10, Rarity.MYTHIC, mage.cards.s.SliverOverlord.class)); cards.add(new SetCardInfo("The Ur-Dragon", 11, Rarity.MYTHIC, mage.cards.t.TheUrDragon.class)); - cards.add(new SetCardInfo("Bitterblossom", 12, Rarity.MYTHIC, mage.cards.b.Bitterblossom.class)); + cards.add(new SetCardInfo("Bitterblossom", 12, Rarity.MYTHIC, mage.cards.b.Bitterblossom.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Goblin Bushwhacker", 17, Rarity.RARE, mage.cards.g.GoblinBushwhacker.class)); cards.add(new SetCardInfo("Goblin Sharpshooter", 18, Rarity.RARE, mage.cards.g.GoblinSharpshooter.class)); cards.add(new SetCardInfo("Goblin King", 19, Rarity.RARE, mage.cards.g.GoblinKing.class, NON_FULL_USE_VARIOUS)); @@ -101,7 +101,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Mogis, God of Slaughter", 78, Rarity.MYTHIC, mage.cards.m.MogisGodOfSlaughter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Keranos, God of Storms", 79, Rarity.MYTHIC, mage.cards.k.KeranosGodOfStorms.class)); cards.add(new SetCardInfo("Nylea, God of the Hunt", 80, Rarity.MYTHIC, mage.cards.n.NyleaGodOfTheHunt.class)); - cards.add(new SetCardInfo("Xenagos, God of Revels", 81, Rarity.MYTHIC, mage.cards.x.XenagosGodOfRevels.class)); + cards.add(new SetCardInfo("Xenagos, God of Revels", 81, Rarity.MYTHIC, mage.cards.x.XenagosGodOfRevels.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Pharika, God of Affliction", 82, Rarity.MYTHIC, mage.cards.p.PharikaGodOfAffliction.class)); cards.add(new SetCardInfo("Lightning Bolt", 83, Rarity.RARE, mage.cards.l.LightningBolt.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lightning Bolt", 84, Rarity.RARE, mage.cards.l.LightningBolt.class, NON_FULL_USE_VARIOUS)); @@ -170,9 +170,9 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Malik, Grim Manipulator", 147, Rarity.MYTHIC, mage.cards.m.MalikGrimManipulator.class)); cards.add(new SetCardInfo("Admonition Angel", 154, Rarity.MYTHIC, mage.cards.a.AdmonitionAngel.class)); cards.add(new SetCardInfo("Roil Elemental", 155, Rarity.RARE, mage.cards.r.RoilElemental.class)); - cards.add(new SetCardInfo("Zulaport Cutthroat", 156, Rarity.RARE, mage.cards.z.ZulaportCutthroat.class)); + cards.add(new SetCardInfo("Zulaport Cutthroat", 156, Rarity.RARE, mage.cards.z.ZulaportCutthroat.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Warren Instigator", 157, Rarity.MYTHIC, mage.cards.w.WarrenInstigator.class)); - cards.add(new SetCardInfo("Avenger of Zendikar", 158, Rarity.MYTHIC, mage.cards.a.AvengerOfZendikar.class)); + cards.add(new SetCardInfo("Avenger of Zendikar", 158, Rarity.MYTHIC, mage.cards.a.AvengerOfZendikar.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Demonlord Belzenlok", 159, Rarity.MYTHIC, mage.cards.d.DemonlordBelzenlok.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Demonlord Belzenlok", "159*", Rarity.MYTHIC, mage.cards.d.DemonlordBelzenlok.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Griselbrand", 160, Rarity.MYTHIC, mage.cards.g.Griselbrand.class, NON_FULL_USE_VARIOUS)); @@ -198,7 +198,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Decree of Pain", 187, Rarity.RARE, mage.cards.d.DecreeOfPain.class)); cards.add(new SetCardInfo("Gamble", 188, Rarity.RARE, mage.cards.g.Gamble.class)); cards.add(new SetCardInfo("Nature's Lore", 189, Rarity.RARE, mage.cards.n.NaturesLore.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Soul-Scar Mage", 190, Rarity.RARE, mage.cards.s.SoulScarMage.class)); + cards.add(new SetCardInfo("Soul-Scar Mage", 190, Rarity.RARE, mage.cards.s.SoulScarMage.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dryad of the Ilysian Grove", 191, Rarity.RARE, mage.cards.d.DryadOfTheIlysianGrove.class)); cards.add(new SetCardInfo("Sakura-Tribe Elder", 192, Rarity.RARE, mage.cards.s.SakuraTribeElder.class)); cards.add(new SetCardInfo("Spell Queller", 193, Rarity.RARE, mage.cards.s.SpellQueller.class)); @@ -212,7 +212,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Chromatic Lantern", 202, Rarity.RARE, mage.cards.c.ChromaticLantern.class)); cards.add(new SetCardInfo("Commander's Sphere", 203, Rarity.RARE, mage.cards.c.CommandersSphere.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Darksteel Ingot", 204, Rarity.RARE, mage.cards.d.DarksteelIngot.class)); - cards.add(new SetCardInfo("Gilded Lotus", 205, Rarity.RARE, mage.cards.g.GildedLotus.class)); + cards.add(new SetCardInfo("Gilded Lotus", 205, Rarity.RARE, mage.cards.g.GildedLotus.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Exquisite Blood", 206, Rarity.RARE, mage.cards.e.ExquisiteBlood.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Night's Whisper", 207, Rarity.RARE, mage.cards.n.NightsWhisper.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Phyrexian Tower", 208, Rarity.RARE, mage.cards.p.PhyrexianTower.class)); @@ -223,7 +223,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Vorinclex, Voice of Hunger", 213, Rarity.MYTHIC, mage.cards.v.VorinclexVoiceOfHunger.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Heliod, Sun-Crowned", 214, Rarity.MYTHIC, mage.cards.h.HeliodSunCrowned.class)); cards.add(new SetCardInfo("Goblin Rabblemaster", 215, Rarity.RARE, mage.cards.g.GoblinRabblemaster.class)); - cards.add(new SetCardInfo("Monastery Swiftspear", 216, Rarity.RARE, mage.cards.m.MonasterySwiftspear.class)); + cards.add(new SetCardInfo("Monastery Swiftspear", 216, Rarity.RARE, mage.cards.m.MonasterySwiftspear.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Boros Charm", 217, Rarity.RARE, mage.cards.b.BorosCharm.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gisela, Blade of Goldnight", 218, Rarity.MYTHIC, mage.cards.g.GiselaBladeOfGoldnight.class)); cards.add(new SetCardInfo("Frost Titan", 220, Rarity.MYTHIC, mage.cards.f.FrostTitan.class)); @@ -366,7 +366,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Cut // Ribbons", 367, Rarity.RARE, mage.cards.c.CutRibbons.class)); cards.add(new SetCardInfo("Teferi's Puzzle Box", 368, Rarity.RARE, mage.cards.t.TeferisPuzzleBox.class)); cards.add(new SetCardInfo("Generous Gift", 369, Rarity.RARE, mage.cards.g.GenerousGift.class)); - cards.add(new SetCardInfo("Chain Lightning", 370, Rarity.RARE, mage.cards.c.ChainLightning.class)); + cards.add(new SetCardInfo("Chain Lightning", 370, Rarity.RARE, mage.cards.c.ChainLightning.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Kodama's Reach", 371, Rarity.RARE, mage.cards.k.KodamasReach.class)); cards.add(new SetCardInfo("Heirloom Blade", 372, Rarity.RARE, mage.cards.h.HeirloomBlade.class)); cards.add(new SetCardInfo("Mulldrifter", 373, Rarity.RARE, mage.cards.m.Mulldrifter.class, NON_FULL_USE_VARIOUS)); @@ -472,7 +472,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Depala, Pilot Exemplar", 464, Rarity.RARE, mage.cards.d.DepalaPilotExemplar.class)); cards.add(new SetCardInfo("Nomad Outpost", 465, Rarity.RARE, mage.cards.n.NomadOutpost.class)); cards.add(new SetCardInfo("Island", 466, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Concordant Crossroads", 467, Rarity.RARE, mage.cards.c.ConcordantCrossroads.class)); + cards.add(new SetCardInfo("Concordant Crossroads", 467, Rarity.RARE, mage.cards.c.ConcordantCrossroads.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ghost Quarter", 468, Rarity.RARE, mage.cards.g.GhostQuarter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ash Barrens", 469, Rarity.RARE, mage.cards.a.AshBarrens.class)); cards.add(new SetCardInfo("Command Beacon", 470, Rarity.RARE, mage.cards.c.CommandBeacon.class, NON_FULL_USE_VARIOUS)); @@ -495,7 +495,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Mountain", 487, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 488, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Akroma, Angel of Wrath", 489, Rarity.MYTHIC, mage.cards.a.AkromaAngelOfWrath.class)); - cards.add(new SetCardInfo("Mikaeus, the Unhallowed", 490, Rarity.MYTHIC, mage.cards.m.MikaeusTheUnhallowed.class)); + cards.add(new SetCardInfo("Mikaeus, the Unhallowed", 490, Rarity.MYTHIC, mage.cards.m.MikaeusTheUnhallowed.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Glissa Sunseeker", 491, Rarity.RARE, mage.cards.g.GlissaSunseeker.class)); cards.add(new SetCardInfo("Olivia, Mobilized for War", 492, Rarity.MYTHIC, mage.cards.o.OliviaMobilizedForWar.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Kozilek, the Great Distortion", 493, Rarity.MYTHIC, mage.cards.k.KozilekTheGreatDistortion.class)); @@ -631,11 +631,11 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Basal Sliver", 629, Rarity.RARE, mage.cards.b.BasalSliver.class)); cards.add(new SetCardInfo("Dregscape Sliver", 631, Rarity.RARE, mage.cards.d.DregscapeSliver.class)); cards.add(new SetCardInfo("Leeching Sliver", 632, Rarity.RARE, mage.cards.l.LeechingSliver.class)); - cards.add(new SetCardInfo("Plague Sliver", 633, Rarity.RARE, mage.cards.p.PlagueSliver.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plague Sliver", "633Ph", Rarity.RARE, mage.cards.p.PlagueSliver.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plague Sliver", 633, Rarity.RARE, mage.cards.p.PlagueSliver.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Syphon Sliver", 634, Rarity.RARE, mage.cards.s.SyphonSliver.class)); - cards.add(new SetCardInfo("Toxin Sliver", 635, Rarity.RARE, mage.cards.t.ToxinSliver.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Toxin Sliver", "635Ph", Rarity.RARE, mage.cards.t.ToxinSliver.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Toxin Sliver", 635, Rarity.RARE, mage.cards.t.ToxinSliver.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Belligerent Sliver", 636, Rarity.RARE, mage.cards.b.BelligerentSliver.class)); cards.add(new SetCardInfo("Blur Sliver", 637, Rarity.RARE, mage.cards.b.BlurSliver.class)); cards.add(new SetCardInfo("Fury Sliver", 638, Rarity.RARE, mage.cards.f.FurySliver.class)); @@ -657,8 +657,8 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Quick Sliver", 655, Rarity.RARE, mage.cards.q.QuickSliver.class)); cards.add(new SetCardInfo("Root Sliver", 656, Rarity.RARE, mage.cards.r.RootSliver.class)); cards.add(new SetCardInfo("Tempered Sliver", 657, Rarity.RARE, mage.cards.t.TemperedSliver.class)); - cards.add(new SetCardInfo("Virulent Sliver", 659, Rarity.RARE, mage.cards.v.VirulentSliver.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Virulent Sliver", "659Ph", Rarity.RARE, mage.cards.v.VirulentSliver.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Virulent Sliver", 659, Rarity.RARE, mage.cards.v.VirulentSliver.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Cloudshredder Sliver", 660, Rarity.RARE, mage.cards.c.CloudshredderSliver.class)); cards.add(new SetCardInfo("Crystalline Sliver", 661, Rarity.RARE, mage.cards.c.CrystallineSliver.class)); cards.add(new SetCardInfo("Frenetic Sliver", 662, Rarity.RARE, mage.cards.f.FreneticSliver.class)); @@ -680,8 +680,8 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Torbran, Thane of Red Fell", 678, Rarity.RARE, mage.cards.t.TorbranThaneOfRedFell.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ghost Quarter", 679, Rarity.RARE, mage.cards.g.GhostQuarter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shadowborn Apostle", 680, Rarity.RARE, mage.cards.s.ShadowbornApostle.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Shadowborn Apostle", 681, Rarity.RARE, mage.cards.s.ShadowbornApostle.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shadowborn Apostle", "681Ph", Rarity.RARE, mage.cards.s.ShadowbornApostle.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shadowborn Apostle", 681, Rarity.RARE, mage.cards.s.ShadowbornApostle.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shadowborn Apostle", 682, Rarity.RARE, mage.cards.s.ShadowbornApostle.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shadowborn Apostle", 683, Rarity.RARE, mage.cards.s.ShadowbornApostle.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shadowborn Apostle", 684, Rarity.RARE, mage.cards.s.ShadowbornApostle.class, NON_FULL_USE_VARIOUS)); @@ -734,7 +734,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Seraph Sanctuary", 733, Rarity.RARE, mage.cards.s.SeraphSanctuary.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Grima Wormtongue", 734, Rarity.RARE, mage.cards.g.GrimaWormtongue.class)); cards.add(new SetCardInfo("Gaea's Blessing", 735, Rarity.RARE, mage.cards.g.GaeasBlessing.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Colossus Hammer", 736, Rarity.RARE, mage.cards.c.ColossusHammer.class)); + cards.add(new SetCardInfo("Colossus Hammer", 736, Rarity.RARE, mage.cards.c.ColossusHammer.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain Goat", 737, Rarity.RARE, mage.cards.m.MountainGoat.class)); cards.add(new SetCardInfo("Woodland Cemetery", 738, Rarity.RARE, mage.cards.w.WoodlandCemetery.class)); cards.add(new SetCardInfo("Isolated Chapel", 739, Rarity.RARE, mage.cards.i.IsolatedChapel.class)); @@ -811,17 +811,19 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Seven Dwarves", 813, Rarity.RARE, mage.cards.s.SevenDwarves.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Seven Dwarves", 814, Rarity.RARE, mage.cards.s.SevenDwarves.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Seven Dwarves", 815, Rarity.RARE, mage.cards.s.SevenDwarves.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Seven Dwarves", 816, Rarity.RARE, mage.cards.s.SevenDwarves.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Arcane Signet", 820, Rarity.RARE, mage.cards.a.ArcaneSignet.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Arcane Signet", "820*", Rarity.RARE, mage.cards.a.ArcaneSignet.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Echo of Eons", 821, Rarity.RARE, mage.cards.e.EchoOfEons.class, RETRO_ART)); cards.add(new SetCardInfo("Hive Mind", 822, Rarity.RARE, mage.cards.h.HiveMind.class, RETRO_ART)); cards.add(new SetCardInfo("Chaos Warp", 823, Rarity.RARE, mage.cards.c.ChaosWarp.class, RETRO_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Evolving Wilds", 824, Rarity.RARE, mage.cards.e.EvolvingWilds.class, RETRO_ART_USE_VARIOUS)); - cards.add(new SetCardInfo("Goblin Bombardment", 825, Rarity.RARE, mage.cards.g.GoblinBombardment.class)); + cards.add(new SetCardInfo("Goblin Bombardment", 825, Rarity.RARE, mage.cards.g.GoblinBombardment.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Kezzerdrix", 826, Rarity.RARE, mage.cards.k.Kezzerdrix.class)); cards.add(new SetCardInfo("Norin the Wary", 827, Rarity.RARE, mage.cards.n.NorinTheWary.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Norin the Wary", "827b", Rarity.RARE, mage.cards.n.NorinTheWary.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Keen Duelist", 828, Rarity.RARE, mage.cards.k.KeenDuelist.class)); + cards.add(new SetCardInfo("Fatestitcher", 835, Rarity.RARE, mage.cards.f.Fatestitcher.class, RETRO_ART)); cards.add(new SetCardInfo("Champion of the Perished", 837, Rarity.RARE, mage.cards.c.ChampionOfThePerished.class, RETRO_ART)); cards.add(new SetCardInfo("Corpse Connoisseur", 838, Rarity.RARE, mage.cards.c.CorpseConnoisseur.class, RETRO_ART)); cards.add(new SetCardInfo("Cryptbreaker", 839, Rarity.RARE, mage.cards.c.Cryptbreaker.class, RETRO_ART)); @@ -831,13 +833,16 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Haakon, Stromgald Scourge", 843, Rarity.RARE, mage.cards.h.HaakonStromgaldScourge.class, RETRO_ART)); cards.add(new SetCardInfo("Headless Rider", 844, Rarity.RARE, mage.cards.h.HeadlessRider.class, RETRO_ART)); cards.add(new SetCardInfo("Liliana's Standard Bearer", 845, Rarity.RARE, mage.cards.l.LilianasStandardBearer.class, RETRO_ART)); + cards.add(new SetCardInfo("Mikaeus, the Unhallowed", 846, Rarity.MYTHIC, mage.cards.m.MikaeusTheUnhallowed.class, RETRO_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Pontiff of Blight", 848, Rarity.RARE, mage.cards.p.PontiffOfBlight.class, RETRO_ART)); cards.add(new SetCardInfo("Ravenous Rotbelly", 849, Rarity.RARE, mage.cards.r.RavenousRotbelly.class, RETRO_ART)); cards.add(new SetCardInfo("Relentless Dead", 850, Rarity.MYTHIC, mage.cards.r.RelentlessDead.class, RETRO_ART)); + cards.add(new SetCardInfo("Rotting Regisaur", 852, Rarity.RARE, mage.cards.r.RottingRegisaur.class, RETRO_ART)); cards.add(new SetCardInfo("Tomb Tyrant", 854, Rarity.RARE, mage.cards.t.TombTyrant.class, RETRO_ART)); cards.add(new SetCardInfo("Tormod, the Desecrator", 855, Rarity.RARE, mage.cards.t.TormodTheDesecrator.class, RETRO_ART)); cards.add(new SetCardInfo("Vindictive Lich", 856, Rarity.RARE, mage.cards.v.VindictiveLich.class, RETRO_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Diregraf Captain", 858, Rarity.RARE, mage.cards.d.DiregrafCaptain.class, RETRO_ART)); + cards.add(new SetCardInfo("Havengul Lich", 859, Rarity.MYTHIC, mage.cards.h.HavengulLich.class, RETRO_ART)); cards.add(new SetCardInfo("Nekusar, the Mindrazer", 860, Rarity.MYTHIC, mage.cards.n.NekusarTheMindrazer.class, RETRO_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Varina, Lich Queen", 861, Rarity.MYTHIC, mage.cards.v.VarinaLichQueen.class, RETRO_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Wilhelt, the Rotcleaver", 862, Rarity.MYTHIC, mage.cards.w.WilheltTheRotcleaver.class, RETRO_ART)); @@ -852,18 +857,20 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Yargle and Multani", 872, Rarity.RARE, mage.cards.y.YargleAndMultani.class)); cards.add(new SetCardInfo("Dark Deal", 873, Rarity.RARE, mage.cards.d.DarkDeal.class)); cards.add(new SetCardInfo("Archivist of Oghma", 874, Rarity.RARE, mage.cards.a.ArchivistOfOghma.class)); - cards.add(new SetCardInfo("Battle Angels of Tyr", 875, Rarity.RARE, mage.cards.b.BattleAngelsOfTyr.class)); + cards.add(new SetCardInfo("Battle Angels of Tyr", 875, Rarity.MYTHIC, mage.cards.b.BattleAngelsOfTyr.class)); cards.add(new SetCardInfo("Xorn", 876, Rarity.RARE, mage.cards.x.Xorn.class)); cards.add(new SetCardInfo("Druid of Purification", 877, Rarity.RARE, mage.cards.d.DruidOfPurification.class)); cards.add(new SetCardInfo("Prosperous Innkeeper", 878, Rarity.RARE, mage.cards.p.ProsperousInnkeeper.class)); cards.add(new SetCardInfo("Minsc & Boo, Timeless Heroes", 879, Rarity.MYTHIC, mage.cards.m.MinscBooTimelessHeroes.class)); cards.add(new SetCardInfo("Stuffy Doll", 880, Rarity.RARE, mage.cards.s.StuffyDoll.class)); cards.add(new SetCardInfo("Silence", 881, Rarity.RARE, mage.cards.s.Silence.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Winds of Abandon", 882, Rarity.RARE, mage.cards.w.WindsOfAbandon.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Culling the Weak", 883, Rarity.RARE, mage.cards.c.CullingTheWeak.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fatal Push", 884, Rarity.RARE, mage.cards.f.FatalPush.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Young Wolf", 885, Rarity.RARE, mage.cards.y.YoungWolf.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Solve the Equation", 886, Rarity.RARE, mage.cards.s.SolveTheEquation.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Enduring Ideal", 887, Rarity.RARE, mage.cards.e.EnduringIdeal.class)); + cards.add(new SetCardInfo("Changeling Outcast", 894, Rarity.RARE, mage.cards.c.ChangelingOutcast.class)); cards.add(new SetCardInfo("Helpful Hunter", 895, Rarity.RARE, mage.cards.h.HelpfulHunter.class)); cards.add(new SetCardInfo("Spirited Companion", 896, Rarity.RARE, mage.cards.s.SpiritedCompanion.class)); cards.add(new SetCardInfo("The Scarab God", 900, Rarity.MYTHIC, mage.cards.t.TheScarabGod.class)); @@ -874,6 +881,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Ignoble Hierarch", 906, Rarity.RARE, mage.cards.i.IgnobleHierarch.class)); cards.add(new SetCardInfo("Seedborn Muse", 907, Rarity.RARE, mage.cards.s.SeedbornMuse.class)); cards.add(new SetCardInfo("Arcane Signet", 908, Rarity.RARE, mage.cards.a.ArcaneSignet.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gilded Lotus", 909, Rarity.RARE, mage.cards.g.GildedLotus.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sol Ring", 910, Rarity.RARE, mage.cards.s.SolRing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Elspeth, Knight-Errant", 1001, Rarity.MYTHIC, mage.cards.e.ElspethKnightErrant.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Patron Wizard", 1002, Rarity.RARE, mage.cards.p.PatronWizard.class)); @@ -896,10 +904,10 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Idyllic Tutor", 1020, Rarity.RARE, mage.cards.i.IdyllicTutor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swords to Plowshares", 1021, Rarity.RARE, mage.cards.s.SwordsToPlowshares.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Solve the Equation", 1022, Rarity.RARE, mage.cards.s.SolveTheEquation.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Praetor's Grasp", 1023, Rarity.RARE, mage.cards.p.PraetorsGrasp.class)); + cards.add(new SetCardInfo("Praetor's Grasp", 1023, Rarity.RARE, mage.cards.p.PraetorsGrasp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Veil of Summer", 1024, Rarity.RARE, mage.cards.v.VeilOfSummer.class)); cards.add(new SetCardInfo("Merciless Executioner", 1025, Rarity.RARE, mage.cards.m.MercilessExecutioner.class)); - cards.add(new SetCardInfo("Aggravated Assault", 1026, Rarity.RARE, mage.cards.a.AggravatedAssault.class)); + cards.add(new SetCardInfo("Aggravated Assault", 1026, Rarity.RARE, mage.cards.a.AggravatedAssault.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Krenko, Tin Street Kingpin", 1027, Rarity.RARE, mage.cards.k.KrenkoTinStreetKingpin.class)); cards.add(new SetCardInfo("Zurgo Helmsmasher", 1028, Rarity.MYTHIC, mage.cards.z.ZurgoHelmsmasher.class)); cards.add(new SetCardInfo("Skysovereign, Consul Flagship", 1029, Rarity.MYTHIC, mage.cards.s.SkysovereignConsulFlagship.class)); @@ -972,7 +980,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Deepglow Skate", 1093, Rarity.RARE, mage.cards.d.DeepglowSkate.class)); cards.add(new SetCardInfo("Tireless Tracker", 1094, Rarity.RARE, mage.cards.t.TirelessTracker.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Contagion Engine", 1095, Rarity.RARE, mage.cards.c.ContagionEngine.class)); - cards.add(new SetCardInfo("Sword of Truth and Justice", 1096, Rarity.MYTHIC, mage.cards.s.SwordOfTruthAndJustice.class)); + cards.add(new SetCardInfo("Sword of Truth and Justice", 1096, Rarity.MYTHIC, mage.cards.s.SwordOfTruthAndJustice.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Laboratory Maniac", 1097, Rarity.RARE, mage.cards.l.LaboratoryManiac.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Stitcher's Supplier", 1098, Rarity.RARE, mage.cards.s.StitchersSupplier.class)); cards.add(new SetCardInfo("Beast Whisperer", 1099, Rarity.RARE, mage.cards.b.BeastWhisperer.class)); @@ -1168,7 +1176,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Grand Abolisher", 1285, Rarity.RARE, mage.cards.g.GrandAbolisher.class)); cards.add(new SetCardInfo("Selfless Savior", 1286, Rarity.RARE, mage.cards.s.SelflessSavior.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Akroma, Angel of Fury", 1287, Rarity.MYTHIC, mage.cards.a.AkromaAngelOfFury.class)); - cards.add(new SetCardInfo("Umezawa's Jitte", 1288, Rarity.RARE, mage.cards.u.UmezawasJitte.class)); + cards.add(new SetCardInfo("Umezawa's Jitte", 1288, Rarity.RARE, mage.cards.u.UmezawasJitte.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Linvala, Keeper of Silence", 1289, Rarity.MYTHIC, mage.cards.l.LinvalaKeeperOfSilence.class)); cards.add(new SetCardInfo("Sunblast Angel", 1290, Rarity.RARE, mage.cards.s.SunblastAngel.class)); cards.add(new SetCardInfo("Emeria, the Sky Ruin", 1291, Rarity.RARE, mage.cards.e.EmeriaTheSkyRuin.class)); @@ -1346,7 +1354,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Fynn, the Fangbearer", 1449, Rarity.RARE, mage.cards.f.FynnTheFangbearer.class)); cards.add(new SetCardInfo("Brion Stoutarm", 1450, Rarity.RARE, mage.cards.b.BrionStoutarm.class)); cards.add(new SetCardInfo("Samut, Voice of Dissent", 1451, Rarity.MYTHIC, mage.cards.s.SamutVoiceOfDissent.class)); - cards.add(new SetCardInfo("Marchesa, the Black Rose", 1452, Rarity.RARE, mage.cards.m.MarchesaTheBlackRose.class)); + cards.add(new SetCardInfo("Marchesa, the Black Rose", 1452, Rarity.RARE, mage.cards.m.MarchesaTheBlackRose.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ajani Goldmane", 1453, Rarity.MYTHIC, mage.cards.a.AjaniGoldmane.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ajani Goldmane", "1453b", Rarity.MYTHIC, mage.cards.a.AjaniGoldmane.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jace Beleren", 1454, Rarity.MYTHIC, mage.cards.j.JaceBeleren.class, NON_FULL_USE_VARIOUS)); @@ -1527,7 +1535,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Anowon, the Ruin Thief", "1568*", Rarity.MYTHIC, mage.cards.a.AnowonTheRuinThief.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Grenzo, Dungeon Warden", 1569, Rarity.RARE, mage.cards.g.GrenzoDungeonWarden.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Grenzo, Dungeon Warden", "1569*", Rarity.RARE, mage.cards.g.GrenzoDungeonWarden.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Blade of Selves", 1570, Rarity.RARE, mage.cards.b.BladeOfSelves.class)); + cards.add(new SetCardInfo("Blade of Selves", 1570, Rarity.RARE, mage.cards.b.BladeOfSelves.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Conqueror's Flail", 1571, Rarity.RARE, mage.cards.c.ConquerorsFlail.class)); cards.add(new SetCardInfo("Darksteel Plate", 1572, Rarity.RARE, mage.cards.d.DarksteelPlate.class)); cards.add(new SetCardInfo("Deathrender", 1573, Rarity.RARE, mage.cards.d.Deathrender.class)); @@ -1540,7 +1548,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Black Market", "1577*", Rarity.RARE, mage.cards.b.BlackMarket.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dire Undercurrents", 1578, Rarity.RARE, mage.cards.d.DireUndercurrents.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dire Undercurrents", "1578*", Rarity.RARE, mage.cards.d.DireUndercurrents.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Obeka, Brute Chronologist", 1579, Rarity.RARE, mage.cards.o.ObekaBruteChronologist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Obeka, Brute Chronologist", 1579, Rarity.MYTHIC, mage.cards.o.ObekaBruteChronologist.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Obeka, Brute Chronologist", "1579*", Rarity.RARE, mage.cards.o.ObekaBruteChronologist.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rose Noble", 1580, Rarity.RARE, mage.cards.r.RoseNoble.class)); cards.add(new SetCardInfo("The Meep", 1581, Rarity.RARE, mage.cards.t.TheMeep.class)); @@ -1686,7 +1694,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Ruhan of the Fomori", 1695, Rarity.MYTHIC, mage.cards.r.RuhanOfTheFomori.class)); cards.add(new SetCardInfo("Sol Ring", 1696, Rarity.RARE, mage.cards.s.SolRing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Command Tower", 1697, Rarity.RARE, mage.cards.c.CommandTower.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Sorin Markov", 1698, Rarity.RARE, mage.cards.s.SorinMarkov.class)); + cards.add(new SetCardInfo("Sorin Markov", 1698, Rarity.MYTHIC, mage.cards.s.SorinMarkov.class)); cards.add(new SetCardInfo("Huatli, Radiant Champion", 1699, Rarity.MYTHIC, mage.cards.h.HuatliRadiantChampion.class)); cards.add(new SetCardInfo("Kiora, Behemoth Beckoner", 1700, Rarity.RARE, mage.cards.k.KioraBehemothBeckoner.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Tezzeret, Master of the Bridge", 1701, Rarity.MYTHIC, mage.cards.t.TezzeretMasterOfTheBridge.class)); @@ -1700,7 +1708,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Shivan Dragon", 1709, Rarity.RARE, mage.cards.s.ShivanDragon.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Elves of Deep Shadow", 1710, Rarity.RARE, mage.cards.e.ElvesOfDeepShadow.class)); cards.add(new SetCardInfo("Good-Fortune Unicorn", 1711, Rarity.RARE, mage.cards.g.GoodFortuneUnicorn.class)); - cards.add(new SetCardInfo("Coat of Arms", 1712, Rarity.RARE, mage.cards.c.CoatOfArms.class)); + cards.add(new SetCardInfo("Coat of Arms", 1712, Rarity.RARE, mage.cards.c.CoatOfArms.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dictate of Erebos", 1713, Rarity.RARE, mage.cards.d.DictateOfErebos.class)); cards.add(new SetCardInfo("Fecundity", 1714, Rarity.RARE, mage.cards.f.Fecundity.class)); cards.add(new SetCardInfo("Mayhem Devil", 1715, Rarity.RARE, mage.cards.m.MayhemDevil.class)); @@ -1737,7 +1745,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Black Panther, Wakandan King", 1747, Rarity.MYTHIC, mage.cards.b.BlackPantherWakandanKing.class)); cards.add(new SetCardInfo("Secure the Wastes", 1748, Rarity.RARE, mage.cards.s.SecureTheWastes.class)); cards.add(new SetCardInfo("Primal Vigor", 1749, Rarity.RARE, mage.cards.p.PrimalVigor.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Heroic Intervention", 1750, Rarity.RARE, mage.cards.h.HeroicIntervention.class)); + cards.add(new SetCardInfo("Heroic Intervention", 1750, Rarity.RARE, mage.cards.h.HeroicIntervention.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Karn's Bastion", 1751, Rarity.RARE, mage.cards.k.KarnsBastion.class)); cards.add(new SetCardInfo("Deadly Rollick", 1754, Rarity.RARE, mage.cards.d.DeadlyRollick.class)); cards.add(new SetCardInfo("Saw in Half", 1755, Rarity.RARE, mage.cards.s.SawInHalf.class)); @@ -1799,7 +1807,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Twinflame", 1810, Rarity.RARE, mage.cards.t.Twinflame.class)); cards.add(new SetCardInfo("Genesis Chamber", 1811, Rarity.RARE, mage.cards.g.GenesisChamber.class)); cards.add(new SetCardInfo("Silence", 1816, Rarity.RARE, mage.cards.s.Silence.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Winds of Abandon", 1817, Rarity.RARE, mage.cards.w.WindsOfAbandon.class)); + cards.add(new SetCardInfo("Winds of Abandon", 1817, Rarity.RARE, mage.cards.w.WindsOfAbandon.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Culling the Weak", 1818, Rarity.RARE, mage.cards.c.CullingTheWeak.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fatal Push", 1819, Rarity.RARE, mage.cards.f.FatalPush.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Young Wolf", 1820, Rarity.RARE, mage.cards.y.YoungWolf.class, NON_FULL_USE_VARIOUS)); @@ -1834,6 +1842,21 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Crib Swap", 1850, Rarity.UNCOMMON, mage.cards.c.CribSwap.class)); cards.add(new SetCardInfo("Homeward Path", 1851, Rarity.RARE, mage.cards.h.HomewardPath.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Go-Shintai of Life's Origin", 1853, Rarity.MYTHIC, mage.cards.g.GoShintaiOfLifesOrigin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Day of Judgment", 1858, Rarity.RARE, mage.cards.d.DayOfJudgment.class)); + cards.add(new SetCardInfo("Temporal Extortion", 1859, Rarity.RARE, mage.cards.t.TemporalExtortion.class)); + cards.add(new SetCardInfo("Toxic Deluge", 1860, Rarity.RARE, mage.cards.t.ToxicDeluge.class)); + cards.add(new SetCardInfo("Praetor's Grasp", 1861, Rarity.RARE, mage.cards.p.PraetorsGrasp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Star of Extinction", 1862, Rarity.RARE, mage.cards.s.StarOfExtinction.class)); + cards.add(new SetCardInfo("Staff of the Storyteller", 1863, Rarity.RARE, mage.cards.s.StaffOfTheStoryteller.class)); + cards.add(new SetCardInfo("Blade of Selves", 1864, Rarity.RARE, mage.cards.b.BladeOfSelves.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Umezawa's Jitte", 1865, Rarity.RARE, mage.cards.u.UmezawasJitte.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Colossus Hammer", 1866, Rarity.RARE, mage.cards.c.ColossusHammer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sword of Truth and Justice", 1867, Rarity.MYTHIC, mage.cards.s.SwordOfTruthAndJustice.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Prismatic Ending", 1868, Rarity.RARE, mage.cards.p.PrismaticEnding.class)); + cards.add(new SetCardInfo("Cyclonic Rift", 1869, Rarity.RARE, mage.cards.c.CyclonicRift.class)); + cards.add(new SetCardInfo("Damn", 1870, Rarity.RARE, mage.cards.d.Damn.class)); + cards.add(new SetCardInfo("Lightning Bolt", 1871, Rarity.RARE, mage.cards.l.LightningBolt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Heroic Intervention", 1872, Rarity.RARE, mage.cards.h.HeroicIntervention.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Aesi, Tyrant of Gyre Strait", 1873, Rarity.MYTHIC, mage.cards.a.AesiTyrantOfGyreStrait.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Aesi, Tyrant of Gyre Strait", "1873b", Rarity.MYTHIC, mage.cards.a.AesiTyrantOfGyreStrait.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Anje Falkenrath", 1874, Rarity.MYTHIC, mage.cards.a.AnjeFalkenrath.class, NON_FULL_USE_VARIOUS)); @@ -1851,7 +1874,25 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Benevolent Hydra", 1889, Rarity.RARE, mage.cards.b.BenevolentHydra.class)); cards.add(new SetCardInfo("Forgotten Ancient", 1890, Rarity.RARE, mage.cards.f.ForgottenAncient.class)); cards.add(new SetCardInfo("Animar, Soul of Elements", 1891, Rarity.MYTHIC, mage.cards.a.AnimarSoulOfElements.class)); + cards.add(new SetCardInfo("Secret Rendezvous", 1892, Rarity.RARE, mage.cards.s.SecretRendezvous.class)); + cards.add(new SetCardInfo("Serenity", 1893, Rarity.RARE, mage.cards.s.Serenity.class)); cards.add(new SetCardInfo("Esika's Chariot", 1894, Rarity.RARE, mage.cards.e.EsikasChariot.class)); + cards.add(new SetCardInfo("Realms Uncharted", 1895, Rarity.RARE, mage.cards.r.RealmsUncharted.class)); + cards.add(new SetCardInfo("Morophon, the Boundless", 1896, Rarity.MYTHIC, mage.cards.m.MorophonTheBoundless.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Raise the Palisade", 1897, Rarity.RARE, mage.cards.r.RaiseThePalisade.class)); + cards.add(new SetCardInfo("Bitterblossom", 1898, Rarity.MYTHIC, mage.cards.b.Bitterblossom.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Taurean Mauler", 1899, Rarity.RARE, mage.cards.t.TaureanMauler.class)); + cards.add(new SetCardInfo("Avenger of Zendikar", 1900, Rarity.MYTHIC, mage.cards.a.AvengerOfZendikar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kindred Summons", 1901, Rarity.RARE, mage.cards.k.KindredSummons.class)); + cards.add(new SetCardInfo("Tendershoot Dryad", 1902, Rarity.RARE, mage.cards.t.TendershootDryad.class)); + cards.add(new SetCardInfo("Coat of Arms", 1903, Rarity.RARE, mage.cards.c.CoatOfArms.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Maskwood Nexus", 1904, Rarity.RARE, mage.cards.m.MaskwoodNexus.class)); + cards.add(new SetCardInfo("Sol Ring", 1905, Rarity.RARE, mage.cards.s.SolRing.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shapeshifter", 1906, Rarity.RARE, mage.cards.s.Shapeshifter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shapeshifter", 1907, Rarity.RARE, mage.cards.s.Shapeshifter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shapeshifter", 1908, Rarity.RARE, mage.cards.s.Shapeshifter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shapeshifter", 1909, Rarity.RARE, mage.cards.s.Shapeshifter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rin and Seri, Inseparable", 1910, Rarity.MYTHIC, mage.cards.r.RinAndSeriInseparable.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Karmic Guide", 1911, Rarity.RARE, mage.cards.k.KarmicGuide.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ninja of the Deep Hours", 1912, Rarity.RARE, mage.cards.n.NinjaOfTheDeepHours.class)); cards.add(new SetCardInfo("Captain Sisay", 1913, Rarity.MYTHIC, mage.cards.c.CaptainSisay.class, NON_FULL_USE_VARIOUS)); @@ -1884,11 +1925,16 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Swamp", 1941, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Mountain", 1942, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Forest", 1943, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Morophon, the Boundless", 1944, Rarity.MYTHIC, mage.cards.m.MorophonTheBoundless.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Big Score", 1955, Rarity.RARE, mage.cards.b.BigScore.class)); cards.add(new SetCardInfo("Final Fortune", 1956, Rarity.RARE, mage.cards.f.FinalFortune.class)); cards.add(new SetCardInfo("Heat Shimmer", 1957, Rarity.RARE, mage.cards.h.HeatShimmer.class)); cards.add(new SetCardInfo("Roiling Vortex", 1958, Rarity.RARE, mage.cards.r.RoilingVortex.class)); cards.add(new SetCardInfo("Wheel of Misfortune", 1959, Rarity.RARE, mage.cards.w.WheelOfMisfortune.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Marwyn, the Nurturer", 1960, Rarity.RARE, mage.cards.m.MarwynTheNurturer.class)); + cards.add(new SetCardInfo("Liesa, Shroud of Dusk", 1961, Rarity.RARE, mage.cards.l.LiesaShroudOfDusk.class)); + cards.add(new SetCardInfo("Oloro, Ageless Ascetic", 1962, Rarity.MYTHIC, mage.cards.o.OloroAgelessAscetic.class)); + cards.add(new SetCardInfo("Sythis, Harvest's Hand", 1963, Rarity.RARE, mage.cards.s.SythisHarvestsHand.class)); cards.add(new SetCardInfo("Parhelion II", 1964, Rarity.RARE, mage.cards.p.ParhelionII.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Parhelion II", "1964b", Rarity.RARE, mage.cards.p.ParhelionII.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mechtitan Core", 1965, Rarity.RARE, mage.cards.m.MechtitanCore.class, NON_FULL_USE_VARIOUS)); @@ -1911,21 +1957,81 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Dragonlord Ojutai", "1973b", Rarity.MYTHIC, mage.cards.d.DragonlordOjutai.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dragonlord Silumgar", 1974, Rarity.MYTHIC, mage.cards.d.DragonlordSilumgar.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dragonlord Silumgar", "1974b", Rarity.MYTHIC, mage.cards.d.DragonlordSilumgar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Deadly Dispute", 1980, Rarity.RARE, mage.cards.d.DeadlyDispute.class)); + cards.add(new SetCardInfo("Murderous Rider", 1981, Rarity.RARE, mage.cards.m.MurderousRider.class)); + cards.add(new SetCardInfo("Zulaport Cutthroat", 1982, Rarity.RARE, mage.cards.z.ZulaportCutthroat.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Aggravated Assault", 1983, Rarity.RARE, mage.cards.a.AggravatedAssault.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Desperate Ritual", 1984, Rarity.RARE, mage.cards.d.DesperateRitual.class)); + cards.add(new SetCardInfo("Agent of Treachery", 2005, Rarity.RARE, mage.cards.a.AgentOfTreachery.class)); + cards.add(new SetCardInfo("Priest of Forgotten Gods", 2006, Rarity.RARE, mage.cards.p.PriestOfForgottenGods.class)); + cards.add(new SetCardInfo("Treasonous Ogre", 2007, Rarity.RARE, mage.cards.t.TreasonousOgre.class)); + cards.add(new SetCardInfo("Uncivil Unrest", 2008, Rarity.RARE, mage.cards.u.UncivilUnrest.class)); + cards.add(new SetCardInfo("Marchesa, the Black Rose", 2009, Rarity.RARE, mage.cards.m.MarchesaTheBlackRose.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ashaya, Soul of the Wild", 2014, Rarity.MYTHIC, mage.cards.a.AshayaSoulOfTheWild.class)); cards.add(new SetCardInfo("Elvish Reclaimer", 2015, Rarity.RARE, mage.cards.e.ElvishReclaimer.class)); cards.add(new SetCardInfo("Harrow", 2016, Rarity.RARE, mage.cards.h.Harrow.class)); cards.add(new SetCardInfo("World Shaper", 2017, Rarity.RARE, mage.cards.w.WorldShaper.class)); cards.add(new SetCardInfo("Horn of Greed", 2018, Rarity.RARE, mage.cards.h.HornOfGreed.class)); + cards.add(new SetCardInfo("Goblin Bombardment", 2024, Rarity.RARE, mage.cards.g.GoblinBombardment.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Orcish Lumberjack", 2025, Rarity.RARE, mage.cards.o.OrcishLumberjack.class)); + cards.add(new SetCardInfo("Constant Mists", 2026, Rarity.RARE, mage.cards.c.ConstantMists.class)); + cards.add(new SetCardInfo("Song of the Dryads", 2027, Rarity.RARE, mage.cards.s.SongOfTheDryads.class)); + cards.add(new SetCardInfo("Consecrated Sphinx", 2028, Rarity.MYTHIC, mage.cards.c.ConsecratedSphinx.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Resculpt", 2029, Rarity.RARE, mage.cards.r.Resculpt.class)); + cards.add(new SetCardInfo("Mirage Mirror", 2030, Rarity.RARE, mage.cards.m.MirageMirror.class)); + cards.add(new SetCardInfo("Scion of Draco", 2031, Rarity.MYTHIC, mage.cards.s.ScionOfDraco.class)); + cards.add(new SetCardInfo("Lava Dart", 2037, Rarity.RARE, mage.cards.l.LavaDart.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Monastery Swiftspear", 2038, Rarity.RARE, mage.cards.m.MonasterySwiftspear.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soul-Scar Mage", 2039, Rarity.RARE, mage.cards.s.SoulScarMage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Underworld Breach", 2040, Rarity.RARE, mage.cards.u.UnderworldBreach.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mishra's Bauble", 2041, Rarity.RARE, mage.cards.m.MishrasBauble.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lava Dart", 2042, Rarity.RARE, mage.cards.l.LavaDart.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Monastery Swiftspear", 2043, Rarity.RARE, mage.cards.m.MonasterySwiftspear.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soul-Scar Mage", 2044, Rarity.RARE, mage.cards.s.SoulScarMage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Underworld Breach", 2045, Rarity.RARE, mage.cards.u.UnderworldBreach.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mishra's Bauble", 2046, Rarity.RARE, mage.cards.m.MishrasBauble.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chain Lightning", 2047, Rarity.RARE, mage.cards.c.ChainLightning.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dragon's Rage Channeler", 2048, Rarity.RARE, mage.cards.d.DragonsRageChanneler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lava Spike", 2049, Rarity.RARE, mage.cards.l.LavaSpike.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rift Bolt", 2050, Rarity.RARE, mage.cards.r.RiftBolt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Skewer the Critics", 2051, Rarity.RARE, mage.cards.s.SkewerTheCritics.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chain Lightning", 2052, Rarity.RARE, mage.cards.c.ChainLightning.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dragon's Rage Channeler", 2053, Rarity.RARE, mage.cards.d.DragonsRageChanneler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lava Spike", 2054, Rarity.RARE, mage.cards.l.LavaSpike.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rift Bolt", 2055, Rarity.RARE, mage.cards.r.RiftBolt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Skewer the Critics", 2056, Rarity.RARE, mage.cards.s.SkewerTheCritics.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tireless Provisioner", 2057, Rarity.RARE, mage.cards.t.TirelessProvisioner.class)); + cards.add(new SetCardInfo("Sylvan Library", 2058, Rarity.RARE, mage.cards.s.SylvanLibrary.class)); + cards.add(new SetCardInfo("Ancient Greenwarden", 2059, Rarity.MYTHIC, mage.cards.a.AncientGreenwarden.class)); + cards.add(new SetCardInfo("Expressive Iteration", 2060, Rarity.RARE, mage.cards.e.ExpressiveIteration.class)); + cards.add(new SetCardInfo("Xenagos, God of Revels", 2061, Rarity.MYTHIC, mage.cards.x.XenagosGodOfRevels.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lightning Greaves", 2062, Rarity.RARE, mage.cards.l.LightningGreaves.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sol Ring", 2063, Rarity.RARE, mage.cards.s.SolRing.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cultural Exchange", 2071, Rarity.RARE, mage.cards.c.CulturalExchange.class)); + cards.add(new SetCardInfo("Folio of Fancies", 2072, Rarity.RARE, mage.cards.f.FolioOfFancies.class)); + cards.add(new SetCardInfo("Concordant Crossroads", 2073, Rarity.RARE, mage.cards.c.ConcordantCrossroads.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rites of Flourishing", 2074, Rarity.RARE, mage.cards.r.RitesOfFlourishing.class)); + cards.add(new SetCardInfo("Font of Mythos", 2075, Rarity.RARE, mage.cards.f.FontOfMythos.class)); + cards.add(new SetCardInfo("Plains", 2076, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Island", 2077, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 2078, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 2079, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Forest", 2080, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Feed the Swarm", 7001, Rarity.RARE, mage.cards.f.FeedTheSwarm.class)); + cards.add(new SetCardInfo("Forge Anew", 7002, Rarity.RARE, mage.cards.f.ForgeAnew.class)); + cards.add(new SetCardInfo("Silence", 7003, Rarity.RARE, mage.cards.s.Silence.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Smothering Tithe", 7009, Rarity.RARE, mage.cards.s.SmotheringTithe.class)); cards.add(new SetCardInfo("Counterspell", 7010, Rarity.RARE, mage.cards.c.Counterspell.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dismember", 7011, Rarity.RARE, mage.cards.d.Dismember.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Command Tower", 7012, Rarity.RARE, mage.cards.c.CommandTower.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Minds Aglow", 7028, Rarity.RARE, mage.cards.m.MindsAglow.class)); + cards.add(new SetCardInfo("Command Tower", 7029, Rarity.RARE, mage.cards.c.CommandTower.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jace, the Mind Sculptor", 8001, Rarity.MYTHIC, mage.cards.j.JaceTheMindSculptor.class)); cards.add(new SetCardInfo("Doom Blade", 9990, Rarity.RARE, mage.cards.d.DoomBlade.class)); cards.add(new SetCardInfo("Massacre", 9991, Rarity.RARE, mage.cards.m.Massacre.class)); cards.add(new SetCardInfo("Torment of Hailfire", 9992, Rarity.RARE, mage.cards.t.TormentOfHailfire.class)); cards.add(new SetCardInfo("Ruination", 9993, Rarity.RARE, mage.cards.r.Ruination.class)); - cards.add(new SetCardInfo("Mogis, God of Slaughter", 9994, Rarity.RARE, mage.cards.m.MogisGodOfSlaughter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mogis, God of Slaughter", 9994, Rarity.MYTHIC, mage.cards.m.MogisGodOfSlaughter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Garruk, Caller of Beasts", 9995, Rarity.MYTHIC, mage.cards.g.GarrukCallerOfBeasts.class)); cards.add(new SetCardInfo("Rograkh, Son of Rohgahh", 9996, Rarity.RARE, mage.cards.r.RograkhSonOfRohgahh.class)); cards.add(new SetCardInfo("Geralf's Messenger", 9997, Rarity.RARE, mage.cards.g.GeralfsMessenger.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage.Sets/src/mage/sets/SecretLairShowdown.java b/Mage.Sets/src/mage/sets/SecretLairShowdown.java index 49044f8cb02..2d6802320d5 100644 --- a/Mage.Sets/src/mage/sets/SecretLairShowdown.java +++ b/Mage.Sets/src/mage/sets/SecretLairShowdown.java @@ -25,6 +25,7 @@ public class SecretLairShowdown extends ExpansionSet { cards.add(new SetCardInfo("Dark Ritual", 16, Rarity.RARE, mage.cards.d.DarkRitual.class)); cards.add(new SetCardInfo("Death's Shadow", 8, Rarity.RARE, mage.cards.d.DeathsShadow.class)); cards.add(new SetCardInfo("Dragonlord Silumgar", 9, Rarity.MYTHIC, mage.cards.d.DragonlordSilumgar.class)); + cards.add(new SetCardInfo("Echo of Death's Wail", 356, Rarity.RARE, mage.cards.e.EchoOfDeathsWail.class)); cards.add(new SetCardInfo("Eldritch Evolution", 5, Rarity.RARE, mage.cards.e.EldritchEvolution.class)); cards.add(new SetCardInfo("Explore", 12, Rarity.RARE, mage.cards.e.Explore.class)); cards.add(new SetCardInfo("Expressive Iteration", 13, Rarity.RARE, mage.cards.e.ExpressiveIteration.class)); @@ -35,10 +36,12 @@ public class SecretLairShowdown extends ExpansionSet { cards.add(new SetCardInfo("Goblin Guide", 23, Rarity.RARE, mage.cards.g.GoblinGuide.class)); cards.add(new SetCardInfo("Island", 32, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Karn Liberated", 36, Rarity.MYTHIC, mage.cards.k.KarnLiberated.class)); + cards.add(new SetCardInfo("Laughing Jasper Flint", 44, Rarity.RARE, mage.cards.l.LaughingJasperFlint.class)); cards.add(new SetCardInfo("Lightning Bolt", 21, Rarity.RARE, mage.cards.l.LightningBolt.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lightning Bolt", 37, Rarity.RARE, mage.cards.l.LightningBolt.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Living End", 30, Rarity.MYTHIC, mage.cards.l.LivingEnd.class)); cards.add(new SetCardInfo("Mayhem Devil", 28, Rarity.RARE, mage.cards.m.MayhemDevil.class)); + cards.add(new SetCardInfo("Mountain", 34, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Murktide Regent", 17, Rarity.MYTHIC, mage.cards.m.MurktideRegent.class)); cards.add(new SetCardInfo("Nexus of Fate", 27, Rarity.RARE, mage.cards.n.NexusOfFate.class)); cards.add(new SetCardInfo("Plains", 31, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); @@ -49,14 +52,15 @@ public class SecretLairShowdown extends ExpansionSet { cards.add(new SetCardInfo("Relentless Rats", 10, Rarity.RARE, mage.cards.r.RelentlessRats.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Relentless Rats", 11, Rarity.RARE, mage.cards.r.RelentlessRats.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Seasoned Pyromancer", 24, Rarity.MYTHIC, mage.cards.s.SeasonedPyromancer.class)); + cards.add(new SetCardInfo("Shoot the Sheriff", 43, Rarity.RARE, mage.cards.s.ShootTheSheriff.class)); cards.add(new SetCardInfo("Sleight of Hand", 25, Rarity.RARE, mage.cards.s.SleightOfHand.class)); cards.add(new SetCardInfo("Spell Pierce", 18, Rarity.RARE, mage.cards.s.SpellPierce.class)); cards.add(new SetCardInfo("Springleaf Drum", 22, Rarity.RARE, mage.cards.s.SpringleafDrum.class)); cards.add(new SetCardInfo("Sudden Edict", 39, Rarity.RARE, mage.cards.s.SuddenEdict.class)); cards.add(new SetCardInfo("Supreme Verdict", 26, Rarity.RARE, mage.cards.s.SupremeVerdict.class)); + cards.add(new SetCardInfo("Swamp", 33, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Swords to Plowshares", 20, Rarity.RARE, mage.cards.s.SwordsToPlowshares.class)); - cards.add(new SetCardInfo("Tribute to Horobi", 356, Rarity.RARE, mage.cards.t.TributeToHorobi.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Echo of Death's Wail", 356, Rarity.RARE, mage.cards.e.EchoOfDeathsWail.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tribute to Horobi", 356, Rarity.RARE, mage.cards.t.TributeToHorobi.class)); cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 6, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class)); cards.add(new SetCardInfo("Unholy Heat", 4, Rarity.RARE, mage.cards.u.UnholyHeat.class)); cards.add(new SetCardInfo("Valakut, the Molten Pinnacle", 14, Rarity.RARE, mage.cards.v.ValakutTheMoltenPinnacle.class)); diff --git a/Mage.Tests/src/frozen/org/mage/test/clientside/base/MageBase.java b/Mage.Tests/src/frozen/org/mage/test/clientside/base/MageBase.java index fd148123c09..5bd7fcc6e29 100644 --- a/Mage.Tests/src/frozen/org/mage/test/clientside/base/MageBase.java +++ b/Mage.Tests/src/frozen/org/mage/test/clientside/base/MageBase.java @@ -63,7 +63,7 @@ public class MageBase { connect("player", "localhost", 17171); UUID roomId = server.getMainRoomId(); - MatchOptions options = new MatchOptions("1", "Two Player Duel"); + MatchOptions options = new MatchOptions("1", "Two Player Duel", false); options.getPlayerTypes().add("Human"); options.getPlayerTypes().add("Computer - default"); options.setDeckType("Limited"); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EvokeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EvokeTest.java index 2797e335221..6190386f55a 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EvokeTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/EvokeTest.java @@ -42,7 +42,7 @@ public class EvokeTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Shriekmaw"); setChoice(playerA, "Cast with Evoke alternative cost: {1}{B} (source: Shriekmaw"); - setChoice(playerA, "When this permanent enters the battlefield, if its evoke cost was paid, its controller sacrifices it."); // stack triggers + setChoice(playerA, "When this permanent enters, if its evoke cost was paid, its controller sacrifices it."); // stack triggers addTarget(playerA, "Silvercoat Lion"); // choice for Shriekmaw Destroy trigger castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Exhume"); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/OneShotNonTargetTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/OneShotNonTargetTest.java new file mode 100644 index 00000000000..686b7ae7597 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/OneShotNonTargetTest.java @@ -0,0 +1,77 @@ +package org.mage.test.cards.abilities.oneshot; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.player.TestPlayer; +import org.mage.test.serverside.base.CardTestPlayerBase; +/** + * + * @author notgreat + */ +public class OneShotNonTargetTest extends CardTestPlayerBase { + @Test + public void YorionChooseAfterTriggerTest() { + addCard(Zone.HAND, playerA, "Yorion, Sky Nomad"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 7); + addCard(Zone.HAND, playerA, "Resolute Reinforcements"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Yorion, Sky Nomad"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true); + checkPermanentCount("Yorion on battlefield", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Yorion, Sky Nomad", 1); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Resolute Reinforcements"); + setChoice(playerA, "Resolute Reinforcements"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertTokenCount(playerA, "Soldier Token", 2); + assertPermanentCount(playerA, "Yorion, Sky Nomad", 1); + assertPermanentCount(playerA, "Resolute Reinforcements", 1); + assertTappedCount("Plains", true, 7); + } + @Test + public void NonTargetAdjusterTest() { + addCard(Zone.HAND, playerA, "Temporal Firestorm"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.BATTLEFIELD, playerA, "Squire"); + addCard(Zone.BATTLEFIELD, playerA, "Python"); + addCard(Zone.BATTLEFIELD, playerA, "Watchwolf"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Temporal Firestorm"); + setChoice(playerA, true); + setChoice(playerA, true); + setChoice(playerA, "Squire^Python"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertGraveyardCount(playerA, "Squire", 0); + assertGraveyardCount(playerA, "Python", 0); + assertGraveyardCount(playerA, "Watchwolf", 1); + } + @Test + public void ModeSelectionTest() { + addCard(Zone.HAND, playerA, "SOLDIER Military Program"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + addCard(Zone.BATTLEFIELD, playerA, "Squire", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "SOLDIER Military Program"); + setModeChoice(playerA, "2"); + setChoice(playerA, "Squire"); + setChoice(playerA, TestPlayer.CHOICE_SKIP); + + setModeChoice(playerA, "1"); + + setModeChoice(playerA, "2"); + setChoice(playerA, "Squire^Soldier Token"); + + setStrictChooseMode(true); + setStopAt(5, PhaseStep.END_TURN); + execute(); + assertPowerToughness(playerA, "Squire", 3, 4); + assertPowerToughness(playerA, "Soldier Token", 2, 2); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/FeatherTheRedeemedTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/FeatherTheRedeemedTest.java index 02b014f3a0d..0aed0945d09 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/FeatherTheRedeemedTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/FeatherTheRedeemedTest.java @@ -115,4 +115,47 @@ public class FeatherTheRedeemedTest extends CardTestPlayerBase { setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); execute(); } + + @Test + public void test_SplitCard() { + // cast fire, put to exile, return to hand + addCard(Zone.BATTLEFIELD, playerA, "Feather, the Redeemed"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.HAND, playerA, "Fire // Ice", 1); + + // cast and put to exile + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fire"); + addTargetAmount(playerA, "Feather, the Redeemed", 2); + checkExileCount("turn 1", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Fire // Ice", 1); + checkHandCardCount("turn 1", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Fire // Ice", 0); + + // return to hand at the next end step + checkExileCount("turn 1 after", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Fire // Ice", 0); + checkHandCardCount("turn 1 after", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Fire // Ice", 1); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + } + + @Test + public void test_Adventure() { + // cast stomp, put to exile, no return to hand + addCard(Zone.BATTLEFIELD, playerA, "Feather, the Redeemed"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.HAND, playerA, "Bonecrusher Giant", 1); + + // cast and put to exile + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Stomp", "Feather, the Redeemed"); + checkExileCount("turn 1", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Bonecrusher Giant", 1); + checkHandCardCount("turn 1", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Bonecrusher Giant", 0); + + // no return to hand, as it does not go to graveyard on resolve + checkExileCount("turn 1 after", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Bonecrusher Giant", 1); + checkHandCardCount("turn 1 after", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Bonecrusher Giant", 0); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/control/PlayerUnderControlTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/control/PlayerUnderControlTest.java new file mode 100644 index 00000000000..995f5895180 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/control/PlayerUnderControlTest.java @@ -0,0 +1,83 @@ +package org.mage.test.cards.control; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.view.GameView; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * Test Framework do not support under control commands, so check only game related info and data + * + * @author JayDi85 + */ +public class PlayerUnderControlTest extends CardTestPlayerBase { + + @Test + public void test_ClientSideDataMustBeHidden() { + // possible bug: after control ends - player still able to view opponent's hands + + // When you cast Emrakul, you gain control of target opponent during that player's next turn. + // After that turn, that player takes an extra turn. + addCard(Zone.HAND, playerA, "Emrakul, the Promised End"); // {13} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 13); + // + addCard(Zone.HAND, playerB, "Lightning Bolt"); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + + // prepare control effect + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Emrakul, the Promised End"); + addTarget(playerA, playerB); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + checkUnderControl("turn 1 - A, B normal", 1, PhaseStep.PRECOMBAT_MAIN, false); + checkUnderControl("turn 2 - B under A", 2, PhaseStep.PRECOMBAT_MAIN, true); + checkUnderControl("turn 3 - A, B normal", 3, PhaseStep.PRECOMBAT_MAIN, false); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.END_TURN); + execute(); + } + + private void checkUnderControl(String info, int turnNum, PhaseStep step, boolean mustHaveControl) { + runCode(info, turnNum, step, playerA, (info1, player, game) -> { + GameView viewA = getGameView(playerA); + GameView viewB = getGameView(playerB); + if (mustHaveControl) { + Assert.assertTrue(info, playerA.isGameUnderControl()); + Assert.assertFalse(info, playerB.isGameUnderControl()); + + Assert.assertTrue(info, playerA.getPlayersUnderYourControl().contains(playerB.getId())); + Assert.assertTrue(info, playerB.getPlayersUnderYourControl().isEmpty()); + + Assert.assertTrue(info, playerA.getTurnControllers().isEmpty()); + Assert.assertTrue(info, playerB.getTurnControllers().contains(playerA.getId())); + + Assert.assertEquals(info, playerA.getTurnControlledBy(), playerA.getId()); + Assert.assertEquals(info, playerB.getTurnControlledBy(), playerA.getId()); + + // client side + Assert.assertFalse(info, viewA.getOpponentHands().isEmpty()); + Assert.assertTrue(info, viewB.getOpponentHands().isEmpty()); + } else { + // A,B normal + Assert.assertTrue(info, playerA.isGameUnderControl()); + Assert.assertTrue(info, playerB.isGameUnderControl()); + + Assert.assertTrue(info, playerA.getPlayersUnderYourControl().isEmpty()); + Assert.assertTrue(info, playerB.getPlayersUnderYourControl().isEmpty()); + + Assert.assertTrue(info, playerA.getTurnControllers().isEmpty()); + Assert.assertTrue(info, playerB.getTurnControllers().isEmpty()); + + Assert.assertEquals(info, playerA.getTurnControlledBy(), playerA.getId()); + Assert.assertEquals(info, playerB.getTurnControlledBy(), playerB.getId()); + + // client side + Assert.assertTrue(info, viewA.getOpponentHands().isEmpty()); + Assert.assertTrue(info, viewB.getOpponentHands().isEmpty()); + } + }); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CopySpellTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CopySpellTest.java index 4c01474b53c..77090668c95 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CopySpellTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CopySpellTest.java @@ -1,6 +1,9 @@ package org.mage.test.cards.copy; import mage.abilities.MageSingleton; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.AdventureCard; import mage.cards.Card; @@ -957,4 +960,23 @@ public class CopySpellTest extends CardTestPlayerBase { setStopAt(1, PhaseStep.END_COMBAT); execute(); } + + private static final String engine = "Lithoform Engine"; + + @Test + public void testAbilityCantBeCopied() { + addCustomCardWithAbility("activator", playerA, new SimpleActivatedAbility(new GainLifeEffect(1), new TapSourceCost()).withCanBeCopied(false)); + addCard(Zone.BATTLEFIELD, playerA, "Wastes", 2); + addCard(Zone.BATTLEFIELD, playerA, engine); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}", "stack ability ({T"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertTapped(engine, true); + assertLife(playerA, 20 + 1); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/SagaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/SagaTest.java index 25d12b4ea0e..0218ceb7a94 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/SagaTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/SagaTest.java @@ -1,5 +1,8 @@ package org.mage.test.cards.enchantments; +import mage.abilities.common.SagaAbility; +import mage.abilities.mana.ColorlessManaAbility; +import mage.abilities.mana.RedManaAbility; import mage.constants.PhaseStep; import mage.constants.Zone; import mage.counters.CounterType; @@ -148,8 +151,10 @@ public class SagaTest extends CardTestPlayerBase { setStopAt(1, PhaseStep.END_TURN); execute(); - assertPermanentCount(playerA, saga, 0); - assertGraveyardCount(playerA, saga, 1); + assertGraveyardCount(playerA, saga, 0); + assertAbilityCount(playerA, saga, ColorlessManaAbility.class, 1); + assertAbilityCount(playerA, saga, RedManaAbility.class, 1); + assertAbilityCount(playerA, saga, SagaAbility.class, 0); assertPermanentCount(playerA, moon, 1); } @@ -171,8 +176,11 @@ public class SagaTest extends CardTestPlayerBase { setStopAt(1, PhaseStep.END_TURN); execute(); - assertPermanentCount(playerA, saga, 0); - assertGraveyardCount(playerA, saga, 1); + assertGraveyardCount(playerA, saga, 0); + // TODO: This should be 0 but the ability still triggers due to blood moon issues + // assertAbilityCount(playerA, saga, ColorlessManaAbility.class, 0); + assertAbilityCount(playerA, saga, RedManaAbility.class, 1); + assertAbilityCount(playerA, saga, SagaAbility.class, 0); assertPermanentCount(playerA, moon, 1); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/rules/DoThisOnlyOnceEachTurnTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/rules/DoThisOnlyOnceEachTurnTest.java index 462169c4e42..25825af7b7e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/rules/DoThisOnlyOnceEachTurnTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/rules/DoThisOnlyOnceEachTurnTest.java @@ -124,4 +124,31 @@ public class DoThisOnlyOnceEachTurnTest extends CardTestPlayerBase { setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); } + + private static final String emetSelch = "Emet-Selch of the Third Seat"; + private static final String hazard = "Tectonic Hazard"; + private static final String slinger = "Goblin Fireslinger"; + + @Test + public void testEmetSelch() { + addCard(Zone.HAND, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, emetSelch); + addCard(Zone.BATTLEFIELD, playerA, slinger, 2); + addCard(Zone.GRAVEYARD, playerA, hazard); + + // player is unable to cast spell despite choosing to, choice is reset + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}:", playerB); + addTarget(playerA, hazard); + setChoice(playerA, true); + + // this time player can cast and does, so ability doesn't trigger again + playLand(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Mountain"); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}:", playerB); + addTarget(playerA, hazard); + setChoice(playerA, true); + + setStrictChooseMode(true); + execute(); + assertLife(playerB, 20 - 1 - 1 - 1); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/_40k/TheHorusHeresyTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/_40k/TheHorusHeresyTest.java new file mode 100644 index 00000000000..bbfe73a07ea --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/_40k/TheHorusHeresyTest.java @@ -0,0 +1,56 @@ +package org.mage.test.cards.single._40k; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class TheHorusHeresyTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.t.TheHorusHeresy The Horus Heresy} {3}{U}{B}{R} + * Enchantment — Saga + * I — For each opponent, gain control of up to one target nonlegendary creature that player controls for as long as this Saga remains on the battlefield. + * II — Draw a card for each creature you control but don’t own. + * III — Starting with you, each player chooses a creature. Destroy each creature chosen this way. + */ + private static final String heresy = "The Horus Heresy"; + + @Test + public void test_SimplePlay() { + addCard(Zone.HAND, playerA, heresy, 1); + + addCard(Zone.BATTLEFIELD, playerA, "Volcanic Island", 3); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + + addCard(Zone.BATTLEFIELD, playerA, "Memnite"); + addCard(Zone.BATTLEFIELD, playerB, "Ornithopter"); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, heresy); + addTarget(playerA, "Goblin Piker"); + + checkPermanentCount("after I, A control of Goblin Piker", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Goblin Piker", 1); + + // turn 3 + // draw 1 with II + + // turn 5 + checkPermanentCount("before III, A control of Goblin Piker", 5, PhaseStep.UPKEEP, playerA, "Goblin Piker", 1); + + setChoice(playerA, "Ornithopter"); + setChoice(playerB, "Memnite"); + + setStrictChooseMode(true); + setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerB, "Goblin Piker", 1); + assertGraveyardCount(playerB, "Ornithopter", 1); + assertGraveyardCount(playerA, "Memnite", 1); + assertHandCount(playerA, 3); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/acr/TheAesirEscapeValhallaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/acr/TheAesirEscapeValhallaTest.java new file mode 100644 index 00000000000..0a4dd929de2 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/acr/TheAesirEscapeValhallaTest.java @@ -0,0 +1,50 @@ +package org.mage.test.cards.single.acr; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class TheAesirEscapeValhallaTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.t.TheAesirEscapeValhalla The Aesir Escape Valhalla} {2}{G} + * Enchantment — Saga + * I — Exile a permanent card from your graveyard. You gain life equal to its mana value. + * II — Put a number of +1/+1 counters on target creature you control equal to the mana value of the exiled card. + * III — Return this Saga and the exiled card to their owner’s hand. + */ + private static final String aesir = "The Aesir Escape Valhalla"; + + @Test + public void test_SimplePlay() { + addCard(Zone.HAND, playerA, aesir, 1); + addCard(Zone.GRAVEYARD, playerA, "Gigantosaurus"); // 10/10 {G}{G}{G}{G}{G} + addCard(Zone.BATTLEFIELD, playerA, "Memnite", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, aesir); + setChoice(playerA, "Gigantosaurus"); + + checkLife("after I, lifecount", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, 20 + 5); + checkExileCount("after I, exiled", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Gigantosaurus", 1); + + // turn 3 + addTarget(playerA, "Memnite"); + + checkPermanentCounters("after II, +1/+1 counters", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", CounterType.P1P1, 5); + + // turn 5 + + setStrictChooseMode(true); + setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertHandCount(playerA, "Gigantosaurus", 1); + assertHandCount(playerA, aesir, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/aer/AjanisAidTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/aer/AjanisAidTest.java new file mode 100644 index 00000000000..5534353c066 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/aer/AjanisAidTest.java @@ -0,0 +1,77 @@ +package org.mage.test.cards.single.aer; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class AjanisAidTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.a.AjanisAid Ajani's Aid} {2}{G}{W} + * Enchantment + * When this enchantment enters, you may search your library and/or graveyard for a card named Ajani, Valiant Protector, reveal it, and put it into your hand. If you search your library this way, shuffle. + * Sacrifice this enchantment: Prevent all combat damage a creature of your choice would deal this turn. + */ + private static final String aid = "Ajani's Aid"; + + @Test + public void test_DamageOnCreature_Prevent() { + addCard(Zone.BATTLEFIELD, playerA, aid, 1); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Caelorna, Coral Tyrant"); // 0/8 + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Sacrifice"); + setChoice(playerA, "Goblin Piker"); // creature to prevent from + + attack(2, playerB, "Goblin Piker", playerA); + block(2, playerA, "Caelorna, Coral Tyrant", "Goblin Piker"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertDamageReceived(playerA, "Caelorna, Coral Tyrant", 0); + assertTapped("Goblin Piker", true); + assertGraveyardCount(playerA, aid, 1); + } + + @Test + public void test_DamageOnYou_Prevent() { + addCard(Zone.BATTLEFIELD, playerA, aid, 1); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Sacrifice"); + setChoice(playerA, "Goblin Piker"); // source to prevent from + + attack(2, playerB, "Goblin Piker", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + assertTapped("Goblin Piker", true); + assertGraveyardCount(playerA, aid, 1); + } + + @Test + public void test_DamageNonCombat_NoPrevent() { + addCard(Zone.BATTLEFIELD, playerA, aid, 1); + addCard(Zone.BATTLEFIELD, playerB, "Prodigal Pyromancer", 1); // {T}: This creature deals 1 damage to any target. + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sacrifice"); + setChoice(playerA, "Prodigal Pyromancer"); // source to prevent from + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "{T}: ", playerA); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 20 - 1); + assertGraveyardCount(playerA, aid, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/CraterhoofBehemothTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/CraterhoofBehemothTest.java new file mode 100644 index 00000000000..131e1454fba --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/CraterhoofBehemothTest.java @@ -0,0 +1,58 @@ +package org.mage.test.cards.single.avr; + +import mage.abilities.keyword.TrampleAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class CraterhoofBehemothTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.c.CraterhoofBehemoth Craterhoof Behemoth} {5}{G}{G}{G} + * Creature — Beast + * Haste + * When this creature enters, creatures you control gain trample and get +X/+X until end of turn, where X is the number of creatures you control. + * 5/5 + */ + private static final String hoof = "Craterhoof Behemoth"; + + @Test + public void test_simple() { + + addCard(Zone.HAND, playerA, hoof, 1); + addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard", 1); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 8); + + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); + addCard(Zone.BATTLEFIELD, playerB, "Merfolk of the Pearl Trident", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, hoof); + + checkAbility("hoof gets trample", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, hoof, TrampleAbility.class, true); + checkAbility("vanguard gets trample", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Elite Vanguard", TrampleAbility.class, true); + checkAbility("bears gets trample", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Grizzly Bears", TrampleAbility.class, true); + checkAbility("opp's piker no trample", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Goblin Piker", TrampleAbility.class, false); + checkAbility("opp's merfolk no trample", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Merfolk of the Pearl Trident", TrampleAbility.class, false); + + checkPT("hoof gets +3/+3", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, hoof, 5 + 3, 5 + 3); + checkPT("vanguard gets +3/+3", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Elite Vanguard", 2 + 3, 1 + 3); + checkPT("bears gets +3/+3", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Grizzly Bears", 2 + 3, 2 + 3); + checkPT("opp's piker no PT change", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Goblin Piker", 2, 1); + checkPT("opp's merfolk no PT change", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Merfolk of the Pearl Trident", 1, 1); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.PRECOMBAT_MAIN); + execute(); + + // check effect ended. + assertPowerToughness(playerA, hoof, 5, 5); + assertPowerToughness(playerA, "Elite Vanguard", 2, 1); + assertAbility(playerA, hoof, TrampleAbility.getInstance(), false); + assertAbility(playerA, "Elite Vanguard", TrampleAbility.getInstance(), false); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/EssenceHarvestTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/EssenceHarvestTest.java new file mode 100644 index 00000000000..d83ca2cfae9 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/avr/EssenceHarvestTest.java @@ -0,0 +1,59 @@ +package org.mage.test.cards.single.avr; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class EssenceHarvestTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.e.EssenceHarvest Essence Harvest} {2}{B} + * Sorcery + * Target player loses X life and you gain X life, where X is the greatest power among creatures you control. + */ + private static final String harvest = "Essence Harvest"; + + @Test + public void test_no_creature() { + addCard(Zone.HAND, playerA, harvest, 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); + addCard(Zone.BATTLEFIELD, playerB, "Merfolk of the Pearl Trident", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, harvest, playerB); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + } + + @Test + public void test_4_power() { + addCard(Zone.HAND, playerA, harvest, 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + + addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard", 1); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); + addCard(Zone.BATTLEFIELD, playerA, "Borderland Minotaur", 1); // 4/3 + + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); + addCard(Zone.BATTLEFIELD, playerB, "Merfolk of the Pearl Trident", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, harvest, playerB); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 + 4); + assertLife(playerB, 20 - 4); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/bbd/ComboAttackTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/bbd/ComboAttackTest.java new file mode 100644 index 00000000000..2acc03e2f9c --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/bbd/ComboAttackTest.java @@ -0,0 +1,86 @@ +package org.mage.test.cards.single.bbd; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author xenohedron + */ +public class ComboAttackTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.c.ComboAttack Combo Attack} {2}{G} + * Sorcery + * Two target creatures your team controls each deal damage equal to their power to target creature + */ + private static final String combo = "Combo Attack"; + + @Test + public void test_Normal() { + addCard(Zone.HAND, playerA, combo, 1); + addCard(Zone.BATTLEFIELD, playerA, "Memnite", 1); + addCard(Zone.BATTLEFIELD, playerA, "Runeclaw Bear", 1); + addCard(Zone.BATTLEFIELD, playerB, "Fortress Crab", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, combo, "Memnite^Runeclaw Bear^Fortress Crab"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, combo, 1); + assertDamageReceived(playerB, "Fortress Crab", 3); + + } + + @Test + public void test_IllegalFirst() { + addCard(Zone.HAND, playerA, combo, 1); + addCard(Zone.BATTLEFIELD, playerA, "Memnite", 1); + addCard(Zone.BATTLEFIELD, playerA, "Runeclaw Bear", 1); + addCard(Zone.BATTLEFIELD, playerB, "Fortress Crab", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + addCard(Zone.HAND, playerB, "Unsummon"); + addCard(Zone.BATTLEFIELD, playerB, "Island"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, combo, "Memnite^Runeclaw Bear^Fortress Crab"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Unsummon", "Memnite", combo); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, combo, 1); + assertGraveyardCount(playerB, "Unsummon", 1); + assertHandCount(playerA, "Memnite", 1); + assertDamageReceived(playerB, "Fortress Crab", 2); + + } + + @Test + public void test_IllegalSecond() { + addCard(Zone.HAND, playerA, combo, 1); + addCard(Zone.BATTLEFIELD, playerA, "Memnite", 1); + addCard(Zone.BATTLEFIELD, playerA, "Runeclaw Bear", 1); + addCard(Zone.BATTLEFIELD, playerB, "Fortress Crab", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + addCard(Zone.HAND, playerB, "Unsummon"); + addCard(Zone.BATTLEFIELD, playerB, "Island"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, combo, "Memnite^Runeclaw Bear^Fortress Crab"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Unsummon", "Runeclaw Bear", combo); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, combo, 1); + assertGraveyardCount(playerB, "Unsummon", 1); + assertHandCount(playerA, "Runeclaw Bear", 1); + assertDamageReceived(playerB, "Fortress Crab", 1); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/bfz/UginsInsightTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/bfz/UginsInsightTest.java new file mode 100644 index 00000000000..b65e8c409f7 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/bfz/UginsInsightTest.java @@ -0,0 +1,66 @@ +package org.mage.test.cards.single.bfz; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class UginsInsightTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.u.UginsInsight Ugin's Insight} {3}{U}{U} + * Sorcery + * Scry X, where X is the greatest mana value among permanents you control, then draw three cards. + */ + private static final String insight = "Ugin's Insight"; + + @Test + public void test_greatest_0() { + addCard(Zone.HAND, playerA, insight); + addCard(Zone.BATTLEFIELD, playerA, "Island", 5); + + addCard(Zone.BATTLEFIELD, playerB, "Barbarian Horde", 1); // 3/3 {3}{R} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, insight); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertHandCount(playerA, 3); + } + + @Test + public void test_greatest_4() { + skipInitShuffling(); + addCard(Zone.LIBRARY, playerA, "Ageless Guardian"); + addCard(Zone.LIBRARY, playerA, "Barktooth Warbeard"); + addCard(Zone.LIBRARY, playerA, "Catacomb Crocodile"); + addCard(Zone.LIBRARY, playerA, "Devilthorn Fox"); + + addCard(Zone.HAND, playerA, insight); + addCard(Zone.BATTLEFIELD, playerA, "Island", 5); + addCard(Zone.BATTLEFIELD, playerA, "Acolyte of Bahamut", 1); // {1}{G} + addCard(Zone.BATTLEFIELD, playerA, "Barbarian Horde", 1); // 3/3 {3}{R} + addCard(Zone.BATTLEFIELD, playerA, "Sword of Fire and Ice", 1); // {3} + + addCard(Zone.BATTLEFIELD, playerB, "Barbarian Horde", 1); // 3/3 {3}{R} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, insight); + addTarget(playerA, "Devilthorn Fox^Barktooth Warbeard"); // scry two to the bottom. + setChoice(playerA, "Barktooth Warbeard"); // order 2 to the bottom. + setChoice(playerA, "Catacomb Crocodile"); // order 2 to the top. + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertHandCount(playerA, 3); + assertHandCount(playerA, "Mountain", 1); + assertHandCount(playerA, "Catacomb Crocodile", 1); + assertHandCount(playerA, "Ageless Guardian", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/blc/RapidAugmenterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/blc/RapidAugmenterTest.java index 14a755f7eb0..d692e7c7e6f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/blc/RapidAugmenterTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/blc/RapidAugmenterTest.java @@ -4,14 +4,9 @@ import mage.abilities.keyword.HasteAbility; import mage.constants.PhaseStep; import mage.constants.Zone; import mage.counters.CounterType; -import mage.game.permanent.Permanent; -import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; -import static junit.framework.TestCase.assertEquals; -import static org.junit.Assert.fail; - /** * @author Susucr */ @@ -131,7 +126,7 @@ public class RapidAugmenterTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ephemerate", true); addTarget(playerA, "Memnite"); - setChoice(playerA, "Whenever another creature you control you control enters"); // order triggers (doesnt matter the order but a choice must be made) + setChoice(playerA, "Whenever another creature you control enters"); // order triggers (doesnt matter the order but a choice must be made) attack(1, playerA, rapidAugmenter, playerB); // Rapid Augmenter can't be blocked, Alpine Watchdog wont take damage diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/bro/SpotterThopterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/bro/SpotterThopterTest.java new file mode 100644 index 00000000000..eca868df272 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/bro/SpotterThopterTest.java @@ -0,0 +1,66 @@ +package org.mage.test.cards.single.bro; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class SpotterThopterTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.s.SpotterThopter Spotter Thopter} {8} + * Artifact Creature — Thopter + * Prototype {3}{U} — 2/3 (You may cast this spell with different mana cost, color, and size. It keeps its abilities and types.) + * Flying + * When this creature enters, scry X, where X is its power. + * 4/5 + */ + private static final String thopter = "Spotter Thopter"; + + @Test + public void test_prototype_scry2() { + skipInitShuffling(); + addCard(Zone.LIBRARY, playerA, "Ancient Amphitheater"); + addCard(Zone.LIBRARY, playerA, "Baleful Strix"); + addCard(Zone.LIBRARY, playerA, "Crucible of Worlds"); + addCard(Zone.LIBRARY, playerA, "Desecration Demon"); + + addCard(Zone.HAND, playerA, thopter, 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, thopter + " using Prototype"); + + // Oath of Jace's upkeep trigger triggers + addTarget(playerA, "Desecration Demon"); // put on bottom with scry 2 + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + } + + @Test + public void test_normal_scry4() { + skipInitShuffling(); + addCard(Zone.LIBRARY, playerA, "Ancient Amphitheater"); + addCard(Zone.LIBRARY, playerA, "Baleful Strix"); + addCard(Zone.LIBRARY, playerA, "Crucible of Worlds"); + addCard(Zone.LIBRARY, playerA, "Desecration Demon"); + + addCard(Zone.HAND, playerA, thopter, 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 8); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, thopter); + + // Oath of Jace's upkeep trigger triggers + addTarget(playerA, "Desecration Demon^Crucible of Worlds"); // put on bottom with scry 2 + setChoice(playerA, "Crucible of Worlds"); // order for bottom + setChoice(playerA, "Baleful Strix"); // order for top + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/c21/AlibouAncientWitnessTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/c21/AlibouAncientWitnessTest.java new file mode 100644 index 00000000000..ca0d5387a14 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/c21/AlibouAncientWitnessTest.java @@ -0,0 +1,49 @@ +package org.mage.test.cards.single.c21; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class AlibouAncientWitnessTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.a.AlibouAncientWitness Alibou, Ancient Witness {3}{R}{W} + * Legendary Artifact Creature — Golem + * Other artifact creatures you control have haste. + * Whenever one or more artifact creatures you control attack, Alibou deals X damage to any target and you scry X, where X is the number of tapped artifacts you control. + * 4/5 + */ + private static final String alibou = "Alibou, Ancient Witness"; + + @Test + public void test_scry2() { + skipInitShuffling(); + addCard(Zone.LIBRARY, playerA, "Ancient Amphitheater"); + addCard(Zone.LIBRARY, playerA, "Baleful Strix"); + + addCard(Zone.BATTLEFIELD, playerA, alibou); + addCard(Zone.BATTLEFIELD, playerA, "Memnite"); + addCard(Zone.BATTLEFIELD, playerA, "Ornithopter"); + addCard(Zone.BATTLEFIELD, playerA, "Goblin Piker"); + addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard"); + + attack(1, playerA, alibou, playerB); + attack(1, playerA, "Memnite", playerB); + attack(1, playerA, "Goblin Piker", playerB); + attack(1, playerA, "Elite Vanguard", playerB); + + addTarget(playerA, playerB); // Alibou's trigger + addTarget(playerA, "Baleful Strix^Ancient Amphitheater"); // put on bottom with Scry 2 + setChoice(playerA, "Ancient Amphitheater"); // order for bottom + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerB, 20 - 4 - 1 - 2 - 2 - 2); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/cmm/BattleAtTheHelvaultTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/cmm/BattleAtTheHelvaultTest.java new file mode 100644 index 00000000000..03327878361 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/cmm/BattleAtTheHelvaultTest.java @@ -0,0 +1,62 @@ +package org.mage.test.cards.single.cmm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class BattleAtTheHelvaultTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.b.BattleAtTheHelvault Battle at the Helvault} {4}{W}{W} + * Enchantment — Saga + * I, II — For each player, exile up to one target non-Saga, nonland permanent that player controls until this Saga leaves the battlefield. + * III — Create Avacyn, a legendary 8/8 white Angel creature token with flying, vigilance, and indestructible. + */ + private static final String battle = "Battle at the Helvault"; + + @Ignore // TODO: goal of #11619 is to fix this nicely + @Test + public void test_SimplePlay() { + addCard(Zone.HAND, playerA, battle, 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 6); + addCard(Zone.BATTLEFIELD, playerA, "Memnite", 1); + addCard(Zone.BATTLEFIELD, playerA, "Augmenting Automaton", 1); + addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears", 1); + addCard(Zone.BATTLEFIELD, playerB, "Bear Cub", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, battle); + addTarget(playerA, "Memnite"); + addTarget(playerA, "Grizzly Bears"); + + checkExileCount("T1: Memnite exiled", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", 1); + checkExileCount("T1: Augmenting Automaton not exiled", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Augmenting Automaton", 0); + checkExileCount("T1: Grizzly Bears exiled", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Grizzly Bears", 1); + checkExileCount("T1: Bear Cub not exiled", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Bear Cub", 0); + + // turn 3 + addTarget(playerA, "Augmenting Automaton"); + addTarget(playerA, "Bear Cub"); + + checkExileCount("T3: Memnite exiled", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", 1); + checkExileCount("T3: Augmenting Automaton exiled", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Augmenting Automaton", 1); + checkExileCount("T3: Grizzly Bears exiled", 3, PhaseStep.POSTCOMBAT_MAIN, playerB, "Grizzly Bears", 1); + checkExileCount("T3: Bear Cub exiled", 3, PhaseStep.POSTCOMBAT_MAIN, playerB, "Bear Cub", 1); + + // turn 5 + + setStrictChooseMode(true); + setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Memnite", 1); + assertPermanentCount(playerA, "Augmenting Automaton", 1); + assertPermanentCount(playerB, "Grizzly Bears", 1); + assertPermanentCount(playerB, "Bear Cub", 1); + assertPermanentCount(playerA, "Avacyn", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/cmr/AlenaKessigTrapperTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/cmr/AlenaKessigTrapperTest.java new file mode 100644 index 00000000000..b8aff4bfd13 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/cmr/AlenaKessigTrapperTest.java @@ -0,0 +1,46 @@ +package org.mage.test.cards.single.cmr; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class AlenaKessigTrapperTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.a.AlenaKessigTrapper Alena, Kessig Trapper} {4}{R} + * Legendary Creature — Human Scout + * First strike + * {T}: Add an amount of {R} equal to the greatest power among creatures you control that entered this turn. + * Partner + * 4/3 + */ + private static final String alena = "Alena, Kessig Trapper"; + + @Test + public void test_alena() { + addCard(Zone.BATTLEFIELD, playerA, alena); + addCard(Zone.HAND, playerA, "Frost Walker"); // 4/1 {1}{U} + addCard(Zone.HAND, playerA, "Barbarian Horde", 2); // 3/3 {3}{R} + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + + // start on turn 3 as Alena has technically entered t1. + checkPlayableAbility("1: Can not cast Horde", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Barbarian Horde", false); + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Frost Walker"); + + checkPlayableAbility("2: Can cast Horde", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast Barbarian Horde", true); + castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Barbarian Horde"); + + checkPlayableAbility("3: Can not cast Horde", 5, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Barbarian Horde", false); + + setStrictChooseMode(true); + setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Barbarian Horde", 1); + assertHandCount(playerA, "Barbarian Horde", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/cmr/SianiEyeOfTheStormTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/cmr/SianiEyeOfTheStormTest.java new file mode 100644 index 00000000000..3ca53ca8e6d --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/cmr/SianiEyeOfTheStormTest.java @@ -0,0 +1,49 @@ +package org.mage.test.cards.single.cmr; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class SianiEyeOfTheStormTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.s.SianiEyeOfTheStorm Siani, Eye of the Storm} {3}{U} + * Legendary Creature — Djinn Monk + * Flying + * Whenever Siani attacks, scry X, where X is the number of attacking creatures with flying. + * Partner (You can have two commanders if both have partner.) + * 3/2 + */ + private static final String siani = "Siani, Eye of the Storm"; + + @Test + public void test_scry2() { + skipInitShuffling(); + addCard(Zone.LIBRARY, playerA, "Ancient Amphitheater"); + addCard(Zone.LIBRARY, playerA, "Baleful Strix"); + + addCard(Zone.BATTLEFIELD, playerA, siani); + addCard(Zone.BATTLEFIELD, playerA, "Air Elemental"); + addCard(Zone.BATTLEFIELD, playerA, "Abbey Griffin"); + addCard(Zone.BATTLEFIELD, playerA, "Goblin Piker"); + addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard"); + + attack(1, playerA, siani, playerB); + attack(1, playerA, "Air Elemental", playerB); + attack(1, playerA, "Goblin Piker", playerB); + attack(1, playerA, "Elite Vanguard", playerB); + + addTarget(playerA, "Baleful Strix^Ancient Amphitheater"); // put on bottom with Scry 2 + setChoice(playerA, "Ancient Amphitheater"); // order for bottom + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerB, 20 - 3 - 4 - 2 - 2); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/cns/MuzzioVisionaryArchitectTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/cns/MuzzioVisionaryArchitectTest.java new file mode 100644 index 00000000000..3e5f42e1f34 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/cns/MuzzioVisionaryArchitectTest.java @@ -0,0 +1,62 @@ +package org.mage.test.cards.single.cns; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class MuzzioVisionaryArchitectTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.m.MuzzioVisionaryArchitect Muzzio, Visionary Architect} {1}{U}{U} + * Legendary Creature — Human Artificer + * {3}{U}, {T}: Look at the top X cards of your library, where X is the greatest mana value among artifacts you control. You may put an artifact card from among them onto the battlefield. Put the rest on the bottom of your library in any order. + * 1/3 + */ + private static final String muzzio = "Muzzio, Visionary Architect"; + + @Test + public void test_no_artifact() { + skipInitShuffling(); + addCard(Zone.LIBRARY, playerA, "Mox Ruby"); + addCard(Zone.LIBRARY, playerA, "Black Lotus"); + + addCard(Zone.BATTLEFIELD, playerA, muzzio); + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}{U}"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertTapped(muzzio, true); + assertPermanentCount(playerA, 5); + } + + @Test + public void test_mv2() { + skipInitShuffling(); + addCard(Zone.LIBRARY, playerA, "Mox Ruby"); + addCard(Zone.LIBRARY, playerA, "Black Lotus"); + + addCard(Zone.BATTLEFIELD, playerA, muzzio); + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + addCard(Zone.BATTLEFIELD, playerA, "Ace's Baseball Bat"); // mv 2 + addCard(Zone.BATTLEFIELD, playerA, "Chrome Mox", 2); // mv 0 + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}{U}"); + setChoice(playerA, "Mox Ruby"); // chosen to put on battlefield + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertTapped(muzzio, true); + assertPermanentCount(playerA, 9); + assertPermanentCount(playerA, "Mox Ruby", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/dmc/BaruWurmspeakerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dmc/BaruWurmspeakerTest.java new file mode 100644 index 00000000000..b5e3d7a4751 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dmc/BaruWurmspeakerTest.java @@ -0,0 +1,54 @@ +package org.mage.test.cards.single.dmc; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class BaruWurmspeakerTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.b.BaruWurmspeaker Baru, Wurmspeaker} {2}{G}{G} + * Legendary Creature — Human Druid + * Wurms you control get +2/+2 and have trample. + * {7}{G}, {T}: Create a 4/4 green Wurm creature token. This ability costs {X} less to activate, where X is the greatest power among Wurms you control. + * 3/3 + */ + private static final String baru = "Baru, Wurmspeaker"; + + @Test + public void test_no_wurm() { + addCard(Zone.BATTLEFIELD, playerA, baru); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 8); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{7}{G},"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertTapped(baru, true); + assertPermanentCount(playerA, "Wurm Token", 1); + assertTappedCount("Forest", true, 8); + } + + @Test + public void test_wurm() { + addCard(Zone.BATTLEFIELD, playerA, baru); + addCard(Zone.BATTLEFIELD, playerA, "Voracious Wurm"); // 2/2 Wurm + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{7}{G},"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertTapped(baru, true); + assertPermanentCount(playerA, "Wurm Token", 1); + assertTappedCount("Forest", true, 4); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/dmc/TetsuoImperialChampionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dmc/TetsuoImperialChampionTest.java new file mode 100644 index 00000000000..29d8032dd82 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dmc/TetsuoImperialChampionTest.java @@ -0,0 +1,83 @@ +package org.mage.test.cards.single.dmc; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class TetsuoImperialChampionTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.t.TetsuoImperialChampion Tetsuo, Imperial Champion} {U}{B}{R} + *

    + * Legendary Creature — Human Samurai + * Whenever Tetsuo attacks, if it's equipped, choose one — + * • Tetsuo deals damage equal to the greatest mana value among Equipment attached to it to any target. + * • You may cast an instant or sorcery spell from your hand with mana value less than or equal to the greatest mana value among Equipment attached to Tetsuo without paying its mana cost. + * 3/3 + */ + private static final String tetsuo = "Tetsuo, Imperial Champion"; + + @Test + public void test_attack_notrigger() { + addCard(Zone.BATTLEFIELD, playerA, tetsuo); + + attack(1, playerA, tetsuo, playerB); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerB, 20 - 3); + } + + @Test + public void test_attack_equipped_damage_mv3() { + addCard(Zone.BATTLEFIELD, playerA, tetsuo); + addCard(Zone.BATTLEFIELD, playerA, "Civic Saber"); // {1} +1/+0 for each color equip {1} + addCard(Zone.BATTLEFIELD, playerA, "Barbed Battlegear"); // {3} +4/-1 equip {2} + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip {2}", tetsuo); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip {1}", tetsuo); + + attack(1, playerA, tetsuo, playerB); + setModeChoice(playerA, "1"); // choose damage mode + addTarget(playerA, playerB); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerB, 20 - 3 - 3 - 4 - 3); + } + + + @Test + public void test_attack_equipped_damage_mv1() { + addCard(Zone.BATTLEFIELD, playerA, tetsuo); + addCard(Zone.BATTLEFIELD, playerA, "Brokers Initiate"); // 0/4 + addCard(Zone.BATTLEFIELD, playerA, "Civic Saber"); // {1} +1/+0 for each color equip {1} + addCard(Zone.BATTLEFIELD, playerA, "Barbed Battlegear"); // {3} +4/-1 equip {2} + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip {2}", "Brokers Initiate"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip {1}", tetsuo); + + attack(1, playerA, tetsuo, playerB); + attack(1, playerA, "Brokers Initiate", playerB); + setModeChoice(playerA, "1"); // choose damage mode + addTarget(playerA, playerB); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerB, 20 - 3 - 3 - 4 - 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/dmu/SerraParagonTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dmu/SerraParagonTest.java index edaa9049494..2e830e4beb0 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/dmu/SerraParagonTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dmu/SerraParagonTest.java @@ -130,7 +130,7 @@ public class SerraParagonTest extends CardTestPlayerBase { public void testAetherworksMarvel() { setStrictChooseMode(true); - // Whenever a permanent you control is put into a graveyard from the battlefield, you get {E} + // Whenever a permanent you control is put into a graveyard, you get {E} addCard(Zone.BATTLEFIELD, playerA, "Aetherworks Marvel"); addCard(Zone.BATTLEFIELD, playerA, paragon); addCard(Zone.GRAVEYARD, playerA, "Chromatic Star"); @@ -140,7 +140,7 @@ public class SerraParagonTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Chromatic Star", true); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Creeping Corrosion", true); setChoice(playerA, "When this permanent is put into a graveyard from the battlefield, exile it and you gain 2 life."); // stack triggers - setChoice(playerA, "Whenever a permanent you control is put into a graveyard from the battlefield, you get {E}", 2); // stack triggers + setChoice(playerA, "Whenever a permanent you control is put into a graveyard, you get {E}", 2); // stack triggers // Last trigger is: "When {this} is put into a graveyard from the battlefield, draw a card." from Chromatic Star waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); runCode("energy counter is 2", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> checkEnergyCount(info, player, 2)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/dmu/ThePhasingOfZhalfirTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dmu/ThePhasingOfZhalfirTest.java new file mode 100644 index 00000000000..d092fcc0a62 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dmu/ThePhasingOfZhalfirTest.java @@ -0,0 +1,63 @@ +package org.mage.test.cards.single.dmu; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class ThePhasingOfZhalfirTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.t.ThePhasingOfZhalfir The Phasing of Zhalfir} {2}{U}{U} + * Enchantment — Saga + * Read ahead (Choose a chapter and start with that many lore counters. Add one after your draw step. Skipped chapters don’t trigger. Sacrifice after III.) + * I, II — Another target nonland permanent phases out. It can’t phase in for as long as you control this Saga. + * III — Destroy all creatures. For each creature destroyed this way, its controller creates a 2/2 black Phyrexian creature token. + */ + private static final String phasing = "The Phasing of Zhalfir"; + + @Test + public void test_SimplePlay() { + addCard(Zone.HAND, playerA, phasing, 1); + addCard(Zone.BATTLEFIELD, playerA, "Memnite", 1); + addCard(Zone.BATTLEFIELD, playerB, "Ornithopter", 1); + addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, phasing); + setChoiceAmount(playerA, 1); // chosing to start at I with Read ahead + addTarget(playerA, "Memnite"); + + checkPermanentCount("1: Memnite is phased out", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", 0); + checkPermanentCount("1: Ornithopter not phased out", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Ornithopter", 1); + + // turn 3 + addTarget(playerA, "Ornithopter"); + + checkPermanentCount("3: Memnite is phased out", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", 0); + checkPermanentCount("3: Ornithopter is phased out", 3, PhaseStep.POSTCOMBAT_MAIN, playerB, "Ornithopter", 0); + + // turn 5 + checkPermanentCount("5: Memnite is phased out", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", 0); + checkPermanentCount("5: Ornithopter is phased out", 5, PhaseStep.POSTCOMBAT_MAIN, playerB, "Ornithopter", 0); + checkPermanentCount("5: Vanguard got destroyed", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, "Elite Vanguard", 0); + checkPermanentCount("5: phasing done after III", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, phasing, 0); + checkPermanentCount("5: has a Phyrexian creature token", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, "Phyrexian Token", 1); + + // T6: Ornithopter phases in + checkPermanentCount("6: Memnite is phased out", 6, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", 0); + checkPermanentCount("6: Ornithopter not phased out", 6, PhaseStep.POSTCOMBAT_MAIN, playerB, "Ornithopter", 1); + + // T7: Memnite phases in + + setStrictChooseMode(true); + setStopAt(7, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Memnite", 1); + assertPermanentCount(playerB, "Ornithopter", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/drk/DarkSphereTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/drk/DarkSphereTest.java new file mode 100644 index 00000000000..4aa9b9f0b18 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/drk/DarkSphereTest.java @@ -0,0 +1,73 @@ +package org.mage.test.cards.single.drk; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class DarkSphereTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.d.DarkSphere Dark Sphere} {0} + * Artifact + * {T}, Sacrifice this artifact: The next time a source of your choice would deal damage to you this turn, prevent half that damage, rounded down. + */ + private static final String sphere = "Dark Sphere"; + + @Test + public void test_DamageOnCreature_NoPrevent() { + addCard(Zone.BATTLEFIELD, playerA, sphere, 1); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Caelorna, Coral Tyrant"); // 0/8 + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice"); + setChoice(playerA, "Goblin Piker"); // source to prevent from + + attack(2, playerB, "Goblin Piker", playerA); + block(2, playerA, "Caelorna, Coral Tyrant", "Goblin Piker"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertDamageReceived(playerA, "Caelorna, Coral Tyrant", 2); // no prevent + } + + @Test + public void test_DamageOnYou_Prevent() { + addCard(Zone.BATTLEFIELD, playerA, sphere, 1); + addCard(Zone.BATTLEFIELD, playerB, "Craw Wurm", 1); // 6/4 + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice"); + setChoice(playerA, "Craw Wurm"); // source to prevent from + + attack(2, playerB, "Craw Wurm", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 3); + } + + @Test + public void test_DoubleStrike_Prevent_ThenConsumedAndNoPrevent() { + addCard(Zone.BATTLEFIELD, playerA, sphere, 1); + addCard(Zone.BATTLEFIELD, playerB, "Blade Historian", 1); // 2/3 "Attacking creatures you control have double strike." + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice"); + setChoice(playerA, "Blade Historian"); // source to prevent from + + attack(2, playerB, "Blade Historian", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 3); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/exo/PenanceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/exo/PenanceTest.java new file mode 100644 index 00000000000..0b938b3f260 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/exo/PenanceTest.java @@ -0,0 +1,61 @@ +package org.mage.test.cards.single.exo; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class PenanceTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.p.Penance Penance} {2}{W} + * Enchantment + * Put a card from your hand on top of your library: The next time a black or red source of your choice would deal damage this turn, prevent that damage. + */ + private static final String penance = "Penance"; + + @Test + public void test_DamageOnCreature_Prevent() { + addCard(Zone.BATTLEFIELD, playerA, penance, 1); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Caelorna, Coral Tyrant"); // 0/8 + addCard(Zone.HAND, playerA, "Plains", 1); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Put a card"); + setChoice(playerA, "Plains"); // card to put on top + setChoice(playerA, "Goblin Piker"); // source to prevent from + + attack(2, playerB, "Goblin Piker", playerA); + block(2, playerA, "Caelorna, Coral Tyrant", "Goblin Piker"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertDamageReceived(playerA, "Caelorna, Coral Tyrant", 0); + assertTapped("Goblin Piker", true); + } + + @Test + public void test_DamageOnYou_Prevent() { + addCard(Zone.BATTLEFIELD, playerA, penance, 1); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.HAND, playerA, "Plains", 1); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Put a card"); + setChoice(playerA, "Plains"); // card to put on top + setChoice(playerA, "Goblin Piker"); // source to prevent from + + attack(2, playerB, "Goblin Piker", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + assertTapped("Goblin Piker", true); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/fic/EdgarMasterMachinistTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/fic/EdgarMasterMachinistTest.java new file mode 100644 index 00000000000..639101c0d7a --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/fic/EdgarMasterMachinistTest.java @@ -0,0 +1,180 @@ +package org.mage.test.cards.single.fic; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class EdgarMasterMachinistTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.e.EdgarMasterMachinist Edgar, Master Machinist} {2}{R}{W} + * Legendary Creature — Human Artificer Noble + * Once during each of your turns, you may cast an artifact spell from your graveyard. If you cast a spell this way, that artifact enters tapped. + * Tools — Whenever Edgar attacks, it gets +X/+0 until end of turn, where X is the greatest mana value among artifacts you control. + * 2/4 + */ + private static final String edgar = "Edgar, Master Machinist"; + + @Test + public void test_cast_from_yard() { + addCard(Zone.BATTLEFIELD, playerA, edgar); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.GRAVEYARD, playerA, "Golgari Signet"); + addCard(Zone.GRAVEYARD, playerA, "Elite Vanguard"); + + checkPlayableAbility("can not cast Elite Vanguard", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Elite Vanguard", false); + checkPlayableAbility("can cast Golgari Signet", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Golgari Signet", true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Golgari Signet"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Golgari Signet", 1); + assertTappedCount("Plains", true, 2); + assertTappedCount("Golgari Signet", true, 1); + } + + @Test + public void test_tapped_effect_wait_for_cleanup() { + // test to make sure the discarding of the "enters tapped effect" only happens when the spell leave the stack + addCard(Zone.BATTLEFIELD, playerA, edgar); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.GRAVEYARD, playerA, "Golgari Signet"); + addCard(Zone.HAND, playerA, "Lightning Bolt"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Golgari Signet"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Golgari Signet", 1); + assertTappedCount("Mountain", true, 3); + assertTappedCount("Golgari Signet", true, 1); + } + + @Test + public void test_cast_limits() { + addCard(Zone.BATTLEFIELD, playerA, edgar); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.GRAVEYARD, playerA, "Golgari Signet"); + addCard(Zone.GRAVEYARD, playerA, "Bear Trap"); // has flash + + addCard(Zone.GRAVEYARD, playerB, "Orzhov Signet"); + + checkPlayableAbility("can not cast opponent Orzhov Signet", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Orzhov Signet", false); + checkPlayableAbility("can cast Golgari Signet", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Golgari Signet", true); + checkPlayableAbility("can cast Bear Trap", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Bear Trap", true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Golgari Signet"); + + checkPlayableAbility("can not cast 2 per turn", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast Bear Trap", false); + + checkPlayableAbility("can not cast opponent Orzhov Signet", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Orzhov Signet", false); + checkPlayableAbility("opp can not cast Orzhov Signet", 2, PhaseStep.PRECOMBAT_MAIN, playerB, "Cast Orzhov Signet", false); + checkPlayableAbility("can not cast Bear Trap on opp turn", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Bear Trap", false); + + castSpell(3, PhaseStep.UPKEEP, playerA, "Bear Trap"); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Golgari Signet", 1); + assertPermanentCount(playerA, "Bear Trap", 1); + assertTappedCount("Bear Trap", true, 1); + } + + @Test + public void test_mdfc() { + addCard(Zone.BATTLEFIELD, playerA, edgar); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.GRAVEYARD, playerA, "Halvar, God of Battle"); + + checkPlayableAbility("can not cast Halvar, God of Battle", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Halvar, God of Battle", false); + checkPlayableAbility("can cast Sword of the Realms", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Sword of the Realms", true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sword of the Realms"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Sword of the Realms", 1); + assertTappedCount("Plains", true, 2); + assertTappedCount("Sword of the Realms", true, 1); + } + + @Test + public void test_adventure() { + addCard(Zone.BATTLEFIELD, playerA, edgar); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.GRAVEYARD, playerA, "Horn of Valhalla"); + + checkPlayableAbility("can not cast Ysgard's Call", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Ysgard's Call", false); + checkPlayableAbility("can cast Horn of Valhalla", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Horn of Valhalla", true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Horn of Valhalla"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Horn of Valhalla", 1); + assertTappedCount("Plains", true, 2); + assertTappedCount("Horn of Valhalla", true, 1); + } + + @Test + public void test_remand_recast() { + addCard(Zone.BATTLEFIELD, playerA, edgar); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + addCard(Zone.HAND, playerB, "Remand"); + + addCard(Zone.GRAVEYARD, playerA, "Golgari Signet"); + addCard(Zone.GRAVEYARD, playerA, "Elite Vanguard"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Golgari Signet"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Remand", "Golgari Signet", "Golgari Signet"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Golgari Signet"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Golgari Signet", 1); + assertGraveyardCount(playerB, "Remand", 1); + assertTappedCount("Plains", true, 4); + assertTappedCount("Golgari Signet", false, 1); + } + + @Test + public void test_blink() { + addCard(Zone.BATTLEFIELD, playerA, edgar); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.HAND, playerA, "Cloudshift", 1); + addCard(Zone.GRAVEYARD, playerA, "Memnite"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Memnite"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + checkPermanentTapped("Memnite entered tapped", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Memnite", true, 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cloudshift", "Memnite"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, "Cloudshift", 1); + assertTappedCount("Memnite", false, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/fic/LifestreamsBlessingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/fic/LifestreamsBlessingTest.java new file mode 100644 index 00000000000..7330cb5246e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/fic/LifestreamsBlessingTest.java @@ -0,0 +1,92 @@ +package org.mage.test.cards.single.fic; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class LifestreamsBlessingTest extends CardTestPlayerBase { + + private static final String blessing = "Lifestream's Blessing"; + private static final String jackal = "Trained Jackal"; + private static final String bear = "Ashcoat Bear"; + private static final String giant = "Hill Giant"; + + @Test + public void testRegular() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 6); + addCard(Zone.BATTLEFIELD, playerA, jackal); + addCard(Zone.BATTLEFIELD, playerB, giant); + addCard(Zone.HAND, playerA, blessing); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, blessing); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertHandCount(playerA, 1); + assertLife(playerA, 20); + } + + @Test + public void testFlashIn22() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 6 + 2); + addCard(Zone.BATTLEFIELD, playerA, jackal); + addCard(Zone.BATTLEFIELD, playerB, giant); + addCard(Zone.HAND, playerA, blessing); + addCard(Zone.HAND, playerA, bear); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, blessing); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bear); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertHandCount(playerA, 1); + assertLife(playerA, 20); + } + + private static final String twincast = "Twincast"; + + @Test + public void testTwincast() { + addCard(Zone.BATTLEFIELD, playerA, "Tropical Island", 6 + 2); + addCard(Zone.BATTLEFIELD, playerA, jackal); + addCard(Zone.BATTLEFIELD, playerB, giant); + addCard(Zone.HAND, playerA, blessing); + addCard(Zone.HAND, playerA, twincast); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, blessing); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, twincast, blessing); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertHandCount(playerA, 1); + assertLife(playerA, 20); + } + + @Test + public void testForetell() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 5); + addCard(Zone.BATTLEFIELD, playerA, jackal); + addCard(Zone.BATTLEFIELD, playerB, giant); + addCard(Zone.HAND, playerA, blessing); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "For"); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "For"); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertHandCount(playerA, 1 + 1); + assertLife(playerA, 20 + 2); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/fic/LockeTreasureHunterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/fic/LockeTreasureHunterTest.java new file mode 100644 index 00000000000..5691cb82d71 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/fic/LockeTreasureHunterTest.java @@ -0,0 +1,101 @@ +package org.mage.test.cards.single.fic; + +import mage.cards.l.LockeTreasureHunter; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public class LockeTreasureHunterTest extends CardTestPlayerBase { + + private static final String dwarvenGrunt = "Dwarven Grunt"; + private static final String goblinMountaineer = "Goblin Mountaineer"; + private static final String mountainGoat = "Mountain Goat"; + private static final String zodiacGoat = "Zodiac Goat"; + + private void makeTester() { + addCustomCardWithAbility("tester", playerA, LockeTreasureHunter.makeTestAbility()); + } + + private void assertOptions(String... optionsToExpect) { + Set options = playerA + .getPlayable(currentGame, false) + .stream() + .map(Objects::toString) + .collect(Collectors.toSet()); + Set failures = new HashSet<>(); + for (String option : optionsToExpect) { + if (options.stream().noneMatch(s -> s.contains(option))) { + failures.add(option); + } + } + Assert.assertEquals( + "The following cards should be available to cast but aren't: " + + failures.stream().collect(Collectors.joining(", ")), 0, failures.size() + ); + Assert.assertEquals( + "There should be " + (2 + optionsToExpect.length) + " available actions", + 2 + optionsToExpect.length, options.size() + ); + } + + @Test + public void testCastSome() { + skipInitShuffling(); + makeTester(); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.LIBRARY, playerA, dwarvenGrunt); + addCard(Zone.LIBRARY, playerA, goblinMountaineer); + addCard(Zone.LIBRARY, playerB, mountainGoat); + addCard(Zone.LIBRARY, playerB, zodiacGoat); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{0}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{0}"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, dwarvenGrunt); + waitStackResolved(1, PhaseStep.POSTCOMBAT_MAIN); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertOptions(goblinMountaineer, zodiacGoat); + assertPermanentCount(playerA, dwarvenGrunt, 1); + } + + @Test + public void testCastAll() { + skipInitShuffling(); + makeTester(); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.LIBRARY, playerA, dwarvenGrunt); + addCard(Zone.LIBRARY, playerA, goblinMountaineer); + addCard(Zone.LIBRARY, playerB, mountainGoat); + addCard(Zone.LIBRARY, playerB, zodiacGoat); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{0}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{0}"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, dwarvenGrunt); + waitStackResolved(1, PhaseStep.POSTCOMBAT_MAIN); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, zodiacGoat); + waitStackResolved(1, PhaseStep.POSTCOMBAT_MAIN); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertOptions(); + assertPermanentCount(playerA, dwarvenGrunt, 1); + assertPermanentCount(playerA, zodiacGoat, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/fic/SummonEsperValigarmandaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/fic/SummonEsperValigarmandaTest.java new file mode 100644 index 00000000000..fec699aaaa5 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/fic/SummonEsperValigarmandaTest.java @@ -0,0 +1,57 @@ +package org.mage.test.cards.single.fic; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.player.TestPlayer; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class SummonEsperValigarmandaTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.s.SummonEsperValigarmanda Summon: Esper Valigarmanda} {3}{R} + * Enchantment Creature — Saga Drake + * I — Exile an instant or sorcery card from each graveyard. + * II, III, IV — Add {R} for each lore counter on this Saga. You may cast an instant or sorcery card exiled with this Saga, and mana of any type can be spent to cast that spell. + * Flying, haste + */ + private static final String esper = "Summon: Esper Valigarmanda"; + + @Test + public void test_SimplePlay() { + addCard(Zone.HAND, playerA, esper, 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.GRAVEYARD, playerB, "Lightning Bolt", 1); + addCard(Zone.GRAVEYARD, playerA, "Shock", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, esper); + setChoice(playerA, "Shock"); + setChoice(playerA, "Lightning Bolt"); + + // turn 3 + setChoice(playerA, "Shock"); // choose to be cast + setChoice(playerA, true); // choose to cast + addTarget(playerA, playerB); + + checkLife("T3: after bolt life", 3, PhaseStep.POSTCOMBAT_MAIN, playerB, 20 - 2); + + // turn 5 + setChoice(playerA, TestPlayer.CHOICE_SKIP); + + checkLife("T5: no cast", 5, PhaseStep.POSTCOMBAT_MAIN, playerB, 20 - 2); + + // turn 7 + setChoice(playerA, "Lightning Bolt"); + setChoice(playerA, true); // choose to cast + addTarget(playerA, playerB); + + setStrictChooseMode(true); + setStopAt(7, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerB, 20 - 2 - 3); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/fic/SummonIxionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/fic/SummonIxionTest.java new file mode 100644 index 00000000000..82fb895ccce --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/fic/SummonIxionTest.java @@ -0,0 +1,59 @@ +package org.mage.test.cards.single.fic; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.player.TestPlayer; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class SummonIxionTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.s.SummonIxion Summon: Ixion} {2}{W} + * Enchantment Creature — Saga Unicorn + * I - Aerospark — Exile target creature an opponent controls until this Saga leaves the battlefield. + * II, III - Put a +1/+1 counter on each of up to two target creatures you control. You gain 2 life. + * First strike + * 3/3 + */ + private static final String ixion = "Summon: Ixion"; + + @Ignore // TODO: goal of #11619 is to fix this nicely + @Test + public void test_SimplePlay() { + addCard(Zone.HAND, playerA, ixion, 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); + addCard(Zone.BATTLEFIELD, playerA, "Memnite", 1); + addCard(Zone.BATTLEFIELD, playerB, "Elite Vanguard", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ixion); + addTarget(playerA, "Elite Vanguard"); + + checkExileCount("after I, Vanguard exiled", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Elite Vanguard", 1); + + // turn 3 + addTarget(playerA, "Memnite"); + addTarget(playerA, TestPlayer.TARGET_SKIP); + + // turn 5 + checkExileCount("before III, Vanguard exiled", 5, PhaseStep.UPKEEP, playerB, "Elite Vanguard", 1); + + addTarget(playerA, "Grizzly Bears"); + addTarget(playerA, "Memnite"); + + setStrictChooseMode(true); + setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 + 2 * 2); + assertCounterCount(playerA, "Grizzly Bears", CounterType.P1P1, 1); + assertCounterCount(playerA, "Memnite", CounterType.P1P1, 2); + assertPermanentCount(playerB, "Elite Vanguard", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/fin/AncientAdamantoiseTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/fin/AncientAdamantoiseTest.java new file mode 100644 index 00000000000..8278348a5b4 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/fin/AncientAdamantoiseTest.java @@ -0,0 +1,32 @@ +package org.mage.test.cards.single.fin; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.game.permanent.Permanent; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class AncientAdamantoiseTest extends CardTestPlayerBase { + private static final String adamantoise = "Ancient Adamantoise"; + private static final String bolt = "Lightning Bolt"; + + @Test + public void testDamageStays() { + addCard(Zone.HAND, playerA, bolt); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, adamantoise); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, adamantoise); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.PRECOMBAT_MAIN); + execute(); + + Permanent permanent = getPermanent(adamantoise); + Assert.assertEquals(adamantoise + " should have 3 damage on it on the next turn", 3, permanent.getDamage()); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/fin/EdgarKingOfFigaroTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/fin/EdgarKingOfFigaroTest.java new file mode 100644 index 00000000000..3a03379f89e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/fin/EdgarKingOfFigaroTest.java @@ -0,0 +1,77 @@ +package org.mage.test.cards.single.fin; + +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class EdgarKingOfFigaroTest extends CardTestPlayerBase { + + private static final String edgar = "Edgar, King of Figaro"; + private static final String traprunner = "Goblin Traprunner"; + private static final String swindler = "Tavern Swindler"; + + @Test + public void testTraprunnerThenSwindler() { + addCard(Zone.BATTLEFIELD, playerA, edgar); + addCard(Zone.BATTLEFIELD, playerA, traprunner); + addCard(Zone.BATTLEFIELD, playerA, swindler); + + attack(1, playerA, traprunner); + + setFlipCoinResult(playerA, false); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Goblin Token", 3); + assertLife(playerA, 20 - 3); + assertLife(playerB, 20 - 4 - 1 - 1 - 1); + } + + @Test + public void testSwindlerThenTraprunner() { + addCard(Zone.BATTLEFIELD, playerA, edgar); + addCard(Zone.BATTLEFIELD, playerA, traprunner); + addCard(Zone.BATTLEFIELD, playerA, swindler); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}"); + + setFlipCoinResult(playerA, false); + setFlipCoinResult(playerA, false); + setFlipCoinResult(playerA, false); + attack(1, playerA, traprunner); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Goblin Token", 0); + assertLife(playerA, 20 - 3 + 6); + assertLife(playerB, 20 - 4); + } + + private static final String giant = "Two-Headed Giant"; + + @Test + public void testTwoHeadedGiant() { + addCard(Zone.BATTLEFIELD, playerA, edgar); + addCard(Zone.BATTLEFIELD, playerA, giant); + + attack(1, playerA, giant); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertTapped(giant, true); + assertAbility(playerA, giant, DoubleStrikeAbility.getInstance(), true); + assertLife(playerB, 20 - 4 - 4); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/fin/SorceresssSchemesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/fin/SorceresssSchemesTest.java new file mode 100644 index 00000000000..427ffd253a0 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/fin/SorceresssSchemesTest.java @@ -0,0 +1,98 @@ +package org.mage.test.cards.single.fin; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class SorceresssSchemesTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.s.SorceresssSchemes Sorceress's Schemes} {3}{R} + * Sorcery + * Return target instant or sorcery card from your graveyard or exiled card with flashback you own to your hand. Add {R}. + * Flashback {4}{R} + */ + private static final String schemes = "Sorceress's Schemes"; + + @Test + public void test_target_in_graveyard() { + addCard(Zone.HAND, playerA, schemes, 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.GRAVEYARD, playerA, "Lightning Bolt", 1); + addCard(Zone.EXILED, playerA, "Deep Analysis", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, schemes, "Lightning Bolt"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertHandCount(playerA, "Lightning Bolt", 1); + } + + @Test + public void test_target_in_exile() { + addCard(Zone.HAND, playerA, schemes, 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.GRAVEYARD, playerA, "Lightning Bolt", 1); + addCard(Zone.EXILED, playerA, "Deep Analysis", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, schemes, "Deep Analysis"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertHandCount(playerA, "Deep Analysis", 1); + } + + @Test + public void test_auto_target_in_graveyard() { + addCard(Zone.HAND, playerA, schemes, 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.GRAVEYARD, playerA, "Lightning Bolt", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, schemes); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertHandCount(playerA, "Lightning Bolt", 1); + } + + @Test + public void test_auto_target_in_exile() { + addCard(Zone.HAND, playerA, schemes, 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.EXILED, playerA, "Deep Analysis", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, schemes); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertHandCount(playerA, "Deep Analysis", 1); + } + + @Test + public void test_various_nontarget() { + addCard(Zone.HAND, playerA, schemes, 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + + addCard(Zone.GRAVEYARD, playerA, "Goblin Piker", 1); // not instant/sorcery + addCard(Zone.GRAVEYARD, playerA, "Bloomvine Regent", 1); // omen part not looked at + addCard(Zone.EXILED, playerA, "Lightning Bolt", 1); // doesn't have flashback + addCard(Zone.GRAVEYARD, playerB, "Lightning Bolt", 1); // not in your graveyard + addCard(Zone.EXILED, playerB, "Deep Analysis", 1); // in exile not owned + + checkPlayableAbility("can not cast Schemes", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + schemes, false); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/fin/ZackFairTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/fin/ZackFairTest.java new file mode 100644 index 00000000000..842b7a213f8 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/fin/ZackFairTest.java @@ -0,0 +1,110 @@ +package org.mage.test.cards.single.fin; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class ZackFairTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.z.ZackFair Zack Fair} {W} + * Legendary Creature — Human Soldier + * Zack Fair enters with a +1/+1 counter on it. + * {1}, Sacrifice Zack Fair: Target creature you control gains indestructible until end of turn. Put Zack Fair’s counters on that creature and attach an Equipment that was attached to Zack Fair to that creature. + * 0/1 + */ + private static final String zack = "Zack Fair"; + + @Test + public void test_NoEquip() { + addCard(Zone.HAND, playerA, zack, 1); + addCard(Zone.BATTLEFIELD, playerA, "Squire"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, zack, true); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}", "Squire"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertCounterCount(playerA, "Squire", CounterType.P1P1, 1); + } + + @Test + public void test_OtherCountersToo() { + addCard(Zone.HAND, playerA, zack, 1); + addCard(Zone.BATTLEFIELD, playerA, "Squire"); + /** + * Lifelink + * Cycling {1}{W} ({1}{W}, Discard this card: Draw a card.) + * When you cycle this card, put a lifelink counter on target creature you control. + */ + addCard(Zone.HAND, playerA, "Splendor Mare"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, zack, true); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cycling "); + addTarget(playerA, zack); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}", "Squire"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertCounterCount(playerA, "Squire", CounterType.P1P1, 1); + assertCounterCount(playerA, "Squire", CounterType.LIFELINK, 1); + } + + @Test + public void test_OneEquip() { + addCard(Zone.HAND, playerA, zack, 1); + addCard(Zone.BATTLEFIELD, playerA, "Squire"); + addCard(Zone.BATTLEFIELD, playerA, "Short Sword"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, zack, true); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip {1}", zack); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}, Sacrifice", "Squire"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertCounterCount(playerA, "Squire", CounterType.P1P1, 1); + assertAttachedTo(playerA, "Short Sword", "Squire", true); + } + + @Test + public void test_TwoEquip() { + addCard(Zone.HAND, playerA, zack, 1); + addCard(Zone.BATTLEFIELD, playerA, "Squire"); + addCard(Zone.BATTLEFIELD, playerA, "Short Sword"); + addCard(Zone.BATTLEFIELD, playerA, "Golem-Skin Gauntlets"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, zack, true); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip {1}", zack); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip {2}", zack); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}, Sacrifice", "Squire"); + + setChoice(playerA, "Short Sword"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertCounterCount(playerA, "Squire", CounterType.P1P1, 1); + assertAttachedTo(playerA, "Short Sword", "Squire", true); + assertAttachedTo(playerA, "Golem-Skin Gauntlets", "Squire", false); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/gtc/SpellRuptureTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/gtc/SpellRuptureTest.java new file mode 100644 index 00000000000..344fae0ed87 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/gtc/SpellRuptureTest.java @@ -0,0 +1,73 @@ +package org.mage.test.cards.single.gtc; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class SpellRuptureTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.s.SpellRupture Spell Rupture} {1}{U} + * Instant + * Counter target spell unless its controller pays {X}, where X is the greatest power among creatures you control. + */ + private static final String rupture = "Spell Rupture"; + + @Test + public void test_no_creature() { + addCard(Zone.HAND, playerA, rupture, 1); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Volcanic Island", 10); + + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); + addCard(Zone.BATTLEFIELD, playerB, "Merfolk of the Pearl Trident", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, rupture, "Lightning Bolt"); + setChoice(playerA, true); // yes for pays {0} + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 3); + assertTappedCount("Volcanic Island", true, 2 + 1); + } + + @Test + public void test_4_power() { + disableManaAutoPayment(playerA); // TODO: investigate why TestPlayer can't pay the Spell Rupture's tax mana here. + + addCard(Zone.HAND, playerA, rupture, 1); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Volcanic Island", 10); + + addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard", 1); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); + addCard(Zone.BATTLEFIELD, playerA, "Borderland Minotaur", 1); // 4/3 + + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); + addCard(Zone.BATTLEFIELD, playerB, "Merfolk of the Pearl Trident", 1); + + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}.", 5); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}.", 2); + + setChoice(playerA, "Red", 1); // pay for bolt + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerA); + setChoice(playerA, "Blue", 2); // pay for rupture + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, rupture, "Lightning Bolt"); + setChoice(playerA, true); // yes for pays {4} + setChoice(playerA, "Red", 4); // pay the 4 + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 3); + assertTappedCount("Volcanic Island", true, 2 + 1 + 4); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/hou/TheScarabGodTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/hou/TheScarabGodTest.java new file mode 100644 index 00000000000..e905244e7e0 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/hou/TheScarabGodTest.java @@ -0,0 +1,41 @@ +package org.mage.test.cards.single.hou; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class TheScarabGodTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.t.TheScarabGod The Scarab God} {3}{U}{B} + * Legendary Creature — God + * At the beginning of your upkeep, each opponent loses X life and you scry X, where X is the number of Zombies you control. + * {2}{U}{B}: Exile target creature card from a graveyard. Create a token that’s a copy of it, except it’s a 4/4 black Zombie. + * When The Scarab God dies, return it to its owner’s hand at the beginning of the next end step. + * 5/5 + */ + private static final String god = "The Scarab God"; + + @Test + public void test_scry2() { + skipInitShuffling(); + addCard(Zone.LIBRARY, playerA, "Ancient Amphitheater"); + addCard(Zone.LIBRARY, playerA, "Baleful Strix"); + + addCard(Zone.BATTLEFIELD, playerA, god, 1); + addCard(Zone.BATTLEFIELD, playerA, "Legions of Lim-Dul", 2); + + // The Scarab God's upkeep trigger triggers + addTarget(playerA, "Ancient Amphitheater"); // put on bottom with scry 2 + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertLife(playerB, 20 - 2); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/khm/GravenLoreTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/khm/GravenLoreTest.java new file mode 100644 index 00000000000..a59da04619f --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/khm/GravenLoreTest.java @@ -0,0 +1,40 @@ +package org.mage.test.cards.single.khm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class GravenLoreTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.g.GravenLore Graven Lore} {3}{U}{U} + * Snow Instant + * Scry X, where X is the amount of {S} spent to cast this spell, then draw three cards. ({S} is mana from a snow source.) + */ + private static final String lore = "Graven Lore"; + + @Test + public void test_scry2() { + skipInitShuffling(); + addCard(Zone.LIBRARY, playerA, "Ancient Amphitheater"); + addCard(Zone.LIBRARY, playerA, "Baleful Strix"); + + addCard(Zone.HAND, playerA, lore, 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + addCard(Zone.BATTLEFIELD, playerA, "Snow-Covered Island", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lore); + addTarget(playerA, "Baleful Strix^Ancient Amphitheater"); // put on bottom with Scry 2 + setChoice(playerA, "Ancient Amphitheater"); // order for bottom + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertHandCount(playerA, "Mountain", 3); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/khm/KingNarfisBetrayalTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/khm/KingNarfisBetrayalTest.java new file mode 100644 index 00000000000..b7edb300228 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/khm/KingNarfisBetrayalTest.java @@ -0,0 +1,55 @@ +package org.mage.test.cards.single.khm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class KingNarfisBetrayalTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.k.KingNarfisBetrayal King Narfi's Betrayal} {1}{U}{B} + * Enchantment — Saga + * I — Each player mills four cards. Then you may exile a creature or planeswalker card from each graveyard. + * II, III — Until end of turn, you may cast spells from among cards exiled with this Saga, and you may spend mana as though it were mana of any color to cast those spells. + */ + private static final String betrayal = "King Narfi's Betrayal"; + + @Test + public void test_SimplePlay() { + addCard(Zone.HAND, playerA, betrayal, 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.GRAVEYARD, playerA, "Grizzly Bears", 1); + addCard(Zone.GRAVEYARD, playerB, "Elite Vanguard", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, betrayal); + setChoice(playerA, true); + addTarget(playerA, "Grizzly Bears"); + setChoice(playerA, true); + addTarget(playerA, "Elite Vanguard"); + + checkExileCount("after I, Bears exiled", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Grizzly Bears", 1); + checkExileCount("after I, Vanguard exiled", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Elite Vanguard", 1); + + // turn 3 + // do nothing there. + + // turn 5 + waitStackResolved(5, PhaseStep.PRECOMBAT_MAIN, playerA); + castSpell(5, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", true); + castSpell(5, PhaseStep.PRECOMBAT_MAIN, playerA, "Elite Vanguard"); + + setStrictChooseMode(true); + setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, 5); + assertGraveyardCount(playerB, 4); + assertPermanentCount(playerA, "Grizzly Bears", 1); + assertPermanentCount(playerA, "Elite Vanguard", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ktk/DeflectingPalmTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ktk/DeflectingPalmTest.java new file mode 100644 index 00000000000..50c424ce6c9 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ktk/DeflectingPalmTest.java @@ -0,0 +1,78 @@ +package org.mage.test.cards.single.ktk; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class DeflectingPalmTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.d.DeflectingPalm Deflecting Palm} {R}{W} + * Instant + * The next time a source of your choice would deal damage to you this turn, prevent that damage. If damage is prevented this way, Deflecting Palm deals that much damage to that source’s controller. + */ + private static final String palm = "Deflecting Palm"; + + @Test + public void test_DamageOnCreature_NoPrevent() { + addCard(Zone.HAND, playerA, palm, 1); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Caelorna, Coral Tyrant"); // 0/8 + addCard(Zone.BATTLEFIELD, playerA, "Plateau", 2); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, palm); + setChoice(playerA, "Goblin Piker"); // source to prevent from + + attack(2, playerB, "Goblin Piker", playerA); + block(2, playerA, "Caelorna, Coral Tyrant", "Goblin Piker"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertDamageReceived(playerA, "Caelorna, Coral Tyrant", 2); // no prevent + assertLife(playerB, 20); + } + + @Test + public void test_DamageOnYou_Prevent() { + addCard(Zone.HAND, playerA, palm, 1); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Plateau", 2); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, palm); + setChoice(playerA, "Goblin Piker"); // source to prevent from + + attack(2, playerB, "Goblin Piker", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20 - 2); + } + + @Test + public void test_DoubleStrike_Prevent_ThenConsumedAndNoPrevent() { + addCard(Zone.HAND, playerA, palm, 1); + addCard(Zone.BATTLEFIELD, playerB, "Blade Historian", 1); // 2/3 "Attacking creatures you control have double strike." + addCard(Zone.BATTLEFIELD, playerA, "Plateau", 2); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, palm); + setChoice(playerA, "Blade Historian"); // source to prevent from + + attack(2, playerB, "Blade Historian", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 2); + assertLife(playerB, 20 - 2); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ktk/KinTreeInvocationTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ktk/KinTreeInvocationTest.java new file mode 100644 index 00000000000..8299bfec0e5 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ktk/KinTreeInvocationTest.java @@ -0,0 +1,57 @@ +package org.mage.test.cards.single.ktk; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class KinTreeInvocationTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.k.KinTreeInvocation Kin-Tree Invocation} {B}{G} + * Sorcery + * Create an X/X black and green Spirit Warrior creature token, where X is the greatest toughness among creatures you control. + */ + private static final String invocation = "Kin-Tree Invocation"; + + @Test + public void test_no_creature() { + addCard(Zone.HAND, playerA, invocation); + addCard(Zone.BATTLEFIELD, playerA, "Glorious Anthem", 1); // Creatures you control get +1/+1. + addCard(Zone.BATTLEFIELD, playerA, "Bayou", 2); + + addCard(Zone.BATTLEFIELD, playerB, "Barbarian Horde", 1); // 3/3 {3}{R} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, invocation); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Spirit Warrior Token", 1); + assertPowerToughness(playerA, "Spirit Warrior Token", 1, 1); + } + + @Test + public void test_varied_creatures() { + addCard(Zone.HAND, playerA, invocation); + addCard(Zone.BATTLEFIELD, playerA, "Axegrinder Giant", 1); // 6/4 + addCard(Zone.BATTLEFIELD, playerA, "Ancient Carp", 1); // 2/5 + addCard(Zone.BATTLEFIELD, playerA, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Bayou", 2); + + addCard(Zone.BATTLEFIELD, playerB, "Barbarian Horde", 1); // 3/3 {3}{R} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, invocation); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Spirit Warrior Token", 1); + assertPowerToughness(playerA, "Spirit Warrior Token", 5, 5); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/lci/TriumphantChompTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/lci/TriumphantChompTest.java new file mode 100644 index 00000000000..aacaf38a372 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/lci/TriumphantChompTest.java @@ -0,0 +1,54 @@ +package org.mage.test.cards.single.lci; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class TriumphantChompTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.t.TriumphantChomp Triumphant Chomp} {R} + * Sorcery + * Triumphant Chomp deals damage to target creature equal to 2 or the greatest power among Dinosaurs you control, whichever is greater. + */ + private static final String chomp = "Triumphant Chomp"; + + @Test + public void test_chomp_nodinosaur() { + addCard(Zone.HAND, playerA, chomp); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + + addCard(Zone.BATTLEFIELD, playerA, "Enormous Baloth"); + addCard(Zone.BATTLEFIELD, playerB, "Colossadactyl"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, chomp, "Colossadactyl"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertDamageReceived(playerB, "Colossadactyl", 2); + } + + @Test + public void test_chomp_dinosaur() { + addCard(Zone.HAND, playerA, chomp); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + + addCard(Zone.BATTLEFIELD, playerA, "Bellowing Aegisaur"); // 3/5 dino + addCard(Zone.BATTLEFIELD, playerA, "Raging Regisaur"); // 4/4 dino + addCard(Zone.BATTLEFIELD, playerB, "Colossadactyl"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, chomp, "Colossadactyl"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertDamageReceived(playerB, "Colossadactyl", 4); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/lea/CircleOfProtectionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/lea/CircleOfProtectionTest.java new file mode 100644 index 00000000000..3dc7052a307 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/lea/CircleOfProtectionTest.java @@ -0,0 +1,227 @@ +package org.mage.test.cards.single.lea; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class CircleOfProtectionTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.c.CircleOfProtectionRed Circle of Protection: Red} {1}{W} + * Enchantment + * {1}: The next time a red source of your choice would deal damage to you this turn, prevent that damage. + */ + private static final String circleRed = "Circle of Protection: Red"; + + /** + * {@link mage.cards.c.CircleOfProtectionGreen Circle of Protection: Green} {1}{W} + * Enchantment + * {1}: The next time a green source of your choice would deal damage to you this turn, prevent that damage. + */ + private static final String circleGreen = "Circle of Protection: Green"; + + @Test + public void test_DamageOnCreature_NoPrevent() { + addCard(Zone.BATTLEFIELD, playerA, circleRed, 1); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Caelorna, Coral Tyrant"); // 0/8 + addCard(Zone.BATTLEFIELD, playerA, "Plains", 14); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}"); + setChoice(playerA, "Goblin Piker"); // source to prevent from + + attack(2, playerB, "Goblin Piker", playerA); + block(2, playerA, "Caelorna, Coral Tyrant", "Goblin Piker"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertDamageReceived(playerA, "Caelorna, Coral Tyrant", 2); // no prevent + assertLife(playerA, 20); + assertTappedCount("Plains", true, 1); + } + + @Test + public void test_DamageOnYou_Prevent() { + addCard(Zone.BATTLEFIELD, playerA, circleRed, 1); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}"); + setChoice(playerA, "Goblin Piker"); // source to prevent from + + attack(2, playerB, "Goblin Piker", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + assertTappedCount("Plains", true, 1); + } + + @Test + public void test_DoubleStrike_Prevent_ThenConsumedAndNoPrevent() { + addCard(Zone.BATTLEFIELD, playerA, circleRed, 1); + addCard(Zone.BATTLEFIELD, playerB, "Blade Historian", 1); // 2/3 "Attacking creatures you control have double strike." + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}"); + setChoice(playerA, "Blade Historian"); // source to prevent from + + attack(2, playerB, "Blade Historian", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 2); + assertTappedCount("Plains", true, 1); + } + + @Test + public void test_NoSourceOfCircleColor_NoPrevent() { + addCard(Zone.BATTLEFIELD, playerA, circleGreen, 1); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}"); + // No possible choice for a green source + + attack(2, playerB, "Goblin Piker", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 2); // no prevention + assertTappedCount("Plains", true, 1); + } + + @Test + public void test_SpellDamageOnYou_Prevent() { + addCard(Zone.BATTLEFIELD, playerA, circleRed, 1); + addCard(Zone.HAND, playerB, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerB, "Mountain"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", playerA); + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}", null, "Lightning Bolt"); + setChoice(playerA, "Lightning Bolt"); // source to prevent from + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + assertGraveyardCount(playerB, "Lightning Bolt", 1); + assertTappedCount("Plains", true, 1); + } + + @Test + public void test_SpellNotChosenColorDamageOnYou_NoPrevent() { + addCard(Zone.BATTLEFIELD, playerA, circleGreen, 1); + addCard(Zone.HAND, playerB, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerB, "Mountain"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", playerA); + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}", null, "Lightning Bolt"); + // no possible choice for "a green source" + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 3); + assertGraveyardCount(playerB, "Lightning Bolt", 1); + assertTappedCount("Plains", true, 1); + } + + @Test + public void test_TriggerDamageOnYou_Prevent() { + addCard(Zone.BATTLEFIELD, playerA, circleRed, 1); + addCard(Zone.HAND, playerB, "Blisterstick Shaman", 1); // etb deals 1 to any target + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Blisterstick Shaman"); + addTarget(playerB, playerA); // target for Shaman trigger + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}", null, "stack ability (When"); + setChoice(playerA, "Blisterstick Shaman"); // source to prevent from + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + assertPermanentCount(playerB, "Blisterstick Shaman", 1); + assertTappedCount("Plains", true, 1); + } + + @Test + public void test_TriggerNotChosenColorDamageOnYou_NoPrevent() { + addCard(Zone.BATTLEFIELD, playerA, circleGreen, 1); + addCard(Zone.HAND, playerB, "Blisterstick Shaman", 1); // etb deals 1 to any target + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Blisterstick Shaman"); + addTarget(playerB, playerA); // target for Shaman trigger + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}", null, "stack ability (When"); + // no possible choice for "a green source" + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 1); + assertPermanentCount(playerB, "Blisterstick Shaman", 1); + assertTappedCount("Plains", true, 1); + } + + @Test + public void test_ActivationDamageOnYou_Prevent() { + addCard(Zone.BATTLEFIELD, playerA, circleRed, 1); + addCard(Zone.BATTLEFIELD, playerB, "Anaba Shaman", 1); // {R}, {T}: This creature deals 1 damage to any target. + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{R},", playerA); + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}", null, "stack ability ({R}"); + setChoice(playerA, "Anaba Shaman"); // source to prevent from + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertTappedCount("Anaba Shaman", true, 1); + assertTappedCount("Plains", true, 1); + assertLife(playerA, 20); + } + + @Test + public void test_ActivationNotChosenColorDamageOnYou_NoPrevent() { + addCard(Zone.BATTLEFIELD, playerA, circleGreen, 1); + addCard(Zone.BATTLEFIELD, playerB, "Anaba Shaman", 1); // {R}, {T}: This creature deals 1 damage to any target. + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{R},", playerA); + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}", null, "stack ability ({R}"); + // no possible choice for "a green source" + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertTappedCount("Anaba Shaman", true, 1); + assertTappedCount("Plains", true, 1); + assertLife(playerA, 20 - 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/MulldrifterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/MulldrifterTest.java index f8590a7dc9a..2c48a315486 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/MulldrifterTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/lrw/MulldrifterTest.java @@ -51,7 +51,7 @@ public class MulldrifterTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mulldrifter"); setChoice(playerA, "Cast with Evoke alternative cost: {2}{U} (source: Mulldrifter"); setStopAt(1, PhaseStep.BEGIN_COMBAT); - setChoice(playerA, "When this permanent enters the battlefield, if its evoke cost was paid, its controller sacrifices it"); // stack triggers + setChoice(playerA, "When this permanent enters, if its evoke cost was paid, its controller sacrifices it"); // stack triggers execute(); @@ -67,7 +67,7 @@ public class MulldrifterTest extends CardTestPlayerBase { public void testMulldrifterFlickered() { setStrictChooseMode(true); - // {4}{U} When Mulldrifter enters the battlefield, draw two cards. Evoke {2}{U} + // {4}{U} When Mulldrifter enters, draw two cards. Evoke {2}{U} addCard(Zone.BATTLEFIELD, playerA, "Mulldrifter"); // 2/2 addCard(Zone.BATTLEFIELD, playerA, "Merfolk Looter"); // 1/1 addCard(Zone.BATTLEFIELD, playerA, "Island", 5); @@ -86,4 +86,4 @@ public class MulldrifterTest extends CardTestPlayerBase { assertGraveyardCount(playerA, "Ghostly Flicker", 1); assertHandCount(playerA, 2); // should have drawn 2 cards } -} \ No newline at end of file +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ltc/GandalfOfTheSecretFireTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ltc/GandalfOfTheSecretFireTest.java new file mode 100644 index 00000000000..e082e07831e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ltc/GandalfOfTheSecretFireTest.java @@ -0,0 +1,161 @@ +package org.mage.test.cards.single.ltc; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class GandalfOfTheSecretFireTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.g.GandalfOfTheSecretFire Gandalf of the Secret Fire} {1}{U}{R}{W} + * Legendary Creature — Avatar Wizard + * Whenever you cast an instant or sorcery spell from your hand during an opponent’s turn, exile that card with three time counters on it instead of putting it into your graveyard as it resolves. Then if the exiled card doesn’t have suspend, it gains suspend. (At the beginning of your upkeep, remove a time counter. When the last is removed, you may play it without paying its mana cost.) + * 3/4 + */ + private static final String gandalf = "Gandalf of the Secret Fire"; + + @Test + public void test_yourturn() { + addCard(Zone.BATTLEFIELD, playerA, gandalf, 1); + + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerB, 20 - 3); + assertGraveyardCount(playerA, "Lightning Bolt", 1); + } + + @Test + public void test_oppturn_suspend() { + addCard(Zone.BATTLEFIELD, playerA, gandalf, 1); + + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + + checkExileCount("1: bolt in exile", 2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", 1); + checkCardCounters("1: bolt has 3 time counters", 2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", CounterType.TIME, 3); + + // turn 3: from 3 to 2 time counter + checkCardCounters("2: bolt has 2 time counters", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", CounterType.TIME, 2); + + // turn 5: from 2 to 1 time counter + checkCardCounters("3: bolt has 1 time counters", 5, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", CounterType.TIME, 1); + + setChoice(playerA, true); // yes to cast from suspend removing last counter + addTarget(playerA, playerB); + + setStrictChooseMode(true); + setStopAt(7, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerB, 20 - 6); + assertGraveyardCount(playerA, "Lightning Bolt", 1); + } + + @Test + public void test_split_suspend() { + addCard(Zone.BATTLEFIELD, playerA, gandalf, 1); + + addCard(Zone.HAND, playerA, "Fire // Ice", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Fire", playerB); + addTargetAmount(playerA, playerB, 2); + + checkExileCount("1: fire//ice in exile", 2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Fire // Ice", 1); + checkCardCounters("1: fire//ice has 3 time counters", 2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Fire // Ice", CounterType.TIME, 3); + + // turn 3: from 3 to 2 time counter + checkCardCounters("2: fire//ice has 2 time counters", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Fire // Ice", CounterType.TIME, 2); + + // turn 5: from 2 to 1 time counter + checkCardCounters("3: fire//ice has 1 time counters", 5, PhaseStep.PRECOMBAT_MAIN, playerA, "Fire // Ice", CounterType.TIME, 1); + + setChoice(playerA, true); // yes to cast from suspend removing last counter + setChoice(playerA, "Cast Fire"); // choose to cast Fire side + addTargetAmount(playerA, playerB, 2); + + setStrictChooseMode(true); + setStopAt(7, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerB, 20 - 4); + assertGraveyardCount(playerA, "Fire // Ice", 1); + } + + @Test + public void test_oppturn_counterspell_suspend() { + addCard(Zone.BATTLEFIELD, playerA, gandalf, 1); + + addCard(Zone.HAND, playerA, "Counterspell", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + + addCard(Zone.HAND, playerB, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", playerB); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Counterspell", "Lightning Bolt"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerB, 20); + assertExileCount(playerA, "Counterspell", 1); + assertGraveyardCount(playerB, "Lightning Bolt", 1); + } + + @Test + public void test_oppturn_countered_nosuspend() { + addCard(Zone.BATTLEFIELD, playerB, gandalf, 1); + + addCard(Zone.HAND, playerA, "Counterspell", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + + addCard(Zone.HAND, playerB, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Counterspell", "Lightning Bolt", "Lightning Bolt"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerB, 20); + assertGraveyardCount(playerA, "Counterspell", 1); + assertGraveyardCount(playerB, "Lightning Bolt", 1); + } + + @Test + public void test_adventure_nosuspend() { + addCard(Zone.BATTLEFIELD, playerA, gandalf, 1); + + addCard(Zone.HAND, playerA, "Bonecrusher Giant", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Stomp", playerB); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerB, 20 - 2); + assertExileCount(playerA, "Bonecrusher Giant", 1); + // since an Adventure card is not going to the graveyard on resolve, Gandalf's trigger does not suspend it. + assertCounterOnExiledCardCount("Bonecrusher Giant", CounterType.TIME, 0); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/FriendlyRivalryTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/FriendlyRivalryTest.java new file mode 100644 index 00000000000..d5bd6eca155 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/FriendlyRivalryTest.java @@ -0,0 +1,61 @@ +package org.mage.test.cards.single.ltr; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.player.TestPlayer; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class FriendlyRivalryTest extends CardTestPlayerBase { + + @Test + public void test_target_both() { + // Target creature you control and up to one other target legendary creature you control each deal damage + // equal to their power to target creature you don't control. + addCard(Zone.HAND, playerA, "Friendly Rivalry"); // {R}{G} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + // + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); // 2/2 + addCard(Zone.BATTLEFIELD, playerA, "Aesi, Tyrant of Gyre Strait"); // legendary, 5/5 + addCard(Zone.BATTLEFIELD, playerB, "Agonasaur Rex"); // 8/8 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Friendly Rivalry"); + addTarget(playerA, "Grizzly Bears"); + addTarget(playerA, "Aesi, Tyrant of Gyre Strait"); + addTarget(playerA, "Agonasaur Rex"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertDamageReceived(playerB, "Agonasaur Rex", 2 + 5); + } + + @Test + public void test_target_single() { + // Target creature you control and up to one other target legendary creature you control each deal damage + // equal to their power to target creature you don't control. + addCard(Zone.HAND, playerA, "Friendly Rivalry"); // {R}{G} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + // + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); // 2/2 + addCard(Zone.BATTLEFIELD, playerA, "Aesi, Tyrant of Gyre Strait"); // legendary, 5/5 + addCard(Zone.BATTLEFIELD, playerB, "Agonasaur Rex"); // 8/8 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Friendly Rivalry"); + addTarget(playerA, "Grizzly Bears"); + addTarget(playerA, TestPlayer.TARGET_SKIP); // skip second target due "up to" + addTarget(playerA, "Agonasaur Rex"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertDamageReceived(playerB, "Agonasaur Rex", 2); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/LongListOfTheEntsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/LongListOfTheEntsTest.java new file mode 100644 index 00000000000..c05c1923a19 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/LongListOfTheEntsTest.java @@ -0,0 +1,75 @@ +package org.mage.test.cards.single.ltr; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class LongListOfTheEntsTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.l.LongListOfTheEnts Long List of the Ents} {G} + * Enchantment — Saga + * (As this Saga enters and after your draw step, add a lore counter. Sacrifice after VI.) + * I, II, III, IV, V, VI — Note a creature type that hasn’t been noted for this Saga. When you next cast a creature spell of that type this turn, that creature enters with an additional +1/+1 counter on it. + */ + private static final String list = "Long List of the Ents"; + + @Test + public void test_SimplePlay() { + addCard(Zone.HAND, playerA, list, 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + addCard(Zone.HAND, playerA, "Memnite", 1); // Construct + addCard(Zone.HAND, playerA, "Augmenting Automaton", 1); // Construct + addCard(Zone.HAND, playerA, "Grizzly Bears", 1); // Bear + addCard(Zone.HAND, playerA, "Bear Cub", 1); // Bear + addCard(Zone.HAND, playerA, "Woodland Changeling", 1); // Changeling + addCard(Zone.HAND, playerA, "Llanowar Elves", 1); // Elf Druid + addCard(Zone.HAND, playerA, "Balduvian Bears", 1); // Bear + addCard(Zone.HAND, playerA, "Centaur Courser", 1); // Centaur Warrior + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, list); + setChoice(playerA, "Construct"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", true); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Augmenting Automaton"); + + // turn 3 + setChoice(playerA, "Bear"); + castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Grizzly Bears"); + + // turn 5 + setChoice(playerA, "Warrior"); + castSpell(5, PhaseStep.POSTCOMBAT_MAIN, playerA, "Bear Cub"); + + // turn 7 + setChoice(playerA, "Centaur"); + castSpell(7, PhaseStep.POSTCOMBAT_MAIN, playerA, "Centaur Courser"); + + // turn 9 + setChoice(playerA, "Druid"); + castSpell(9, PhaseStep.POSTCOMBAT_MAIN, playerA, "Balduvian Bears", true); + castSpell(9, PhaseStep.POSTCOMBAT_MAIN, playerA, "Llanowar Elves"); + + // turn 11 + setChoice(playerA, "Elf"); + castSpell(11, PhaseStep.POSTCOMBAT_MAIN, playerA, "Woodland Changeling"); + + setStrictChooseMode(true); + setStopAt(12, PhaseStep.END_TURN); + execute(); + + assertCounterCount(playerA, "Memnite", CounterType.P1P1, 1); + assertCounterCount(playerA, "Augmenting Automaton", CounterType.P1P1, 0); + assertCounterCount(playerA, "Grizzly Bears", CounterType.P1P1, 1); + assertCounterCount(playerA, "Bear Cub", CounterType.P1P1, 0); + assertCounterCount(playerA, "Centaur Courser", CounterType.P1P1, 1); + assertCounterCount(playerA, "Balduvian Bears", CounterType.P1P1, 0); + assertCounterCount(playerA, "Llanowar Elves", CounterType.P1P1, 1); + assertCounterCount(playerA, "Woodland Changeling", CounterType.P1P1, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/ScrollOfIsildurTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/ScrollOfIsildurTest.java new file mode 100644 index 00000000000..4a119bb8ec0 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/ScrollOfIsildurTest.java @@ -0,0 +1,53 @@ +package org.mage.test.cards.single.ltr; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class ScrollOfIsildurTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.s.ScrollOfIsildur Scroll of Isildur} {2}{U} + * Enchantment — Saga + * (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) + * I — Gain control of up to one target artifact for as long as you control this Saga. The Ring tempts you. + * II — Tap up to two target creatures. Put a stun counter on each of them. + * III — Draw a card for each tapped creature target opponent controls. + */ + private static final String scroll = "Scroll of Isildur"; + + @Test + public void test_SimplePlay() { + addCard(Zone.HAND, playerA, scroll, 1); + addCard(Zone.BATTLEFIELD, playerA, "Symbiotic Deployment", 1); // skip your draw step + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + addCard(Zone.BATTLEFIELD, playerB, "Memnite", 1); + addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears", 1); + addCard(Zone.BATTLEFIELD, playerB, "Bear Cub", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, scroll); + addTarget(playerA, "Memnite"); + + checkPermanentCount("T1: Memnite is controlled by A", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", 1); + + // turn 3 + addTarget(playerA, "Grizzly Bears^Bear Cub"); + + checkPermanentCount("T3: Memnite is controlled by A", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", 1); + + // turn 5 + checkPermanentCount("T5: Memnite is controlled by A before III", 5, PhaseStep.UPKEEP, playerA, "Memnite", 1); + addTarget(playerA, playerB); + + setStrictChooseMode(true); + setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerB, "Memnite", 1); + assertHandCount(playerA, 2); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/TaleOfTinuvielTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/TaleOfTinuvielTest.java new file mode 100644 index 00000000000..c6665b6ae01 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/TaleOfTinuvielTest.java @@ -0,0 +1,56 @@ +package org.mage.test.cards.single.ltr; + +import mage.abilities.keyword.IndestructibleAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class TaleOfTinuvielTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.t.TaleOfTinuviel Tale of Tinuviel} {3}{W}{W} + * Enchantment — Saga + * I — Target creature you control gains indestructible for as long as you control this Saga. + * II — Return target creature card from your graveyard to the battlefield. + * III — Up to two target creatures you control each gain lifelink until end of turn. + */ + private static final String tale = "Tale of Tinuviel"; + + @Test + public void test_SimplePlay() { + addCard(Zone.HAND, playerA, tale, 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); + addCard(Zone.GRAVEYARD, playerA, "Memnite", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, tale); + addTarget(playerA, "Grizzly Bears"); + + checkAbility("after I, Grizzly Bears indestructible", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Grizzly Bears", IndestructibleAbility.class, true); + + // turn 3 + addTarget(playerA, "Memnite"); + + // turn 5 + checkAbility("before III, Grizzly Bears indestructible", 5, PhaseStep.UPKEEP, playerA, "Grizzly Bears", IndestructibleAbility.class, true); + + addTarget(playerA, "Grizzly Bears"); + addTarget(playerA, "Memnite"); + + checkAbility("after III, Grizzly Bears not indestructible", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, "Grizzly Bears", IndestructibleAbility.class, false); + checkAbility("after III, Grizzly Bears lifelink", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, "Grizzly Bears", LifelinkAbility.class, true); + checkAbility("after III, Memnite lifelink", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", LifelinkAbility.class, true); + + setStrictChooseMode(true); + setStopAt(6, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertAbility(playerA, "Grizzly Bears", LifelinkAbility.getInstance(), false); + assertAbility(playerA, "Memnite", LifelinkAbility.getInstance(), false); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/ThereAndBackAgainTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/ThereAndBackAgainTest.java new file mode 100644 index 00000000000..6b7c8261a6b --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/ThereAndBackAgainTest.java @@ -0,0 +1,53 @@ +package org.mage.test.cards.single.ltr; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class ThereAndBackAgainTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.t.ThereAndBackAgain There and Back Again} {3}{R}{R} + * Enchantment — Saga + * I — Up to one target creature can’t block for as long as you control this Saga. The Ring tempts you. + * II — Search your library for a Mountain card, put it onto the battlefield, then shuffle. + * III — Create Smaug, a legendary 6/6 red Dragon creature token with flying, haste, and “When Smaug dies, create fourteen Treasure tokens.” + */ + private static final String again = "There and Back Again"; + + @Test + public void test_SimplePlay() { + addCard(Zone.HAND, playerA, again, 1); + + addCard(Zone.BATTLEFIELD, playerA, "Volcanic Island", 5); + addCard(Zone.BATTLEFIELD, playerA, "Gaea's Protector"); // 4/2 Must be blocked if able + addCard(Zone.BATTLEFIELD, playerB, "Amaranthine Wall"); // 0/6 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, again); + addTarget(playerA, "Amaranthine Wall"); + + attack(1, playerA, "Gaea's Protector"); + // Wall can't block. + + // turn 3 + addTarget(playerA, "Mountain"); + attack(3, playerA, "Gaea's Protector"); + // Wall can't block. + + // turn 5 + attack(5, playerA, "Gaea's Protector"); + // Wall must block + + setStrictChooseMode(true); + setStopAt(5, PhaseStep.END_TURN); + execute(); + + assertLife(playerB, 20 - 4 * 2); + assertDamageReceived(playerB, "Amaranthine Wall", 4); + assertPermanentCount(playerA, "Smaug", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m11/AutumnsVeilTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m11/AutumnsVeilTest.java new file mode 100644 index 00000000000..30e945ae8a8 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m11/AutumnsVeilTest.java @@ -0,0 +1,63 @@ +package org.mage.test.cards.single.m11; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class AutumnsVeilTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.a.AutumnsVeil Autumn's Veil} {G} + * Instant + * Spells you control can’t be countered by blue or black spells this turn, and creatures you control can’t be the targets of blue or black spells this turn. + */ + private static final String veil = "Autumn's Veil"; + + @Test + public void test_BlueCounter() { + addCard(Zone.HAND, playerA, veil, 1); + addCard(Zone.HAND, playerA, "Elvish Mystic", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + + addCard(Zone.HAND, playerB, "Counterspell"); + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, veil, true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Elvish Mystic"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Counterspell", "Elvish Mystic", "Elvish Mystic"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Elvish Mystic", 1); + assertGraveyardCount(playerB, "Counterspell", 1); + } + + @Test + public void test_WhiteCounter() { + addCard(Zone.HAND, playerA, veil, 1); + addCard(Zone.HAND, playerA, "Elvish Mystic", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + + addCard(Zone.HAND, playerB, "Mana Tithe"); + addCard(Zone.BATTLEFIELD, playerB, "Plains", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, veil, true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Elvish Mystic"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Mana Tithe", "Elvish Mystic", "Elvish Mystic"); + + setChoice(playerA, false); // can't pay the Tithe Tax + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, "Elvish Mystic", 1); + assertGraveyardCount(playerB, "Mana Tithe", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m11/OverwhelmingStampedeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m11/OverwhelmingStampedeTest.java new file mode 100644 index 00000000000..06ba3cac604 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m11/OverwhelmingStampedeTest.java @@ -0,0 +1,57 @@ +package org.mage.test.cards.single.m11; + +import mage.abilities.keyword.TrampleAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class OverwhelmingStampedeTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.o.OverwhelmingStampede Overwhelming Stampede} {3}{G}{G} + * Sorcery + * Until end of turn, creatures you control gain trample and get +X/+X, where X is the greatest power among creatures you control. + */ + private static final String stampede = "Overwhelming Stampede"; + + @Test + public void test_simple() { + + addCard(Zone.HAND, playerA, stampede, 1); + addCard(Zone.BATTLEFIELD, playerA, "Borderland Minotaur", 1); // 4/3 + addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); // 2/2 + addCard(Zone.BATTLEFIELD, playerA, "Forest", 5); + + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerB, "Merfolk of the Pearl Trident", 1); // 1/1 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, stampede); + + checkAbility("minotaur gets trample", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Borderland Minotaur", TrampleAbility.class, true); + checkAbility("vanguard gets trample", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Elite Vanguard", TrampleAbility.class, true); + checkAbility("bears gets trample", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Grizzly Bears", TrampleAbility.class, true); + checkAbility("opp's piker no trample", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Goblin Piker", TrampleAbility.class, false); + checkAbility("opp's merfolk no trample", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Merfolk of the Pearl Trident", TrampleAbility.class, false); + + checkPT("minotaur gets +4/+4", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Borderland Minotaur", 4 + 4, 3 + 4); + checkPT("vanguard gets +4/+4", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Elite Vanguard", 2 + 4, 1 + 4); + checkPT("bears gets +4/+4", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Grizzly Bears", 2 + 4, 2 + 4); + checkPT("opp's piker no PT change", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Goblin Piker", 2, 1); + checkPT("opp's merfolk no PT change", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Merfolk of the Pearl Trident", 1, 1); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.PRECOMBAT_MAIN); + execute(); + + // check effect ended. + assertPowerToughness(playerA, "Borderland Minotaur", 4, 3); + assertPowerToughness(playerA, "Elite Vanguard", 2, 1); + assertAbility(playerA, "Borderland Minotaur", TrampleAbility.getInstance(), false); + assertAbility(playerA, "Elite Vanguard", TrampleAbility.getInstance(), false); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mh2/UrzasSagaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mh2/UrzasSagaTest.java new file mode 100644 index 00000000000..95f87801343 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mh2/UrzasSagaTest.java @@ -0,0 +1,54 @@ +package org.mage.test.cards.single.mh2; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class UrzasSagaTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.u.UrzasSaga Urza's Saga} + * Enchantment Land — Urza's Saga + * I — This Saga gains “{T}: Add {C}.” + * II — This Saga gains “{2}, {T}: Create a 0/0 colorless Construct artifact creature token with ‘This token gets +1/+1 for each artifact you control.’” + * III — Search your library for an artifact card with mana cost {0} or {1}, put it onto the battlefield, then shuffle. + */ + private static final String saga = "Urza's Saga"; + + @Test + public void test_SimplePlay() { + skipInitShuffling(); + addCard(Zone.LIBRARY, playerA, "Black Lotus"); + addCard(Zone.LIBRARY, playerA, "Plateau", 2); + + addCard(Zone.HAND, playerA, saga, 1); + addCard(Zone.HAND, playerA, "Sol Ring", 1); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, saga); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); // let trigger I resolve + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sol Ring"); + + waitStackResolved(3, PhaseStep.PRECOMBAT_MAIN, playerA); // let trigger II + activateManaAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {C}{C}"); + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}"); + + checkPermanentCount("after first token creation", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Construct Token", 1); + + activateManaAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {C}{C}"); + activateAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}"); + addTarget(playerA, "Black Lotus"); + + setStrictChooseMode(true); + setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, saga, 1); + assertPermanentCount(playerA, "Black Lotus", 1); + assertPermanentCount(playerA, "Construct Token", 2); + assertPowerToughness(playerA, "Construct Token", 4, 4); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/PheliaExuberantShepherdTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/PheliaExuberantShepherdTest.java index 1abb61f00cf..64b36e9b337 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/PheliaExuberantShepherdTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/PheliaExuberantShepherdTest.java @@ -149,4 +149,35 @@ public class PheliaExuberantShepherdTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Memnite", 1); } + // bug: return trigger should not return cards exiled the turn before and not + // returned due to a Stifle effect + @Test + public void test_Stifle() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, phelia, 1); + addCard(Zone.BATTLEFIELD, playerA, "Memnite", 1); + addCard(Zone.BATTLEFIELD, playerA, "Ornithopter", 1); + addCard(Zone.HAND, playerB, "Stifle", 1); + addCard(Zone.BATTLEFIELD, playerB, "Island", 1); + + attack(1, playerA, phelia, playerB); + addTarget(playerA, "Memnite"); + + castSpell(1, PhaseStep.END_TURN, playerB, "Stifle", "stack ability (At the beginning"); + + checkExileCount("Memnite still exiled", 2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", 1); + + attack(3, playerA, phelia, playerB); + addTarget(playerA, "Ornithopter"); + + // end of turn trigger: Ornithopter returns. + + setStopAt(4, PhaseStep.UPKEEP); + execute(); + + assertPowerToughness(playerA, phelia, 2 + 1, 2 + 1); + assertExileCount(playerA, "Memnite", 1); + assertPermanentCount(playerA, "Ornithopter", 1); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mid/OminousRoostTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mid/OminousRoostTest.java index 447c0e8d246..b0bc901ec55 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/mid/OminousRoostTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mid/OminousRoostTest.java @@ -15,48 +15,48 @@ import org.mage.test.serverside.base.CardTestPlayerBase; * @author alexander-novo, Alex-Vasile */ public class OminousRoostTest extends CardTestPlayerBase { - private static final String ominousRoost = "Ominous Roost"; + private static final String ominousRoost = "Ominous Roost"; - /** - * Reported bug: https://github.com/magefree/mage/issues/9078 - * If Ominous Roost is in your library, it will trigger any time you cast a spell from your graveyard. - */ - @Test - public void doesNotTriggerFromOtherZones() { - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + /** + * Reported bug: https://github.com/magefree/mage/issues/9078 + * If Ominous Roost is in your library, it will trigger any time you cast a spell from your graveyard. + */ + @Test + public void doesNotTriggerFromOtherZones() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); - addCard(Zone.EXILED, playerA, ominousRoost); - addCard(Zone.LIBRARY, playerA, ominousRoost); - addCard(Zone.GRAVEYARD, playerA, ominousRoost); - addCard(Zone.HAND, playerA, ominousRoost); + addCard(Zone.EXILED, playerA, ominousRoost); + addCard(Zone.LIBRARY, playerA, ominousRoost); + addCard(Zone.GRAVEYARD, playerA, ominousRoost); + addCard(Zone.HAND, playerA, ominousRoost); - // Flashback {2}{R} - // Create a treasure token - addCard(Zone.GRAVEYARD, playerA, "Strike It Rich"); + // Flashback {2}{R} + // Create a treasure token + addCard(Zone.GRAVEYARD, playerA, "Strike It Rich"); - setStrictChooseMode(true); + setStrictChooseMode(true); - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flashback {2}{R}"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flashback {2}{R}"); - setStopAt(1, PhaseStep.END_TURN); - execute(); - assertPermanentCount(playerA, ominousRoost, 0); - assertPermanentCount(playerA, "Bird Token", 0); // None of the cards are on the field, so it should not have triggered - } + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertPermanentCount(playerA, ominousRoost, 0); + assertPermanentCount(playerA, "Bird Token", 0); // None of the cards are on the field, so it should not have triggered + } - /** - * Test that it triggers on ETB - */ - @Test - public void triggersOnOwnETB() { - addCard(Zone.HAND, playerA, ominousRoost); - addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + /** + * Test that it triggers on ETB + */ + @Test + public void triggersOnOwnETB() { + addCard(Zone.HAND, playerA, ominousRoost); + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ominousRoost); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ominousRoost); - setStopAt(1, PhaseStep.END_TURN); - execute(); - assertPermanentCount(playerA, ominousRoost, 1); - assertPermanentCount(playerA, "Bird Token", 1); - } + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertPermanentCount(playerA, ominousRoost, 1); + assertPermanentCount(playerA, "Bird Token", 1); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mid/SigardasSplendorTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mid/SigardasSplendorTest.java new file mode 100644 index 00000000000..95b7f30edda --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mid/SigardasSplendorTest.java @@ -0,0 +1,76 @@ +package org.mage.test.cards.single.mid; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * {@link mage.cards.s.SigardasSplendor Sigarda's Splendor} + * {2}{W}{W} + * Enchantment + * As Sigarda’s Splendor enters the battlefield, note your life total. + * At the beginning of your upkeep, draw a card if your life total is greater than or equal + * to the last noted life total for Sigarda’s Splendor. Then note your life total. + * Whenever you cast a white spell, you gain 1 life. + * + * @author notgreat + */ +public class SigardasSplendorTest extends CardTestPlayerBase { + private static final String sigardasSplendor = "Sigarda's Splendor"; + + //Original bug: [BUG] Sigarda's Splendor always draws you a card the turn after it came into play #9872 + //Test added while changing abilities' zcc while entering the battlefield + + @Test + public void sigardasSplendorTestBasic() { + addCard(Zone.HAND, playerA, sigardasSplendor, 2); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + + setStrictChooseMode(true); + + checkHandCount("Initial hand size", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, sigardasSplendor); + checkHandCount("Initial hand size (2)", 1, PhaseStep.END_TURN, playerA, 1); //-1 sigarda + checkLife("Initial life", 1, PhaseStep.END_TURN, playerA, 20); + checkHandCount("Did not draw on 1st upkeep", 3, PhaseStep.PRECOMBAT_MAIN, playerA, 3); //-1 sigarda, +1 natural draw, +1 trigger + castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, sigardasSplendor); + checkHandCount("Did not draw on 1st upkeep (2)", 3, PhaseStep.END_TURN, playerA, 2); //-2 sigarda, +1 natural draw, +1 trigger + checkLife("Initial life", 3, PhaseStep.END_TURN, playerA, 21); + + setChoice(playerA, "At the beginning of your upkeep"); //stack triggers + setStopAt(5, PhaseStep.PRECOMBAT_MAIN); + execute(); + assertPermanentCount(playerA, sigardasSplendor, 2); + assertHandCount(playerA, 5); //-2 sigardas, +2 natural draw, +3 trigger + } + + @Test + public void sigardasSplendorTestDamaged() { + addCard(Zone.HAND, playerA, sigardasSplendor, 2); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + + addCard(Zone.HAND, playerB, "Scorching Spear", 2); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + setStrictChooseMode(true); + + checkHandCount("Initial hand size", 1, PhaseStep.PRECOMBAT_MAIN, playerA, 2); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, sigardasSplendor); + checkHandCount("Initial hand size (2)", 1, PhaseStep.END_TURN, playerA, 1); //-1 sigarda + checkLife("Initial life", 1, PhaseStep.END_TURN, playerA, 20); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Scorching Spear", playerA); + checkLife("Post-spear 1", 2, PhaseStep.END_TURN, playerA, 19); + checkHandCount("Did not draw on 1st upkeep", 3, PhaseStep.PRECOMBAT_MAIN, playerA, 2); //-1 sigarda, +1 natural draw + castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerA, sigardasSplendor); + checkHandCount("Did not draw on 1st upkeep (2)", 3, PhaseStep.END_TURN, playerA, 1); //-2 sigarda, +1 natural draw + checkLife("Post-splendors", 3, PhaseStep.END_TURN, playerA, 20); + castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Scorching Spear", playerA); + checkLife("Post-spear 2", 4, PhaseStep.END_TURN, playerA, 19); + + setChoice(playerA, "At the beginning of your upkeep"); //stack triggers + setStopAt(5, PhaseStep.PRECOMBAT_MAIN); + execute(); + assertPermanentCount(playerA, sigardasSplendor, 2); + assertHandCount(playerA, 3); //-2 sigardas, +2 natural draw, +1 trigger + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/BoneMaskTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/BoneMaskTest.java new file mode 100644 index 00000000000..2a0fe812f5e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/BoneMaskTest.java @@ -0,0 +1,78 @@ +package org.mage.test.cards.single.mir; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class BoneMaskTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.b.BoneMask Bone Mask} {4} + * Artifact + * {2}, {T}: The next time a source of your choice would deal damage to you this turn, prevent that damage. Exile cards from the top of your library equal to the damage prevented this way. + */ + private static final String mask = "Bone Mask"; + + @Test + public void test_DamageOnCreature_NoPrevent() { + addCard(Zone.BATTLEFIELD, playerA, mask, 1); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Caelorna, Coral Tyrant"); // 0/8 + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}"); + setChoice(playerA, "Goblin Piker"); // source to prevent from + + attack(2, playerB, "Goblin Piker", playerA); + block(2, playerA, "Caelorna, Coral Tyrant", "Goblin Piker"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertDamageReceived(playerA, "Caelorna, Coral Tyrant", 2); // no prevent + assertExileCount(playerA, 0); + } + + @Test + public void test_DamageOnYou_Prevent() { + addCard(Zone.BATTLEFIELD, playerA, mask, 1); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}"); + setChoice(playerA, "Goblin Piker"); // source to prevent from + + attack(2, playerB, "Goblin Piker", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + assertExileCount(playerA, 2); + } + + @Test + public void test_DoubleStrike_Prevent_ThenConsumedAndNoPrevent() { + addCard(Zone.BATTLEFIELD, playerA, mask, 1); + addCard(Zone.BATTLEFIELD, playerB, "Blade Historian", 1); // 2/3 "Attacking creatures you control have double strike." + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}"); + setChoice(playerA, "Blade Historian"); // source to prevent from + + attack(2, playerB, "Blade Historian", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 2); + assertExileCount(playerA, 2); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/ShadowbaneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/ShadowbaneTest.java new file mode 100644 index 00000000000..3de86bef715 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mir/ShadowbaneTest.java @@ -0,0 +1,177 @@ +package org.mage.test.cards.single.mir; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class ShadowbaneTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.s.Shadowbane Shadowbane} {1}{W} + * The next time a source of your choice would deal damage to you and/or creatures you control this turn, prevent that damage. If damage from a black source is prevented this way, you gain that much life. + */ + private static final String shadowbane = "Shadowbane"; + + @Test + public void test_DamageOnCreature_Prevent_SourceNotBlack() { + addCard(Zone.HAND, playerA, shadowbane, 1); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Caelorna, Coral Tyrant"); // 0/8 + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, shadowbane); + setChoice(playerA, "Goblin Piker"); // source to prevent from + + attack(2, playerB, "Goblin Piker", playerA); + block(2, playerA, "Caelorna, Coral Tyrant", "Goblin Piker"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertDamageReceived(playerA, "Caelorna, Coral Tyrant", 0); + assertLife(playerA, 20); // no life gain + } + + @Test + public void test_DamageOnCreature_Prevent_SourceBlack() { + addCard(Zone.HAND, playerA, shadowbane, 1); + addCard(Zone.BATTLEFIELD, playerB, "Cabal Evangel", 1); // 2/2 black + addCard(Zone.BATTLEFIELD, playerA, "Caelorna, Coral Tyrant"); // 0/8 + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, shadowbane); + setChoice(playerA, "Cabal Evangel"); // source to prevent from + + attack(2, playerB, "Cabal Evangel", playerA); + block(2, playerA, "Caelorna, Coral Tyrant", "Cabal Evangel"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertDamageReceived(playerA, "Caelorna, Coral Tyrant", 0); + assertLife(playerA, 20 + 2); // life gain + } + + @Test + public void test_DamageOnYou_Prevent_SourceNotBlack() { + addCard(Zone.HAND, playerA, shadowbane, 1); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, shadowbane); + setChoice(playerA, "Goblin Piker"); // source to prevent from + + attack(2, playerB, "Goblin Piker", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + } + + @Test + public void test_DamageOnYou_Prevent_SourceBlack() { + addCard(Zone.HAND, playerA, shadowbane, 1); + addCard(Zone.BATTLEFIELD, playerB, "Cabal Evangel", 1); // 2/2 black + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, shadowbane); + setChoice(playerA, "Cabal Evangel"); // source to prevent from + + attack(2, playerB, "Cabal Evangel", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 + 2); + } + + @Test + public void test_SpellDamage_Prevent_SourceNotBlack() { + addCard(Zone.HAND, playerA, shadowbane, 1); + addCard(Zone.HAND, playerB, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, shadowbane, null, "Lightning Bolt"); + setChoice(playerA, "Lightning Bolt"); // source to prevent from + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + } + + @Test + public void test_SpellDamage_Prevent_SourceBlack() { + addCard(Zone.HAND, playerA, shadowbane, 1); + addCard(Zone.HAND, playerB, "Agonizing Syphon", 1); // 3 damage to any target. gain 3 life + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 4); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Agonizing Syphon", playerA); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, shadowbane, null, "Agonizing Syphon"); + setChoice(playerA, "Agonizing Syphon"); // source to prevent from + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 + 3); + assertLife(playerB, 20 + 3); + } + + @Test + public void test_SpellDamage_Prevent_Famine_SourceBlack() { + addCard(Zone.HAND, playerA, shadowbane, 1); + addCard(Zone.HAND, playerB, "Famine", 1); // Famine deals 3 damage to each creature and each player. + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 5); + addCard(Zone.BATTLEFIELD, playerA, "Ageless Guardian"); // 1/4 + addCard(Zone.BATTLEFIELD, playerB, "Dune Beetle"); // 1/4 + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Famine"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, shadowbane, null, "Famine"); + setChoice(playerA, "Famine"); // source to prevent from + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertDamageReceived(playerA, "Ageless Guardian", 0); // prevented + assertDamageReceived(playerB, "Dune Beetle", 3); // not prevented on opponent's creature + assertLife(playerB, 20 - 3); // not prevented on opponent + assertLife(playerA, 20 + 3 * 2); // 2 preventions of 3 damage + } + + @Test + public void test_SpellDamage_Prevent_FieryConfluence() { + addCard(Zone.HAND, playerA, shadowbane, 1); + addCard(Zone.HAND, playerB, "Fiery Confluence", 3); // three instances of "Fiery Confluence deals 2 damage to each opponent." + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 4); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Fiery Confluence"); + setModeChoice(playerB, "2"); // Fiery Confluence deals 2 damage to each opponent + setModeChoice(playerB, "2"); // Fiery Confluence deals 2 damage to each opponent + setModeChoice(playerB, "2"); // Fiery Confluence deals 2 damage to each opponent + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, shadowbane, null, "Fiery Confluence"); + setChoice(playerA, "Fiery Confluence"); // source to prevent from + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 2 * 2); // only 1 of the 3 damage instance is prevented + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mmq/ChoArrimAlchemistTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mmq/ChoArrimAlchemistTest.java new file mode 100644 index 00000000000..4b045c8acd1 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mmq/ChoArrimAlchemistTest.java @@ -0,0 +1,86 @@ +package org.mage.test.cards.single.mmq; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class ChoArrimAlchemistTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.c.ChoArrimAlchemist Cho-Arrim Alchemist} {W} + * Creature — Human Spellshaper + * {1}{W}{W}, {T}, Discard a card: The next time a source of your choice would deal damage to you this turn, prevent that damage. You gain life equal to the damage prevented this way. + * 1/1 + */ + private static final String alchemist = "Cho-Arrim Alchemist"; + + @Test + public void test_DamageOnCreature_NoPrevent() { + addCard(Zone.BATTLEFIELD, playerA, alchemist, 1); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); // to discard + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Caelorna, Coral Tyrant"); // 0/8 + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{W}{W}"); + setChoice(playerA, "Lightning Bolt"); // discard to activate + setChoice(playerA, "Goblin Piker"); // source to prevent from + + attack(2, playerB, "Goblin Piker", playerA); + block(2, playerA, "Caelorna, Coral Tyrant", "Goblin Piker"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertDamageReceived(playerA, "Caelorna, Coral Tyrant", 2); // no prevent + assertGraveyardCount(playerA, "Lightning Bolt", 1); + } + + @Test + public void test_DamageOnYou_Prevent() { + addCard(Zone.BATTLEFIELD, playerA, alchemist, 1); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); // to discard + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{W}{W}"); + setChoice(playerA, "Lightning Bolt"); // discard to activate + setChoice(playerA, "Goblin Piker"); // source to prevent from + + attack(2, playerB, "Goblin Piker", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 + 2); + assertGraveyardCount(playerA, "Lightning Bolt", 1); + } + + @Test + public void test_DoubleStrike_Prevent_ThenConsumedAndNoPrevent() { + addCard(Zone.BATTLEFIELD, playerA, alchemist, 1); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); // to discard + addCard(Zone.BATTLEFIELD, playerB, "Blade Historian", 1); // 2/3 "Attacking creatures you control have double strike." + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{W}{W}"); + setChoice(playerA, "Lightning Bolt"); // discard to activate + setChoice(playerA, "Blade Historian"); // source to prevent from + + attack(2, playerB, "Blade Historian", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + assertTapped("Blade Historian", true); + assertGraveyardCount(playerA, "Lightning Bolt", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mmq/StoryCircleTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mmq/StoryCircleTest.java new file mode 100644 index 00000000000..947b3e477ac --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mmq/StoryCircleTest.java @@ -0,0 +1,251 @@ +package org.mage.test.cards.single.mmq; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class StoryCircleTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.s.StoryCircle Story Circle} {1}{W}{W} + * Enchantment + * As this enchantment enters, choose a color. + * {W}: The next time a source of your choice of the chosen color would deal damage to you this turn, prevent that damage. + */ + private static final String circle = "Story Circle"; + + @Test + public void test_DamageOnCreature_NoPrevent() { + addCard(Zone.HAND, playerA, circle, 1); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Caelorna, Coral Tyrant"); // 0/8 + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, circle); + setChoice(playerA, "Red"); // color chosen + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{W}"); + setChoice(playerA, "Goblin Piker"); // source to prevent from + + attack(2, playerB, "Goblin Piker", playerA); + block(2, playerA, "Caelorna, Coral Tyrant", "Goblin Piker"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertDamageReceived(playerA, "Caelorna, Coral Tyrant", 2); // no prevent + assertLife(playerB, 20); + assertTappedCount("Plains", true, 4); + } + + @Test + public void test_DamageOnYou_Prevent() { + addCard(Zone.HAND, playerA, circle, 1); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, circle); + setChoice(playerA, "Red"); // color chosen + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{W}"); + setChoice(playerA, "Goblin Piker"); // source to prevent from + + attack(2, playerB, "Goblin Piker", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + assertTappedCount("Plains", true, 4); + } + + @Test + public void test_DoubleStrike_Prevent_ThenConsumedAndNoPrevent() { + addCard(Zone.HAND, playerA, circle, 1); + addCard(Zone.BATTLEFIELD, playerB, "Blade Historian", 1); // 2/3 "Attacking creatures you control have double strike." + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, circle); + setChoice(playerA, "Red"); // color chosen + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{W}"); + setChoice(playerA, "Blade Historian"); // source to prevent from + + attack(2, playerB, "Blade Historian", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 2); + assertTappedCount("Plains", true, 4); + } + + @Test + public void test_NoSourceOfChosenColor_NoPrevent() { + addCard(Zone.HAND, playerA, circle, 1); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, circle); + setChoice(playerA, "Green"); // color chosen + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{W}"); + // no valid choice + + attack(2, playerB, "Goblin Piker", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 2); + assertTappedCount("Plains", true, 4); + } + + @Test + public void test_SpellDamageOnYou_Prevent() { + addCard(Zone.HAND, playerA, circle, 1); + addCard(Zone.HAND, playerB, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.BATTLEFIELD, playerB, "Mountain"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, circle); + setChoice(playerA, "Red"); // color chosen + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", playerA); + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{W}", null, "Lightning Bolt"); + setChoice(playerA, "Lightning Bolt"); // source to prevent from + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + assertGraveyardCount(playerB, "Lightning Bolt", 1); + assertTappedCount("Plains", true, 4); + } + + @Test + public void test_SpellNotChosenColorDamageOnYou_NoPrevent() { + addCard(Zone.HAND, playerA, circle, 1); + addCard(Zone.HAND, playerB, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.BATTLEFIELD, playerB, "Mountain"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, circle); + setChoice(playerA, "Green"); // color chosen + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", playerA); + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{W}", null, "Lightning Bolt"); + // no valid choice + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 3); + assertGraveyardCount(playerB, "Lightning Bolt", 1); + assertTappedCount("Plains", true, 4); + } + + @Test + public void test_TriggerDamageOnYou_Prevent() { + addCard(Zone.HAND, playerA, circle, 1); + addCard(Zone.HAND, playerB, "Blisterstick Shaman", 1); // etb deals 1 to any target + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, circle); + setChoice(playerA, "Red"); // color chosen + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Blisterstick Shaman"); + addTarget(playerB, playerA); // target for Shaman trigger + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{W}", null, "stack ability (When"); + setChoice(playerA, "Blisterstick Shaman"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + assertPermanentCount(playerB, "Blisterstick Shaman", 1); + assertTappedCount("Plains", true, 4); + } + + @Test + public void test_TriggerNotChosenColorDamageOnYou_NoPrevent() { + addCard(Zone.HAND, playerA, circle, 1); + addCard(Zone.HAND, playerB, "Blisterstick Shaman", 1); // etb deals 1 to any target + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 3); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, circle); + setChoice(playerA, "Green"); // color chosen + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Blisterstick Shaman"); + addTarget(playerB, playerA); // target for Shaman trigger + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{W}", null, "stack ability (When"); + // no valid choice + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 1); + assertPermanentCount(playerB, "Blisterstick Shaman", 1); + assertTappedCount("Plains", true, 4); + } + + @Test + public void test_ActivationDamageOnYou_Prevent() { + addCard(Zone.HAND, playerA, circle, 1); + addCard(Zone.BATTLEFIELD, playerB, "Anaba Shaman", 1); // {R}, {T}: This creature deals 1 damage to any target. + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, circle); + setChoice(playerA, "Red"); // color chosen + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{R},", playerA); + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{W}", null, "stack ability ({R}"); + setChoice(playerA, "Anaba Shaman"); // source to prevent from + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertTappedCount("Anaba Shaman", true, 1); + assertLife(playerA, 20); + assertTappedCount("Plains", true, 4); + } + + @Test + public void test_ActivationNotChosenColorDamageOnYou_NoPrevent() { + addCard(Zone.HAND, playerA, circle, 1); + addCard(Zone.BATTLEFIELD, playerB, "Anaba Shaman", 1); // {R}, {T}: This creature deals 1 damage to any target. + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, circle); + setChoice(playerA, "Green"); // color chosen + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{R},", playerA); + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{W}", null, "stack ability ({R}"); + // no valid choice + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertTappedCount("Anaba Shaman", true, 1); + assertLife(playerA, 20 - 1); + assertTappedCount("Plains", true, 4); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mom/GhaltaAndMavrenTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mom/GhaltaAndMavrenTest.java new file mode 100644 index 00000000000..98f9fb172db --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mom/GhaltaAndMavrenTest.java @@ -0,0 +1,177 @@ +package org.mage.test.cards.single.mom; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class GhaltaAndMavrenTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.g.GhaltaAndMavren Ghalta and Mavren} {3}{G}{G}{W}{W} + * Legendary Creature — Dinosaur Vampire + * Trample + * Whenever you attack, choose one — + * • Create a tapped and attacking X/X green Dinosaur creature token with trample, where X is the greatest power among other attacking creatures. + * • Create X 1/1 white Vampire creature tokens with lifelink, where X is the number of other attacking creatures. + * 12/12 + */ + private static final String ghalta = "Ghalta and Mavren"; + + @Test + public void test_ghalta_attack_alone_dinosaur() { + addCard(Zone.BATTLEFIELD, playerA, ghalta); + addCard(Zone.BATTLEFIELD, playerA, "Blood Artist", 1); // to trigger on token death + + attack(1, playerA, ghalta, playerB); + setModeChoice(playerA, "1"); // create 0/0 Dinosaur + addTarget(playerA, playerB); // Blood Artist trigger + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Dinosaur Token", 0); + assertPermanentCount(playerA, "Vampire Token", 0); + assertLife(playerB, 20 - 12 - 1); + assertLife(playerA, 20 + 1); + } + + @Test + public void test_ghalta_attack_alone_vampire() { + addCard(Zone.BATTLEFIELD, playerA, ghalta); + addCard(Zone.BATTLEFIELD, playerA, "Blood Artist", 1); // to trigger on token death + + attack(1, playerA, ghalta, playerB); + setModeChoice(playerA, "2"); // create 0 1/1 Vampire + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Dinosaur Token", 0); + assertPermanentCount(playerA, "Vampire Token", 0); + assertLife(playerB, 20 - 12); + assertLife(playerA, 20); + } + + @Test + public void test_vanguard_attack_alone_dinosaur() { + addCard(Zone.BATTLEFIELD, playerA, ghalta); + addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard", 1); // 2/1 + + attack(1, playerA, "Elite Vanguard", playerB); + setModeChoice(playerA, "1"); // create 2/2 Dinosaur + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Dinosaur Token", 1); + assertPowerToughness(playerA, "Dinosaur Token", 2, 2); + assertPermanentCount(playerA, "Vampire Token", 0); + assertLife(playerB, 20 - 2 - 2); + } + + @Test + public void test_bear_attack_alone_vampire() { + addCard(Zone.BATTLEFIELD, playerA, ghalta); + addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard", 1); // 2/1 + + attack(1, playerA, "Elite Vanguard", playerB); + setModeChoice(playerA, "2"); // create 1 1/1 Vampire + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Dinosaur Token", 0); + assertPermanentCount(playerA, "Vampire Token", 1); + assertLife(playerB, 20 - 2); + } + + + @Test + public void test_vanguard_and_ghalta_attack_dinosaur() { + addCard(Zone.BATTLEFIELD, playerA, ghalta); + addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard", 1); // 2/1 + + attack(1, playerA, ghalta, playerB); + attack(1, playerA, "Elite Vanguard", playerB); + setModeChoice(playerA, "1"); // create 2/2 Dinosaur + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Dinosaur Token", 1); + assertPowerToughness(playerA, "Dinosaur Token", 2, 2); + assertPermanentCount(playerA, "Vampire Token", 0); + assertLife(playerB, 20 - 12 - 2 - 2); + } + + @Test + public void test_vanguard_and_ghalta_attack_vampire() { + addCard(Zone.BATTLEFIELD, playerA, ghalta); + addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard", 1); // 2/1 + + attack(1, playerA, ghalta, playerB); + attack(1, playerA, "Elite Vanguard", playerB); + setModeChoice(playerA, "2"); // create 1 1/1 Vampire + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Dinosaur Token", 0); + assertPermanentCount(playerA, "Vampire Token", 1); + assertLife(playerB, 20 - 12 - 2); + } + + + @Test + public void test_varied_attackers_dinosaur() { + addCard(Zone.BATTLEFIELD, playerA, ghalta); + addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard"); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Barony Vampire"); // 3/2 + addCard(Zone.BATTLEFIELD, playerA, "Armored Wolf-Rider"); // 4/6 + + attack(1, playerA, "Elite Vanguard", playerB); + attack(1, playerA, "Barony Vampire", playerB); + attack(1, playerA, "Armored Wolf-Rider", playerB); + setModeChoice(playerA, "1"); // create a 4/4 Dinosaur + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Dinosaur Token", 1); + assertPowerToughness(playerA, "Dinosaur Token", 4, 4); + assertPermanentCount(playerA, "Vampire Token", 0); + assertLife(playerB, 20 - 4 - 3 - 2 - 4); + } + + @Test + public void test_5_attackers_vampire() { + addCard(Zone.BATTLEFIELD, playerA, ghalta); + addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard", 5); // 2/1 + + attack(1, playerA, "Elite Vanguard", playerB); + attack(1, playerA, "Elite Vanguard", playerB); + attack(1, playerA, "Elite Vanguard", playerB); + attack(1, playerA, "Elite Vanguard", playerB); + attack(1, playerA, "Elite Vanguard", playerB); + setModeChoice(playerA, "2"); // create 5 1/1 Vampire + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, "Dinosaur Token", 0); + assertPermanentCount(playerA, "Vampire Token", 5); + assertLife(playerB, 20 - 2 * 5); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mom/JinGitaxiasTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mom/JinGitaxiasTest.java new file mode 100644 index 00000000000..c26a0e71c6b --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mom/JinGitaxiasTest.java @@ -0,0 +1,53 @@ +package org.mage.test.cards.single.mom; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class JinGitaxiasTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.j.JinGitaxias Jin-Gitaxias // The Great Synthesis} + * Jin-Gitaxias {3}{U}{U} + * Legendary Creature — Phyrexian Praetor + * Ward {2} + * Whenever you cast a noncreature spell with mana value 3 or greater, draw a card. + * {3}{U}: Exile Jin-Gitaxias, then return it to the battlefield transformed under its owner’s control. Activate only as a sorcery and only if you have seven or more cards in hand. + * 5/5 + * The Great Synthesis + * Enchantment — Saga + * I — Draw cards equal to the number of cards in your hand. You have no maximum hand size for as long as you control this Saga. + * II — Return all non-Phyrexian creatures to their owners’ hands. + * III — You may cast any number of spells from your hand without paying their mana costs. Exile this Saga, then return it to the battlefield (front face up). + */ + private static final String jin = "Jin-Gitaxias"; + + @Test + public void test_SimplePlay() { + addCard(Zone.BATTLEFIELD, playerA, jin, 1); + addCard(Zone.HAND, playerA, "Mountain", 7); + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + addCard(Zone.BATTLEFIELD, playerB, "Memnite", 4); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}{U}: Exile "); + + checkHandCount("after I", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, 14); + + // turn 3 + checkHandCount("after III", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, 15); + checkHandCardCount("after III", 3, PhaseStep.POSTCOMBAT_MAIN, playerB, "Memnite", 4); + + // turn 5 + checkHandCount("after III", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, 16); + + setStrictChooseMode(true); + setStopAt(6, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertHandCount(playerB, 7); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ody/PilgrimOfJusticeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ody/PilgrimOfJusticeTest.java new file mode 100644 index 00000000000..4753884ede7 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ody/PilgrimOfJusticeTest.java @@ -0,0 +1,61 @@ +package org.mage.test.cards.single.ody; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class PilgrimOfJusticeTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.p.PilgrimOfJustice Pilgrim of Justice} {2}{W} + * Creature — Human Cleric + * Protection from red + * {W}, Sacrifice this creature: The next time a red source of your choice would deal damage this turn, prevent that damage. + * 1/3 + */ + private static final String pilgrim = "Pilgrim of Justice"; + + @Test + public void test_DamageOnCreature_Prevent() { + addCard(Zone.BATTLEFIELD, playerA, pilgrim, 1); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Caelorna, Coral Tyrant"); // 0/8 + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{W}"); + setChoice(playerA, "Goblin Piker"); // source to prevent from + + attack(2, playerB, "Goblin Piker", playerA); + block(2, playerA, "Caelorna, Coral Tyrant", "Goblin Piker"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertDamageReceived(playerA, "Caelorna, Coral Tyrant", 0); + assertTapped("Goblin Piker", true); + } + + @Test + public void test_DamageOnYou_Prevent() { + addCard(Zone.BATTLEFIELD, playerA, pilgrim, 1); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{W}"); + setChoice(playerA, "Goblin Piker"); // source to prevent from + + attack(2, playerB, "Goblin Piker", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + assertTapped("Goblin Piker", true); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ogw/OathOfJaceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ogw/OathOfJaceTest.java new file mode 100644 index 00000000000..1f774b62d82 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ogw/OathOfJaceTest.java @@ -0,0 +1,42 @@ +package org.mage.test.cards.single.ogw; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class OathOfJaceTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.o.OathOfJace Oath of Jace} {2}{U} + * Legendary Enchantment + * When Oath of Jace enters, draw three cards, then discard two cards. + * At the beginning of your upkeep, scry X, where X is the number of planeswalkers you control. + */ + private static final String oath = "Oath of Jace"; + + @Test + public void test_scry2() { + skipInitShuffling(); + addCard(Zone.LIBRARY, playerA, "Ancient Amphitheater"); + addCard(Zone.LIBRARY, playerA, "Baleful Strix"); + + addCard(Zone.BATTLEFIELD, playerA, oath, 1); + addCard(Zone.BATTLEFIELD, playerA, "Legions of Lim-Dul", 2); // not a planeswalker + addCard(Zone.BATTLEFIELD, playerA, "Chandra Nalaar"); + addCard(Zone.BATTLEFIELD, playerA, "Jace Beleren"); + + addCard(Zone.BATTLEFIELD, playerB, "Ajani, Caller of the Pride"); // not in your control + + // Oath of Jace's upkeep trigger triggers + addTarget(playerA, "Ancient Amphitheater^Baleful Strix"); // put on bottom with scry 2 + setChoice(playerA, "Baleful Strix"); // ordering the bottom + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/HELIOSOneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/pip/HELIOSOneTest.java similarity index 99% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/HELIOSOneTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/single/pip/HELIOSOneTest.java index 366176c3c01..01adff3acf3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/HELIOSOneTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/pip/HELIOSOneTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single.ltr; +package org.mage.test.cards.single.pip; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/pip/Vault13DwellersJourneyTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/pip/Vault13DwellersJourneyTest.java new file mode 100644 index 00000000000..16b0a3f9ae9 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/pip/Vault13DwellersJourneyTest.java @@ -0,0 +1,133 @@ +package org.mage.test.cards.single.pip; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.player.TestPlayer; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class Vault13DwellersJourneyTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.v.Vault13DwellersJourney Vault 13: Dweller's Journey} {3}{W} + * Enchantment — Saga + * I — For each player, exile up to one other target enchantment or creature that player controls until this Saga leaves the battlefield. + * II — You gain 2 life and scry 2. + * III — Return two cards exiled with this Saga to the battlefield under their owners’ control and put the rest on the bottom of their owners’ libraries. + */ + private static final String vault = "Vault 13: Dweller's Journey"; + + @Ignore // TODO: goal of #11619 is to fix this nicely + @Test + public void test_SimplePlay_ReturnOne() { + addCard(Zone.HAND, playerA, vault, 1); + + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.BATTLEFIELD, playerA, "Memnite"); + addCard(Zone.BATTLEFIELD, playerB, "Ornithopter"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, vault); + addTarget(playerA, "Memnite"); + addTarget(playerA, TestPlayer.TARGET_SKIP); + + checkExileCount("after I: Memnite exiled", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", 1); + + // turn 3 + addTarget(playerA, "Mountain"); // for Scry 2 + + // turn 5 + checkExileCount("before III: Memnite exiled", 5, PhaseStep.UPKEEP, playerA, "Memnite", 1); + + setStrictChooseMode(true); + setStopAt(5, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Memnite", 1); + assertLife(playerA, 20 + 2); + } + @Ignore // TODO: goal of #11619 is to fix this nicely + @Test + public void test_SimplePlay_Return() { + addCard(Zone.HAND, playerA, vault, 1); + + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.BATTLEFIELD, playerA, "Memnite"); + addCard(Zone.BATTLEFIELD, playerB, "Ornithopter"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, vault); + addTarget(playerA, "Memnite"); + addTarget(playerA, "Ornithopter"); + + checkExileCount("after I: Memnite exiled", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", 1); + checkExileCount("after I: Ornithopter exiled", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Ornithopter", 1); + + // turn 3 + addTarget(playerA, "Mountain"); // for Scry 2 + + // turn 5 + checkExileCount("before III: Memnite exiled", 5, PhaseStep.UPKEEP, playerA, "Memnite", 1); + checkExileCount("before III: Ornithopter exiled", 5, PhaseStep.UPKEEP, playerB, "Ornithopter", 1); + + setStrictChooseMode(true); + setStopAt(5, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Memnite", 1); + assertPermanentCount(playerB, "Ornithopter", 1); + assertLife(playerA, 20 + 2); + } + + @Ignore // TODO: goal of #11619 is to fix this nicely + @Test + public void test_SimplePlay_NoReturn() { + addCard(Zone.HAND, playerA, vault, 1); + + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.BATTLEFIELD, playerA, "Memnite"); + addCard(Zone.BATTLEFIELD, playerA, "Squire"); + addCard(Zone.BATTLEFIELD, playerB, "Vampire Hexmage"); + addCard(Zone.BATTLEFIELD, playerB, "Ornithopter"); + addCard(Zone.BATTLEFIELD, playerB, "Watchwolf"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, vault); + addTarget(playerA, "Memnite"); + addTarget(playerA, "Ornithopter"); + + // turn 2 + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Sacrifice"); + addTarget(playerB, vault); + + // turn 3 + addTarget(playerA, "Squire"); + addTarget(playerA, "Watchwolf"); + + checkExileCount("after I x2: Memnite exiled", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", 1); + checkExileCount("after I x2: Ornithopter exiled", 3, PhaseStep.POSTCOMBAT_MAIN, playerB, "Ornithopter", 1); + checkExileCount("after I x2: Squire exiled", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Squire", 1); + checkExileCount("after I x2: Watchwolf exiled", 3, PhaseStep.POSTCOMBAT_MAIN, playerB, "Watchwolf", 1); + + // turn 5 + addTarget(playerA, "Mountain"); // for Scry 2 + + // turn 7 + checkExileCount("before III: Memnite exiled", 7, PhaseStep.UPKEEP, playerB, "Memnite", 1); + checkExileCount("before III: Ornithopter exiled", 7, PhaseStep.UPKEEP, playerB, "Ornithopter", 1); + checkExileCount("before III: Squire exiled", 7, PhaseStep.UPKEEP, playerB, "Squire", 1); + checkExileCount("before III: Watchwolf exiled", 7, PhaseStep.UPKEEP, playerB, "Watchwolf", 1); + setChoice(playerA, "Memnite^Ornithopter"); + + setStrictChooseMode(true); + setStopAt(7, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Memnite", 1); + assertPermanentCount(playerB, "Ornithopter", 1); + assertLibraryCount(playerA, "Squire", 1); + assertLibraryCount(playerB, "Watchwolf", 1); + assertLife(playerA, 20 + 2); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/pip/Vault87ForcedEvolutionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/pip/Vault87ForcedEvolutionTest.java new file mode 100644 index 00000000000..e494b85d2e3 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/pip/Vault87ForcedEvolutionTest.java @@ -0,0 +1,52 @@ +package org.mage.test.cards.single.pip; + +import mage.constants.PhaseStep; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class Vault87ForcedEvolutionTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.v.Vault87ForcedEvolution Vault 87: Forced Evolution} {3}{G}{U} + * Enchantment — Saga + * I — Gain control of target non-Mutant creature for as long as you control this Saga. + * II — Put a +1/+1 counter on target creature you control. It becomes a Mutant in addition to its other types. + * III — Draw cards equal to the greatest power among Mutants you control. + */ + private static final String vault = "Vault 87: Forced Evolution"; + + @Test + public void test_SimplePlay() { + addCard(Zone.HAND, playerA, vault, 1); + + addCard(Zone.BATTLEFIELD, playerA, "Tropical Island", 5); + addCard(Zone.BATTLEFIELD, playerB, "Memnite"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, vault); + addTarget(playerA, "Memnite"); + + checkPermanentCount("after I: Memnite control by A", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", 1); + + // turn 3 + addTarget(playerA, "Memnite"); + + // turn 5 + checkPermanentCount("before III: Memnite control by A", 5, PhaseStep.UPKEEP, playerA, "Memnite", 1); + + setStrictChooseMode(true); + setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerB, "Memnite", 1); + assertCounterCount(playerB, "Memnite", CounterType.P1P1, 1); + assertSubtype("Memnite", SubType.MUTANT); + assertSubtype("Memnite", SubType.CONSTRUCT); + assertHandCount(playerA, 4); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/rna/FontOfAgoniesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/rna/FontOfAgoniesTest.java new file mode 100644 index 00000000000..d0ae259645d --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/rna/FontOfAgoniesTest.java @@ -0,0 +1,53 @@ +package org.mage.test.cards.single.rna; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class FontOfAgoniesTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.f.FontOfAgonies Font of Agonies} {B} + * Enchantment + * Whenever you pay life, put that many blood counters on this enchantment. + * {1}{B}, Remove four blood counters from this enchantment: Destroy target creature. + */ + private static final String font = "Font of Agonies"; + + @Test + public void test_Fetchland() { + addCard(Zone.BATTLEFIELD, playerA, font, 1); + addCard(Zone.BATTLEFIELD, playerA, "Arid Mesa", 1); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}"); + addTarget(playerA, "Mountain"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 1); + assertCounterCount(playerA, font, CounterType.BLOOD, 1); + } + + @Test + public void test_Pay2() { + addCard(Zone.BATTLEFIELD, playerA, font, 1); + addCard(Zone.BATTLEFIELD, playerA, "Book of Rass", 1); // {2}, Pay 2 life: Draw a card. + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 2); + assertCounterCount(playerA, font, CounterType.BLOOD, 2); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/scg/AcceleratedMutationTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/scg/AcceleratedMutationTest.java new file mode 100644 index 00000000000..207a0157cb1 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/scg/AcceleratedMutationTest.java @@ -0,0 +1,39 @@ +package org.mage.test.cards.single.scg; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class AcceleratedMutationTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.a.AcceleratedMutation Accelerated Mutation} {3}{G}{G} + * Instant + * Target creature gets +X/+X until end of turn, where X is the greatest mana value among permanents you control. + */ + private static final String mutation = "Accelerated Mutation"; + + @Test + public void test_simple() { + addCard(Zone.HAND, playerA, mutation); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 5); + addCard(Zone.BATTLEFIELD, playerA, "Centaur Courser"); // {2}{G} 3/3 + addCard(Zone.BATTLEFIELD, playerA, "Ingenuity Engine"); // {7} Artifact + + addCard(Zone.BATTLEFIELD, playerB, "Arboretum Elemental"); // {7}{G}{G} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, mutation, "Centaur Courser"); + checkPT("10/10 Courser", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Centaur Courser", 10, 10); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.PRECOMBAT_MAIN); + execute(); + + // check effect stop end of turn + assertPowerToughness(playerA, "Centaur Courser", 3, 3); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/sir/BrainInAJarTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/sir/BrainInAJarTest.java new file mode 100644 index 00000000000..05e353eca1c --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/sir/BrainInAJarTest.java @@ -0,0 +1,49 @@ +package org.mage.test.cards.single.sir; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class BrainInAJarTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.b.BrainInAJar Brain in a Jar} {2} + * Artifact + * {1}, {T}: Put a charge counter on this artifact, then you may cast an instant or sorcery spell with mana value equal to the number of charge counters on this artifact from your hand without paying its mana cost. + * {3}, {T}, Remove X charge counters from this artifact: Scry X. + */ + private static final String brain = "Brain in a Jar"; + + @Test + public void test_scry2() { + skipInitShuffling(); + addCard(Zone.LIBRARY, playerA, "Ancient Amphitheater"); + addCard(Zone.LIBRARY, playerA, "Baleful Strix"); + + addCard(Zone.BATTLEFIELD, playerA, brain, 1); + addCard(Zone.BATTLEFIELD, playerA, "Dragon Appeasement"); // enchantement for skip draw + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}, {T}"); + + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}, {T}"); + + activateAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}, {T}"); + + activateAbility(7, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}, {T}"); + setChoice(playerA, "X=2"); // how many counters to remove + addTarget(playerA, "Baleful Strix^Ancient Amphitheater"); // put on bottom with Scry 2 + setChoice(playerA, "Ancient Amphitheater"); // order for bottom + + setStrictChooseMode(true); + setStopAt(7, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertCounterCount(playerA, brain, CounterType.CHARGE, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/stx/SpitefulSquadTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/stx/SpitefulSquadTest.java new file mode 100644 index 00000000000..a9cb149f535 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/stx/SpitefulSquadTest.java @@ -0,0 +1,40 @@ +package org.mage.test.cards.single.stx; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class SpitefulSquadTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.s.SpitefulSquad} {2}{W}{B} + * Creature — Human Warlock + * Deathtouch + * This creature enters with two +1/+1 counters on it. + * When this creature dies, put its counters on target creature you control. + * 0/0 + */ + private static final String squad = "Spiteful Squad"; + + @Test + public void test_Simple() { + addCard(Zone.BATTLEFIELD, playerA, squad); + addCard(Zone.HAND, playerA, "Murder"); + addCard(Zone.BATTLEFIELD, playerA, "Squire"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Murder", squad, true); + addTarget(playerA, "Squire"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertCounterCount(playerA, "Squire", CounterType.P1P1, 2); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/tdc/BetorAncestorsVoiceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/tdc/BetorAncestorsVoiceTest.java new file mode 100644 index 00000000000..ce8df2192bb --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/tdc/BetorAncestorsVoiceTest.java @@ -0,0 +1,45 @@ +package org.mage.test.cards.single.tdc; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.player.TestPlayer; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class BetorAncestorsVoiceTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.b.BetorAncestorsVoice Betor, Ancestor's Voice} {2}{W}{B}{G} + * Legendary Creature — Spirit Dragon + * Flying, lifelink + * At the beginning of your end step, put a number of +1/+1 counters on up to one other target creature you control equal to the amount of life you gained this turn. Return up to one target creature card with mana value less than or equal to the amount of life you lost this turn from your graveyard to the battlefield. + * 3/5 + */ + private static final String betor = "Betor, Ancestor's Voice"; + + // Bug Report: If you got Betor and Rhox Faithmender on the field, and attack with both. + // The gui shows you got the 8 life you are supposed to get, but in the end of turn trigger, + // there will be only 4 counters put on a creature. + @Test + public void test_RhowFaithmender() { + addCard(Zone.BATTLEFIELD, playerA, betor, 1); + addCard(Zone.BATTLEFIELD, playerA, "Rhox Faithmender", 1); + + attack(1, playerA, betor, playerB); + attack(1, playerA, "Rhox Faithmender", playerB); + + addTarget(playerA, "Rhox Faithmender"); // Betor trigger's first target + addTarget(playerA, TestPlayer.TARGET_SKIP); // second target + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 + 8); + assertCounterCount(playerA, "Rhox Faithmender", CounterType.P1P1, 8); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/tdm/NewWayForwardTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/tdm/NewWayForwardTest.java new file mode 100644 index 00000000000..3c60fee8906 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/tdm/NewWayForwardTest.java @@ -0,0 +1,84 @@ +package org.mage.test.cards.single.tdm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class NewWayForwardTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.n.NewWayForward New Way Forward} {2}{U}{R}{W} + * Instant + * The next time a source of your choice would deal damage to you this turn, prevent that damage. When damage is prevented this way, New Way Forward deals that much damage to that source’s controller and you draw that many cards. + */ + private static final String way = "New Way Forward"; + + @Test + public void test_DamageOnCreature_NoPrevent() { + addCard(Zone.HAND, playerA, way, 1); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Caelorna, Coral Tyrant"); // 0/8 + addCard(Zone.BATTLEFIELD, playerA, "Plateau", 2); + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, way); + setChoice(playerA, "Goblin Piker"); // source to prevent from + + attack(2, playerB, "Goblin Piker", playerA); + block(2, playerA, "Caelorna, Coral Tyrant", "Goblin Piker"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertDamageReceived(playerA, "Caelorna, Coral Tyrant", 2); // no prevent + assertLife(playerB, 20); + assertHandCount(playerA, 0); + } + + @Test + public void test_DamageOnYou_Prevent() { + addCard(Zone.HAND, playerA, way, 1); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Plateau", 2); + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, way); + setChoice(playerA, "Goblin Piker"); // source to prevent from + + attack(2, playerB, "Goblin Piker", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20 - 2); + assertHandCount(playerA, 2); + } + + @Test + public void test_DoubleStrike_Prevent_ThenConsumedAndNoPrevent() { + addCard(Zone.HAND, playerA, way, 1); + addCard(Zone.BATTLEFIELD, playerB, "Blade Historian", 1); // 2/3 "Attacking creatures you control have double strike." + addCard(Zone.BATTLEFIELD, playerA, "Plateau", 2); + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, way); + setChoice(playerA, "Blade Historian"); // source to prevent from + + attack(2, playerB, "Blade Historian", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 2); + assertLife(playerB, 20 - 2); + assertHandCount(playerA, 2); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/thb/TheTriumphOfAnaxTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/thb/TheTriumphOfAnaxTest.java new file mode 100644 index 00000000000..16bd7039eb5 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/thb/TheTriumphOfAnaxTest.java @@ -0,0 +1,58 @@ +package org.mage.test.cards.single.thb; + +import mage.abilities.keyword.TrampleAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class TheTriumphOfAnaxTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.t.TheTriumphOfAnax The Triumph of Anax} {2}{R} + * Enchantment — Saga + * I, II, III — Until end of turn, target creature gains trample and gets +X/+0, where X is the number of lore counters on this Saga. + * IV — Target creature you control fights up to one target creature you don’t control. + */ + private static final String triumph = "The Triumph of Anax"; + + @Test + public void test_SimplePlay() { + addCard(Zone.HAND, playerA, triumph, 1); + addCard(Zone.BATTLEFIELD, playerA, "Memnite", 1); + addCard(Zone.BATTLEFIELD, playerB, "Ornithopter", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, triumph); + addTarget(playerA, "Memnite"); + + checkPT("T1: Memnite is 2/1", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", 2, 1); + checkAbility("T1: Memnite has trample", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", TrampleAbility.class, true); + + // turn 3 + addTarget(playerA, "Memnite"); + checkPT("T3: Memnite is 3/1", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", 3, 1); + checkAbility("T3: Memnite has trample", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", TrampleAbility.class, true); + + // turn 5 + addTarget(playerA, "Memnite"); + checkPT("T4: Memnite is 4/1", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", 4, 1); + checkAbility("T4: Memnite has trample", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, "Memnite", TrampleAbility.class, true); + + // turn 7 + addTarget(playerA, "Memnite"); + addTarget(playerA, "Ornithopter"); + + setStrictChooseMode(true); + setStopAt(7, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, triumph, 1); + assertDamageReceived(playerB, "Ornithopter", 1); + assertPowerToughness(playerA, "Memnite", 1, 1); + assertAbility(playerA, "Memnite", TrampleAbility.getInstance(), false); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/tmp/CircleOfProtectionShadowTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/tmp/CircleOfProtectionShadowTest.java new file mode 100644 index 00000000000..0d5aea0b782 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/tmp/CircleOfProtectionShadowTest.java @@ -0,0 +1,57 @@ +package org.mage.test.cards.single.tmp; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class CircleOfProtectionShadowTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.c.CircleOfProtectionShadow Circle of Protection: Shadow} {1}{W} + * Enchantment + * {1}: The next time a creature of your choice with shadow would deal damage to you this turn, prevent that damage. + */ + private static final String circle = "Circle of Protection: Shadow"; + + @Test + public void test_NotShadow_NoPrevent() { + addCard(Zone.BATTLEFIELD, playerA, circle, 1); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}"); + // no creature with shadow to choose + + attack(2, playerB, "Goblin Piker", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 2); + assertTappedCount("Plains", true, 1); + } + + @Test + public void test_Shadow_Prevent() { + addCard(Zone.BATTLEFIELD, playerA, circle, 1); + addCard(Zone.BATTLEFIELD, playerB, "Dauthi Marauder", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}"); + setChoice(playerA, "Dauthi Marauder"); + + attack(2, playerB, "Dauthi Marauder", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + assertTappedCount("Plains", true, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/tmp/InvulnerabilityTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/tmp/InvulnerabilityTest.java new file mode 100644 index 00000000000..73cd21456bd --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/tmp/InvulnerabilityTest.java @@ -0,0 +1,45 @@ +package org.mage.test.cards.single.tmp; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class InvulnerabilityTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.i.Invulnerability Invulnerability} {1}{W} + * Instant + * Buyback {3} (You may pay an additional {3} as you cast this spell. If you do, put this card into your hand as it resolves.) + * The next time a source of your choice would deal damage to you this turn, prevent that damage. + */ + private static final String invulnerability = "Invulnerability"; + + @Test + public void test_EmblemDamage_Prevent() { + addCard(Zone.HAND, playerA, invulnerability, 1); + addCard(Zone.BATTLEFIELD, playerB, "Chandra, Awakened Inferno", 1); // +2: Each opponent gets an emblem with “At the beginning of your upkeep, this emblem deals 1 damage to you.” + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "+2"); + + checkEmblemCount("Chandra Emblem has been created", 2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Emblem Chandra", 1); + + // turn 3: prevent + castSpell(3, PhaseStep.UPKEEP, playerA, invulnerability); + setChoice(playerA, false); // don't pay for Buyback + setChoice(playerA, "Emblem Chandra"); // choice for source + checkLife("prevention turn 3", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, 20); + + // turn 5: no prevent to check effect ending + + setStrictChooseMode(true); + setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/tor/SternJudgeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/tor/SternJudgeTest.java new file mode 100644 index 00000000000..695387f5461 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/tor/SternJudgeTest.java @@ -0,0 +1,29 @@ +package org.mage.test.cards.single.tor; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author xenohedron + */ +public class SternJudgeTest extends CardTestPlayerBase { + + @Test + public void testCounts() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); + addCard(Zone.BATTLEFIELD, playerA, "Stern Judge"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Each player loses"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 17); + assertLife(playerB, 18); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/who/DayOfTheMoonTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/who/DayOfTheMoonTest.java new file mode 100644 index 00000000000..bd0c49f72d1 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/who/DayOfTheMoonTest.java @@ -0,0 +1,54 @@ +package org.mage.test.cards.single.who; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class DayOfTheMoonTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.d.DayOfTheMoon Day of the Moon} {2}{R} + * Enchantment — Saga + * I, II, III — Choose a creature card name, then goad all creatures with a name chosen for this enchantment. (Until your next turn, they attack each combat if able and attack a player other than you if able.) + */ + private static final String day = "Day of the Moon"; + + @Ignore // TODO: goal of #11619 is to fix this nicely + @Test + public void test_SimplePlay() { + addCard(Zone.HAND, playerA, day, 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerB, "Memnite", 1); + addCard(Zone.BATTLEFIELD, playerB, "Elite Vanguard", 1); + addCard(Zone.BATTLEFIELD, playerB, "Centaur Courser", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, day); + setChoice(playerA, "Memnite"); + + // turn 2, Memnite has to attack. + + // turn 3 + setChoice(playerA, "Centaur Courser"); + + // turn 4, Memnite and Centaur Courser have to attack. + + // turn 5 + setChoice(playerA, "Elite Vanguard"); + + // turn 6, Memnite, Centaur Courser and Elite Vanguard have to attack. + + // turn 8, Nothing goaded anymore + + setStrictChooseMode(true); + setStopAt(8, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 1 * 3 - 3 * 2 - 2); + assertGraveyardCount(playerA, day, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/who/GenesisOfTheDaleksTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/who/GenesisOfTheDaleksTest.java new file mode 100644 index 00000000000..5664cef1ca2 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/who/GenesisOfTheDaleksTest.java @@ -0,0 +1,47 @@ +package org.mage.test.cards.single.who; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class GenesisOfTheDaleksTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.g.GenesisOfTheDaleks Genesis of the Daleks} {4}{B}{B} + * Enchantment — Saga + * I, II, III — Create a 3/3 black Dalek artifact creature token with menace for each lore counter on Genesis of the Daleks. + * IV — Target opponent faces a villainous choice — Destroy all Dalek creatures and each of your opponents loses life equal to the total power of Daleks that died this turn, or destroy all non-Dalek creatures. + */ + private static final String genesis = "Genesis of the Daleks"; + + @Test + public void test_SimplePlay() { + addCard(Zone.HAND, playerA, genesis, 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, genesis); + + checkPermanentCount("T1: after I", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Dalek Token", 1); + + // turn 3 + checkPermanentCount("T3: after II", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Dalek Token", 3); + + // turn 5 + checkPermanentCount("T5: after III", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, "Dalek Token", 6); + + // turn 7 + addTarget(playerA, playerB); + setChoice(playerB, true); // choose Destroy all Dalek creatures and each of your opponents loses life equal to the total power of Daleks that died this turn + + setStrictChooseMode(true); + setStopAt(7, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, 6); + assertLife(playerB, 20 - 18); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/who/TheDayOfTheDoctorTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/who/TheDayOfTheDoctorTest.java new file mode 100644 index 00000000000..1c2681074eb --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/who/TheDayOfTheDoctorTest.java @@ -0,0 +1,72 @@ +package org.mage.test.cards.single.who; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class TheDayOfTheDoctorTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.t.TheDayOfTheDoctor The Day of the Doctor} {3}{R}{W} + * Enchantment — Saga + * I, II, III — Exile cards from the top of your library until you exile a legendary card. You may play that card for as long as this Saga remains on the battlefield. Put the rest of those exiled cards on the bottom of your library in a random order. + * IV — Choose up to three Doctors. You may exile all other creatures. If you do, this Saga deals 13 damage to you. + */ + private static final String day = "The Day of the Doctor"; + + @Test + public void test_SimplePlay() { + skipInitShuffling(); + addCard(Zone.BATTLEFIELD, playerA, "Yawgmoth's Bargain"); // skip draw step + addCard(Zone.LIBRARY, playerA, "Academy Ruins"); + addCard(Zone.LIBRARY, playerA, "Arcade Gannon"); + addCard(Zone.LIBRARY, playerA, "Memnite", 4); + addCard(Zone.LIBRARY, playerA, "Thalia, Guardian of Thraben"); + addCard(Zone.BATTLEFIELD, playerA, "The Ninth Doctor"); // is a doctor + + addCard(Zone.HAND, playerA, day, 1); + + addCard(Zone.BATTLEFIELD, playerA, "Plateau", 3); + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, day); + + checkExileCount("after I, Thalia exiled", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Thalia, Guardian of Thraben", 1); + checkExileCount("after I, Arcade Gannon not exiled", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Arcade Gannon", 0); + checkExileCount("after I, Academy Ruins not exiled", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Academy Ruins", 0); + + // turn 3 + checkExileCount("after II, Thalia exiled", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Thalia, Guardian of Thraben", 1); + checkExileCount("after II, Arcade Gannon exiled", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Arcade Gannon", 1); + checkExileCount("after II, Academy Ruins not exiled", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Academy Ruins", 0); + + // turn 5 + waitStackResolved(5, PhaseStep.PRECOMBAT_MAIN); + checkExileCount("after III, Thalia exiled", 5, PhaseStep.PRECOMBAT_MAIN, playerA, "Thalia, Guardian of Thraben", 1); + checkExileCount("after III, Arcade Gannon exiled", 5, PhaseStep.PRECOMBAT_MAIN, playerA, "Arcade Gannon", 1); + checkExileCount("after III, Academy Ruins not exiled", 5, PhaseStep.PRECOMBAT_MAIN, playerA, "Academy Ruins", 1); + + playLand(5, PhaseStep.PRECOMBAT_MAIN, playerA, "Academy Ruins"); + castSpell(5, PhaseStep.PRECOMBAT_MAIN, playerA, "Thalia, Guardian of Thraben"); + checkPlayableAbility("after III: can play Arcade Gannon", 5, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast Arcade Gannon", true); + + // turn 7 + setChoice(playerA, "The Ninth Doctor"); + setChoice(playerA, true); // choose to exile others + checkPlayableAbility("after IV: can not play Arcade Gannon", 7, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast Arcade Gannon", false); + + setStrictChooseMode(true); + setStopAt(7, PhaseStep.END_TURN); + execute(); + + assertExileCount(playerA, "Thalia, Guardian of Thraben", 1); + assertPermanentCount(playerA, "The Ninth Doctor", 1); + assertPermanentCount(playerA, "Academy Ruins", 1); + assertExileCount(playerA, "Arcade Gannon", 1); + assertLife(playerA, 20 - 13); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/who/TheWarGamesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/who/TheWarGamesTest.java new file mode 100644 index 00000000000..7bddf646de1 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/who/TheWarGamesTest.java @@ -0,0 +1,107 @@ +package org.mage.test.cards.single.who; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class TheWarGamesTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.t.TheWarGames The War Games} {2}{W}{W} + * Enchantment — Saga + * I — Each player creates three tapped 1/1 white Warrior creature tokens. The tokens are goaded for as long as this Saga remains on the battlefield. + * II, III — Put a +1/+1 counter on each Warrior creature. + * IV — You may exile a nontoken creature you control. When you do, exile all Warriors. + */ + private static final String war = "The War Games"; + + @Ignore // TODO: goal of #11619 is to fix this nicely + @Test + public void test_SimplePlay_NoExile() { + addCard(Zone.HAND, playerA, war, 1); + + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, war); + + // no attack + + // turn 2 + // 3 1/1 must attack A + + // turn 3 + // 3 2/2 must attack B + + // turn 4 + // 3 2/2 must attack A + + // turn 5 + // 3 3/3 must attack B + + // turn 6 + // 3 3/3 must attack A + + // turn 7 + setChoice(playerA, false); + // no mandatory attack + + // turn 8 + // no mandatory attack + + setStrictChooseMode(true); + setStopAt(8, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Warrior Token", 3); + assertPermanentCount(playerB, "Warrior Token", 3); + assertLife(playerA, 20 - 3 - 6 - 9); + assertLife(playerB, 20 - 6 - 9); + } + + @Ignore // TODO: goal of #11619 is to fix this nicely + @Test + public void test_SimplePlay_Exile() { + addCard(Zone.HAND, playerA, war, 1); + + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.BATTLEFIELD, playerA, "Memnite"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, war); + + // no attack + + // turn 2 + // 3 1/1 must attack A + + // turn 3 + // 3 2/2 must attack B + + // turn 4 + // 3 2/2 must attack A + + // turn 5 + // 3 3/3 must attack B + + // turn 6 + // 3 3/3 must attack A + + // turn 7 + setChoice(playerA, true); + setChoice(playerA, "Memnite"); // exiling all Warriors + + setStrictChooseMode(true); + setStopAt(7, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Warrior Token", 0); + assertPermanentCount(playerB, "Warrior Token", 0); + assertExileCount(playerA, "Memnite", 1); + assertLife(playerA, 20 - 3 - 6 - 9); + assertLife(playerB, 20 - 6 - 9); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/who/TraverseEternityTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/who/TraverseEternityTest.java new file mode 100644 index 00000000000..a6c28a49974 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/who/TraverseEternityTest.java @@ -0,0 +1,58 @@ +package org.mage.test.cards.single.who; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class TraverseEternityTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.t.TraverseEternity Traverse Eternity} {2}{U}{U} + * Sorcery + * Draw cards equal to the greatest mana value among historic permanents you control. (Artifacts, legendaries, and Sagas are historic.) + */ + private static final String traverse = "Traverse Eternity"; + + @Test + public void test_greatest_2() { + addCard(Zone.HAND, playerA, traverse); + addCard(Zone.BATTLEFIELD, playerA, "Island", 5); + addCard(Zone.BATTLEFIELD, playerA, "Acolyte of Bahamut", 1); // {1}{G} + addCard(Zone.BATTLEFIELD, playerA, "Barbarian Horde", 1); // 3/3 {3}{R} + addCard(Zone.BATTLEFIELD, playerA, "Arwen Undomiel", 1); // {G}{U} -- historic + + addCard(Zone.BATTLEFIELD, playerB, "Barktooth Warbeard", 1); // 7 mv -- historic + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, traverse); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertHandCount(playerA, 2); + } + + @Test + public void test_greatest_3() { + addCard(Zone.HAND, playerA, traverse); + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + addCard(Zone.BATTLEFIELD, playerA, "Acolyte of Bahamut", 1); // {1}{G} + addCard(Zone.BATTLEFIELD, playerA, "Barbarian Horde", 1); // 3/3 {3}{R} + addCard(Zone.BATTLEFIELD, playerA, "Sword of Fire and Ice", 1); // {3} -- historic + addCard(Zone.BATTLEFIELD, playerA, "Isamaru, Hound of Konda", 1); // {W} -- historic + + addCard(Zone.BATTLEFIELD, playerB, "Barktooth Warbeard", 1); // 7 mv -- historic + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, traverse); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertHandCount(playerA, 3); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/who/TrialOfATimeLordTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/who/TrialOfATimeLordTest.java new file mode 100644 index 00000000000..09ae8e86c72 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/who/TrialOfATimeLordTest.java @@ -0,0 +1,65 @@ +package org.mage.test.cards.single.who; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class TrialOfATimeLordTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.t.TrialOfATimeLord Trial of a Time Lord} {1}{W}{W} + * Enchantment — Saga + * (As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.) + * I, II, III — Exile target nontoken creature an opponent controls until this Saga leaves the battlefield. + * IV — Starting with you, each player votes for innocent or guilty. If guilty gets more votes, the owner of each card exiled with this Saga puts that card on the bottom of their library. + */ + private static final String trial = "Trial of a Time Lord"; + + @Ignore // TODO: goal of #11619 is to fix this nicely + @Test + public void test_SimplePlay() { + addCard(Zone.HAND, playerA, trial, 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + addCard(Zone.BATTLEFIELD, playerB, "Memnite", 1); + addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears", 1); + addCard(Zone.BATTLEFIELD, playerB, "Bear Cub", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, trial); + addTarget(playerA, "Memnite"); + + checkExileCount("T1: Memnite exiled", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Memnite", 1); + checkExileCount("T1: Grizzly Bears not exiled", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Grizzly Bears", 0); + checkExileCount("T1: Bear Cub not exiled", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Bear Cub", 0); + + // turn 3 + addTarget(playerA, "Grizzly Bears"); + + checkExileCount("T3: Memnite exiled", 3, PhaseStep.POSTCOMBAT_MAIN, playerB, "Memnite", 1); + checkExileCount("T3: Grizzly Bears exiled", 3, PhaseStep.POSTCOMBAT_MAIN, playerB, "Grizzly Bears", 1); + checkExileCount("T3: Bear Cub not exiled", 3, PhaseStep.POSTCOMBAT_MAIN, playerB, "Bear Cub", 0); + + // turn 5 + addTarget(playerA, "Bear Cub"); + + checkExileCount("T5: Memnite exiled", 5, PhaseStep.POSTCOMBAT_MAIN, playerB, "Memnite", 1); + checkExileCount("T5: Grizzly Bears exiled", 5, PhaseStep.POSTCOMBAT_MAIN, playerB, "Grizzly Bears", 1); + checkExileCount("T5: Bear Cub exiled", 5, PhaseStep.POSTCOMBAT_MAIN, playerB, "Bear Cub", 1); + + // turn 7 + setChoice(playerA, true); // Innocent + setChoice(playerB, false); // Guilty + + setStrictChooseMode(true); + setStopAt(7, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerB, "Memnite", 1); + assertPermanentCount(playerB, "Grizzly Bears", 1); + assertPermanentCount(playerB, "Bear Cub", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/woe/AshiokWickedManipulatorTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/woe/AshiokWickedManipulatorTest.java index d20993bf27b..dc14f0f83a3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/woe/AshiokWickedManipulatorTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/woe/AshiokWickedManipulatorTest.java @@ -249,8 +249,7 @@ public class AshiokWickedManipulatorTest extends CardTestPlayerBase { activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "+1:"); addTarget(playerA, poet); // 2 tokens, so stacking trigger. - setChoice(playerA, "At the beginning of combat on your turn, if a card was put " - + "into exile this turn, put a +1/+1 counter on this creature."); + setChoice(playerA, "At the beginning of combat on your turn,"); setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); execute(); @@ -260,8 +259,7 @@ public class AshiokWickedManipulatorTest extends CardTestPlayerBase { activateAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "+1:"); addTarget(playerA, lion); // 2 tokens, so stacking trigger. - setChoice(playerA, "At the beginning of combat on your turn, if a card was put " - + "into exile this turn, put a +1/+1 counter on this creature."); + setChoice(playerA, "At the beginning of combat on your turn,"); setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); execute(); @@ -305,4 +303,4 @@ public class AshiokWickedManipulatorTest extends CardTestPlayerBase { assertExileCount(playerB, 2 + 3 + 3); } -} \ No newline at end of file +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/woe/TheAkroanWarTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/woe/TheAkroanWarTest.java new file mode 100644 index 00000000000..6774678952b --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/woe/TheAkroanWarTest.java @@ -0,0 +1,53 @@ +package org.mage.test.cards.single.woe; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class TheAkroanWarTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.t.TheAkroanWar The Akroan War} {3}{R} + * Enchantment — Saga + * I — Gain control of target creature for as long as this Saga remains on the battlefield. + * II — Until your next turn, creatures your opponents control attack each combat if able. + * III — Each tapped creature deals damage to itself equal to its power. + */ + private static final String war = "The Akroan War"; + + @Test + public void test_SimplePlay() { + addCard(Zone.HAND, playerA, war, 1); + addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears", 1); + addCard(Zone.BATTLEFIELD, playerB, "Memnite", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, war); + addTarget(playerA, "Grizzly Bears"); + + checkPermanentCount("after I, Bears under A control", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + checkPermanentCount("T3: Bears still in control ", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + checkPermanentCount("T3: Bears still in control ", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + checkPermanentCount("T4: Bears still in control ", 4, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + // turn 4 -- mandatory attack after II, so no need to declare + //attack(4, playerB, "Memnite", playerA); + + // turn 5 + checkPermanentCount("before III, Bears still in control ", 5, PhaseStep.UPKEEP, playerA, "Grizzly Bears", 1); + + setStrictChooseMode(true); + setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 1); + assertGraveyardCount(playerA, war, 1); + assertGraveyardCount(playerB, "Memnite", 1); + assertPermanentCount(playerB, "Grizzly Bears", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/woe/ThePrincessTakesFlightTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/woe/ThePrincessTakesFlightTest.java new file mode 100644 index 00000000000..e7b9d197590 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/woe/ThePrincessTakesFlightTest.java @@ -0,0 +1,85 @@ +package org.mage.test.cards.single.woe; + +import mage.abilities.keyword.FlyingAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class ThePrincessTakesFlightTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.t.ThePrincessTakesFlight The Princess Takes Flight} {2}{W} + * Enchantment — Saga + * I — Exile up to one target creature. + * II — Target creature you control gets +2/+2 and gains flying until end of turn. + * III — Return the exiled card to the battlefield under its owner’s control. + */ + private static final String flight = "The Princess Takes Flight"; + + @Ignore // TODO: goal of #11619 is to fix this nicely + @Test + public void test_SimplePlay() { + addCard(Zone.HAND, playerA, flight, 1); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 1); + addCard(Zone.BATTLEFIELD, playerB, "Memnite", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, flight); + addTarget(playerA, "Memnite"); + + checkExileCount("after I, exiled Memnite", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Memnite", 1); + + // turn 3 + addTarget(playerA, "Grizzly Bears"); + + checkPT("after II, 4/4 Bears", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Grizzly Bears", 2 + 2, 2 + 2); + checkAbility("after II, flying Bears", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, "Grizzly Bears", FlyingAbility.class, true); + checkExileCount("after II, still exiled Memnite", 3, PhaseStep.POSTCOMBAT_MAIN, playerB, "Memnite", 1); + + // boosts are temporary + checkPT("4: back to 2/2 Bears", 4, PhaseStep.POSTCOMBAT_MAIN, playerA, "Grizzly Bears", 2, 2); + checkAbility("4: back to non-flying Bears", 4, PhaseStep.POSTCOMBAT_MAIN, playerA, "Grizzly Bears", FlyingAbility.class, false); + + // turn 5 + + setStrictChooseMode(true); + setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertExileCount(playerB, "Memnite", 0); + assertPermanentCount(playerB, "Memnite", 1); + } + @Ignore // TODO: goal of #11619 is to fix this nicely + @Test + public void testFlicker() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.HAND, playerA, flight); + addCard(Zone.HAND, playerA, "Flicker of Fate"); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); + addCard(Zone.BATTLEFIELD, playerA, "Memnite"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, flight); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, 1); //Saga resolves, Bear exile on stack + addTarget(playerA, "Grizzly Bears"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flicker of Fate", flight); + addTarget(playerA, "Memnite"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, 2); //Flicker resolves, Memnite exile resolves + checkExileCount("Memnite exiled first", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Memnite", 1); + checkExileCount("Bear not yet exiled", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 0); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); //Bear exile resolves + checkExileCount("Bear exiled", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + setStrictChooseMode(true); + setStopAt(5, PhaseStep.END_TURN); + execute(); + + assertExileCount(playerA, "Grizzly Bears", 1); //Bear stays exiled + assertPermanentCount(playerA, "Memnite", 1); + assertGraveyardCount(playerA, flight, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/game/FreeformUnlimitedCommander/FreeformUnlimitedCommanderMatchTest.java b/Mage.Tests/src/test/java/org/mage/test/game/FreeformUnlimitedCommander/FreeformUnlimitedCommanderMatchTest.java index e40706dfe5d..25897120080 100644 --- a/Mage.Tests/src/test/java/org/mage/test/game/FreeformUnlimitedCommander/FreeformUnlimitedCommanderMatchTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/game/FreeformUnlimitedCommander/FreeformUnlimitedCommanderMatchTest.java @@ -15,8 +15,7 @@ public class FreeformUnlimitedCommanderMatchTest extends MageTestPlayerBase { MatchOptions options = new MatchOptions( "test name", "test match name", - false, - 2 + false ); // Act @@ -32,8 +31,7 @@ public class FreeformUnlimitedCommanderMatchTest extends MageTestPlayerBase { MatchOptions options = new MatchOptions( "test name", "test match name", - false, - 2 + false ); Match match = new FreeformUnlimitedCommanderMatch(options); @@ -50,8 +48,7 @@ public class FreeformUnlimitedCommanderMatchTest extends MageTestPlayerBase { MatchOptions options = new MatchOptions( "test name", "test match name", - false, - 2 + false ); Match match = new FreeformUnlimitedCommanderMatch(options); diff --git a/Mage.Tests/src/test/java/org/mage/test/load/LoadTest.java b/Mage.Tests/src/test/java/org/mage/test/load/LoadTest.java index 8fad6bac7ef..97b27a023ea 100644 --- a/Mage.Tests/src/test/java/org/mage/test/load/LoadTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/load/LoadTest.java @@ -578,7 +578,7 @@ public class LoadTest { } private MatchOptions createSimpleGameOptions(String gameName, GameTypeView gameTypeView, Session session, PlayerType playersType) { - MatchOptions options = new MatchOptions(gameName, gameTypeView.getName(), true, 2); + MatchOptions options = new MatchOptions(gameName, gameTypeView.getName(), true); options.getPlayerTypes().add(playersType); options.getPlayerTypes().add(playersType); diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index e79cc0bc719..80ce3115065 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -51,6 +51,7 @@ import mage.util.RandomUtil; import mage.watchers.common.AttackedOrBlockedThisCombatWatcher; import org.apache.log4j.Logger; import org.junit.Assert; +import static org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl.*; import java.io.Serializable; import java.util.*; @@ -58,8 +59,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; -import static org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl.*; - /** * Basic implementation of testable player * @@ -1512,7 +1511,7 @@ public class TestPlayer implements Player { private void assertPermanentCount(PlayerAction action, Game game, Player player, String permanentName, int count) { int foundCount = 0; - for (Permanent perm : game.getBattlefield().getAllPermanents()) { + for (Permanent perm : game.getBattlefield().getAllActivePermanents()) { if (hasObjectTargetNameOrAlias(perm, permanentName) && perm.getControllerId().equals(player.getId())) { foundCount++; } @@ -1528,7 +1527,7 @@ public class TestPlayer implements Player { private void assertPermanentTapped(PlayerAction action, Game game, Player player, String permanentName, boolean tapped, int count) { int foundCount = 0; - for (Permanent perm : game.getBattlefield().getAllPermanents()) { + for (Permanent perm : game.getBattlefield().getAllActivePermanents()) { if (hasObjectTargetNameOrAlias(perm, permanentName) && perm.getControllerId().equals(player.getId()) && perm.isTapped() == tapped) { @@ -1547,7 +1546,7 @@ public class TestPlayer implements Player { private void assertPermanentCounters(PlayerAction action, Game game, Player player, String permanentName, CounterType counterType, int count) { int foundCount = 0; - for (Permanent perm : game.getBattlefield().getAllPermanents()) { + for (Permanent perm : game.getBattlefield().getAllActivePermanents()) { if (hasObjectTargetNameOrAlias(perm, permanentName) && perm.getControllerId().equals(player.getId())) { foundCount = perm.getCounters(game).getCount(counterType); } @@ -3135,13 +3134,13 @@ public class TestPlayer implements Player { } @Override - public void setGameUnderYourControl(boolean value) { - computerPlayer.setGameUnderYourControl(value); + public void setGameUnderYourControl(Game game, boolean value) { + computerPlayer.setGameUnderYourControl(game, value); } @Override - public void setGameUnderYourControl(boolean value, boolean fullRestore) { - computerPlayer.setGameUnderYourControl(value, fullRestore); + public void setGameUnderYourControl(Game game, boolean value, boolean fullRestore) { + computerPlayer.setGameUnderYourControl(game, value, fullRestore); } @Override @@ -3240,8 +3239,8 @@ public class TestPlayer implements Player { } @Override - public boolean putCardsOnBottomOfLibrary(Card card, Game game, Ability source, boolean anyOrder) { - return computerPlayer.putCardsOnBottomOfLibrary(card, game, source, anyOrder); + public boolean putCardsOnBottomOfLibrary(Card card, Game game, Ability source) { + return computerPlayer.putCardsOnBottomOfLibrary(card, game, source); } @Override @@ -3794,6 +3793,11 @@ public class TestPlayer implements Player { return computerPlayer.flipCoin(source, game, true); } + @Override + public List flipCoins(Ability source, Game game, int amount, boolean winnable) { + return computerPlayer.flipCoins(source, game, amount, winnable); + } + @Override public boolean flipCoinResult(Game game) { assertAliasSupportInChoices(false); diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index 014e2fb865e..a0c6cf587e1 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -178,8 +178,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } // prepare fake match (needs for testing some client-server code) - // always 4 seats - MatchOptions matchOptions = new MatchOptions("test match", "test game type", true, 4); + MatchOptions matchOptions = new MatchOptions("test match", "test game type", true); currentMatch = new FreeForAllMatch(matchOptions); currentGame = createNewGameAndPlayers(); diff --git a/Mage.Verify/src/main/java/mage/verify/mtgjson/SpellBookCard.java b/Mage.Verify/src/main/java/mage/verify/mtgjson/SpellBookCard.java new file mode 100644 index 00000000000..319a3e24ec7 --- /dev/null +++ b/Mage.Verify/src/main/java/mage/verify/mtgjson/SpellBookCard.java @@ -0,0 +1,13 @@ +package mage.verify.mtgjson; + +/** + * Commanders Spellbook api: card class + *

    + * API docs here + * + * @author JayDi85 + */ +public final class SpellBookCard { + public int id; + public String name; +} diff --git a/Mage.Verify/src/main/java/mage/verify/mtgjson/SpellBookCardsPage.java b/Mage.Verify/src/main/java/mage/verify/mtgjson/SpellBookCardsPage.java new file mode 100644 index 00000000000..70ad102054f --- /dev/null +++ b/Mage.Verify/src/main/java/mage/verify/mtgjson/SpellBookCardsPage.java @@ -0,0 +1,17 @@ +package mage.verify.mtgjson; + +import java.util.List; + +/** + * Commanders Spellbook api: page class + *

    + * API docs here + * + * @author JayDi85 + */ +public final class SpellBookCardsPage { + public int count; + public String next; // can be null on last page + public String previous; + public List results; // empty on too big page +} diff --git a/Mage.Verify/src/main/java/mage/verify/mtgjson/SpellBookCombo.java b/Mage.Verify/src/main/java/mage/verify/mtgjson/SpellBookCombo.java new file mode 100644 index 00000000000..f78a8781542 --- /dev/null +++ b/Mage.Verify/src/main/java/mage/verify/mtgjson/SpellBookCombo.java @@ -0,0 +1,17 @@ +package mage.verify.mtgjson; + +import java.util.List; + +/** + * Commanders Spellbook api: full combo + *

    + * API docs here + * + * @author JayDi85 + */ +public final class SpellBookCombo { + public String id; // combo id + public List uses; // combo parts + public String bracketTag; // can be useful + public String description; +} diff --git a/Mage.Verify/src/main/java/mage/verify/mtgjson/SpellBookComboPart.java b/Mage.Verify/src/main/java/mage/verify/mtgjson/SpellBookComboPart.java new file mode 100644 index 00000000000..8cdaca9429c --- /dev/null +++ b/Mage.Verify/src/main/java/mage/verify/mtgjson/SpellBookComboPart.java @@ -0,0 +1,13 @@ +package mage.verify.mtgjson; + +/** + * Commanders Spellbook api: combo part (one card info) + *

    + * API docs here + * + * @author JayDi85 + */ +public final class SpellBookComboPart { + public SpellBookCard card; + public int quantity; +} diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java index e8563d8e838..9c9afcfc5bd 100644 --- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java +++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java @@ -1,6 +1,7 @@ package mage.verify; import com.google.common.base.CharMatcher; +import com.google.gson.Gson; import mage.MageObject; import mage.Mana; import mage.ObjectColor; @@ -24,14 +25,18 @@ import mage.abilities.hint.common.MonarchHint; import mage.abilities.keyword.*; import mage.cards.*; import mage.cards.decks.CardNameUtil; +import mage.cards.decks.Deck; +import mage.cards.decks.DeckCardInfo; import mage.cards.decks.DeckCardLists; import mage.cards.decks.importer.DeckImporter; import mage.cards.repository.*; import mage.choices.Choice; +import mage.client.remote.XmageURLConnection; import mage.constants.*; import mage.filter.Filter; import mage.filter.predicate.Predicate; import mage.filter.predicate.Predicates; +import mage.game.Game; import mage.game.command.Dungeon; import mage.game.command.Plane; import mage.game.draft.DraftCube; @@ -40,13 +45,17 @@ import mage.game.permanent.token.TokenImpl; import mage.game.permanent.token.custom.CreatureToken; import mage.game.permanent.token.custom.XmageToken; import mage.sets.TherosBeyondDeath; +import mage.target.Target; import mage.target.targetpointer.TargetPointer; +import mage.tournament.cubes.CubeFromDeck; import mage.util.CardUtil; import mage.utils.SystemUtil; import mage.verify.mtgjson.MtgJsonCard; import mage.verify.mtgjson.MtgJsonService; import mage.verify.mtgjson.MtgJsonSet; +import mage.verify.mtgjson.SpellBookCardsPage; import mage.watchers.Watcher; +import net.java.truevfs.access.TFile; import org.apache.log4j.Logger; import org.junit.Assert; import org.junit.Ignore; @@ -54,10 +63,14 @@ import org.junit.Test; import org.mage.plugins.card.dl.sources.ScryfallImageSupportCards; import org.reflections.Reflections; +import java.io.File; import java.io.IOException; import java.lang.reflect.*; +import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -70,7 +83,7 @@ public class VerifyCardDataTest { private static final Logger logger = Logger.getLogger(VerifyCardDataTest.class); - private static final String FULL_ABILITIES_CHECK_SET_CODES = "WHO"; // check ability text due mtgjson, can use multiple sets like MAT;CMD or * for all + private static final String FULL_ABILITIES_CHECK_SET_CODES = "BLC"; // check ability text due mtgjson, can use multiple sets like MAT;CMD or * for all private static final boolean CHECK_ONLY_ABILITIES_TEXT = false; // use when checking text locally, suppresses unnecessary checks and output messages private static final boolean CHECK_COPYABLE_FIELDS = true; // disable for better verify test performance @@ -132,6 +145,7 @@ public class VerifyCardDataTest { // color // skipListAddName(SKIP_LIST_COLOR, set, cardName); + skipListAddName(SKIP_LIST_COLOR, "FIN", "Summon: Alexander"); // cost // skipListAddName(SKIP_LIST_COST, set, cardName); @@ -307,9 +321,6 @@ public class VerifyCardDataTest { check(((CardWithHalves) card).getLeftHalfCard(), cardIndex); check(((CardWithHalves) card).getRightHalfCard(), cardIndex); } else if (card instanceof CardWithSpellOption) { - if (card.isLand()) { // temporary until scryfall fixes the mana cost issue for adventure lands - continue; - } check(card, cardIndex); check(((CardWithSpellOption) card).getSpellCard(), cardIndex); } else { @@ -1049,7 +1060,7 @@ public class VerifyCardDataTest { // CHECK: miss booster settings Set ignoreBoosterSets = new HashSet<>(); // temporary, TODO: remove after set release and mtgjson get info - ignoreBoosterSets.add("Innistrad Remastered"); + ignoreBoosterSets.add("Final Fantasy"); // jumpstart, TODO: must implement from JumpstartPoolGenerator, see #13264 ignoreBoosterSets.add("Jumpstart"); ignoreBoosterSets.add("Jumpstart 2022"); @@ -1065,7 +1076,13 @@ public class VerifyCardDataTest { ignoreBoosterSets.add("Zendikar Rising Expeditions"); // box toppers ignoreBoosterSets.add("March of the Machine: The Aftermath"); // epilogue boosters aren't for draft + // make sure mtgjson has booster data + boolean hasBoostersInfo = MtgJsonService.sets().values().stream().anyMatch(s -> s.booster != null && !s.booster.isEmpty()); for (ExpansionSet set : sets) { + if (!hasBoostersInfo) { + System.out.println("Warning, mtgjson data lost boosters info"); + break; + } MtgJsonSet jsonSet = MtgJsonService.sets().getOrDefault(set.getCode().toUpperCase(Locale.ENGLISH), null); if (jsonSet == null) { continue; @@ -1122,9 +1139,6 @@ public class VerifyCardDataTest { } for (ExpansionSet.SetCardInfo card : set.getSetCardInfo()) { - if (OmenCard.class.isAssignableFrom(card.getCardClass())) { // temporary until mtgjson fixed - continue; - } boolean cardHaveDoubleName = (doubleNames.getOrDefault(card.getName(), 0) > 1); boolean cardHaveVariousSetting = card.getGraphicInfo() != null && card.getGraphicInfo().getUsesVariousArt(); @@ -1722,13 +1736,17 @@ public class VerifyCardDataTest { return; } + // TODO: temporary fix - scryfall/mtgjson wrongly add [colors, mana cost] from spell part to main part/card, + // example: Ishgard, the Holy See // Faith & Grief + if (card instanceof CardWithSpellOption && card.isLand() + || card instanceof SpellOptionCard && ((SpellOptionCard) card).getParentCard().isLand()) { + return; + } + Set expected = new HashSet<>(); if (ref.colors != null) { expected.addAll(ref.colors); } - if (card.isFlipCard()) { - expected.addAll(ref.colorIdentity); - } ObjectColor color = card.getColor(null); @@ -2167,7 +2185,7 @@ public class VerifyCardDataTest { // - it's can be a keyword action (only mtg rules contains a target word), so add it to the targetedKeywords // * on "must be targeted": // - TODO: enable and research checkMissTargeted - too much errors with it (is it possible to use that checks?) - boolean checkMissNonTargeted = !(card instanceof OmenCard); // must set withNotTarget(true) temporarily set to ignore omen cards + boolean checkMissNonTargeted = true; // must set withNotTarget(true) boolean checkMissTargeted = false; // must be targeted List targetedKeywords = Arrays.asList( "target", @@ -2177,9 +2195,10 @@ public class VerifyCardDataTest { "modular", "partner" ); - // card can contain rules text from both sides, so must search ref card for all sides too + // xmage card can contain rules text from both sides, so must search ref card for all sides too String additionalName; - if (card instanceof AdventureCard) { // temporary to prevent failure due to upstream error + if (card instanceof CardWithSpellOption) { + // adventure/omen cards additionalName = ((CardWithSpellOption) card).getSpellCard().getName(); } else if (card.isTransformable() && !card.isNightCard()) { additionalName = card.getSecondCardFace().getName(); @@ -2197,6 +2216,7 @@ public class VerifyCardDataTest { } } } + boolean needTargetedAbility = targetedKeywords.stream().anyMatch(refLowerText::contains); boolean foundTargetedAbility = card.getAbilities() .stream() @@ -2268,6 +2288,19 @@ public class VerifyCardDataTest { } }); + // special check: target tags must be used for all targets and starts with 1 + card.getAbilities().stream().filter(a -> a.getTargets().size() >= 2).forEach(a -> { + Set tags = a.getTargets().stream().map(Target::getTargetTag).collect(Collectors.toSet()); + if (tags.size() == 1 && tags.stream().findFirst().get().equals(0)) { + // no tags usage + return; + } + if (tags.size() != a.getTargets().size() || (tags.size() > 1 && tags.contains(0))) { + // how-to fix: make sure each target has it's own target tag like 1 and 2 (don't use 0 because it's default) + fail(card, "abilities", "wrong target tags: miss tag in one of the targets, current list: " + tags); + } + }); + // spells have only 1 ability if (card.isInstantOrSorcery()) { return; @@ -2313,7 +2346,81 @@ public class VerifyCardDataTest { // TODO: add legality checks (by sets and cards, by banned) } - private String prepareRule(String cardName, String rule) { + private static final List selfRefNamedSubtypes = Arrays.asList( + SubType.EQUIPMENT, + SubType.VEHICLE, + SubType.AURA, + SubType.CLASS, + SubType.SAGA, + SubType.SIEGE + ); + + private static String applySelfReference(String rule, MageObject mageObject, Game game) { + return rule + .replace("{this}", getCardSelfReference(mageObject, game)) + .replace(". this", ". This") + .replace("\nthis", "\nThis") + .replace("-this", "-This") + .replace(": this", ": This") + .replace("&bull this", "&bull This") + .replace("— this", "— This") + .replace("—this", "—This") + .replace("- this", "- This"); + } + + /** + * Returns the description of the card for rules text that references itself. + * Applies to abilities that function only on the battlefield. + * Does not account for every exception. + * Details here + */ + private static String getCardSelfReference(MageObject mageObject, Game game) { + if (!mageObject.isPermanent(game)) { + return mageObject.getName(); + } + if (mageObject.isPlaneswalker(game)) { + // try to ref by planeswalker name which is subtype + return mageObject + .getSubtype(game) + .stream() + .filter(SubType.getPlaneswalkerTypes()::contains) + .findFirst() + .map(SubType::getDescription) + .orElse(mageObject.getName()); + } + if (mageObject.isLegendary(game)) { + // Generate shortname for legendary permanent (other than planeswalker) + List parts = Arrays.asList(mageObject.getName().split("(, | of the )")); + if (parts.size() > 1) { + return parts.get(0); + } else { + return mageObject.getName(); + } + } + if (mageObject.isCreature(game)) { + return "this creature"; + } + if (mageObject.isLand(game)) { + return "this land"; + } + for (SubType subType : selfRefNamedSubtypes) { + if (mageObject.hasSubtype(subType, game)) { + return "this " + subType.getDescription(); + } + } + if (mageObject.isBattle(game)) { + return "this battle"; + } + if (mageObject.isEnchantment(game)) { + return "this enchantment"; + } + if (mageObject.isArtifact(game)) { + return "this artifact"; + } + return "this permanent"; + } + + private String prepareRule(Card card, String rule) { // remove and optimize rule text for analyze String newRule = rule; @@ -2329,8 +2436,7 @@ public class VerifyCardDataTest { } // replace special text and symbols - newRule = newRule - .replace("{this}", cardName) + newRule = applySelfReference(newRule, card, null) .replace("−", "-") .replace("—", "-") .replace("—", "-"); @@ -2342,7 +2448,7 @@ public class VerifyCardDataTest { newRule = CardNameUtil.normalizeCardName(newRule); - return newRule.trim(); + return CardUtil.getTextWithFirstCharUpperCase(newRule.trim()); } @Test @@ -2575,7 +2681,7 @@ public class VerifyCardDataTest { String[] refRules = refText.split("[\\$\\\n]"); // ref card's abilities can be splited by \n or $ chars for (int i = 0; i < refRules.length; i++) { - refRules[i] = prepareRule(card.getName(), refRules[i]); + refRules[i] = prepareRule(card, refRules[i]); } if (ref.subtypes.contains("Adventure")) { @@ -2608,7 +2714,7 @@ public class VerifyCardDataTest { .replace("", "") .split("[\\$\\\n]"); for (int i = 0; i < cardRules.length; i++) { - cardRules[i] = prepareRule(card.getName(), cardRules[i]); + cardRules[i] = prepareRule(card, cardRules[i]); } boolean isFine = true; @@ -2876,6 +2982,13 @@ public class VerifyCardDataTest { return; } + // TODO: temporary fix - scryfall/mtgjson wrongly add [colors, mana cost] from spell part to main part/card, + // example: Ishgard, the Holy See // Faith & Grief + if (card instanceof CardWithSpellOption && card.isLand() + || card instanceof SpellOptionCard && ((SpellOptionCard) card).getParentCard().isLand()) { + return; + } + String expected = ref.manaCost; String cost = String.join("", card.getManaCostSymbols()); if (cost.isEmpty()) { @@ -3002,7 +3115,7 @@ public class VerifyCardDataTest { } @Test - public void test_checkCardsInCubes() { + public void test_checkCardsInCubes() throws Exception { Reflections reflections = new Reflections("mage.tournament.cubes."); Set> cubesList = reflections.getSubTypesOf(DraftCube.class); Assert.assertFalse("Can't find any cubes", cubesList.isEmpty()); @@ -3015,7 +3128,24 @@ public class VerifyCardDataTest { continue; } - DraftCube cube = (DraftCube) createNewObject(cubeClass); + // cube from deck must use real deck to check complete + DraftCube cube; + if (cubeClass.isAssignableFrom(CubeFromDeck.class)) { + DeckCardLists deckCardLists = new DeckCardLists(); + deckCardLists.setCards(Arrays.asList( + new DeckCardInfo("Adanto Vanguard", "1", "XLN"), + new DeckCardInfo("Boneyard Parley", "94", "XLN"), + new DeckCardInfo("Forest", "276", "XLN") + )); + Deck deck = Deck.load(deckCardLists, true); + Constructor con = cubeClass.getConstructor(Deck.class); + cube = (DraftCube) con.newInstance(deck); + Assert.assertFalse(deckCardLists.getCards().isEmpty()); + Assert.assertEquals("deck must be loaded to cube", cube.getCubeCards().size(), deckCardLists.getCards().size()); + Assert.assertTrue("cube's name must contains cards count", cube.getName().contains(deckCardLists.getCards().size() + " cards")); + } else { + cube = (DraftCube) createNewObject(cubeClass); + } if (cube.getCubeCards().isEmpty()) { errorsList.add("Error: broken cube, empty cards list: " + cube.getClass().getCanonicalName()); } @@ -3133,4 +3263,79 @@ public class VerifyCardDataTest { + "%29&order=set&as=grid&unique=cards" ); } -} + + /** + * Helper test to download infinite combos data and prepare it to use in Commander Brackets score system + *

    + * How-to use: run it before each main release and upload updated files to github + */ + @Ignore + @Test + public void downloadAndPrepareCommanderBracketsData() { + // download data + // load data by pages, max limit = 100, no needs to setup it, ~2000 combos or 20 pages + String nextUrl = "https://backend.commanderspellbook.com/variants?format=json&group_by_combo=true&ordering=created&q=cards%3D2+result%3Ainfinite"; + SpellBookCardsPage page; + int pageNumber = 0; + List allCombos = new ArrayList<>(); + while (nextUrl != null && !nextUrl.isEmpty()) { + pageNumber++; + System.out.println("downloading page " + pageNumber); + String res = XmageURLConnection.downloadText(nextUrl); + page = new Gson().fromJson(res, SpellBookCardsPage.class); + if (page == null || page.results == null) { + System.out.println("ERROR, unknown data format: " + res.substring(0, 10) + "..."); + return; + } + page.results.forEach(combo -> { + if (combo.uses.isEmpty()) { + System.out.println("wrong combo uses: " + combo.uses); + return; + } + // Stella Lee, Wild Card - can generate infinite combo by itself, so allow 1 card + String card1 = combo.uses.get(0).card.name; + String card2 = combo.uses.size() == 1 ? card1 : combo.uses.get(1).card.name; + if (card1 != null && card2 != null) { + allCombos.add(String.format("%s@%s", card1, card2)); + } + }); + + nextUrl = page.next; + } + + // save results (run from Mage.Verify) + File destFile = new TFile("..\\Mage\\src\\main\\resources\\brackets\\infinite-combos.txt"); + if (!destFile.exists()) { + System.out.println("Can't find dest file " + destFile); + return; + } + + List infiniteCombosRes; + try { + infiniteCombosRes = Files.readAllLines(destFile.toPath(), StandardCharsets.UTF_8); + } catch (IOException e) { + System.out.println("Can't read file " + destFile + " " + e); + return; + } + + // replace all cards data, but keep comments + infiniteCombosRes.removeIf(s -> !s.startsWith("#") + || s.startsWith("# Source") + || s.startsWith("# Updated") + || s.startsWith("# Found") + ); + infiniteCombosRes.add(String.format("# Source: %s", "https://commanderspellbook.com")); + infiniteCombosRes.add(String.format("# Updated: %s", LocalDate.now().format(DateTimeFormatter.ISO_DATE))); + infiniteCombosRes.add(String.format("# Found: %d", allCombos.size())); + infiniteCombosRes.addAll(allCombos); // do not sort, all new combos will be added to the end due spell book default order + + try { + Files.write(destFile.toPath(), infiniteCombosRes, StandardCharsets.UTF_8); + } catch (IOException e) { + System.out.println("Can't write file " + destFile + " " + e); + return; + } + + System.out.println(String.format("ALL DONE, found and write %d combos", allCombos.size())); + } +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/MageIdentifier.java b/Mage/src/main/java/mage/MageIdentifier.java index fd60b2e5524..a4fc6d9cc7a 100644 --- a/Mage/src/main/java/mage/MageIdentifier.java +++ b/Mage/src/main/java/mage/MageIdentifier.java @@ -19,7 +19,8 @@ public enum MageIdentifier { // e.g. [[Johann, Apprentice Sorcerer]] // "Once each turn, you may cast an instant or sorcery spell from the top of your library." // - CastFromGraveyardOnceWatcher, + OnceOnYourTurnCastFromGraveyard, + OnceOnYourTurnCastFromGraveyardEntersTapped, OnceEachTurnCastWatcher, HaukensInsightWatcher, IntrepidPaleontologistWatcher, @@ -36,6 +37,7 @@ public enum MageIdentifier { LaraCroftTombRaiderWatcher, CoramTheUndertakerWatcher, ThundermanDragonWatcher, + LockeTreasureHunterWatcher, // ----------------------------// // alternate casts // @@ -63,6 +65,7 @@ public enum MageIdentifier { IntoThePitAlternateCast, MaestrosAscendencyAlternateCast, NashiMoonSagesScionAlternateCast, + NoctisPrinceOfLucisAlternateCast, OsteomancerAdeptAlternateCast, RafinnesGuidanceAlternateCast, RonaSheoldredsFaithfulAlternateCast, @@ -80,6 +83,7 @@ public enum MageIdentifier { PrimalPrayersAlternateCast, QuilledGreatwurmAlternateCast, WickerfolkIndomitableAlternateCast, + UriangerAugureltAlternateCast, ValgavothTerrorEaterAlternateCast; /** diff --git a/Mage/src/main/java/mage/MageObject.java b/Mage/src/main/java/mage/MageObject.java index 660759e8ab5..4fdc1aa7fcd 100644 --- a/Mage/src/main/java/mage/MageObject.java +++ b/Mage/src/main/java/mage/MageObject.java @@ -77,16 +77,12 @@ public interface MageObject extends MageItem, Serializable, Copyable /** * Return original object's subtypes - * - * @return */ SubTypes getSubtype(); /** - * Return full subtypes list (from object, from effects) - * - * @param game - * @return + * Return full subtypes list (from object, from effects). + * Don't use this to check if an object has a subtype, use hasSubtype for that which accounts for changelings */ SubTypes getSubtype(Game game); diff --git a/Mage/src/main/java/mage/abilities/Ability.java b/Mage/src/main/java/mage/abilities/Ability.java index 512da5e120a..31dee95d241 100644 --- a/Mage/src/main/java/mage/abilities/Ability.java +++ b/Mage/src/main/java/mage/abilities/Ability.java @@ -474,6 +474,11 @@ public interface Ability extends Controllable, Serializable { */ Ability withFlavorWord(String flavorWord); + /** + * Gets rule prefix for text generation + */ + String addRulePrefix(String rule); + /** * Sets flavor word for first mode */ @@ -499,6 +504,17 @@ public interface Ability extends Controllable, Serializable { MageObject getSourceObject(Game game); void setSourceObjectZoneChangeCounter(int zoneChangeCounter); + /** + * Initializes the internally stored Source Object ZCC value + * to be equal to the source object's current ZCC. + *

    + * If the source is an entering permanent, then + * the ZCC is set as if the permanent had already entered the battlefield. + * + * @param game + * @param force Update only occurs if stored ZCC is zero or if force is true. + */ + void initSourceObjectZoneChangeCounter(Game game, boolean force); int getSourceObjectZoneChangeCounter(); @@ -534,6 +550,10 @@ public interface Ability extends Controllable, Serializable { boolean canFizzle(); + Ability withCanBeCopied(boolean canBeCopied); + + boolean canBeCopied(); + /** * Adds a target adjuster to this ability. * If using a generic adjuster, only use after adding the blueprint target! diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index 1b8223083ba..ad735b98e74 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -83,6 +83,7 @@ public abstract class AbilityImpl implements Ability { private List watchers = new ArrayList<>(); // access to it by GetWatchers only (it can be overridden by some abilities) private List subAbilities = null; private boolean canFizzle = true; // for Gilded Drake + private boolean canBeCopied = true; private TargetAdjuster targetAdjuster = null; private CostAdjuster costAdjuster = null; private List hints = new ArrayList<>(); @@ -129,6 +130,7 @@ public abstract class AbilityImpl implements Ability { this.flavorWord = ability.flavorWord; this.sourceObjectZoneChangeCounter = ability.sourceObjectZoneChangeCounter; this.canFizzle = ability.canFizzle; + this.canBeCopied = ability.canBeCopied; this.targetAdjuster = ability.targetAdjuster; this.costAdjuster = ability.costAdjuster; this.hints = CardUtil.deepCopyObject(ability.hints); @@ -270,9 +272,7 @@ public abstract class AbilityImpl implements Ability { game.applyEffects(); MageObject sourceObject = getSourceObject(game); - if (getSourceObjectZoneChangeCounter() == 0) { - setSourceObjectZoneChangeCounter(game.getState().getZoneChangeCounter(getSourceId())); - } + initSourceObjectZoneChangeCounter(game, false); setSourcePermanentTransformCount(game); // if ability can be cast for no mana, clear the mana costs now, because additional mana costs must be paid. @@ -376,7 +376,7 @@ public abstract class AbilityImpl implements Ability { // unit tests only: it allows to add targets/choices by two ways: // 1. From cast/activate command params (process it here) - // 2. From single addTarget/setChoice, it's a preffered method for tests (process it in normal choose dialogs like human player) + // 2. From single addTarget/setChoice, it's a preferred method for tests (process it in normal choose dialogs like human player) if (controller.isTestsMode()) { if (!controller.addTargets(this, game)) { return false; @@ -1016,38 +1016,28 @@ public abstract class AbilityImpl implements Ability { String ruleStart = sbRule.toString(); String text = getModes().getText(); - String rule; + StringBuilder rule = new StringBuilder(); if (!text.isEmpty()) { if (ruleStart.length() > 1) { String end = ruleStart.substring(ruleStart.length() - 2).trim(); if (end.isEmpty() || end.equals(":") || end.equals(".")) { - rule = ruleStart + CardUtil.getTextWithFirstCharUpperCase(text); + rule.append(ruleStart + CardUtil.getTextWithFirstCharUpperCase(text)); } else { - rule = ruleStart + text; + rule.append(ruleStart + text); } } else { - rule = ruleStart + text; + rule.append(ruleStart + text); } } else { - rule = ruleStart; - } - String prefix; - if (this instanceof TriggeredAbility || this instanceof EntersBattlefieldAbility) { - prefix = null; - } else if (abilityWord != null) { - prefix = abilityWord.formatWord(); - } else if (flavorWord != null) { - prefix = CardUtil.italicizeWithEmDash(flavorWord); - } else { - prefix = null; - } - if (prefix != null) { - rule = prefix + CardUtil.getTextWithFirstCharUpperCase(rule); + rule.append(ruleStart); } if (appendToRule != null) { - rule = rule.concat(appendToRule); + rule.append(appendToRule); } - return rule; + if (this instanceof TriggeredAbility || this instanceof EntersBattlefieldAbility) { + return rule.toString(); + } + return addRulePrefix(rule.toString()); } @Override @@ -1477,6 +1467,17 @@ public abstract class AbilityImpl implements Ability { return this; } + @Override + public String addRulePrefix(String rule) { + if (abilityWord != null) { + return abilityWord.formatWord() + CardUtil.getTextWithFirstCharUpperCase(rule); + } else if (flavorWord != null) { + return CardUtil.italicizeWithEmDash(flavorWord) + CardUtil.getTextWithFirstCharUpperCase(rule); + } else { + return rule; + } + } + @Override public Ability withFirstModeFlavorWord(String flavorWord) { this.modes.getMode().withFlavorWord(flavorWord); @@ -1661,7 +1662,7 @@ public abstract class AbilityImpl implements Ability { @Override public MageObject getSourceObjectIfItStillExists(Game game) { if (getSourceObjectZoneChangeCounter() == 0 - || getSourceObjectZoneChangeCounter() == game.getState().getZoneChangeCounter(getSourceId())) { + || getSourceObjectZoneChangeCounter() == getCurrentSourceObjectZoneChangeCounter(game)) { // exists or lki from battlefield return game.getObject(getSourceId()); } @@ -1700,6 +1701,27 @@ public abstract class AbilityImpl implements Ability { this.sourceObjectZoneChangeCounter = sourceObjectZoneChangeCounter; } + @Override + public void initSourceObjectZoneChangeCounter(Game game, boolean force) { + if (!(this instanceof MageSingleton) && (force || sourceObjectZoneChangeCounter == 0 )) { + setSourceObjectZoneChangeCounter(getCurrentSourceObjectZoneChangeCounter(game)); + } + } + + private int getCurrentSourceObjectZoneChangeCounter(Game game){ + int zcc = game.getState().getZoneChangeCounter(getSourceId()); + // TODO: Enable this, #13710 + /*if (game.getPermanentEntering(getSourceId()) != null){ + // If the triggered ability triggered while the permanent is entering the battlefield + // then add 1 zcc so that it triggers as if the permanent was already on the battlefield + // So "Enters with counters" causes "Whenever counters are placed" to trigger with battlefield zcc + // Particularly relevant for Sagas, which always involve both + // Note that this does NOT apply to "As ~ ETB" effects, those still use the stack zcc + zcc += 1; + }*/ + return zcc; + } + @Override public int getSourceObjectZoneChangeCounter() { return sourceObjectZoneChangeCounter; @@ -1733,8 +1755,23 @@ public abstract class AbilityImpl implements Ability { this.canFizzle = canFizzle; } + @Override + public boolean canBeCopied() { + return canBeCopied; + } + + @Override + public Ability withCanBeCopied(boolean canBeCopied) { + this.canBeCopied = canBeCopied; + return this; + } + @Override public AbilityImpl setTargetAdjuster(TargetAdjuster targetAdjuster) { + if (targetAdjuster == null) { + this.targetAdjuster = null; + return this; + } if (targetAdjuster instanceof GenericTargetAdjuster && this.getTargets().isEmpty()) { throw new IllegalStateException("Target adjuster being added but no targets are set!"); } diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbility.java b/Mage/src/main/java/mage/abilities/TriggeredAbility.java index 24266aeaaad..37785373766 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbility.java @@ -3,7 +3,9 @@ package mage.abilities; import mage.abilities.condition.Condition; import mage.game.Game; import mage.game.events.GameEvent; +import mage.util.CardUtil; +import java.util.Optional; import java.util.UUID; /** @@ -66,13 +68,36 @@ public interface TriggeredAbility extends Ability { */ TriggeredAbility withRuleTextReplacement(boolean replaceRuleText); + /** + * 603.4. A triggered ability may read "When/Whenever/At [trigger event], if [condition], [effect]." + * When the trigger event occurs, the ability checks whether the stated condition is true. + * The ability triggers only if it is; otherwise it does nothing. If the ability triggers, + * it checks the stated condition again as it resolves. If the condition isn't true at that time, + * the ability is removed from the stack and does nothing. Note that this mirrors the check for legal targets. + * This rule is referred to as the "intervening 'if' clause" rule. + * (The word "if" has only its normal English meaning anywhere else in the text of a card; + * this rule only applies to an "if" that immediately follows a trigger condition.) + * + * @param condition the condition to be checked + * @return + */ TriggeredAbility withInterveningIf(Condition condition); boolean checkInterveningIfClause(Game game); - boolean isOptional(); + /** + * Unlike intervening if, this is for a condition that's checked only on trigger and not also on resolution. + * + * @param condition the condition to be checked + * @return + */ + TriggeredAbility withTriggerCondition(Condition condition); - TriggeredAbility setOptional(); + Condition getTriggerCondition(); + + boolean checkTriggerCondition(Game game); + + boolean isOptional(); /** * Allow trigger to fire after source leave the battlefield (example: will use LKI on itself sacrifice) @@ -106,4 +131,34 @@ public interface TriggeredAbility extends Ability { TriggeredAbility setTriggerPhrase(String triggerPhrase); String getTriggerPhrase(); + + static String makeDidThisTurnString(Ability ability, Game game) { + return CardUtil.getCardZoneString("lastTurnUsed" + ability.getOriginalId(), ability.getSourceId(), game); + } + + static void setDidThisTurn(Ability ability, Game game) { + game.getState().setValue(makeDidThisTurnString(ability, game), game.getTurnNum()); + } + + /** + * For abilities which say "Do this only once each turn". + * Most of the time this is handled automatically by calling setDoOnlyOnceEachTurn(true), + * but sometimes the ability will need a way to clear whether it's been used this turn within an effect. + * + * @param ability + * @param game + */ + static void clearDidThisTurn(Ability ability, Game game) { + game.getState().removeValue(makeDidThisTurnString(ability, game)); + } + + static boolean checkDidThisTurn(Ability ability, Game game) { + return Optional + .ofNullable(makeDidThisTurnString(ability, game)) + .map(game.getState()::getValue) + .filter(Integer.class::isInstance) + .map(Integer.class::cast) + .filter(x -> x == game.getTurnNum()) + .isPresent(); + } } diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java b/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java index 0daef751810..93100ce08c3 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java @@ -27,6 +27,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge private boolean optional; private Condition interveningIfCondition; + private Condition triggerCondition; private boolean leavesTheBattlefieldTrigger; private int triggerLimitEachTurn = Integer.MAX_VALUE; // for "triggers only once|twice each turn" private int triggerLimitEachGame = Integer.MAX_VALUE; // for "triggers only once|twice" @@ -57,6 +58,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge super(ability); this.optional = ability.optional; this.interveningIfCondition = ability.interveningIfCondition; + this.triggerCondition = ability.triggerCondition; this.leavesTheBattlefieldTrigger = ability.leavesTheBattlefieldTrigger; this.triggerLimitEachTurn = ability.triggerLimitEachTurn; this.triggerLimitEachGame = ability.triggerLimitEachGame; @@ -69,7 +71,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge @Override public void trigger(Game game, UUID controllerId, GameEvent triggeringEvent) { //20091005 - 603.4 - if (checkInterveningIfClause(game)) { + if (checkInterveningIfClause(game) && checkTriggerCondition(game)) { updateTurnCount(game); updateGameCount(game); game.addTriggeredAbility(this, triggeringEvent); @@ -154,13 +156,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge @Override public boolean checkUsedAlready(Game game) { - if (!doOnlyOnceEachTurn) { - return false; - } - Integer lastTurnUsed = (Integer) game.getState().getValue( - CardUtil.getCardZoneString("lastTurnUsed" + getOriginalId(), sourceId, game) - ); - return lastTurnUsed != null && lastTurnUsed == game.getTurnNum(); + return doOnlyOnceEachTurn && TriggeredAbility.checkDidThisTurn(this, game); } @Override @@ -207,7 +203,9 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge @Override public TriggeredAbility setDoOnlyOnceEachTurn(boolean doOnlyOnce) { this.doOnlyOnceEachTurn = doOnlyOnce; - setOptional(); + if (CardUtil.castStream(this.getAllEffects(), DoIfCostPaid.class).noneMatch(DoIfCostPaid::isOptional)) { + this.optional = true; + } return this; } @@ -228,6 +226,28 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge return interveningIfCondition == null || interveningIfCondition.apply(game, this); } + @Override + public TriggeredAbility withTriggerCondition(Condition condition) { + this.triggerCondition = condition; + if (this.triggerPhrase != null && !condition.toString().isEmpty()) { + this.setTriggerPhrase( + this.triggerPhrase.substring(0, this.triggerPhrase.length() - 2) + ' ' + + (condition.toString().startsWith("during") ? "" : "while ") + condition + ", " + ); + } + return this; + } + + @Override + public Condition getTriggerCondition() { + return triggerCondition; + } + + @Override + public boolean checkTriggerCondition(Game game) { + return triggerCondition == null || triggerCondition.apply(game, this); + } + @Override public boolean resolve(Game game) { if (!checkInterveningIfClause(game)) { @@ -244,11 +264,9 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge )) { return false; } - } - if (doOnlyOnceEachTurn) { - game.getState().setValue(CardUtil.getCardZoneString( - "lastTurnUsed" + getOriginalId(), sourceId, game - ), game.getTurnNum()); + if (doOnlyOnceEachTurn) { + TriggeredAbility.setDidThisTurn(this, game); + } } //20091005 - 603.4 if (!super.resolve(game)) { @@ -276,16 +294,6 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge @Override public String getRule() { StringBuilder sb = new StringBuilder(); - String prefix; - if (abilityWord != null) { - prefix = abilityWord.formatWord(); - } else if (flavorWord != null) { - prefix = CardUtil.italicizeWithEmDash(flavorWord); - } else { - prefix = ""; - } - sb.append(prefix); - sb.append(triggerPhrase == null ? "" : triggerPhrase); if (interveningIfCondition != null) { @@ -358,11 +366,12 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge sb.append(" Do this only once each turn."); } } - return sb.toString(); + return addRulePrefix(sb.toString()); } private static boolean startsWithVerb(String ruleLow) { return ruleLow.startsWith("attach") + || ruleLow.startsWith("cast") || ruleLow.startsWith("change") || ruleLow.startsWith("counter") || ruleLow.startsWith("create") @@ -477,20 +486,6 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge return optional; } - @Override - public TriggeredAbility setOptional() { - this.optional = true; - - if (getEffects().stream().anyMatch( - effect -> effect instanceof DoIfCostPaid && ((DoIfCostPaid) effect).isOptional())) { - throw new IllegalArgumentException( - "DoIfCostPaid effect must have only one optional settings, but it have two (trigger + DoIfCostPaid): " - + this.getClass().getSimpleName()); - } - - return this; - } - @Override public TriggeredAbilityImpl setAbilityWord(AbilityWord abilityWord) { super.setAbilityWord(abilityWord); diff --git a/Mage/src/main/java/mage/abilities/common/AnimateDeadTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AnimateDeadTriggeredAbility.java index bc3631c80ad..daaf7e7359d 100644 --- a/Mage/src/main/java/mage/abilities/common/AnimateDeadTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/AnimateDeadTriggeredAbility.java @@ -192,7 +192,7 @@ class AnimateDeadPutOntoBattlefieldEffect extends OneShotEffect { player.moveCards(card, Zone.BATTLEFIELD, source, game, tapped, false, false, null); game.processAction(); - Permanent creature = game.getPermanent(CardUtil.getDefaultCardSideForBattlefield(game, card).getId()); + Permanent creature = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (creature == null) { return true; } diff --git a/Mage/src/main/java/mage/abilities/common/AttacksWithCreaturesTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttacksWithCreaturesTriggeredAbility.java index 540dc1852ce..36f5196c1d5 100644 --- a/Mage/src/main/java/mage/abilities/common/AttacksWithCreaturesTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/AttacksWithCreaturesTriggeredAbility.java @@ -1,5 +1,6 @@ package mage.abilities.common; +import mage.MageItem; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.constants.Zone; @@ -23,6 +24,7 @@ public class AttacksWithCreaturesTriggeredAbility extends TriggeredAbilityImpl { // retrieve the number of attackers in triggered effects with getValue public static final String VALUEKEY_NUMBER_ATTACKERS = "number_attackers"; + public static final String VALUEKEY_NUMBER_DEFENDING_PLAYERS = "number_defending_players"; private final FilterPermanent filter; private final int minAttackers; @@ -41,7 +43,11 @@ public class AttacksWithCreaturesTriggeredAbility extends TriggeredAbilityImpl { } public AttacksWithCreaturesTriggeredAbility(Zone zone, Effect effect, int minAttackers, FilterPermanent filter, boolean setTargetPointer) { - super(zone, effect); + this(zone, effect, minAttackers, filter, setTargetPointer, false); + } + + public AttacksWithCreaturesTriggeredAbility(Zone zone, Effect effect, int minAttackers, FilterPermanent filter, boolean setTargetPointer, boolean optional) { + super(zone, effect, optional); this.filter = filter; this.minAttackers = minAttackers; this.setTargetPointer = setTargetPointer; @@ -87,6 +93,17 @@ public class AttacksWithCreaturesTriggeredAbility extends TriggeredAbilityImpl { return false; } getEffects().setValue(VALUEKEY_NUMBER_ATTACKERS, attackers.size()); + getEffects().setValue( + VALUEKEY_NUMBER_DEFENDING_PLAYERS, + attackers.stream() + .map(MageItem::getId) + .map(game.getCombat()::getDefenderId) + .distinct() + .map(game::getPlayer) + .filter(Objects::nonNull) + .mapToInt(x -> 1) + .sum() + ); if (setTargetPointer) { getEffects().setTargetPointer(new FixedTargets(new ArrayList<>(attackers), game)); } diff --git a/Mage/src/main/java/mage/abilities/common/BecomesAuraAttachToManifestSourceEffect.java b/Mage/src/main/java/mage/abilities/common/BecomesAuraAttachToManifestSourceEffect.java index c05b37a1831..ccf51904b2b 100644 --- a/Mage/src/main/java/mage/abilities/common/BecomesAuraAttachToManifestSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/common/BecomesAuraAttachToManifestSourceEffect.java @@ -13,6 +13,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCreaturePermanent; +import mage.util.CardUtil; /** * @author LevelX2 @@ -42,7 +43,7 @@ public class BecomesAuraAttachToManifestSourceEffect extends OneShotEffect { Card card = controller.getLibrary().getFromTop(game); if (card != null) { new ManifestEffect(1).apply(game, source); - Permanent enchantedCreature = game.getPermanent(card.getId()); + Permanent enchantedCreature = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (enchantedCreature != null) { enchantedCreature.addAttachment(enchantment.getId(), source, game); FilterCreaturePermanent filter = new FilterCreaturePermanent(); diff --git a/Mage/src/main/java/mage/abilities/common/CaseAbility.java b/Mage/src/main/java/mage/abilities/common/CaseAbility.java index 2e40b823ca0..21e95e09bd5 100644 --- a/Mage/src/main/java/mage/abilities/common/CaseAbility.java +++ b/Mage/src/main/java/mage/abilities/common/CaseAbility.java @@ -1,6 +1,7 @@ package mage.abilities.common; import mage.abilities.Ability; +import mage.abilities.TriggeredAbility; import mage.abilities.condition.CompoundCondition; import mage.abilities.condition.Condition; import mage.abilities.condition.common.SolvedSourceCondition; @@ -8,7 +9,6 @@ import mage.abilities.decorator.ConditionalActivatedAbility; import mage.abilities.decorator.ConditionalAsThoughEffect; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.decorator.ConditionalReplacementEffect; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; @@ -63,13 +63,13 @@ public class CaseAbility extends SimpleStaticAbility { * The "Solved" ability must be one of the following: *

      *
    • {@link ConditionalActivatedAbility} using the condition {@link SolvedSourceCondition}.SOLVED
    • - *
    • {@link ConditionalTriggeredAbility} using the condition {@link SolvedSourceCondition}.SOLVED
    • + *
    • {@link TriggeredAbility} using the condition {@link SolvedSourceCondition}.SOLVED
    • *
    • {@link SimpleStaticAbility} with only {@link ConditionalAsThoughEffect} or {@link ConditionalContinuousEffect} effects
    • *
    * - * @param initialAbility The ability that a Case has at all times + * @param initialAbility The ability that a Case has at all times * @param toSolveCondition The condition to be checked when solving - * @param solvedAbility The ability that a solved Case has + * @param solvedAbility The ability that a solved Case has */ public CaseAbility(Ability initialAbility, Condition toSolveCondition, Ability solvedAbility) { super(Zone.ALL, null); @@ -81,22 +81,28 @@ public class CaseAbility extends SimpleStaticAbility { addSubAbility(new CaseSolveAbility(toSolveCondition)); - if (solvedAbility instanceof ConditionalActivatedAbility) { - ((ConditionalActivatedAbility) solvedAbility).hideCondition(); - } else if (!(solvedAbility instanceof ConditionalTriggeredAbility)) { - if (solvedAbility instanceof SimpleStaticAbility) { - for (Effect effect : solvedAbility.getEffects()) { - if (!(effect instanceof ConditionalContinuousEffect || - effect instanceof ConditionalAsThoughEffect || - effect instanceof ConditionalReplacementEffect)) { - throw new IllegalArgumentException("Wrong code usage: solvedAbility must be one of ConditionalActivatedAbility, " + - "ConditionalTriggeredAbility, or StaticAbility with conditional effects."); - } + if (!(solvedAbility instanceof ConditionalActivatedAbility)) { + if (solvedAbility instanceof TriggeredAbility) { + if (!(((TriggeredAbility) solvedAbility).getTriggerCondition() instanceof SolvedSourceCondition)) { + throw new IllegalArgumentException("Wrong code usage: if solvedAbility is a TriggeredAbility it must have SolvedSourceCondition as its trigger condition"); } } else { - throw new IllegalArgumentException("Wrong code usage: solvedAbility must be one of ConditionalActivatedAbility, " + - "ConditionalTriggeredAbility, or StaticAbility with conditional effects."); + if (solvedAbility instanceof SimpleStaticAbility) { + for (Effect effect : solvedAbility.getEffects()) { + if (!(effect instanceof ConditionalContinuousEffect || + effect instanceof ConditionalAsThoughEffect || + effect instanceof ConditionalReplacementEffect)) { + throw new IllegalArgumentException("Wrong code usage: solvedAbility must be one of ConditionalActivatedAbility, " + + "TriggeredAbility, or StaticAbility with conditional effects."); + } + } + } else { + throw new IllegalArgumentException("Wrong code usage: solvedAbility must be one of ConditionalActivatedAbility, " + + "TriggeredAbility, or StaticAbility with conditional effects."); + } } + } else { + ((ConditionalActivatedAbility) solvedAbility).hideCondition(); } addSubAbility(solvedAbility.withFlavorWord("Solved")); // TODO: Technically this shouldn't be italicized } diff --git a/Mage/src/main/java/mage/abilities/common/CastFromGraveyardOnceDuringEachOfYourTurnAbility.java b/Mage/src/main/java/mage/abilities/common/CastFromGraveyardOnceDuringEachOfYourTurnAbility.java new file mode 100644 index 00000000000..2da7dc64489 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/CastFromGraveyardOnceDuringEachOfYourTurnAbility.java @@ -0,0 +1,190 @@ +package mage.abilities.common; + +import mage.MageIdentifier; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.Costs; +import mage.abilities.costs.CostsImpl; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.common.replacement.MorEnteringTappedEffect; +import mage.cards.Card; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.watchers.Watcher; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * Once during each of your turns, you may cast... from your graveyard + *

    + * See Lurrus of the Dream Den and Rivaz of the Claw + * + * @author weirddan455, Susucr + */ +public class CastFromGraveyardOnceDuringEachOfYourTurnAbility extends SimpleStaticAbility { + + public CastFromGraveyardOnceDuringEachOfYourTurnAbility(FilterCard filter) { + this(filter, (Cost) null); + } + + public CastFromGraveyardOnceDuringEachOfYourTurnAbility(FilterCard filter, Cost additionalCost) { + this(filter, additionalCost, MageIdentifier.OnceOnYourTurnCastFromGraveyard); + } + + public CastFromGraveyardOnceDuringEachOfYourTurnAbility(FilterCard filter, MageIdentifier mageIdentifier) { + this(filter, null, mageIdentifier); + } + + public CastFromGraveyardOnceDuringEachOfYourTurnAbility(FilterCard filter, Cost additionalCost, MageIdentifier mageIdentifier) { + super(new CastFromGraveyardOnceEffect(filter, additionalCost, mageIdentifier)); + this.addWatcher(new CastFromGraveyardOnceWatcher()); + switch (mageIdentifier) { + case OnceOnYourTurnCastFromGraveyard: + case OnceOnYourTurnCastFromGraveyardEntersTapped: + this.setIdentifier(mageIdentifier); + break; + default: + throw new IllegalArgumentException("Wrong code usage: only specific MageIdentifier are currently supported"); + } + } + + private CastFromGraveyardOnceDuringEachOfYourTurnAbility(final CastFromGraveyardOnceDuringEachOfYourTurnAbility ability) { + super(ability); + } + + @Override + public CastFromGraveyardOnceDuringEachOfYourTurnAbility copy() { + return new CastFromGraveyardOnceDuringEachOfYourTurnAbility(this); + } +} + +class CastFromGraveyardOnceEffect extends AsThoughEffectImpl { + + private final FilterCard filter; + private final Cost additionalCost; + private final MageIdentifier mageIdentifier; + + CastFromGraveyardOnceEffect(FilterCard filter, Cost additionalCost, MageIdentifier mageIdentifier) { + super(AsThoughEffectType.CAST_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit); + this.filter = filter; + this.staticText = "Once during each of your turns, you may cast " + filter.getMessage() + + (filter.getMessage().contains("your graveyard") ? "" : " from your graveyard") + + (additionalCost == null ? "" : " by " + additionalCost.getText() + " in addition to paying its other costs."); + this.additionalCost = additionalCost; + this.mageIdentifier = mageIdentifier; + } + + private CastFromGraveyardOnceEffect(final CastFromGraveyardOnceEffect effect) { + super(effect); + this.filter = effect.filter; + this.additionalCost = effect.additionalCost; + this.mageIdentifier = effect.mageIdentifier; + } + + @Override + public CastFromGraveyardOnceEffect copy() { + return new CastFromGraveyardOnceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + throw new IllegalArgumentException("Wrong code usage: can't call applies method on empty affectedAbility"); + } + + @Override + public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); + CastFromGraveyardOnceWatcher watcher = game.getState().getWatcher(CastFromGraveyardOnceWatcher.class); + Card cardToCast = game.getCard(objectId); + if (controller == null || sourcePermanent == null || watcher == null || cardToCast == null + || !game.isActivePlayer(playerId) // only during your turn + || !source.isControlledBy(playerId) // only you may cast + || !Zone.GRAVEYARD.equals(game.getState().getZone(objectId)) // from graveyard + || !cardToCast.getOwnerId().equals(playerId) // only your graveyard + || !(affectedAbility instanceof SpellAbility) // characteristics to check + || watcher.abilityUsed(new MageObjectReference(sourcePermanent, game)) // once per turn + ) { + return false; + } + SpellAbility spellAbility = (SpellAbility) affectedAbility; + Card cardToCheck = spellAbility.getCharacteristics(game); + if (spellAbility.getManaCosts().isEmpty()) { + return false; + } + Set allowedToBeCastNow = spellAbility.spellCanBeActivatedNow(playerId, game); + if (!allowedToBeCastNow.contains(MageIdentifier.Default) + || !filter.match(cardToCheck, playerId, source, game)) { + return false; + } + if (additionalCost != null) { + Costs costs = new CostsImpl<>(); + costs.add(additionalCost); + controller.setCastSourceIdWithAlternateMana( + objectId, spellAbility.getManaCosts(), + costs, mageIdentifier); + } + return true; + } +} + +class CastFromGraveyardOnceWatcher extends Watcher { + + // TODO: we might want to store (approver, approving ability) instead on the odd chance there + // is more than one such ability on a given approver. (event.getApprovingObject() has + // the exact ability, but not sure its id is stable enough.) + // Set of each approver that approved casting a spell this turn (and is thus done for the turn) + private final Set usedFrom = new HashSet<>(); + + CastFromGraveyardOnceWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (!GameEvent.EventType.SPELL_CAST.equals(event.getType())) { + return; + } + if (event.hasApprovingIdentifier(MageIdentifier.OnceOnYourTurnCastFromGraveyard)) { + usedFrom.add(event.getApprovingObject().getApprovingMageObjectReference()); + return; + } + if (event.hasApprovingIdentifier(MageIdentifier.OnceOnYourTurnCastFromGraveyardEntersTapped)) { + usedFrom.add(event.getApprovingObject().getApprovingMageObjectReference()); + // The cast (most likely permanent) spell enters the battlefield tapped. + Spell target = game.getSpell(event.getTargetId()); + if (target != null) { + MageObjectReference mor = new MageObjectReference(target, game); + game.getState().addEffect( + new MorEnteringTappedEffect(mor), + event.getApprovingObject().getApprovingAbility() // ability that approved the cast is the source of the tapping. + ); + } + return; + } + } + + @Override + public void reset() { + super.reset(); + usedFrom.clear(); + } + + boolean abilityUsed(MageObjectReference mor) { + return usedFrom.contains(mor); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/CastFromGraveyardOnceEachTurnAbility.java b/Mage/src/main/java/mage/abilities/common/CastFromGraveyardOnceEachTurnAbility.java deleted file mode 100644 index 3a0f520b724..00000000000 --- a/Mage/src/main/java/mage/abilities/common/CastFromGraveyardOnceEachTurnAbility.java +++ /dev/null @@ -1,150 +0,0 @@ -package mage.abilities.common; - -import mage.MageIdentifier; -import mage.MageObjectReference; -import mage.abilities.Ability; -import mage.abilities.SpellAbility; -import mage.abilities.costs.Cost; -import mage.abilities.costs.Costs; -import mage.abilities.costs.CostsImpl; -import mage.abilities.effects.AsThoughEffectImpl; -import mage.cards.Card; -import mage.constants.*; -import mage.filter.FilterCard; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.watchers.Watcher; - -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; - -/** - * Once during each of your turns, you may cast... from your graveyard - *

    - * See Lurrus of the Dream Den and Rivaz of the Claw - * - * @author weirddan455 - */ -public class CastFromGraveyardOnceEachTurnAbility extends SimpleStaticAbility { - - public CastFromGraveyardOnceEachTurnAbility(FilterCard filter) { - this(filter, null); - } - - public CastFromGraveyardOnceEachTurnAbility(FilterCard filter, Cost additionalCost) { - super(new CastFromGraveyardOnceEffect(filter, additionalCost)); - this.addWatcher(new CastFromGraveyardOnceWatcher()); - this.setIdentifier(MageIdentifier.CastFromGraveyardOnceWatcher); - } - - private CastFromGraveyardOnceEachTurnAbility(final CastFromGraveyardOnceEachTurnAbility ability) { - super(ability); - } - - @Override - public CastFromGraveyardOnceEachTurnAbility copy() { - return new CastFromGraveyardOnceEachTurnAbility(this); - } -} - -class CastFromGraveyardOnceEffect extends AsThoughEffectImpl { - - private final FilterCard filter; - private final Cost additionalCost; - - CastFromGraveyardOnceEffect(FilterCard filter, Cost additionalCost) { - super(AsThoughEffectType.CAST_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit); - this.filter = filter; - this.staticText = "Once during each of your turns, you may cast " + filter.getMessage() - + (filter.getMessage().contains("your graveyard") ? "" : " from your graveyard") - + (additionalCost == null ? "" : " by " + additionalCost.getText() + " in addition to paying its other costs."); - this.additionalCost = additionalCost; - } - - private CastFromGraveyardOnceEffect(final CastFromGraveyardOnceEffect effect) { - super(effect); - this.filter = effect.filter; - this.additionalCost = effect.additionalCost; - } - - @Override - public CastFromGraveyardOnceEffect copy() { - return new CastFromGraveyardOnceEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - throw new IllegalArgumentException("Wrong code usage: can't call applies method on empty affectedAbility"); - } - - @Override - public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); - CastFromGraveyardOnceWatcher watcher = game.getState().getWatcher(CastFromGraveyardOnceWatcher.class); - Card cardToCast = game.getCard(objectId); - if (controller == null || sourcePermanent == null || watcher == null || cardToCast == null) { - return false; - } - if (game.isActivePlayer(playerId) // only during your turn - && source.isControlledBy(playerId) // only you may cast - && Zone.GRAVEYARD.equals(game.getState().getZone(objectId)) // from graveyard - && cardToCast.getOwnerId().equals(playerId) // only your graveyard - && affectedAbility instanceof SpellAbility // characteristics to check - && watcher.abilityNotUsed(new MageObjectReference(sourcePermanent, game)) // once per turn - ) { - SpellAbility spellAbility = (SpellAbility) affectedAbility; - Card cardToCheck = spellAbility.getCharacteristics(game); - if (spellAbility.getManaCosts().isEmpty()) { - return false; - } - Set allowedToBeCastNow = spellAbility.spellCanBeActivatedNow(playerId, game); - if (allowedToBeCastNow.contains(MageIdentifier.Default)) { - boolean matched = filter.match(cardToCheck, playerId, source, game); - if (matched && additionalCost != null) { - Costs costs = new CostsImpl<>(); - costs.add(additionalCost); - controller.setCastSourceIdWithAlternateMana(objectId, spellAbility.getManaCosts(), - costs, MageIdentifier.CastFromGraveyardOnceWatcher); - } - return matched; - } - } - return false; - } -} - -class CastFromGraveyardOnceWatcher extends Watcher { - - private final Set usedFrom = new HashSet<>(); - - CastFromGraveyardOnceWatcher() { - super(WatcherScope.GAME); - } - - @Override - public void watch(GameEvent event, Game game) { - if (GameEvent.EventType.SPELL_CAST.equals(event.getType()) - && event.hasApprovingIdentifier(MageIdentifier.CastFromGraveyardOnceWatcher)) { - usedFrom.add(event.getApprovingObject().getApprovingMageObjectReference()); - } - } - - @Override - public void reset() { - super.reset(); - usedFrom.clear(); - } - - boolean abilityNotUsed(MageObjectReference mor) { - return !usedFrom.contains(mor); - } -} diff --git a/Mage/src/main/java/mage/abilities/common/DealsCombatDamageToAPlayerTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealsCombatDamageToAPlayerTriggeredAbility.java index 2e48ec35aac..85c1c095e78 100644 --- a/Mage/src/main/java/mage/abilities/common/DealsCombatDamageToAPlayerTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealsCombatDamageToAPlayerTriggeredAbility.java @@ -52,6 +52,7 @@ public class DealsCombatDamageToAPlayerTriggeredAbility extends TriggeredAbility return false; } getAllEffects().setValue("damage", event.getAmount()); + getAllEffects().setValue("damagedPlayer", event.getPlayerId()); if (setTargetPointer) { getAllEffects().setTargetPointer(new FixedTarget(event.getPlayerId())); } diff --git a/Mage/src/main/java/mage/abilities/common/DiesCreatureTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DiesCreatureTriggeredAbility.java index 9b885b1bf6e..7df6846dc67 100644 --- a/Mage/src/main/java/mage/abilities/common/DiesCreatureTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DiesCreatureTriggeredAbility.java @@ -3,6 +3,7 @@ package mage.abilities.common; import mage.MageObject; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; +import mage.constants.SetTargetPointer; import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; @@ -18,7 +19,7 @@ import mage.target.targetpointer.FixedTarget; public class DiesCreatureTriggeredAbility extends TriggeredAbilityImpl { protected FilterPermanent filter; - private boolean setTargetPointer; + private SetTargetPointer setTargetPointer; public DiesCreatureTriggeredAbility(Effect effect, boolean optional) { this(effect, optional, false); @@ -33,7 +34,7 @@ public class DiesCreatureTriggeredAbility extends TriggeredAbilityImpl { if (another) { filter.add(AnotherPredicate.instance); } - this.setTargetPointer = setTargetPointer; + this.setTargetPointer = (setTargetPointer ? SetTargetPointer.PERMANENT : SetTargetPointer.NONE); } public DiesCreatureTriggeredAbility(Effect effect, boolean optional, FilterPermanent filter) { @@ -44,7 +45,14 @@ public class DiesCreatureTriggeredAbility extends TriggeredAbilityImpl { this(Zone.BATTLEFIELD, effect, optional, filter, setTargetPointer); } + public DiesCreatureTriggeredAbility(Effect effect, SetTargetPointer setTargetPointer) { + this(Zone.BATTLEFIELD, effect, false, new FilterCreaturePermanent("a creature"), setTargetPointer); + } + public DiesCreatureTriggeredAbility(Zone zone, Effect effect, boolean optional, FilterPermanent filter, boolean setTargetPointer) { + this(zone, effect, optional, filter, (setTargetPointer ? SetTargetPointer.PERMANENT : SetTargetPointer.NONE)); + } + public DiesCreatureTriggeredAbility(Zone zone, Effect effect, boolean optional, FilterPermanent filter, SetTargetPointer setTargetPointer) { super(zone, effect, optional); this.filter = filter; this.setTargetPointer = setTargetPointer; @@ -75,8 +83,17 @@ public class DiesCreatureTriggeredAbility extends TriggeredAbilityImpl { return false; } getEffects().setValue("creatureDied", zEvent.getTarget()); - if (setTargetPointer) { - this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game)); + switch (setTargetPointer) { + case PLAYER: + this.getAllEffects().setTargetPointer(new FixedTarget(event.getPlayerId(), game)); + break; + case PERMANENT: + this.getAllEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game)); + break; + case NONE: + break; + default: + throw new IllegalArgumentException("Unsupported SetTargetPointer in DiesCreatureTriggeredAbility"); } return true; } diff --git a/Mage/src/main/java/mage/abilities/common/DiscardedByOpponentTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DiscardedByOpponentTriggeredAbility.java index 75e3df42573..0d86a1058c2 100644 --- a/Mage/src/main/java/mage/abilities/common/DiscardedByOpponentTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DiscardedByOpponentTriggeredAbility.java @@ -13,13 +13,8 @@ import mage.game.stack.StackObject; public class DiscardedByOpponentTriggeredAbility extends TriggeredAbilityImpl { public DiscardedByOpponentTriggeredAbility(Effect effect) { - this(effect, false); - } - - public DiscardedByOpponentTriggeredAbility(Effect effect, boolean textCardName) { super(Zone.GRAVEYARD, effect, false); - setTriggerPhrase("When a spell or ability an opponent controls causes you to discard " - + (textCardName ? "{this}, " : "this card, ")); + setTriggerPhrase("When a spell or ability an opponent controls causes you to discard this card"); } protected DiscardedByOpponentTriggeredAbility(final DiscardedByOpponentTriggeredAbility ability) { @@ -38,12 +33,10 @@ public class DiscardedByOpponentTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (getSourceId().equals(event.getTargetId())) { - StackObject stackObject = game.getStack().getStackObject(event.getSourceId()); - if (stackObject != null) { - return game.getOpponents(this.getControllerId()).contains(stackObject.getControllerId()); - } + if (!getSourceId().equals(event.getTargetId())) { + return false; } - return false; + StackObject stackObject = game.getStack().getStackObject(event.getSourceId()); + return stackObject != null && game.getOpponents(this.getControllerId()).contains(stackObject.getControllerId()); } } diff --git a/Mage/src/main/java/mage/abilities/common/EndOfCombatTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/EndOfCombatTriggeredAbility.java index 76526cc7128..a6d36c6fa6a 100644 --- a/Mage/src/main/java/mage/abilities/common/EndOfCombatTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/EndOfCombatTriggeredAbility.java @@ -1,7 +1,8 @@ package mage.abilities.common; -import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; +import mage.abilities.triggers.AtStepTriggeredAbility; +import mage.constants.TargetController; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; @@ -9,23 +10,18 @@ import mage.game.events.GameEvent; /** * @author LevelX2 */ -public class EndOfCombatTriggeredAbility extends TriggeredAbilityImpl { - - private final String rule; +public class EndOfCombatTriggeredAbility extends AtStepTriggeredAbility { public EndOfCombatTriggeredAbility(Effect effect, boolean optional) { - this(effect, optional, null); + this(effect, TargetController.ANY, optional); } - public EndOfCombatTriggeredAbility(Effect effect, boolean optional, String rule) { - super(Zone.BATTLEFIELD, effect, optional); - this.rule = rule; - setTriggerPhrase("At end of combat, "); + public EndOfCombatTriggeredAbility(Effect effect, TargetController targetController, boolean optional) { + super(Zone.BATTLEFIELD, targetController, effect, optional); } protected EndOfCombatTriggeredAbility(final EndOfCombatTriggeredAbility ability) { super(ability); - this.rule = ability.rule; } @Override @@ -39,15 +35,14 @@ public class EndOfCombatTriggeredAbility extends TriggeredAbilityImpl { } @Override - public boolean checkTrigger(GameEvent event, Game game) { - return true; - } - - @Override - public String getRule() { - if (rule != null) { - return rule; + protected String generateTriggerPhrase() { + switch (targetController) { + case ANY: + return "At end of combat, "; + case YOU: + return "At the end of combat on your turn, "; + default: + throw new UnsupportedOperationException("Unsupported TargetController in EndOfCombatTriggeredAbility: " + targetController); } - return super.getRule(); } } diff --git a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAbility.java b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAbility.java index fad90076953..231e5dd9a1d 100644 --- a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAbility.java +++ b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAbility.java @@ -6,7 +6,6 @@ import mage.abilities.condition.Condition; import mage.abilities.effects.Effect; import mage.abilities.effects.EntersBattlefieldEffect; import mage.constants.Zone; -import mage.util.CardUtil; /** * @author BetaSteward_at_googlemail.com @@ -81,19 +80,8 @@ public class EntersBattlefieldAbility extends StaticAbility { return abilityRule; } String superRule = super.getRule(); - String prefix; - if (abilityWord != null) { - prefix = abilityWord.formatWord(); - } else if (flavorWord != null) { - prefix = CardUtil.italicizeWithEmDash(flavorWord); - } else { - prefix = null; - } String rule = (optional ? "you may have " : "") + "{this} enter" + (optional ? "" : "s") + (!superRule.isEmpty() && superRule.charAt(0) == ' ' ? "" : " ") + superRule; - if (prefix != null) { - return prefix + CardUtil.getTextWithFirstCharUpperCase(rule); - } - return rule; + return addRulePrefix(rule); } } diff --git a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAllTriggeredAbility.java index cc1a5544469..b01369d0766 100644 --- a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAllTriggeredAbility.java @@ -23,7 +23,11 @@ public class EntersBattlefieldAllTriggeredAbility extends TriggeredAbilityImpl { * zone = BATTLEFIELD optional = false */ public EntersBattlefieldAllTriggeredAbility(Effect effect, FilterPermanent filter) { - this(Zone.BATTLEFIELD, effect, filter, false); + this(effect, filter, false); + } + + public EntersBattlefieldAllTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional) { + this(Zone.BATTLEFIELD, effect, filter, optional); } public EntersBattlefieldAllTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter, boolean optional) { diff --git a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAnyReturnSourceFromGraveyardAttachedToItTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAnyReturnSourceFromGraveyardAttachedToItTriggeredAbility.java new file mode 100644 index 00000000000..83a26c916fd --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAnyReturnSourceFromGraveyardAttachedToItTriggeredAbility.java @@ -0,0 +1,67 @@ +package mage.abilities.common; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.constants.Outcome; +import mage.constants.SetTargetPointer; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.util.CardUtil; + +/** + * "When a [filter] enters, you may return this card from your graveyard to the battlefield attached to that creature" + * + * @author xenohedron + */ +public class EntersBattlefieldAnyReturnSourceFromGraveyardAttachedToItTriggeredAbility extends EntersBattlefieldAllTriggeredAbility { + + public EntersBattlefieldAnyReturnSourceFromGraveyardAttachedToItTriggeredAbility(FilterPermanent filter) { + super(Zone.GRAVEYARD, new ReturnSourceAttachedToItEffect(), filter, true, SetTargetPointer.PERMANENT); + setTriggerPhrase("When " + CardUtil.addArticle(filter.getMessage()) + " enters, "); + } + + protected EntersBattlefieldAnyReturnSourceFromGraveyardAttachedToItTriggeredAbility(final EntersBattlefieldAnyReturnSourceFromGraveyardAttachedToItTriggeredAbility ability) { + super(ability); + } + + @Override + public EntersBattlefieldAnyReturnSourceFromGraveyardAttachedToItTriggeredAbility copy() { + return new EntersBattlefieldAnyReturnSourceFromGraveyardAttachedToItTriggeredAbility(this); + } +} + +class ReturnSourceAttachedToItEffect extends OneShotEffect { + + ReturnSourceAttachedToItEffect() { + super(Outcome.Benefit); + staticText = "return this card from your graveyard to the battlefield attached to that creature"; + } + + private ReturnSourceAttachedToItEffect(final ReturnSourceAttachedToItEffect effect) { + super(effect); + } + + @Override + public ReturnSourceAttachedToItEffect copy() { + return new ReturnSourceAttachedToItEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Card sourceCard = source.getSourceCardIfItStillExists(game); + Permanent permanent = game.getPermanent(this.getTargetPointer().getFirst(game, source)); + Player controller = game.getPlayer(source.getControllerId()); + if (sourceCard != null && permanent != null && controller != null) { + game.getState().setValue("attachTo:" + sourceCard.getId(), permanent); + if (controller.moveCards(sourceCard, Zone.BATTLEFIELD, source, game)) { + permanent.addAttachment(sourceCard.getId(), source, game); + } + return true; + } + return false; + } +} diff --git a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldOrAttacksAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldOrAttacksAllTriggeredAbility.java index 888838aead2..8c20fae76ad 100644 --- a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldOrAttacksAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldOrAttacksAllTriggeredAbility.java @@ -130,8 +130,9 @@ public class EntersBattlefieldOrAttacksAllTriggeredAbility extends TriggeredAbil } private String generateTriggerPhrase() { - StringBuilder sb = new StringBuilder("Whenever ").append(filter.getMessage()); - sb.append(" enters the battlefield "); + StringBuilder sb = new StringBuilder("Whenever "); + sb.append(filter.getMessage()); + sb.append(" enters "); if (controlledText) { sb.append("under your control, "); } else { diff --git a/Mage/src/main/java/mage/abilities/common/OneOrMoreCombatDamagePlayerTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/OneOrMoreCombatDamagePlayerTriggeredAbility.java index f2aa652cfb2..2a30bf01948 100644 --- a/Mage/src/main/java/mage/abilities/common/OneOrMoreCombatDamagePlayerTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/OneOrMoreCombatDamagePlayerTriggeredAbility.java @@ -3,8 +3,8 @@ package mage.abilities.common; import mage.abilities.effects.Effect; import mage.constants.SetTargetPointer; import mage.constants.Zone; +import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; /** * @author Xanderhall, xenohedron @@ -15,15 +15,19 @@ public class OneOrMoreCombatDamagePlayerTriggeredAbility extends OneOrMoreDamage this(effect, SetTargetPointer.NONE); } - public OneOrMoreCombatDamagePlayerTriggeredAbility(Effect effect, FilterCreaturePermanent filter) { + public OneOrMoreCombatDamagePlayerTriggeredAbility(Effect effect, FilterPermanent filter) { this(Zone.BATTLEFIELD, effect, filter, SetTargetPointer.NONE, false); } public OneOrMoreCombatDamagePlayerTriggeredAbility(Effect effect, SetTargetPointer setTargetPointer) { - this(Zone.BATTLEFIELD, effect, StaticFilters.FILTER_PERMANENT_CREATURES, setTargetPointer, false); + this(effect, setTargetPointer, StaticFilters.FILTER_PERMANENT_CREATURES, false); } - public OneOrMoreCombatDamagePlayerTriggeredAbility(Zone zone, Effect effect, FilterCreaturePermanent filter, + public OneOrMoreCombatDamagePlayerTriggeredAbility(Effect effect, SetTargetPointer setTargetPointer, FilterPermanent filter, boolean optional) { + this(Zone.BATTLEFIELD, effect, filter, setTargetPointer, optional); + } + + public OneOrMoreCombatDamagePlayerTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter, SetTargetPointer setTargetPointer, boolean optional) { super(zone, effect, filter, true, true, setTargetPointer, optional); } diff --git a/Mage/src/main/java/mage/abilities/common/PlayLandOrCastSpellFromExileTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/PlayLandOrCastSpellFromExileTriggeredAbility.java new file mode 100644 index 00000000000..ff24dcc6cd6 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/PlayLandOrCastSpellFromExileTriggeredAbility.java @@ -0,0 +1,38 @@ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * @author TheElk801 + */ +public class PlayLandOrCastSpellFromExileTriggeredAbility extends TriggeredAbilityImpl { + + public PlayLandOrCastSpellFromExileTriggeredAbility(Effect effect) { + super(Zone.BATTLEFIELD, effect); + setTriggerPhrase("Whenever you play a land from exile or cast a spell from exile, "); + } + + private PlayLandOrCastSpellFromExileTriggeredAbility(final PlayLandOrCastSpellFromExileTriggeredAbility ability) { + super(ability); + } + + @Override + public PlayLandOrCastSpellFromExileTriggeredAbility copy() { + return new PlayLandOrCastSpellFromExileTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.LAND_PLAYED + || event.getType() == GameEvent.EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return isControlledBy(event.getPlayerId()) && event.getZone() == Zone.EXILED; + } +} diff --git a/Mage/src/main/java/mage/abilities/common/PutIntoGraveFromBattlefieldAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/PutIntoGraveFromBattlefieldAllTriggeredAbility.java index f67a6bdba61..e798718f028 100644 --- a/Mage/src/main/java/mage/abilities/common/PutIntoGraveFromBattlefieldAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/PutIntoGraveFromBattlefieldAllTriggeredAbility.java @@ -30,8 +30,8 @@ public class PutIntoGraveFromBattlefieldAllTriggeredAbility extends TriggeredAbi this.filter = filter; this.onlyToControllerGraveyard = onlyToControllerGraveyard; this.setTargetPointer = setTargetPointer; - setTriggerPhrase("Whenever " + filter.getMessage() + " is put into " + (onlyToControllerGraveyard ? "your" : "a") - + " graveyard from the battlefield, "); + setTriggerPhrase("Whenever " + filter.getMessage() + " is put into " + + (onlyToControllerGraveyard ? "your" : "a") + " graveyard, "); } protected PutIntoGraveFromBattlefieldAllTriggeredAbility(final PutIntoGraveFromBattlefieldAllTriggeredAbility ability) { diff --git a/Mage/src/main/java/mage/abilities/common/PutIntoGraveFromLibrarySourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/PutIntoGraveFromLibrarySourceTriggeredAbility.java index 22d6e67dd0a..1f61c7b9613 100644 --- a/Mage/src/main/java/mage/abilities/common/PutIntoGraveFromLibrarySourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/PutIntoGraveFromLibrarySourceTriggeredAbility.java @@ -13,7 +13,7 @@ public class PutIntoGraveFromLibrarySourceTriggeredAbility extends ZoneChangeTri } public PutIntoGraveFromLibrarySourceTriggeredAbility(Effect effect, boolean optional) { - super(Zone.LIBRARY, Zone.GRAVEYARD, effect, "When {this} is put into your graveyard from your library, ", optional); + super(Zone.LIBRARY, Zone.GRAVEYARD, effect, "When this card is put into your graveyard from your library, ", optional); } protected PutIntoGraveFromLibrarySourceTriggeredAbility(final PutIntoGraveFromLibrarySourceTriggeredAbility ability) { diff --git a/Mage/src/main/java/mage/abilities/common/SagaAbility.java b/Mage/src/main/java/mage/abilities/common/SagaAbility.java index 803556ae727..957f0f3d9b0 100644 --- a/Mage/src/main/java/mage/abilities/common/SagaAbility.java +++ b/Mage/src/main/java/mage/abilities/common/SagaAbility.java @@ -31,7 +31,7 @@ import java.util.function.Consumer; public class SagaAbility extends SimpleStaticAbility { private final SagaChapter maxChapter; - private final boolean showSacText; + private boolean showSacText; private final boolean readAhead; public SagaAbility(Card card) { @@ -134,6 +134,11 @@ public class SagaAbility extends SimpleStaticAbility { return maxChapter; } + public SagaAbility withShowSacText(boolean showSacText) { + this.showSacText = showSacText; + return this; + } + @Override public String getRule() { return (readAhead diff --git a/Mage/src/main/java/mage/abilities/common/WonCoinFlipControllerTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/WonCoinFlipControllerTriggeredAbility.java index 2510d4f8c4e..be38f9ee93b 100644 --- a/Mage/src/main/java/mage/abilities/common/WonCoinFlipControllerTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/WonCoinFlipControllerTriggeredAbility.java @@ -22,6 +22,7 @@ public class WonCoinFlipControllerTriggeredAbility extends TriggeredAbilityImpl public WonCoinFlipControllerTriggeredAbility(Zone zone, Effect effect, boolean optional) { super(zone, effect, optional); + this.setTriggerPhrase("Whenever you win a coin flip, "); } private WonCoinFlipControllerTriggeredAbility(final WonCoinFlipControllerTriggeredAbility ability) { diff --git a/Mage/src/main/java/mage/abilities/common/delayed/AddCounterNextSpellDelayedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/delayed/AddCounterNextSpellDelayedTriggeredAbility.java new file mode 100644 index 00000000000..f06e2b3eb62 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/delayed/AddCounterNextSpellDelayedTriggeredAbility.java @@ -0,0 +1,112 @@ +package mage.abilities.common.delayed; + +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.filter.FilterSpell; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.util.CardUtil; + +/** + * @author TheElk801 + */ +public class AddCounterNextSpellDelayedTriggeredAbility extends DelayedTriggeredAbility { + + private final FilterSpell filter; + + public AddCounterNextSpellDelayedTriggeredAbility() { + this(StaticFilters.FILTER_SPELL_A_CREATURE); + } + + public AddCounterNextSpellDelayedTriggeredAbility(FilterSpell filter) { + this(1, filter); + } + + public AddCounterNextSpellDelayedTriggeredAbility(int amount, FilterSpell filter) { + super(new AddCounterNextSpellEffect(amount), Duration.EndOfTurn, true, false); + this.filter = filter; + this.setTriggerPhrase("When you next cast " + filter.getMessage() + " this turn, "); + } + + private AddCounterNextSpellDelayedTriggeredAbility(final AddCounterNextSpellDelayedTriggeredAbility ability) { + super(ability); + this.filter = ability.filter; + } + + @Override + public AddCounterNextSpellDelayedTriggeredAbility copy() { + return new AddCounterNextSpellDelayedTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!isControlledBy(event.getPlayerId())) { + return false; + } + Spell spell = game.getSpell(event.getTargetId()); + if (spell == null || !filter.match(spell, getControllerId(), this, game)) { + return false; + } + this.getEffects().setValue("spellCast", spell); + return true; + } +} + +class AddCounterNextSpellEffect extends ReplacementEffectImpl { + + private final int amount; + + AddCounterNextSpellEffect(int amount) { + super(Duration.EndOfStep, Outcome.BoostCreature); + this.amount = amount; + staticText = "that creature enters with " + CardUtil.numberToText(amount, "an") + " additional +1/+1 counter" + (amount > 1 ? "s" : "") + " on it"; + } + + private AddCounterNextSpellEffect(AddCounterNextSpellEffect effect) { + super(effect); + this.amount = effect.amount; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Spell spell = (Spell) getValue("spellCast"); + return spell != null && event.getTargetId().equals(spell.getCard().getId()); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget(); + if (creature == null) { + return false; + } + creature.addCounters( + CounterType.P1P1.createInstance(amount), source.getControllerId(), + source, game, event.getAppliedEffects() + ); + discard(); + return false; + } + + @Override + public AddCounterNextSpellEffect copy() { + return new AddCounterNextSpellEffect(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/condition/InvertCondition.java b/Mage/src/main/java/mage/abilities/condition/InvertCondition.java index ecdf51ba30d..245a46b957f 100644 --- a/Mage/src/main/java/mage/abilities/condition/InvertCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/InvertCondition.java @@ -1,4 +1,3 @@ - package mage.abilities.condition; import mage.abilities.Ability; @@ -7,16 +6,22 @@ import mage.game.Game; /** * A simple {@link Condition} to invert a decorated conditions * {@link Condition#apply(mage.game.Game, mage.abilities.Ability) apply(mage.game.Game, mage.abilities.Ability)} - * method invocation. - * + * method invocation. + * * @author maurer.it_at_gmail.com */ public class InvertCondition implements Condition { private final Condition condition; + private final String message; - public InvertCondition ( Condition condition ) { + public InvertCondition(Condition condition) { + this(condition, null); + } + + public InvertCondition(Condition condition, String message) { this.condition = condition; + this.message = message; } /* @@ -29,7 +34,10 @@ public class InvertCondition implements Condition { @Override public String toString() { + if (message != null && !message.isEmpty()) { + return message; + } return condition.toString(); } - + } diff --git a/Mage/src/main/java/mage/abilities/condition/common/AdamantCondition.java b/Mage/src/main/java/mage/abilities/condition/common/AdamantCondition.java index aefb7c3e6d1..e1d23c639ae 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/AdamantCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/AdamantCondition.java @@ -27,11 +27,11 @@ public enum AdamantCondition implements Condition { private final boolean colorless; - private AdamantCondition(ColoredManaSymbol coloredManaSymbol) { + AdamantCondition(ColoredManaSymbol coloredManaSymbol) { this(coloredManaSymbol, false); } - private AdamantCondition(ColoredManaSymbol coloredManaSymbol, boolean colorless) { + AdamantCondition(ColoredManaSymbol coloredManaSymbol, boolean colorless) { this.coloredManaSymbol = coloredManaSymbol; this.colorless = colorless; } @@ -44,9 +44,9 @@ public enum AdamantCondition implements Condition { } if (coloredManaSymbol == null) { return Arrays - .stream(ColoredManaSymbol.values()) - .map(source.getManaCostsToPay().getUsedManaToPay()::getColor) - .anyMatch(i -> i > 2); + .stream(ColoredManaSymbol.values()) + .map(source.getManaCostsToPay().getUsedManaToPay()::getColor) + .anyMatch(i -> i > 2); } return source.getManaCostsToPay().getUsedManaToPay().getColor(coloredManaSymbol) > 2; } @@ -63,9 +63,9 @@ public enum AdamantCondition implements Condition { } if (coloredManaSymbol == null) { return Arrays - .stream(ColoredManaSymbol.values()) - .map(payment::getColor) - .anyMatch(i -> i > 2); + .stream(ColoredManaSymbol.values()) + .map(payment::getColor) + .anyMatch(i -> i > 2); } return payment.getColor(coloredManaSymbol) > 2; } @@ -74,4 +74,17 @@ public enum AdamantCondition implements Condition { public boolean caresAboutManaColor() { return true; } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("at least three "); + if (coloredManaSymbol == null && !colorless) { + sb.append("mana of the same color"); + } else { + sb.append(coloredManaSymbol != null ? coloredManaSymbol.getColorName() : "colorless"); + sb.append(" mana"); + } + sb.append(" was spent to cast it"); + return sb.toString(); + } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/AttachedAttackingCondition.java b/Mage/src/main/java/mage/abilities/condition/common/AttachedAttackingCondition.java new file mode 100644 index 00000000000..4d65a49a7e3 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/condition/common/AttachedAttackingCondition.java @@ -0,0 +1,30 @@ +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.Optional; + +/** + * @author TheElk801 + */ +public enum AttachedAttackingCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + return Optional + .ofNullable(source.getSourcePermanentIfItStillExists(game)) + .map(Permanent::getAttachedTo) + .map(game::getPermanent) + .map(Permanent::isAttacking) + .orElse(false); + } + + @Override + public String toString() { + return "equipped creature is attacking"; + } +} diff --git a/Mage/src/main/java/mage/abilities/condition/common/BargainedCondition.java b/Mage/src/main/java/mage/abilities/condition/common/BargainedCondition.java index 80435d4b3cc..82ca09582a6 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/BargainedCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/BargainedCondition.java @@ -12,7 +12,6 @@ import mage.util.CardUtil; * @author Susucr */ public enum BargainedCondition implements Condition { - instance; @Override @@ -22,7 +21,6 @@ public enum BargainedCondition implements Condition { @Override public String toString() { - return "{this} was Bargained"; + return "it was bargained"; } - } diff --git a/Mage/src/main/java/mage/abilities/condition/common/ControlACommanderCondition.java b/Mage/src/main/java/mage/abilities/condition/common/ControlACommanderCondition.java index b255fc02f14..44f4579c4ad 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/ControlACommanderCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/ControlACommanderCondition.java @@ -34,6 +34,6 @@ public enum ControlACommanderCondition implements Condition { @Override public String toString() { - return "If you control a commander"; + return "you control a commander"; } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/ControlsPermanentGreatestCMCCondition.java b/Mage/src/main/java/mage/abilities/condition/common/ControlsPermanentGreatestCMCCondition.java deleted file mode 100644 index 92a533b372e..00000000000 --- a/Mage/src/main/java/mage/abilities/condition/common/ControlsPermanentGreatestCMCCondition.java +++ /dev/null @@ -1,54 +0,0 @@ -package mage.abilities.condition.common; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.condition.Condition; -import mage.filter.FilterPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; - -/** - * - * @author LevelX2 - */ -public class ControlsPermanentGreatestCMCCondition implements Condition { - - private final FilterPermanent filter; - - public ControlsPermanentGreatestCMCCondition() { - this(new FilterPermanent()); - } - - public ControlsPermanentGreatestCMCCondition(FilterPermanent filter) { - super(); - this.filter = filter; - } - - @Override - public boolean apply(Game game, Ability source) { - Set controllers = new HashSet<>(); - Integer maxCMC = null; - - List permanents = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source, game); - for (Permanent permanent : permanents) { - int cmc = permanent.getManaCost().manaValue(); - if (maxCMC == null || cmc > maxCMC) { - maxCMC = cmc; - controllers.clear(); - } - if (cmc == maxCMC) { - controllers.add(permanent.getControllerId()); - } - } - return controllers.contains(source.getControllerId()); - } - - @Override - public String toString() { - return "you control the " + filter.getMessage() + " with the highest mana value or tied for the highest mana value"; - } - -} diff --git a/Mage/src/main/java/mage/abilities/condition/common/CovenCondition.java b/Mage/src/main/java/mage/abilities/condition/common/CovenCondition.java index 6d2b558ac0b..0a8a6cee560 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/CovenCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/CovenCondition.java @@ -33,6 +33,6 @@ public enum CovenCondition implements Condition { @Override public String toString() { - return "if you control three or more creatures with different powers"; + return "you control three or more creatures with different powers"; } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/DealtDamageToAnOpponent.java b/Mage/src/main/java/mage/abilities/condition/common/DealtDamageToAnOpponent.java index 57e48306403..e803b3c4aca 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/DealtDamageToAnOpponent.java +++ b/Mage/src/main/java/mage/abilities/condition/common/DealtDamageToAnOpponent.java @@ -1,27 +1,31 @@ - package mage.abilities.condition.common; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.condition.Condition; import mage.game.Game; import mage.watchers.common.PlayerDamagedBySourceWatcher; +import java.util.UUID; + /** * @author LevelX2 */ -public class DealtDamageToAnOpponent implements Condition { +public enum DealtDamageToAnOpponent implements Condition { + instance; @Override public boolean apply(Game game, Ability source) { for (UUID opponentId : game.getOpponents(source.getControllerId())) { PlayerDamagedBySourceWatcher watcher = game.getState().getWatcher(PlayerDamagedBySourceWatcher.class, opponentId); - if (watcher != null) { - if (watcher.hasSourceDoneDamage(source.getSourceId(), game)) { - return true; - } + if (watcher != null && watcher.hasSourceDoneDamage(source.getSourceId(), game)) { + return true; } } return false; } + + @Override + public String toString() { + return "{this} dealt damage to an opponent this turn"; + } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/EnchantedSourceCondition.java b/Mage/src/main/java/mage/abilities/condition/common/EnchantedSourceCondition.java index c4a58e6234c..28e4fb3db24 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/EnchantedSourceCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/EnchantedSourceCondition.java @@ -1,8 +1,6 @@ package mage.abilities.condition.common; -import java.util.UUID; - import mage.abilities.Ability; import mage.abilities.condition.Condition; import mage.constants.ComparisonType; @@ -10,8 +8,9 @@ import mage.constants.SubType; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.UUID; + /** - * * @author North */ public class EnchantedSourceCondition implements Condition { @@ -51,6 +50,6 @@ public class EnchantedSourceCondition implements Condition { @Override public String toString() { - return "enchanted"; + return "{this} is enchanted"; } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/EvokedCondition.java b/Mage/src/main/java/mage/abilities/condition/common/EvokedCondition.java index fda6bed36b4..46c9a79e924 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/EvokedCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/EvokedCondition.java @@ -1,5 +1,3 @@ - - package mage.abilities.condition.common; import mage.abilities.Ability; @@ -9,17 +7,21 @@ import mage.game.Game; import mage.util.CardUtil; /** - * Checks if a the spell was cast with the alternate evoke costs + * Checks if a the spell was cast with the alternate evoke costs * * @author LevelX2 */ public enum EvokedCondition implements Condition { - instance; @Override public boolean apply(Game game, Ability source) { return CardUtil.checkSourceCostsTagExists(game, source, EvokeAbility.getActivationKey()); } + + @Override + public String toString() { + return "its evoke cost was paid"; + } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/FullPartyCondition.java b/Mage/src/main/java/mage/abilities/condition/common/FullPartyCondition.java index 885640c2caa..362b89881ce 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/FullPartyCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/FullPartyCondition.java @@ -8,7 +8,6 @@ import mage.game.Game; /** * @author TheElk801 */ - public enum FullPartyCondition implements Condition { instance; @@ -16,4 +15,9 @@ public enum FullPartyCondition implements Condition { public boolean apply(Game game, Ability source) { return PartyCount.instance.calculate(game, source, null) >= 4; } + + @Override + public String toString() { + return "you have a full party"; + } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/HateCondition.java b/Mage/src/main/java/mage/abilities/condition/common/HateCondition.java index 4d42a696c45..7fcba68d0d2 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/HateCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/HateCondition.java @@ -24,6 +24,6 @@ public enum HateCondition implements Condition { @Override public String toString() { - return "if an opponent lost life from source other than combat damage this turn"; + return "an opponent lost life from source other than combat damage this turn"; } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/HellbentCondition.java b/Mage/src/main/java/mage/abilities/condition/common/HellbentCondition.java index cfb80b59616..f94113f2681 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/HellbentCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/HellbentCondition.java @@ -16,6 +16,6 @@ public enum HellbentCondition implements Condition { @Override public String toString() { - return "if you have no cards in hand"; + return "you have no cards in hand"; } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/KickedCostCondition.java b/Mage/src/main/java/mage/abilities/condition/common/KickedCostCondition.java index b3dcf1ee4d4..3ee36b76a1a 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/KickedCostCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/KickedCostCondition.java @@ -12,9 +12,9 @@ import mage.game.Game; */ public class KickedCostCondition implements Condition { - protected String kickerCostText; + protected final String kickerCostText; - public KickedCostCondition(String kickerCostText) { + public KickedCostCondition(String kickerCostText) { this.kickerCostText = kickerCostText; } @@ -22,4 +22,9 @@ public class KickedCostCondition implements Condition { public boolean apply(Game game, Ability source) { return KickerAbility.getKickedCounterStrict(game, source, kickerCostText) > 0; } + + @Override + public String toString() { + return "it was kicked with its " + kickerCostText + " kicker"; + } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/LastTimeCounterRemovedCondition.java b/Mage/src/main/java/mage/abilities/condition/common/LastTimeCounterRemovedCondition.java index fb2bf28163c..7dbba8a1a62 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/LastTimeCounterRemovedCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/LastTimeCounterRemovedCondition.java @@ -2,29 +2,28 @@ package mage.abilities.condition.common; import mage.abilities.Ability; import mage.abilities.condition.Condition; -import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; -import mage.game.permanent.Permanent; + +import java.util.Optional; /** * Created by glerman on 20/6/15. */ -public enum LastTimeCounterRemovedCondition implements Condition{ +public enum LastTimeCounterRemovedCondition implements Condition { + instance; -instance; + @Override + public boolean apply(Game game, Ability source) { + return Optional + .ofNullable(source.getSourcePermanentOrLKI(game)) + .map(permanent -> permanent.getCounters(game).getCount(CounterType.TIME)) + .filter(x -> x == 0) + .isPresent(); + } - @Override - public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent == null) { - permanent = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); + @Override + public String toString() { + return "it had no time counters on it"; } - if (permanent != null) { - final int timeCounters = permanent.getCounters(game).getCount(CounterType.TIME); - return timeCounters == 0; - } else { - return false; - } - } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/OpponentsTurnCondition.java b/Mage/src/main/java/mage/abilities/condition/common/OpponentsTurnCondition.java index beac66cb7f2..94886164681 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/OpponentsTurnCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/OpponentsTurnCondition.java @@ -17,6 +17,6 @@ public enum OpponentsTurnCondition implements Condition { @Override public String toString() { - return "if it's an opponent's turn"; + return "during an opponent's turn"; } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/ProwlCostWasPaidCondition.java b/Mage/src/main/java/mage/abilities/condition/common/ProwlCostWasPaidCondition.java index 640daa6d727..6975d72c1a0 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/ProwlCostWasPaidCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/ProwlCostWasPaidCondition.java @@ -22,7 +22,7 @@ public enum ProwlCostWasPaidCondition implements Condition { @Override public String toString() { - return "this spell's prowl cost was paid"; + return "its prowl cost was paid"; } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/RaidCondition.java b/Mage/src/main/java/mage/abilities/condition/common/RaidCondition.java index 8c03dd92b39..f2649ac395d 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/RaidCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/RaidCondition.java @@ -21,6 +21,6 @@ public enum RaidCondition implements Condition { @Override public String toString() { - return "if you attacked this turn"; + return "you attacked this turn"; } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/SourceEnteredThisTurnCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceEnteredThisTurnCondition.java index d2165c92393..2ff18a230b8 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/SourceEnteredThisTurnCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/SourceEnteredThisTurnCondition.java @@ -1,4 +1,3 @@ - package mage.abilities.condition.common; import mage.abilities.Ability; @@ -9,20 +8,23 @@ import mage.game.permanent.Permanent; /** * @author xenohedron */ - public enum SourceEnteredThisTurnCondition implements Condition { + DID(true), + DIDNT(false); + private final boolean flag; - instance; + SourceEnteredThisTurnCondition(boolean flag) { + this.flag = flag; + } @Override public boolean apply(Game game, Ability source) { Permanent permanent = source.getSourcePermanentOrLKI(game); - return permanent != null && permanent.getTurnsOnBattlefield() == 0; + return permanent != null && (permanent.getTurnsOnBattlefield() == 0) == flag; } @Override public String toString() { - return "{this} entered the battlefield this turn"; + return "{this} " + (flag ? "entered" : "didn't enter") + " the battlefield this turn"; } - } diff --git a/Mage/src/main/java/mage/abilities/condition/common/SourceHasCounterCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceHasCounterCondition.java index e5d3bf49f5a..c65bf4887d7 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/SourceHasCounterCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/SourceHasCounterCondition.java @@ -1,86 +1,87 @@ - package mage.abilities.condition.common; import mage.abilities.Ability; -import mage.abilities.condition.Condition; -import mage.cards.Card; +import mage.abilities.condition.IntCompareCondition; +import mage.constants.ComparisonType; import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; import mage.util.CardUtil; +import java.util.Optional; + /** - * @author nantuko + * Don't use ComparisonType.OR_GREATER with value 0 + * + * @author TheElk801 */ -public class SourceHasCounterCondition implements Condition { +public class SourceHasCounterCondition extends IntCompareCondition { private final CounterType counterType; - private int amount = 1; - private int from = -1; - private int to; - public SourceHasCounterCondition(CounterType type) { - this.counterType = type; + public SourceHasCounterCondition(CounterType counterType) { + this(counterType, 1); } - public SourceHasCounterCondition(CounterType type, int amount) { - this.counterType = type; - this.amount = amount; + public SourceHasCounterCondition(CounterType counterType, int amount) { + this(counterType, ComparisonType.OR_GREATER, amount); } - public SourceHasCounterCondition(CounterType type, int from, int to) { - this.counterType = type; - this.from = from; - this.to = to; + public SourceHasCounterCondition(CounterType counterType, ComparisonType type, int value) { + super(type, value); + this.counterType = counterType; } @Override - @SuppressWarnings("null") - public boolean apply(Game game, Ability source) { - Card card = null; + protected int getInputValue(Game game, Ability source) { Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (permanent == null) { - card = game.getCard(source.getSourceId()); - if (card == null) { - return false; - } - } - if (from != -1) { //range compare - int count; - if (card != null) { - count = card.getCounters(game).getCount(counterType); - } else { - count = permanent.getCounters(game).getCount(counterType); - } - if (to == Integer.MAX_VALUE) { - return count >= from; - } - return count >= from && count <= to; - } else // single compare (lte) - { - if (card != null) { - return card.getCounters(game).getCount(counterType) >= amount; - } else { - return permanent.getCounters(game).getCount(counterType) >= amount; - } + if (permanent != null) { + return permanent.getCounters(game).getCount(counterType); } + return Optional.ofNullable(source) + .map(Ability::getSourceId) + .map(game::getCard) + .map(card -> card.getCounters(game).getCount(counterType)) + .orElse(0); } @Override public String toString() { - if (from != -1) { - if (from == 0) { - if (to == 0) { - return "{this} has no " + this.counterType.toString() + " counters on it"; + switch (type) { + case EQUAL_TO: + StringBuilder sb = new StringBuilder("there "); + switch (value) { + case 0: + sb.append("are no "); + break; + case 1: + sb.append("is exactly one "); + break; + default: + sb.append("are exactly "); + sb.append(CardUtil.numberToText(value)); + sb.append(' '); } - return "{this} has " + CardUtil.numberToText(to) + " or fewer " + this.counterType.toString() + " counters on it"; - } - if (to == Integer.MAX_VALUE) { - return "{this} has " + CardUtil.numberToText(from) + " or more " + this.counterType.toString() + " counters on it"; - } - return "{this} has between " + from + " and " + to + " " + this.counterType.toString() + " counters on it"; - } else { - return "{this} has " + CardUtil.numberToText(amount) + " or more " + this.counterType.toString() + " counters on it"; + sb.append(counterType.getName()); + sb.append(" counter"); + if (value != 1) { + sb.append('s'); + } + sb.append(" on {this}"); + return sb.toString(); + case OR_GREATER: + if (value == 0) { + throw new IllegalArgumentException("0 or greater should not be used"); + } + return "there are " + CardUtil.numberToText(value) + " or more " + counterType.getName() + " counters on {this}"; + case OR_LESS: + return "{this} has " + CardUtil.numberToText(value) + " or fewer " + counterType.getName() + " counters on it"; + case FEWER_THAN: + return "{this} has fewer than " + CardUtil.numberToText(value) + ' ' + counterType.getName() + " counters on it"; + case MORE_THAN: + return "{this} has more than " + CardUtil.numberToText(value) + ' ' + counterType.getName() + " counters on it"; + default: + throw new UnsupportedOperationException("There should be a comparison type"); } } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/SourceHasCountersCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceHasCountersCondition.java index b7aab4c8403..ed372d5dea7 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/SourceHasCountersCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/SourceHasCountersCondition.java @@ -12,14 +12,17 @@ public enum SourceHasCountersCondition implements Condition { @Override public boolean apply(Game game, Ability source) { Permanent permanent = source.getSourcePermanentOrLKI(game); - if (permanent == null) { - return false; - } - return permanent + return permanent != null + && permanent .getCounters(game) .values() .stream() .mapToInt(Counter::getCount) .anyMatch(x -> x > 0); } + + @Override + public String toString() { + return "{this} has counters on it"; + } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/SourceHasntDealtDamageThisGameCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceHasntDealtDamageThisGameCondition.java new file mode 100644 index 00000000000..bd34c678443 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/condition/common/SourceHasntDealtDamageThisGameCondition.java @@ -0,0 +1,34 @@ +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.game.Game; +import mage.watchers.common.DealtDamageThisGameWatcher; + +/** + * requires DealtDamageThisGameWatcher + * + * @author TheElk801 + */ +public enum SourceHasntDealtDamageThisGameCondition implements Condition { + instance; + private static final Hint hint = new ConditionHint( + instance, "This creature hasn't dealt damage yet this game" + ); + + public static Hint getHint() { + return hint; + } + + @Override + public boolean apply(Game game, Ability source) { + return DealtDamageThisGameWatcher.checkCreature(game, source); + } + + @Override + public String toString() { + return "{this} hasn't dealt damage yet"; + } +} diff --git a/Mage/src/main/java/mage/abilities/condition/common/SourceInGraveyardCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceInGraveyardCondition.java index f224a7d5860..85a1bb2cb60 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/SourceInGraveyardCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/SourceInGraveyardCondition.java @@ -20,7 +20,7 @@ public enum SourceInGraveyardCondition implements Condition { @Override public String toString() { - return "{this} is in your graveyard"; + return "this card is in your graveyard"; } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/SourceTargetsPermanentCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceTargetsPermanentCondition.java index f961c55855f..3d9e142bd29 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/SourceTargetsPermanentCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/SourceTargetsPermanentCondition.java @@ -35,7 +35,7 @@ public class SourceTargetsPermanentCondition implements Condition { @Override public String toString() { - return "it targets " + filter.getMessage(); + return "it targets " + CardUtil.addArticle(filter.getMessage()); } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/TwoOrMoreSpellsWereCastLastTurnCondition.java b/Mage/src/main/java/mage/abilities/condition/common/TwoOrMoreSpellsWereCastLastTurnCondition.java index 1f905605a41..f8d755fa332 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/TwoOrMoreSpellsWereCastLastTurnCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/TwoOrMoreSpellsWereCastLastTurnCondition.java @@ -1,4 +1,3 @@ - package mage.abilities.condition.common; import mage.abilities.Ability; @@ -10,22 +9,21 @@ import mage.watchers.common.CastSpellLastTurnWatcher; * @author nantuko */ public enum TwoOrMoreSpellsWereCastLastTurnCondition implements Condition { - - instance; + instance; @Override public boolean apply(Game game, Ability source) { - CastSpellLastTurnWatcher watcher = game.getState().getWatcher(CastSpellLastTurnWatcher.class); - if(watcher == null){ - return false; - } - // if any player cast more than two spells, return true - for (Integer count : watcher.getAmountOfSpellsCastOnPrevTurn().values()) { - if (count >= 2) { - return true; - } - } - // no one cast two or more spells last turn - return false; + return game + .getState() + .getWatcher(CastSpellLastTurnWatcher.class) + .getAmountOfSpellsCastOnPrevTurn() + .values() + .stream() + .anyMatch(x -> x >= 2); + } + + @Override + public String toString() { + return "a player cast two or more spells last turn"; } } diff --git a/Mage/src/main/java/mage/abilities/condition/common/YouGainedLifeCondition.java b/Mage/src/main/java/mage/abilities/condition/common/YouGainedLifeCondition.java index fd848917998..7e142f8b1a6 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/YouGainedLifeCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/YouGainedLifeCondition.java @@ -32,6 +32,6 @@ public class YouGainedLifeCondition extends IntCompareCondition { @Override public String toString() { - return "if you gained " + (value == 0 ? "" : (value + 1) + " or more ") + "life this turn"; + return "you gained " + (value == 0 ? "" : (value + 1) + " or more ") + "life this turn"; } } diff --git a/Mage/src/main/java/mage/abilities/costs/common/DiscardXTargetCost.java b/Mage/src/main/java/mage/abilities/costs/common/DiscardXTargetCost.java index ac863a0e819..dbf7a3513c9 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/DiscardXTargetCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/DiscardXTargetCost.java @@ -44,6 +44,7 @@ public class DiscardXTargetCost extends VariableCostImpl { public DiscardXTargetCost withRandom() { this.isRandom = true; + this.text += " at random"; return this; } diff --git a/Mage/src/main/java/mage/abilities/costs/common/PutCountersTargetCost.java b/Mage/src/main/java/mage/abilities/costs/common/PutCountersTargetCost.java new file mode 100644 index 00000000000..1baac83c362 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/costs/common/PutCountersTargetCost.java @@ -0,0 +1,63 @@ +package mage.abilities.costs.common; + +import mage.abilities.Ability; +import mage.abilities.costs.Cost; +import mage.abilities.costs.CostImpl; +import mage.constants.Outcome; +import mage.counters.Counter; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author notgreat + */ +public class PutCountersTargetCost extends CostImpl { + + private final Counter counter; + + public PutCountersTargetCost(Counter counter){ + this(counter, new TargetControlledCreaturePermanent()); + } + + public PutCountersTargetCost(Counter counter, TargetControlledPermanent target) { + this.counter = counter.copy(); + target.withNotTarget(true); + this.addTarget(target); + this.text = "put " + counter.getDescription() + " on " + target.getDescription(); + } + + public PutCountersTargetCost(PutCountersTargetCost cost) { + super(cost); + this.counter = cost.counter.copy(); + } + + public PutCountersTargetCost copy() { + return new PutCountersTargetCost(this); + } + + @Override + public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { + Player player = game.getPlayer(ability.getControllerId()); + if (player == null || !this.getTargets().choose(Outcome.Exile, controllerId, source.getSourceId(), source, game)) { + return paid; + } + for (UUID targetId : this.getTargets().get(0).getTargets()) { + Permanent permanent = game.getPermanent(targetId); + if (permanent == null) { + return false; + } + paid |= permanent.addCounters(counter, controllerId, ability, game); + } + return paid; + } + + @Override + public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { + return this.getTargets().canChoose(controllerId, source, game); + } +} diff --git a/Mage/src/main/java/mage/abilities/costs/common/RevealSourceFromYourHandCost.java b/Mage/src/main/java/mage/abilities/costs/common/RevealSourceFromYourHandCost.java index 6af8ad5ad4d..ddaf70e39f9 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/RevealSourceFromYourHandCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/RevealSourceFromYourHandCost.java @@ -1,7 +1,5 @@ - - package mage.abilities.costs.common; -import java.util.UUID; + import mage.abilities.Ability; import mage.abilities.costs.Cost; import mage.abilities.costs.CostImpl; @@ -12,18 +10,17 @@ import mage.constants.Zone; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** - * - * - * * @author LevelX2 - * */ public class RevealSourceFromYourHandCost extends CostImpl { public RevealSourceFromYourHandCost() { - this.text = "reveal {this} from your hand"; + this.text = "reveal this card from your hand"; } + public RevealSourceFromYourHandCost(RevealSourceFromYourHandCost cost) { super(cost); } @@ -32,14 +29,16 @@ public class RevealSourceFromYourHandCost extends CostImpl { public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { paid = false; Player player = game.getPlayer(controllerId); - if (player != null) { - Card card = player.getHand().get(ability.getSourceId(), game); - if (card != null) { - Cards cards = new CardsImpl(card); - paid = true; - player.revealCards("Reveal card cost", cards, game); - } + if (player == null) { + return paid; } + Card card = player.getHand().get(ability.getSourceId(), game); + if (card == null) { + return paid; + } + Cards cards = new CardsImpl(card); + paid = true; + player.revealCards("Reveal card cost", cards, game); return paid; } @@ -52,5 +51,4 @@ public class RevealSourceFromYourHandCost extends CostImpl { public RevealSourceFromYourHandCost copy() { return new RevealSourceFromYourHandCost(this); } - } diff --git a/Mage/src/main/java/mage/abilities/decorator/ConditionalInterveningIfTriggeredAbility.java b/Mage/src/main/java/mage/abilities/decorator/ConditionalInterveningIfTriggeredAbility.java index 6a5245ccc44..f975727ef27 100644 --- a/Mage/src/main/java/mage/abilities/decorator/ConditionalInterveningIfTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/decorator/ConditionalInterveningIfTriggeredAbility.java @@ -9,10 +9,8 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.Effects; import mage.abilities.hint.Hint; import mage.constants.EffectType; -import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.util.CardUtil; import mage.watchers.Watcher; import java.util.ArrayList; @@ -83,9 +81,7 @@ public class ConditionalInterveningIfTriggeredAbility extends TriggeredAbilityIm if (abilityText == null || abilityText.isEmpty()) { return ability.getRule(); } - return (flavorWord != null ? CardUtil.italicizeWithEmDash(flavorWord) : "") + - (abilityWord != null ? abilityWord.formatWord() : "") + - abilityText + (abilityText.endsWith(".") || abilityText.endsWith("\"") || abilityText.endsWith(">") ? "" : "."); + return addRulePrefix(abilityText + (abilityText.endsWith(".") || abilityText.endsWith("\"") || abilityText.endsWith(">") ? "" : ".")); } @Override @@ -135,6 +131,11 @@ public class ConditionalInterveningIfTriggeredAbility extends TriggeredAbilityIm ability.setSourceObjectZoneChangeCounter(sourceObjectZoneChangeCounter); } + @Override + public void initSourceObjectZoneChangeCounter(Game game, boolean force) { + ability.initSourceObjectZoneChangeCounter(game, force); + } + @Override public int getSourceObjectZoneChangeCounter() { return ability.getSourceObjectZoneChangeCounter(); diff --git a/Mage/src/main/java/mage/abilities/decorator/ConditionalTriggeredAbility.java b/Mage/src/main/java/mage/abilities/decorator/ConditionalTriggeredAbility.java deleted file mode 100644 index 0eda93cf9e1..00000000000 --- a/Mage/src/main/java/mage/abilities/decorator/ConditionalTriggeredAbility.java +++ /dev/null @@ -1,143 +0,0 @@ -package mage.abilities.decorator; - -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.Modes; -import mage.abilities.TriggeredAbility; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.condition.Condition; -import mage.abilities.effects.Effect; -import mage.abilities.effects.Effects; -import mage.abilities.hint.Hint; -import mage.constants.EffectType; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.util.CardUtil; -import mage.watchers.Watcher; - -import java.util.ArrayList; -import java.util.List; - -/** - * Adds condition to {@link mage.abilities.effects.ContinuousEffect}. Acts as - * decorator. - * - * @author noahg - */ -public class ConditionalTriggeredAbility extends TriggeredAbilityImpl { - - protected TriggeredAbility ability; - protected Condition condition; - protected String abilityText; - - /** - * Triggered ability with a condition. Set the optionality for the trigger - * ability itself. - * - * @param ability - * @param condition - * @param text explicit rule text for the ability, if null or empty, the - * rule text generated by the triggered ability itself is used. - */ - public ConditionalTriggeredAbility(TriggeredAbility ability, Condition condition, String text) { - super(ability.getZone(), null); - this.ability = ability; - this.condition = condition; - this.abilityText = text; - if (ability.isLeavesTheBattlefieldTrigger()) { - this.setLeavesTheBattlefieldTrigger(true); - } - } - - protected ConditionalTriggeredAbility(final ConditionalTriggeredAbility triggered) { - super(triggered); - this.ability = triggered.ability.copy(); - this.condition = triggered.condition; - this.abilityText = triggered.abilityText; - } - - @Override - public ConditionalTriggeredAbility copy() { - return new ConditionalTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return ability.checkEventType(event, game); - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - ability.setSourceId(this.getSourceId()); - ability.setControllerId(this.getControllerId()); - return ability.checkTrigger(event, game) && condition.apply(game, this); - } - - @Override - public String getRule() { - if (abilityText == null || abilityText.isEmpty()) { - return ability.getRule(); - } - return (flavorWord != null ? CardUtil.italicizeWithEmDash(flavorWord) : "") + - (abilityWord != null ? abilityWord.formatWord() : "") + - abilityText + (abilityText.endsWith(".") || abilityText.endsWith("\"") || abilityText.endsWith(">") ? "" : "."); - } - - @Override - public Effects getEffects() { - return ability.getEffects(); - } - - @Override - public void addEffect(Effect effect) { - ability.addEffect(effect); - } - - @Override - public Modes getModes() { - return ability.getModes(); - } - - @Override - public List getWatchers() { - return ability.getWatchers(); - } - - @Override - public void addWatcher(Watcher watcher) { - ability.addWatcher(watcher); - } - - @Override - public List getHints() { - List res = new ArrayList<>(super.getHints()); - res.addAll(ability.getHints()); - return res; - } - - @Override - public Effects getEffects(Game game, EffectType effectType) { - return ability.getEffects(game, effectType); - } - - @Override - public boolean isOptional() { - return ability.isOptional(); - } - - @Override - public Ability withFlavorWord(String flavorWord) { - ability.withFlavorWord(flavorWord); - return this; - } - - @Override - public boolean isInUseableZone(Game game, MageObject sourceObject, GameEvent event) { - if (isLeavesTheBattlefieldTrigger()) { - // TODO: leaves battlefield and die are not same! Is it possible make a diff logic? - return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, sourceObject, event, game); - } else { - return super.isInUseableZone(game, sourceObject, event); - } - } -} diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardsInControllerHandCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardsInControllerHandCount.java index 6163ffdf6a2..e6d342d7f46 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardsInControllerHandCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardsInControllerHandCount.java @@ -16,7 +16,9 @@ import java.util.Set; public enum CardsInControllerHandCount implements DynamicValue { + ANY(StaticFilters.FILTER_CARD_CARDS), + ANY_SINGULAR(StaticFilters.FILTER_CARD), CREATURES(StaticFilters.FILTER_CARD_CREATURES), LANDS(StaticFilters.FILTER_CARD_LANDS); diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/GreatestAmongPermanentsValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/GreatestAmongPermanentsValue.java new file mode 100644 index 00000000000..c800465d79c --- /dev/null +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/GreatestAmongPermanentsValue.java @@ -0,0 +1,117 @@ +package mage.abilities.dynamicvalue.common; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.function.ToIntFunction; + + +/** + * Dynamic value for "greatest [quality] among [permanent filter]" + * For the most common ones, add a static entry instead of using new GreatestAmongPermanentsValue(...). + * + * @author Susucr + */ +public class GreatestAmongPermanentsValue implements DynamicValue { + + public static final GreatestAmongPermanentsValue POWER_CONTROLLED_CREATURES + = new GreatestAmongPermanentsValue(Quality.Power, StaticFilters.FILTER_CONTROLLED_CREATURES); + public static final GreatestAmongPermanentsValue POWER_OTHER_CONTROLLED_CREATURES + = new GreatestAmongPermanentsValue(Quality.Power, StaticFilters.FILTER_OTHER_CONTROLLED_CREATURES); + public static final GreatestAmongPermanentsValue TOUGHNESS_CONTROLLED_CREATURES + = new GreatestAmongPermanentsValue(Quality.Toughness, StaticFilters.FILTER_CONTROLLED_CREATURES); + public static final GreatestAmongPermanentsValue TOUGHNESS_OTHER_CONTROLLED_CREATURES + = new GreatestAmongPermanentsValue(Quality.Toughness, StaticFilters.FILTER_OTHER_CONTROLLED_CREATURES); + public static final GreatestAmongPermanentsValue POWER_OR_TOUGHNESS_OTHER_CONTROLLED_CREATURES + = new GreatestAmongPermanentsValue(Quality.PowerOrToughness, StaticFilters.FILTER_OTHER_CONTROLLED_CREATURES); + public static final GreatestAmongPermanentsValue MANAVALUE_CONTROLLED_CREATURES + = new GreatestAmongPermanentsValue(Quality.ManaValue, StaticFilters.FILTER_CONTROLLED_CREATURES); + public static final GreatestAmongPermanentsValue MANAVALUE_OTHER_CONTROLLED_CREATURES + = new GreatestAmongPermanentsValue(Quality.ManaValue, StaticFilters.FILTER_OTHER_CONTROLLED_CREATURES); + public static final GreatestAmongPermanentsValue MANAVALUE_CONTROLLED_ARTIFACTS + = new GreatestAmongPermanentsValue(Quality.ManaValue, StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACTS); + public static final GreatestAmongPermanentsValue MANAVALUE_OTHER_CONTROLLED_ARTIFACTS + = new GreatestAmongPermanentsValue(Quality.ManaValue, StaticFilters.FILTER_OTHER_CONTROLLED_ARTIFACTS); + public static final GreatestAmongPermanentsValue MANAVALUE_CONTROLLED_PERMANENTS + = new GreatestAmongPermanentsValue(Quality.ManaValue, StaticFilters.FILTER_CONTROLLED_PERMANENTS); + public static final GreatestAmongPermanentsValue MANAVALUE_OTHER_CONTROLLED_PERMANENTS + = new GreatestAmongPermanentsValue(Quality.ManaValue, StaticFilters.FILTER_OTHER_CONTROLLED_PERMANENTS); + + public enum Quality { + Power("power", (Permanent permanent) -> { + return permanent.getPower().getValue(); + }), + Toughness("toughness", (Permanent permanent) -> { + return permanent.getToughness().getValue(); + }), + ManaValue("mana value", Permanent::getManaValue), + PowerOrToughness("power and/or toughness", + (Permanent permanent) -> { + int power = permanent.getPower().getValue(); + int toughness = permanent.getToughness().getValue(); + return Math.max(power, toughness); + } + ); + + final String text; + final ToIntFunction mapToQuality; + + Quality(String text, ToIntFunction mapToQuality) { + this.text = text; + this.mapToQuality = mapToQuality; + } + } + + private final Quality quality; + private final FilterPermanent filter; + + public GreatestAmongPermanentsValue(Quality quality, FilterPermanent filter) { + this.filter = filter; + this.quality = quality; + } + + private GreatestAmongPermanentsValue(final GreatestAmongPermanentsValue value) { + super(); + this.filter = value.filter; + this.quality = value.quality; + } + + @Override + public GreatestAmongPermanentsValue copy() { + return new GreatestAmongPermanentsValue(this); + } + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return game + .getBattlefield() + .getActivePermanents( + this.filter, sourceAbility.getControllerId(), sourceAbility, game + ) + .stream() + .mapToInt(this.quality.mapToQuality) + .max() + .orElse(0); + } + + @Override + public String getMessage() { + return "the greatest " + quality.text + " among " + this.filter.getMessage(); + } + + @Override + public String toString() { + return "X"; + } + + public Hint getHint() { + return new ValueHint("Greatest " + quality.text + " among " + filter.getMessage(), this); + } +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/GreatestPowerAmongControlledCreaturesValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/GreatestPowerAmongControlledCreaturesValue.java deleted file mode 100644 index 8a305f2e99b..00000000000 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/GreatestPowerAmongControlledCreaturesValue.java +++ /dev/null @@ -1,53 +0,0 @@ -package mage.abilities.dynamicvalue.common; - -import mage.MageInt; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; -import mage.filter.StaticFilters; -import mage.game.Game; - -/** - * @author Styxo - */ -public enum GreatestPowerAmongControlledCreaturesValue implements DynamicValue { - instance; - - private static final Hint hint = new ValueHint("Greatest power among creatures you control", instance); - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - return game - .getBattlefield() - .getActivePermanents( - StaticFilters.FILTER_CONTROLLED_CREATURE, - sourceAbility.getControllerId(), game - ).stream() - .map(MageObject::getPower) - .mapToInt(MageInt::getValue) - .max() - .orElse(0); - } - - @Override - public GreatestPowerAmongControlledCreaturesValue copy() { - return GreatestPowerAmongControlledCreaturesValue.instance; - } - - @Override - public String getMessage() { - return "the greatest power among creatures you control"; - } - - @Override - public String toString() { - return "X"; - } - - public static Hint getHint() { - return hint; - } -} diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/GreatestToughnessAmongControlledCreaturesValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/GreatestToughnessAmongControlledCreaturesValue.java deleted file mode 100644 index edc372293ac..00000000000 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/GreatestToughnessAmongControlledCreaturesValue.java +++ /dev/null @@ -1,58 +0,0 @@ -package mage.abilities.dynamicvalue.common; - -import mage.MageInt; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; -import mage.abilities.hint.Hint; -import mage.abilities.hint.ValueHint; -import mage.filter.FilterPermanent; -import mage.filter.StaticFilters; -import mage.game.Game; - -/** - * @author TheElk801 - */ -public enum GreatestToughnessAmongControlledCreaturesValue implements DynamicValue { - ALL(StaticFilters.FILTER_CONTROLLED_CREATURES), - OTHER(StaticFilters.FILTER_OTHER_CONTROLLED_CREATURES); - private final FilterPermanent filter; - private final Hint hint; - - GreatestToughnessAmongControlledCreaturesValue(FilterPermanent filter) { - this.filter = filter; - this.hint = new ValueHint("The greatest toughness among " + filter.getMessage(), this); - } - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - return game - .getBattlefield() - .getActivePermanents(filter, sourceAbility.getControllerId(), sourceAbility, game) - .stream() - .map(MageObject::getToughness) - .mapToInt(MageInt::getValue) - .max() - .orElse(0); - } - - @Override - public GreatestToughnessAmongControlledCreaturesValue copy() { - return this; - } - - @Override - public String getMessage() { - return "the greatest toughness among " + filter.getMessage(); - } - - @Override - public String toString() { - return "X"; - } - - public Hint getHint() { - return hint; - } -} diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/HighestCMCOfPermanentValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/HighestCMCOfPermanentValue.java deleted file mode 100644 index d92cc26f98a..00000000000 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/HighestCMCOfPermanentValue.java +++ /dev/null @@ -1,61 +0,0 @@ -package mage.abilities.dynamicvalue.common; - -import mage.abilities.Ability; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; -import mage.filter.FilterPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; - -/** - * @author LevelX2 - */ -public class HighestCMCOfPermanentValue implements DynamicValue { - - private final FilterPermanent filter; - private final boolean onlyIfCanBeSacrificed; - - public HighestCMCOfPermanentValue(FilterPermanent filter, boolean onlyIfCanBeSacrificed) { - super(); - this.filter = filter; - this.onlyIfCanBeSacrificed = onlyIfCanBeSacrificed; - } - - protected HighestCMCOfPermanentValue(final HighestCMCOfPermanentValue dynamicValue) { - this.filter = dynamicValue.filter; - this.onlyIfCanBeSacrificed = dynamicValue.onlyIfCanBeSacrificed; - } - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - int value = 0; - Player controller = game.getPlayer(sourceAbility.getControllerId()); - if (controller != null) { - for (Permanent permanent : game.getBattlefield() - .getActivePermanents(filter, sourceAbility.getControllerId(), sourceAbility, game)) { - if ((!onlyIfCanBeSacrificed || controller.canPaySacrificeCost(permanent, sourceAbility, sourceAbility.getControllerId(), game)) - && permanent.getManaValue() > value) { - value = permanent.getManaValue(); - } - - } - } - return value; - } - - @Override - public HighestCMCOfPermanentValue copy() { - return new HighestCMCOfPermanentValue(this); - } - - @Override - public String toString() { - return "X"; - } - - @Override - public String getMessage() { - return filter.getMessage(); - } -} diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/HighestManaValueCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/HighestManaValueCount.java deleted file mode 100644 index af92813ce48..00000000000 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/HighestManaValueCount.java +++ /dev/null @@ -1,60 +0,0 @@ -package mage.abilities.dynamicvalue.common; - -import mage.abilities.Ability; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; -import mage.filter.FilterPermanent; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; - -/** - * @author nigelzor - */ -public class HighestManaValueCount implements DynamicValue { - - private final FilterPermanent filter; - - public HighestManaValueCount() { - this(StaticFilters.FILTER_PERMANENTS); - } - - public HighestManaValueCount(FilterPermanent filter) { - this.filter = filter; - } - - protected HighestManaValueCount(final HighestManaValueCount dynamicValue) { - super(); - this.filter = dynamicValue.filter.copy(); - } - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - Player controller = game.getPlayer(sourceAbility.getControllerId()); - if (controller == null) { - return 0; - } - int highCMC = 0; - for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, controller.getId(), game)) { - int cmc = permanent.getManaValue(); - highCMC = Math.max(highCMC, cmc); - } - return highCMC; - } - - @Override - public HighestManaValueCount copy() { - return new HighestManaValueCount(this); - } - - @Override - public String getMessage() { - return "the highest mana value among " + filter.getMessage() + " you control"; - } - - @Override - public String toString() { - return "X"; - } -} diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/ManaValueInGraveyard.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/ManaValueInGraveyard.java index 752a637cde0..97a0fae8e9d 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/ManaValueInGraveyard.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/ManaValueInGraveyard.java @@ -46,7 +46,7 @@ public class ManaValueInGraveyard implements DynamicValue { return 0; } int value = player.getGraveyard().stream().map(game::getCard).filter(Objects::nonNull) - .filter(card -> filter.match(card, playerId, game)).mapToInt(MageObject::getManaValue).sum(); + .filter(card -> filter.match(card, playerId, sourceAbility, game)).mapToInt(MageObject::getManaValue).sum(); if (multiplier != null) { value *= multiplier; } diff --git a/Mage/src/main/java/mage/abilities/effects/Effects.java b/Mage/src/main/java/mage/abilities/effects/Effects.java index fbfdaf7ba5a..3486b664e15 100644 --- a/Mage/src/main/java/mage/abilities/effects/Effects.java +++ b/Mage/src/main/java/mage/abilities/effects/Effects.java @@ -8,6 +8,7 @@ import mage.util.CardUtil; import java.util.ArrayList; import java.util.Arrays; +import java.util.Optional; /** * @author BetaSteward_at_googlemail.com @@ -29,11 +30,21 @@ public class Effects extends ArrayList { } public String getTextStartingUpperCase(Mode mode) { - String text = getText(mode); - if (text.length() > 3) { - return CardUtil.getTextWithFirstCharUpperCase(text); + StringBuilder text = Optional + .of(getText(mode)) + .map(s -> s.length() > 3 ? CardUtil.getTextWithFirstCharUpperCase(s) : s) + .map(StringBuilder::new) + .get(); + // cost + if (mode.getCost() != null) { + text.insert(0, " — "); + text.insert(0, mode.getCost().getText()); } - return text; + // flavor word + if (mode.getFlavorWord() != null) { + text.insert(0, CardUtil.italicizeWithEmDash(mode.getFlavorWord())); + } + return text.toString(); } public String getText(Mode mode) { @@ -115,20 +126,6 @@ public class Effects extends ArrayList { sbText.append('.'); } - - if (mode.getCost() != null || mode.getFlavorWord() != null) { - sbText.replace(0, 1, sbText.substring(0, 1).toUpperCase()); - } - // cost - if (mode.getCost() != null) { - sbText.insert(0, " — "); - sbText.insert(0, mode.getCost().getText()); - } - // flavor word - if (mode.getFlavorWord() != null) { - sbText.insert(0, CardUtil.italicizeWithEmDash(mode.getFlavorWord())); - } - return sbText.toString(); } diff --git a/Mage/src/main/java/mage/abilities/effects/OneShotNonTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/OneShotNonTargetEffect.java new file mode 100644 index 00000000000..acb09c56ac1 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/OneShotNonTargetEffect.java @@ -0,0 +1,80 @@ +package mage.abilities.effects; + +import mage.abilities.Ability; +import mage.game.Game; +import mage.target.Target; +import mage.target.targetadjustment.TargetAdjuster; +import mage.target.targetpointer.TargetPointer; + +/** + * @author notgreat + */ +public class OneShotNonTargetEffect extends OneShotEffect { + OneShotEffect effect; + Target notTarget; + TargetAdjuster adjuster; + + public OneShotNonTargetEffect(OneShotEffect effect, Target notTarget) { + this(effect, notTarget, null); + } + + public OneShotNonTargetEffect(OneShotEffect effect, Target notTarget, TargetAdjuster adjuster) { + super(effect.outcome); + this.effect = effect; + this.notTarget = notTarget; + this.notTarget.withNotTarget(true); + this.adjuster = adjuster; + if (effect.staticText == null || effect.staticText.equals("")){ + throw new IllegalArgumentException("Effect must use static text"); + } + this.setText(effect.staticText); + } + + private OneShotNonTargetEffect(OneShotNonTargetEffect eff) { + super(eff); + this.effect = eff.effect.copy(); + this.notTarget = eff.notTarget.copy(); + this.adjuster = eff.adjuster; + } + + public OneShotNonTargetEffect copy() { + return new OneShotNonTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + boolean result = false; + Target target = notTarget.copy(); + if (source.getTargetAdjuster() != null || !source.getTargets().isEmpty()){ + throw new IllegalStateException("source ability already has target but is using OneShotNonTargetEffect"); + } + source.addTarget(target); + if (adjuster != null) { + adjuster.clearDefaultTargets(); + source.setTargetAdjuster(adjuster); + source.adjustTargets(game); + source.setTargetAdjuster(null); + } + + if (source.getTargets().choose(outcome, source.getControllerId(), source.getId(), source, game)) { + result = effect.apply(game, source); + } + source.getTargets().clear(); + return result; + } + + @Override + public OneShotEffect setTargetPointer(TargetPointer targetPointer) { + if (targetPointer == null) { + return null; + } + effect.setTargetPointer(targetPointer); + return super.setTargetPointer(targetPointer); + } + + @Override + public void setValue(String key, Object value) { + effect.setValue(key, value); + super.setValue(key, value); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/AttachTargetToTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/AttachTargetToTargetEffect.java new file mode 100644 index 00000000000..df284c409de --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/AttachTargetToTargetEffect.java @@ -0,0 +1,55 @@ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.EachTargetPointer; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public class AttachTargetToTargetEffect extends OneShotEffect { + + public AttachTargetToTargetEffect() { + super(Outcome.BoostCreature); + this.setTargetPointer(new EachTargetPointer()); + } + + private AttachTargetToTargetEffect(final AttachTargetToTargetEffect effect) { + super(effect); + } + + @Override + public AttachTargetToTargetEffect copy() { + return new AttachTargetToTargetEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + List permanents = this.getTargetPointer() + .getTargets(game, source) + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + return permanents.size() >= 2 && permanents.get(1).addAttachment(permanents.get(0).getId(), source, game); + } + + @Override + public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } + if (mode.getTargets().size() != 2) { + throw new IllegalStateException("It must have two targets, but found " + mode.getTargets().size()); + } + return "attach " + mode.getTargets().get(0).getDescription() + " to " + mode.getTargets().get(1).getDescription(); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/CantBeCounteredControlledEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CantBeCounteredControlledEffect.java index f050ad42afa..472c5c4f743 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CantBeCounteredControlledEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CantBeCounteredControlledEffect.java @@ -1,40 +1,40 @@ package mage.abilities.effects.common; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.constants.Duration; import mage.constants.Outcome; -import mage.filter.FilterObject; import mage.filter.FilterSpell; +import mage.filter.FilterStackObject; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; +import mage.game.stack.StackObject; + +import java.util.UUID; /** - * @author BetaSteward_at_googlemail.com + * @author BetaSteward_at_googlemail.com, Susucr */ public class CantBeCounteredControlledEffect extends ContinuousRuleModifyingEffectImpl { - private FilterSpell filterTarget; - private FilterObject filterSource; + private FilterSpell filterTarget; // what can't be countered + private FilterStackObject filterSource; // can filter on what is countering - public CantBeCounteredControlledEffect(FilterSpell filterTarget, FilterObject filterSource, Duration duration) { + public CantBeCounteredControlledEffect(FilterSpell filterTarget, Duration duration) { + this(filterTarget, null, duration); + } + + public CantBeCounteredControlledEffect(FilterSpell filterTarget, FilterStackObject filterSource, Duration duration) { super(duration, Outcome.Benefit); this.filterTarget = filterTarget; this.filterSource = filterSource; setText(); } - public CantBeCounteredControlledEffect(FilterSpell filterTarget, Duration duration) { - this(filterTarget, null, duration); - } - protected CantBeCounteredControlledEffect(final CantBeCounteredControlledEffect effect) { super(effect); - if (effect.filterTarget != null) { - this.filterTarget = effect.filterTarget.copy(); - } + this.filterTarget = effect.filterTarget.copy(); if (effect.filterSource != null) { this.filterSource = effect.filterSource.copy(); } @@ -52,19 +52,13 @@ public class CantBeCounteredControlledEffect extends ContinuousRuleModifyingEffe @Override public boolean applies(GameEvent event, Ability source, Game game) { + UUID controllerId = source.getControllerId(); Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.isControlledBy(source.getControllerId()) - && filterTarget.match(spell, source.getControllerId(), source, game)) { - if (filterSource == null) { - return true; - } else { - MageObject sourceObject = game.getObject(event.getSourceId()); - if (filterSource.match(sourceObject, game)) { - return true; - } - } - } - return false; + StackObject counterSource = game.getStack().getStackObject(event.getSourceId()); + return spell != null + && spell.isControlledBy(controllerId) + && filterTarget.match(spell, controllerId, source, game) + && (filterSource == null || filterSource.match(counterSource, controllerId, source, game)); } private void setText() { diff --git a/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedAllEffect.java index 9f671dfa22f..9fa645e90b6 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CantBeTargetedAllEffect.java @@ -21,7 +21,7 @@ import mage.game.stack.StackObject; public class CantBeTargetedAllEffect extends ContinuousRuleModifyingEffectImpl { private FilterPermanent filterTarget; - private FilterObject filterSource; + private FilterObject filterSource; // TODO: should be FilterStackObject public CantBeTargetedAllEffect(FilterPermanent filterTarget, Duration duration) { this(filterTarget, null, duration); @@ -65,10 +65,12 @@ public class CantBeTargetedAllEffect extends ContinuousRuleModifyingEffectImpl { // only spells have to be selected return false; } + // TODO: this is a hack for Spellbane Centaur? sourceObject = ((StackAbility) stackObject).getSourceObject(game); } else { sourceObject = stackObject; } + // TODO: this should be 4-argument match instead? if (filterSource.match(sourceObject, game)) { return true; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ChooseCreatureEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ChooseCreatureEffect.java index a88e729a4da..bcc3e11aa63 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ChooseCreatureEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ChooseCreatureEffect.java @@ -29,7 +29,7 @@ public class ChooseCreatureEffect extends OneShotEffect { public ChooseCreatureEffect(FilterPermanent filter, boolean useOffset) { super(Outcome.Benefit); this.filter = filter; - this.staticText = "choose " + filter.getMessage(); + this.staticText = "choose " + CardUtil.addArticle(filter.getMessage()); this.useOffset = useOffset; } @@ -48,6 +48,9 @@ public class ChooseCreatureEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Permanent sourcePermanent = game.getPermanentEntering(source.getSourceId()); + if (sourcePermanent == null) { + sourcePermanent = game.getPermanent(source.getSourceId()); + } if (controller == null || sourcePermanent == null) { return false; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/CreateDelayedTriggeredAbilityEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CreateDelayedTriggeredAbilityEffect.java index 8812762b3af..80b35e51bec 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CreateDelayedTriggeredAbilityEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CreateDelayedTriggeredAbilityEffect.java @@ -68,7 +68,10 @@ public class CreateDelayedTriggeredAbilityEffect extends OneShotEffect { return staticText; } if (ability.getRuleVisible()) { - return rulePrefix + CardUtil.getTextWithFirstCharLowerCase(ability.getRule()); + if (rulePrefix == null || rulePrefix.isEmpty()) { + return CardUtil.getTextWithFirstCharLowerCase(ability.getRule()); + } + return rulePrefix + ability.getRule(); } else { return ""; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/DamageTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DamageTargetEffect.java index 768462f1dd4..ae23687ce8a 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DamageTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DamageTargetEffect.java @@ -172,12 +172,17 @@ public class DamageTargetEffect extends OneShotEffect { } else { if (firstTarget.getMinNumberOfTargets() == 0) { int maxTargets = firstTarget.getMaxNumberOfTargets(); - if (maxTargets == Integer.MAX_VALUE) { - sb.append("any number of "); - } else { - sb.append("up to "); - sb.append(CardUtil.numberToText(maxTargets)); - sb.append(' '); + switch (maxTargets) { + case Integer.MAX_VALUE: + sb.append("any number of "); + break; + case 1: + sb.append("up to one "); + break; + default: + sb.append("each of up to "); + sb.append(CardUtil.numberToText(maxTargets)); + sb.append(' '); } } if (!targetName.contains("target ")) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/DestroyAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DestroyAllEffect.java index b0965e43d97..6ddebcda9a9 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DestroyAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DestroyAllEffect.java @@ -23,7 +23,11 @@ public class DestroyAllEffect extends OneShotEffect { super(Outcome.DestroyPermanent); this.filter = filter; this.noRegen = noRegen; - this.staticText = "destroy all " + filter.getMessage() + (noRegen ? ". They can't be regenerated" : ""); + String filterMessage = filter.getMessage(); + if (!filterMessage.startsWith("each") && !filterMessage.startsWith("all")) { + filterMessage = "all " + filterMessage; + } + this.staticText = "destroy " + filterMessage + (noRegen ? ". They can't be regenerated" : ""); } protected DestroyAllEffect(final DestroyAllEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java b/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java index 321c0ef7787..1118d48eb50 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java @@ -3,6 +3,7 @@ package mage.abilities.effects.common; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; +import mage.abilities.TriggeredAbility; import mage.abilities.costs.Cost; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; @@ -110,6 +111,7 @@ public class DoIfCostPaid extends OneShotEffect { didPay = true; game.informPlayers(player.getLogName() + " paid for " + mageObject.getLogName() + " - " + message); applyEffects(game, source, executingEffects); + TriggeredAbility.setDidThisTurn(source, game); player.resetStoredBookmark(game); // otherwise you can e.g. undo card drawn with Mentor of the Meek } else { // Paying cost was cancels so try to undo payment so far diff --git a/Mage/src/main/java/mage/abilities/effects/common/EnterUntappedAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/EnterUntappedAllEffect.java new file mode 100644 index 00000000000..966088bbaa9 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/EnterUntappedAllEffect.java @@ -0,0 +1,52 @@ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +public class EnterUntappedAllEffect extends ReplacementEffectImpl { + + private final FilterPermanent filter; + + public EnterUntappedAllEffect(FilterPermanent filter) { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + this.filter = filter; + staticText = filter.getMessage() + " enter untapped"; + } + + private EnterUntappedAllEffect(final EnterUntappedAllEffect effect) { + super(effect); + this.filter = effect.filter; + } + + @Override + public EnterUntappedAllEffect copy() { + return new EnterUntappedAllEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Permanent target = ((EntersTheBattlefieldEvent) event).getTarget(); + if (target != null) { + target.setTapped(false); + } + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Permanent targetObject = ((EntersTheBattlefieldEvent) event).getTarget(); + return targetObject != null && filter.match(targetObject, source.getControllerId(), source, game); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileCardYouChooseTargetOpponentEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileCardYouChooseTargetOpponentEffect.java index a6b0234d495..9a9a32d454c 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileCardYouChooseTargetOpponentEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileCardYouChooseTargetOpponentEffect.java @@ -9,6 +9,7 @@ import mage.filter.FilterCard; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; +import mage.util.CardUtil; /** * @author LevelX2 @@ -20,7 +21,7 @@ public class ExileCardYouChooseTargetOpponentEffect extends OneShotEffect { public ExileCardYouChooseTargetOpponentEffect(FilterCard filter) { super(Outcome.Discard); this.staticText = "target opponent reveals their hand. You choose " - + filter.getMessage() + (filter.getMessage().contains("from it") ? "" : " from it") + " and exile that card"; + + CardUtil.addArticle(filter.getMessage()) + (filter.getMessage().contains("from it") ? "" : " from it") + " and exile that card"; this.filter = filter; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileSourceAndReturnFaceUpEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileSourceAndReturnFaceUpEffect.java index 3743f977029..d9657b3ee5f 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileSourceAndReturnFaceUpEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileSourceAndReturnFaceUpEffect.java @@ -10,7 +10,7 @@ public class ExileSourceAndReturnFaceUpEffect extends ExileAndReturnSourceEffect public ExileSourceAndReturnFaceUpEffect() { super(PutCards.BATTLEFIELD, Pronoun.IT, true, null); - staticText = "exile {this}, then return it to the battlefield. (front face up)"; + staticText = "exile {this}, then return it to the battlefield (front face up)."; } private ExileSourceAndReturnFaceUpEffect(final ExileSourceAndReturnFaceUpEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java index 8c404a46482..edc5433bba3 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java @@ -12,9 +12,6 @@ import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.game.stack.StackObject; import mage.players.Player; -import mage.target.Target; -import mage.target.targetpointer.FirstTargetPointer; -import mage.target.targetpointer.SecondTargetPointer; import mage.util.CardUtil; import java.util.LinkedHashSet; @@ -27,8 +24,8 @@ import java.util.UUID; public class ExileTargetEffect extends OneShotEffect { private final Zone onlyFromZone; - private String exileZone = null; - private UUID exileId = null; + protected String exileZone = null; + protected UUID exileId = null; private boolean toSourceExileZone = false; // exile the targets to a source object specific exile zone (takes care of zone change counter) private boolean withName = true; // for face down - allows to hide card name in game logs before real face down apply diff --git a/Mage/src/main/java/mage/abilities/effects/common/LoseControlOnOtherPlayersControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/LoseControlOnOtherPlayersControllerEffect.java deleted file mode 100644 index 691553c7f6a..00000000000 --- a/Mage/src/main/java/mage/abilities/effects/common/LoseControlOnOtherPlayersControllerEffect.java +++ /dev/null @@ -1,41 +0,0 @@ - - -package mage.abilities.effects.common; - -import mage.constants.Outcome; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.game.Game; -import mage.players.Player; - -/** - * TODO: delete, there are already end turn code with control reset - * @author nantuko - */ -public class LoseControlOnOtherPlayersControllerEffect extends OneShotEffect { - - public LoseControlOnOtherPlayersControllerEffect(String controllingPlayerName, String controlledPlayerName) { - super(Outcome.Detriment); - staticText = controllingPlayerName + " lost control over " + controlledPlayerName; - } - - protected LoseControlOnOtherPlayersControllerEffect(final LoseControlOnOtherPlayersControllerEffect effect) { - super(effect); - } - - @Override - public LoseControlOnOtherPlayersControllerEffect copy() { - return new LoseControlOnOtherPlayersControllerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - player.resetOtherTurnsControlled(); - return true; - } - return false; - } - -} diff --git a/Mage/src/main/java/mage/abilities/effects/common/MayCastTargetCardEffect.java b/Mage/src/main/java/mage/abilities/effects/common/MayCastTargetCardEffect.java index 0953892768b..121949db9ed 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/MayCastTargetCardEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/MayCastTargetCardEffect.java @@ -156,7 +156,11 @@ public class MayCastTargetCardEffect extends OneShotEffect { } text += "."; if (thenExile) { - text += " " + ThatSpellGraveyardExileReplacementEffect.RULE_YOUR; + if (text.contains("a graveyard")) { + text += " " + ThatSpellGraveyardExileReplacementEffect.RULE_A; + } else { + text += " " + ThatSpellGraveyardExileReplacementEffect.RULE_YOUR; + } } return text; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/MillCardsEachPlayerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/MillCardsEachPlayerEffect.java index 3b979bdecb2..4ae33a78dfa 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/MillCardsEachPlayerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/MillCardsEachPlayerEffect.java @@ -97,11 +97,17 @@ public class MillCardsEachPlayerEffect extends OneShotEffect { throw new UnsupportedOperationException("TargetController type not supported."); } sb.append("mills "); - if (numberCards.toString().equals("1")) { - sb.append("a card"); - } else { - sb.append(CardUtil.numberToText(numberCards.toString())); - sb.append(" cards"); + switch (numberCards.toString()) { + case "1": + sb.append("a card"); + break; + case "X": + sb.append("X cards, where X is "); + sb.append(numberCards.getMessage()); + break; + default: + sb.append(CardUtil.numberToText(numberCards.toString())); + sb.append(" cards"); } return sb.toString(); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageFromChosenSourceToYouEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageFromChosenSourceToYouEffect.java index 967cb6ccc63..5c8d58b8b42 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageFromChosenSourceToYouEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageFromChosenSourceToYouEffect.java @@ -4,7 +4,7 @@ import mage.abilities.Ability; import mage.abilities.effects.PreventionEffectImpl; import mage.constants.Duration; import mage.constants.Outcome; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.game.Game; import mage.game.events.GameEvent; import mage.target.TargetSource; @@ -17,14 +17,14 @@ public class PreventAllDamageFromChosenSourceToYouEffect extends PreventionEffec protected final TargetSource targetSource; public PreventAllDamageFromChosenSourceToYouEffect(Duration duration) { - this(duration, new FilterObject("source")); + this(duration, new FilterSource()); } - public PreventAllDamageFromChosenSourceToYouEffect(Duration duration, FilterObject filter) { + public PreventAllDamageFromChosenSourceToYouEffect(Duration duration, FilterSource filter) { this(duration, filter, false); } - public PreventAllDamageFromChosenSourceToYouEffect(Duration duration, FilterObject filter, boolean onlyCombat) { + public PreventAllDamageFromChosenSourceToYouEffect(Duration duration, FilterSource filter, boolean onlyCombat) { super(duration, Integer.MAX_VALUE, onlyCombat); this.targetSource = new TargetSource(filter); this.staticText = setText(); diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventDamageByChosenSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventDamageByChosenSourceEffect.java new file mode 100644 index 00000000000..e51a566979b --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/PreventDamageByChosenSourceEffect.java @@ -0,0 +1,79 @@ + +package mage.abilities.effects.common; + +import mage.MageObject; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.effects.PreventionEffectImpl; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.FilterPermanent; +import mage.filter.FilterSource; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.target.Target; +import mage.target.TargetPermanent; +import mage.target.TargetSource; + +/** + * @author LevelX2 + */ + +public class PreventDamageByChosenSourceEffect extends PreventionEffectImpl { + + private Target target; + private MageObjectReference mageObjectReference; + + public PreventDamageByChosenSourceEffect() { + this(new FilterSource("a source")); + } + + public PreventDamageByChosenSourceEffect(FilterSource filterSource) { + this(filterSource, false); + } + + public PreventDamageByChosenSourceEffect(FilterSource filterSource, boolean onlyCombat) { + this(new TargetSource(filterSource), filterSource.getMessage(), onlyCombat); + } + + public PreventDamageByChosenSourceEffect(FilterPermanent filterPermanent, boolean onlyCombat) { + this(new TargetPermanent(filterPermanent), filterPermanent.getMessage(), onlyCombat); + } + + private PreventDamageByChosenSourceEffect(Target target, String filterMessage, boolean onlyCombat) { + super(Duration.EndOfTurn, Integer.MAX_VALUE, onlyCombat); + this.target = target; + staticText = "Prevent all" + (onlyCombat ? " combat" : "") + + " damage " + filterMessage + " of your choice would deal this turn"; + } + + protected PreventDamageByChosenSourceEffect(final PreventDamageByChosenSourceEffect effect) { + super(effect); + this.target = effect.target.copy(); + this.mageObjectReference = effect.mageObjectReference; + } + + @Override + public PreventDamageByChosenSourceEffect copy() { + return new PreventDamageByChosenSourceEffect(this); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); + mageObjectReference = new MageObjectReference(target.getFirstTarget(), game); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (super.applies(event, source, game)) { + MageObject mageObject = game.getObject(event.getSourceId()); + if (mageObject != null && mageObjectReference.refersTo(mageObject, game)) { + return true; + } + } + return false; + } + +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventDamageBySourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventDamageBySourceEffect.java deleted file mode 100644 index 2f254477516..00000000000 --- a/Mage/src/main/java/mage/abilities/effects/common/PreventDamageBySourceEffect.java +++ /dev/null @@ -1,66 +0,0 @@ - -package mage.abilities.effects.common; - -import mage.MageObject; -import mage.MageObjectReference; -import mage.abilities.Ability; -import mage.abilities.effects.PreventionEffectImpl; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.filter.FilterObject; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.target.TargetSource; - -/** - * @author LevelX2 - */ - -public class PreventDamageBySourceEffect extends PreventionEffectImpl { - - private TargetSource target; - private MageObjectReference mageObjectReference; - - public PreventDamageBySourceEffect() { - this(new FilterObject("a")); - } - - public PreventDamageBySourceEffect(FilterObject filterObject) { - super(Duration.EndOfTurn); - if (!filterObject.getMessage().endsWith("source")) { - filterObject.setMessage(filterObject.getMessage() + " source"); - } - this.target = new TargetSource(filterObject); - staticText = "Prevent all damage " + filterObject.getMessage() + " of your choice would deal this turn"; - } - - protected PreventDamageBySourceEffect(final PreventDamageBySourceEffect effect) { - super(effect); - this.target = effect.target.copy(); - this.mageObjectReference = effect.mageObjectReference; - } - - @Override - public PreventDamageBySourceEffect copy() { - return new PreventDamageBySourceEffect(this); - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); - mageObjectReference = new MageObjectReference(target.getFirstTarget(), game); - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (super.applies(event, source, game)) { - MageObject mageObject = game.getObject(event.getSourceId()); - if (mageObject != null && mageObjectReference.refersTo(mageObject, game)) { - return true; - } - } - return false; - } - -} diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceEffect.java new file mode 100644 index 00000000000..bec2dc502e9 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceEffect.java @@ -0,0 +1,146 @@ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.PreventionEffectData; +import mage.abilities.effects.PreventionEffectImpl; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.FilterPermanent; +import mage.filter.FilterSource; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetPermanent; +import mage.target.TargetSource; +import mage.util.CardUtil; + +import java.io.Serializable; +import java.util.UUID; + +/** + * @author Quercitron, Susucr + */ +public class PreventNextDamageFromChosenSourceEffect extends PreventionEffectImpl { + + protected final Target target; + private final boolean toYou; + private final ApplierOnPrevention onPrevention; + + public interface ApplierOnPrevention extends Serializable { + boolean apply(PreventionEffectData data, Target target, GameEvent event, Ability source, Game game); + + String getText(); + } + + public static final ApplierOnPrevention ON_PREVENT_YOU_GAIN_THAT_MUCH_LIFE = new ApplierOnPrevention() { + public String getText() { + return "You gain life equal to the damage prevented this way"; + } + + public boolean apply(PreventionEffectData data, Target target, GameEvent event, Ability source, Game game) { + if (data == null || data.getPreventedDamage() <= 0) { + return false; + } + int prevented = data.getPreventedDamage(); + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + controller.gainLife(prevented, game, source); + return true; + } + }; + + public PreventNextDamageFromChosenSourceEffect(Duration duration, boolean toYou) { + this(duration, toYou, new FilterSource()); + } + + public PreventNextDamageFromChosenSourceEffect(Duration duration, boolean toYou, FilterSource filterSource) { + this(duration, toYou, filterSource, false, null); + } + + public PreventNextDamageFromChosenSourceEffect(Duration duration, boolean toYou, ApplierOnPrevention onPrevention) { + this(duration, toYou, new FilterSource(), onPrevention); + } + + public PreventNextDamageFromChosenSourceEffect(Duration duration, boolean toYou, FilterSource filterSource, ApplierOnPrevention onPrevention) { + this(duration, toYou, filterSource, false, onPrevention); + } + + public PreventNextDamageFromChosenSourceEffect(Duration duration, boolean toYou, FilterSource filterSource, boolean onlyCombat, ApplierOnPrevention onPrevention) { + this(duration, toYou, new TargetSource(filterSource), onlyCombat, onPrevention); + } + + public PreventNextDamageFromChosenSourceEffect(Duration duration, boolean toYou, FilterPermanent filterPermanent) { + this(duration, toYou, filterPermanent, false, null); + } + + public PreventNextDamageFromChosenSourceEffect(Duration duration, boolean toYou, FilterPermanent filterPermanent, boolean onlyCombat, ApplierOnPrevention onPrevention) { + this(duration, toYou, new TargetPermanent(filterPermanent), onlyCombat, onPrevention); + } + + private PreventNextDamageFromChosenSourceEffect(Duration duration, boolean toYou, Target target, boolean onlyCombat, ApplierOnPrevention onPrevention) { + super(duration, Integer.MAX_VALUE, onlyCombat); + this.target = target; + this.toYou = toYou; + this.onPrevention = onPrevention; + this.staticText = setText(); + } + + protected PreventNextDamageFromChosenSourceEffect(final PreventNextDamageFromChosenSourceEffect effect) { + super(effect); + this.target = effect.target.copy(); + this.toYou = effect.toYou; + this.onPrevention = effect.onPrevention; + } + + @Override + public PreventNextDamageFromChosenSourceEffect copy() { + return new PreventNextDamageFromChosenSourceEffect(this); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + UUID controllerId = source.getControllerId(); + if (this.target.canChoose(controllerId, source, game)) { + this.target.choose(Outcome.PreventDamage, controllerId, source.getSourceId(), source, game); + } + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + PreventionEffectData data = preventDamageAction(event, source, game); + discard(); + if (onPrevention != null) { + onPrevention.apply(data, target, event, source, game); + } + return false; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return super.applies(event, source, game) + && (!toYou || event.getTargetId().equals(source.getControllerId())) + && target.getTargets().contains(event.getSourceId()); + } + + private String setText() { + StringBuilder sb = new StringBuilder("The next time ") + .append(CardUtil.addArticle(target.getFilter().getMessage())); + sb.append(" of your choice would deal damage"); + if (toYou) { + sb.append(" to you"); + } + if (duration == Duration.EndOfTurn) { + sb.append(" this turn"); + } + sb.append(", prevent that damage"); + if (onPrevention != null) { + sb.append(". ").append(onPrevention.getText()); + } + return sb.toString(); + } + +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToTargetEffect.java index 7b5c872dc7c..1b08d6bb798 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToTargetEffect.java @@ -1,12 +1,11 @@ package mage.abilities.effects.common; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.PreventionEffectImpl; import mage.constants.Duration; import mage.constants.Outcome; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.game.Game; import mage.game.events.GameEvent; import mage.target.TargetSource; @@ -19,14 +18,14 @@ public class PreventNextDamageFromChosenSourceToTargetEffect extends PreventionE protected final TargetSource targetSource; public PreventNextDamageFromChosenSourceToTargetEffect(Duration duration) { - this(duration, new FilterObject<>("source")); + this(duration, new FilterSource()); } - public PreventNextDamageFromChosenSourceToTargetEffect(Duration duration, FilterObject filter) { + public PreventNextDamageFromChosenSourceToTargetEffect(Duration duration, FilterSource filter) { this(duration, filter, false); } - public PreventNextDamageFromChosenSourceToTargetEffect(Duration duration, FilterObject filter, boolean onlyCombat) { + public PreventNextDamageFromChosenSourceToTargetEffect(Duration duration, FilterSource filter, boolean onlyCombat) { super(duration, Integer.MAX_VALUE, onlyCombat); this.targetSource = new TargetSource(filter); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToYouEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToYouEffect.java deleted file mode 100644 index 03ff1e1396a..00000000000 --- a/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToYouEffect.java +++ /dev/null @@ -1,77 +0,0 @@ -package mage.abilities.effects.common; - -import mage.abilities.Ability; -import mage.abilities.effects.PreventionEffectImpl; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.filter.FilterObject; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.target.TargetSource; -import mage.util.CardUtil; - -/** - * @author Quercitron - */ -public class PreventNextDamageFromChosenSourceToYouEffect extends PreventionEffectImpl { - - protected final TargetSource targetSource; - - public PreventNextDamageFromChosenSourceToYouEffect(Duration duration) { - this(duration, new FilterObject("source")); - } - - public PreventNextDamageFromChosenSourceToYouEffect(Duration duration, FilterObject filter) { - this(duration, filter, false); - } - - public PreventNextDamageFromChosenSourceToYouEffect(Duration duration, FilterObject filter, boolean onlyCombat) { - super(duration, Integer.MAX_VALUE, onlyCombat); - this.targetSource = new TargetSource(filter); - this.staticText = setText(); - } - - protected PreventNextDamageFromChosenSourceToYouEffect(final PreventNextDamageFromChosenSourceToYouEffect effect) { - super(effect); - this.targetSource = effect.targetSource.copy(); - } - - @Override - public PreventNextDamageFromChosenSourceToYouEffect copy() { - return new PreventNextDamageFromChosenSourceToYouEffect(this); - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - this.targetSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - preventDamageAction(event, source, game); - this.used = true; - return false; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (!this.used && super.applies(event, source, game)) { - if (event.getTargetId().equals(source.getControllerId()) && event.getSourceId().equals(targetSource.getFirstTarget())) { - return true; - } - } - return false; - } - - private String setText() { - StringBuilder sb = new StringBuilder("The next time ").append(CardUtil.addArticle(targetSource.getFilter().getMessage())); - sb.append(" of your choice would deal damage to you"); - if (duration == Duration.EndOfTurn) { - sb.append(" this turn"); - } - sb.append(", prevent that damage"); - return sb.toString(); - } - -} diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutCardIntoPlayWithHasteAndSacrificeEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutCardIntoPlayWithHasteAndSacrificeEffect.java index 760200024ab..6e03a65e61d 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PutCardIntoPlayWithHasteAndSacrificeEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PutCardIntoPlayWithHasteAndSacrificeEffect.java @@ -60,7 +60,7 @@ public class PutCardIntoPlayWithHasteAndSacrificeEffect extends OneShotEffect { return false; } player.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(CardUtil.getDefaultCardSideForBattlefield(game, card).getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent == null) { return false; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutOnLibrarySourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutOnLibrarySourceEffect.java index 69e70fd9bee..ad72c08f338 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PutOnLibrarySourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PutOnLibrarySourceEffect.java @@ -1,6 +1,5 @@ package mage.abilities.effects.common; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; @@ -46,6 +45,6 @@ public class PutOnLibrarySourceEffect extends OneShotEffect { if (onTop) { return player.putCardsOnTopOfLibrary(card, game, source, false); } - return player.putCardsOnBottomOfLibrary(card, game, source, false); + return player.putCardsOnBottomOfLibrary(card, game, source); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutOnLibraryTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutOnLibraryTargetEffect.java index 43e5c36323b..d337bdfa728 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PutOnLibraryTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PutOnLibraryTargetEffect.java @@ -10,6 +10,7 @@ import mage.cards.CardsImpl; import mage.constants.Outcome; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.game.stack.Spell; import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCardInYourGraveyard; @@ -63,13 +64,15 @@ public class PutOnLibraryTargetEffect extends OneShotEffect { } break; case GRAVEYARD: - Card graveyardCard = game.getCard(targetId); - if (graveyardCard != null) { - cards.add(graveyardCard); + case EXILED: + Card card = game.getCard(targetId); + if (card != null) { + cards.add(card); } break; case STACK: - Card stackSpellCard = game.getSpell(targetId).getCard(); + Spell spell = game.getSpell(targetId); + Card stackSpellCard = (spell != null) ? spell.getCard() : null; if (stackSpellCard != null) { cards.add(stackSpellCard); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutSourceCountersOnTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutSourceCountersOnTargetEffect.java index 455cba7d14e..60e0232529c 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PutSourceCountersOnTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PutSourceCountersOnTargetEffect.java @@ -27,7 +27,7 @@ public class PutSourceCountersOnTargetEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent sourcePermanent = (Permanent) getValue("permanentLeftBattlefield"); + Permanent sourcePermanent = source.getSourcePermanentOrLKI(game); Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (sourcePermanent == null || permanent == null) { return false; diff --git a/Mage/src/main/java/mage/abilities/effects/common/RemoveAllCountersSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RemoveAllCountersSourceEffect.java index 4c08de6dc00..fa0328a9da0 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/RemoveAllCountersSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/RemoveAllCountersSourceEffect.java @@ -18,7 +18,7 @@ public class RemoveAllCountersSourceEffect extends OneShotEffect { public RemoveAllCountersSourceEffect(CounterType counterType) { super(Outcome.Neutral); this.counterType = counterType; - staticText = "remove all " + counterType.getName() + " counters from it."; + staticText = "remove all " + counterType.getName() + " counters from {this}"; } protected RemoveAllCountersSourceEffect(final RemoveAllCountersSourceEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect.java index 8e852cbae62..c9f895f8fc0 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect.java @@ -13,6 +13,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; public class ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect extends OneShotEffect { @@ -39,7 +40,7 @@ public class ReturnCreatureFromGraveyardToBattlefieldAndGainHasteEffect extends Card card = game.getCard(source.getFirstTarget()); if (card != null) { controller.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); effect.setTargetPointer(new FixedTarget(permanent, game)); diff --git a/Mage/src/main/java/mage/abilities/effects/common/UntapLandsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/UntapLandsEffect.java index 376e33c0203..f4a6f812743 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/UntapLandsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/UntapLandsEffect.java @@ -1,7 +1,5 @@ package mage.abilities.effects.common; -import java.util.UUID; - import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; @@ -11,9 +9,11 @@ import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.common.TargetLandPermanent; +import mage.target.TargetPermanent; import mage.util.CardUtil; +import java.util.UUID; + /** * "Untap (up to) X lands" effect */ @@ -65,7 +65,7 @@ public class UntapLandsEffect extends OneShotEffect { } else { tappedLands = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source, game).size(); } - TargetLandPermanent target = new TargetLandPermanent(upTo ? 0 : Math.min(tappedLands, amount), amount, filter, true); + TargetPermanent target = new TargetPermanent(upTo ? 0 : Math.min(tappedLands, amount), amount, filter, true); if (target.canChoose(source.getControllerId(), source, game)) { // UI Shortcut: Check if any lands are already tapped. If there are equal/fewer than amount, give the option to add those in to be untapped now. diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardTypeSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardTypeSourceEffect.java index 5d3f0b1bea0..078e2b09e91 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardTypeSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCardTypeSourceEffect.java @@ -7,10 +7,11 @@ import mage.abilities.effects.ContinuousEffectImpl; import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.util.CardUtil; import java.util.ArrayList; import java.util.List; -import java.util.Locale; +import java.util.stream.Collectors; /** * @author emerald000 @@ -76,19 +77,20 @@ public class AddCardTypeSourceEffect extends ContinuousEffectImpl { } StringBuilder sb = new StringBuilder(); sb.append("{this} becomes "); - boolean article = false; - for (CardType cardType : addedCardTypes) { - if (!article) { - if (cardType.toString().startsWith("A") || cardType.toString().startsWith("E")) { - sb.append("an "); - } else { - sb.append("a "); - } - article = true; - } - sb.append(cardType.toString().toLowerCase(Locale.ENGLISH)).append(" "); + sb.append(CardUtil.addArticle( + addedCardTypes + .stream() + .map(CardType::toString) + .map(String::toLowerCase) + .collect(Collectors.joining(" ")) + )); + if (!addedCardTypes.contains(CardType.ARTIFACT) || !addedCardTypes.contains(CardType.CREATURE)) { + sb.append(" in addition to its other types"); + } + if (!this.getDuration().toString().isEmpty()) { + sb.append(' '); + sb.append(this.getDuration()); } - sb.append("in addition to its other types ").append(this.getDuration().toString()); return sb.toString(); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostControlledEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostControlledEffect.java index fb49dcdea5a..c1db549eaaf 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostControlledEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostControlledEffect.java @@ -9,8 +9,8 @@ import mage.constants.Duration; import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.SubLayer; +import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.util.CardUtil; @@ -25,7 +25,7 @@ public class BoostControlledEffect extends ContinuousEffectImpl { private DynamicValue power; private DynamicValue toughness; - protected FilterCreaturePermanent filter; + protected FilterPermanent filter; protected boolean excludeSource; public BoostControlledEffect(int power, int toughness, Duration duration) { @@ -40,18 +40,18 @@ public class BoostControlledEffect extends ContinuousEffectImpl { this(power, toughness, duration, StaticFilters.FILTER_PERMANENT_CREATURES, excludeSource); } - public BoostControlledEffect(int power, int toughness, Duration duration, FilterCreaturePermanent filter) { + public BoostControlledEffect(int power, int toughness, Duration duration, FilterPermanent filter) { this(StaticValue.get(power), StaticValue.get(toughness), duration, filter, false); } - public BoostControlledEffect(int power, int toughness, Duration duration, FilterCreaturePermanent filter, boolean excludeSource) { + public BoostControlledEffect(int power, int toughness, Duration duration, FilterPermanent filter, boolean excludeSource) { this(StaticValue.get(power), StaticValue.get(toughness), duration, filter, excludeSource); } /** * Note: use excludeSource rather than AnotherPredicate */ - public BoostControlledEffect(DynamicValue power, DynamicValue toughness, Duration duration, FilterCreaturePermanent filter, boolean excludeSource) { + public BoostControlledEffect(DynamicValue power, DynamicValue toughness, Duration duration, FilterPermanent filter, boolean excludeSource) { super(duration, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.BoostCreature); this.power = power; this.toughness = toughness; diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostEquippedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostEquippedEffect.java index 2cc3ce3b1bb..70e9badbd92 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostEquippedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostEquippedEffect.java @@ -1,19 +1,20 @@ - package mage.abilities.effects.common.continuous; -import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; -import mage.constants.SubLayer; import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.ContinuousEffectImpl; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.SubLayer; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; +import java.util.Optional; + /** * @author BetaSteward_at_googlemail.com */ @@ -31,14 +32,14 @@ public class BoostEquippedEffect extends ContinuousEffectImpl { this(StaticValue.get(power), StaticValue.get(toughness), duration); } - public BoostEquippedEffect(DynamicValue powerDynamicValue, DynamicValue toughnessDynamicValue) { - this(powerDynamicValue, toughnessDynamicValue, Duration.WhileOnBattlefield); + public BoostEquippedEffect(DynamicValue power, DynamicValue toughness) { + this(power, toughness, Duration.WhileOnBattlefield); } - public BoostEquippedEffect(DynamicValue powerDynamicValue, DynamicValue toughnessDynamicValue, Duration duration) { + public BoostEquippedEffect(DynamicValue power, DynamicValue toughness, Duration duration) { super(duration, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.BoostCreature); - this.power = powerDynamicValue; - this.toughness = toughnessDynamicValue; + this.power = power; + this.toughness = toughness; if (duration == Duration.EndOfTurn) { fixedTarget = true; } @@ -70,21 +71,22 @@ public class BoostEquippedEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { - Permanent creature = null; + Permanent creature; if (fixedTarget) { creature = game.getPermanent(getTargetPointer().getFirst(game, source)); } else { - Permanent equipment = game.getPermanent(source.getSourceId()); - if (equipment != null && equipment.getAttachedTo() != null) { - creature = game.getPermanent(equipment.getAttachedTo()); - } + creature = Optional + .ofNullable(source) + .map(Ability::getSourceId) + .map(game::getPermanent) + .map(Permanent::getAttachedTo) + .map(game::getPermanent) + .orElse(null); } - if (creature != null) { creature.addPower(power.calculate(game, source, this)); creature.addToughness(toughness.calculate(game, source, this)); } - return true; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java index 16d95141250..76afce20dce 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java @@ -151,7 +151,7 @@ public class GainAbilityAttachedEffect extends ContinuousEffectImpl { if (quotes) { sb.append('"'); } - sb.append(CardUtil.stripReminderText(ability.getRule("This " + targetObjectName))); + sb.append(CardUtil.stripReminderText(ability.getRule("this " + targetObjectName))); if (quotes) { sb.append('"'); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAllAbilitiesTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAllAbilitiesTargetEffect.java index 614be0bd431..81431bc87d3 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAllAbilitiesTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/LoseAllAbilitiesTargetEffect.java @@ -51,7 +51,7 @@ public class LoseAllAbilitiesTargetEffect extends ContinuousEffectImpl { return staticText; } return getTargetPointer().describeTargets(mode.getTargets(), "it") - + " loses all abilities " + (duration.toString().isEmpty() ? "" : ' ' + duration.toString()); + + " loses all abilities" + (duration.toString().isEmpty() ? "" : ' ' + duration.toString()); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/WUBRGInsteadEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/WUBRGInsteadEffect.java index ef516b424f0..1e6ed210a2c 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/WUBRGInsteadEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/WUBRGInsteadEffect.java @@ -25,7 +25,7 @@ public class WUBRGInsteadEffect extends ContinuousEffectImpl { public WUBRGInsteadEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); - staticText = "You may pay {W}{U}{B}{R}{G} rather than pay the mana cost for spells that you cast"; + staticText = "You may pay {W}{U}{B}{R}{G} rather than pay the mana cost for spells you cast"; } protected WUBRGInsteadEffect(final WUBRGInsteadEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/counter/MoveCounterTargetsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/counter/MoveCounterTargetsEffect.java new file mode 100644 index 00000000000..0c64ca5e722 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/counter/MoveCounterTargetsEffect.java @@ -0,0 +1,112 @@ +package mage.abilities.effects.common.counter; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.OneShotEffect; +import mage.choices.Choice; +import mage.choices.ChoiceImpl; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.targetpointer.EachTargetPointer; +import mage.util.RandomUtil; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author Styxo + */ +public class MoveCounterTargetsEffect extends OneShotEffect { + + private final CounterType counterType; + + public MoveCounterTargetsEffect() { + this((CounterType) null); + } + + public MoveCounterTargetsEffect(CounterType counterType) { + super(Outcome.Detriment); + this.counterType = counterType; + this.setTargetPointer(new EachTargetPointer()); + } + + protected MoveCounterTargetsEffect(final MoveCounterTargetsEffect effect) { + super(effect); + this.counterType = effect.counterType; + } + + @Override + public MoveCounterTargetsEffect copy() { + return new MoveCounterTargetsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + List permanents = this + .getTargetPointer() + .getTargets(game, source) + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + if (permanents.size() < 2) { + return false; + } + Permanent fromPermanent = permanents.get(0); + if (counterType != null && !fromPermanent.getCounters(game).containsKey(counterType)) { + return false; + } + CounterType typeToRemove; + if (counterType == null) { + Set types = new HashSet<>(fromPermanent.getCounters(game).keySet()); + switch (types.size()) { + case 0: + return false; + case 1: + typeToRemove = CounterType.findByName(RandomUtil.randomFromCollection(types)); + break; + default: + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Choice choice = new ChoiceImpl(true); + choice.setChoices(types); + choice.setMessage("Choose a type of counter to move"); + player.choose(Outcome.BoostCreature, choice, game); + typeToRemove = CounterType.findByName(choice.getChoice()); + } + } else { + typeToRemove = counterType; + } + if (typeToRemove == null) { + return false; + } + Permanent toPermanent = permanents.get(1); + if (!toPermanent.addCounters(typeToRemove.createInstance(), source, game)) { + return false; + } + fromPermanent.removeCounters(typeToRemove.createInstance(), source, game); + return true; + } + + @Override + public String getText(Mode mode) { + if (!staticText.isEmpty()) { + return staticText; + } + StringBuilder sb = new StringBuilder("move "); + sb.append(Optional + .ofNullable(counterType) + .map(c -> counterType.getArticle() + ' ' + counterType.getName()) + .orElse("a")); + sb.append(" counter from "); + sb.append(mode.getTargets().get(0).getDescription()); + sb.append(" onto "); + sb.append(mode.getTargets().get(1).getDescription()); + return sb.toString(); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/counter/MoveCountersTargetsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/counter/MoveCountersTargetsEffect.java deleted file mode 100644 index e797f82c7e2..00000000000 --- a/Mage/src/main/java/mage/abilities/effects/common/counter/MoveCountersTargetsEffect.java +++ /dev/null @@ -1,77 +0,0 @@ - -package mage.abilities.effects.common.counter; - -import mage.abilities.Ability; -import mage.abilities.Mode; -import mage.abilities.effects.OneShotEffect; -import mage.constants.Outcome; -import mage.counters.CounterType; -import mage.game.Game; -import mage.game.permanent.Permanent; - -/** - * @author Styxo - */ -public class MoveCountersTargetsEffect extends OneShotEffect { - - private final CounterType counterType; - private final int amount; - - public MoveCountersTargetsEffect(CounterType counterType, int amount) { - super(Outcome.Detriment); - this.counterType = counterType; - this.amount = amount; - - } - - protected MoveCountersTargetsEffect(final MoveCountersTargetsEffect effect) { - super(effect); - this.counterType = effect.counterType; - this.amount = effect.amount; - } - - @Override - public MoveCountersTargetsEffect copy() { - return new MoveCountersTargetsEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent removeTargetCreature = game.getPermanent(getTargetPointer().getTargets(game, source).get(0)); - Permanent addTargetCreature = game.getPermanent(getTargetPointer().getTargets(game, source).get(1)); - if (removeTargetCreature != null && addTargetCreature != null && removeTargetCreature.getCounters(game).getCount(counterType) >= amount) { - removeTargetCreature.removeCounters(counterType.createInstance(amount), source, game); - addTargetCreature.addCounters(counterType.createInstance(amount), source.getControllerId(), source, game); - if (!game.isSimulation()) { - game.informPlayers("Moved " + amount + ' ' + counterType.getName() + " counter" + (amount > 1 ? "s" : "") + " from " + removeTargetCreature.getLogName() + " to " + addTargetCreature.getLogName()); - } - return true; - } - return false; - } - - @Override - public String getText(Mode mode) { - if (!staticText.isEmpty()) { - return staticText; - } - - StringBuilder sb = new StringBuilder("move "); - if (amount > 1) { - sb.append(amount); - } else { - sb.append('a'); - } - sb.append(' '); - sb.append(counterType.getName()); - sb.append(" counter"); - if (amount > 1) { - sb.append("s "); - } else { - sb.append(' '); - } - sb.append("from one target creature to another target creature"); - - return sb.toString(); - } -} diff --git a/Mage/src/main/java/mage/abilities/effects/common/replacement/GainDoubleLifeReplacementEffect.java b/Mage/src/main/java/mage/abilities/effects/common/replacement/GainDoubleLifeReplacementEffect.java new file mode 100644 index 00000000000..593c7636950 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/replacement/GainDoubleLifeReplacementEffect.java @@ -0,0 +1,45 @@ +package mage.abilities.effects.common.replacement; + +import mage.abilities.Ability; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.util.CardUtil; + +/** + * @author TheElk801 + */ +public class GainDoubleLifeReplacementEffect extends ReplacementEffectImpl { + + public GainDoubleLifeReplacementEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "if you would gain life, you gain twice that much life instead"; + } + + private GainDoubleLifeReplacementEffect(final GainDoubleLifeReplacementEffect effect) { + super(effect); + } + + @Override + public GainDoubleLifeReplacementEffect copy() { + return new GainDoubleLifeReplacementEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + event.setAmount(CardUtil.overflowMultiply(event.getAmount(), 2)); + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.GAIN_LIFE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.isControlledBy(event.getPlayerId()); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/replacement/MorEnteringTappedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/replacement/MorEnteringTappedEffect.java new file mode 100644 index 00000000000..66bc00ef654 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/replacement/MorEnteringTappedEffect.java @@ -0,0 +1,69 @@ +package mage.abilities.effects.common.replacement; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; + +/** + * Used to affect a spell on the stack. + * The permanent it may become enters tapped. + *

    + * Short-lived replacement effect, auto-cleanup if the mor is no longer a spell. + * + * @author Susucr + */ +public class MorEnteringTappedEffect extends ReplacementEffectImpl { + + private final MageObjectReference mor; + + public MorEnteringTappedEffect(MageObjectReference mor) { + super(Duration.OneUse, Outcome.Tap); + this.staticText = "That permanent enters the battlefield tapped"; + this.mor = mor; + } + + private MorEnteringTappedEffect(final MorEnteringTappedEffect effect) { + super(effect); + this.mor = effect.mor; + } + + @Override + public MorEnteringTappedEffect copy() { + return new MorEnteringTappedEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Spell spell = game.getSpell(event.getSourceId()); + Spell morSpell = mor.getSpell(game); + if (morSpell == null) { + // cleanup if something other than resolving happens to the spell. + discard(); + return false; + } + return spell != null + && morSpell.getSourceId() == spell.getSourceId() + && morSpell.getZoneChangeCounter(game) == spell.getZoneChangeCounter(game); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget(); + if (permanent != null) { + permanent.setTapped(true); + } + return false; + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutInHandEffect.java b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutInHandEffect.java index f94fe375a21..05e7471e6f3 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutInHandEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryGraveyardPutInHandEffect.java @@ -34,8 +34,10 @@ public class SearchLibraryGraveyardPutInHandEffect extends OneShotEffect { super(Outcome.Benefit); this.filter = filter; this.forceToSearchBoth = forceToSearchBoth; - staticText = (youMay ? "you may " : "") + "search your library and" + (forceToSearchBoth ? "" : "/or") + " graveyard for a card named " + filter.getMessage() - + ", reveal it, and put it into your hand. " + (forceToSearchBoth ? "Then shuffle" : "If you search your library this way, shuffle"); + staticText = (youMay ? "you may " : "") + "search your library and" + (forceToSearchBoth ? "" : "/or") + + " graveyard for " + (filter.getMessage().contains(" card") ? "" : "a card named ") + + filter.getMessage() + ", reveal it, and put it into your hand. " + + (forceToSearchBoth ? "Then shuffle" : "If you search your library this way, shuffle"); } protected SearchLibraryGraveyardPutInHandEffect(final SearchLibraryGraveyardPutInHandEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/effects/keyword/ScryEffect.java b/Mage/src/main/java/mage/abilities/effects/keyword/ScryEffect.java index d4bdf682bae..ac4c1b6397f 100644 --- a/Mage/src/main/java/mage/abilities/effects/keyword/ScryEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/keyword/ScryEffect.java @@ -1,6 +1,8 @@ package mage.abilities.effects.keyword; import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; import mage.game.Game; @@ -8,37 +10,45 @@ import mage.players.Player; import mage.util.CardUtil; /** - * @author BetaSteward_at_googlemail.com + * @author BetaSteward_at_googlemail.com, Susucr */ public class ScryEffect extends OneShotEffect { - protected final int scryNumber; + protected DynamicValue amount; protected final boolean showEffectHint; public ScryEffect(int scryNumber) { this(scryNumber, true); } + public ScryEffect(DynamicValue amount) { + this(amount, false); + } + public ScryEffect(int scryNumber, boolean showEffectHint) { + this(StaticValue.get(scryNumber), showEffectHint); + } + + public ScryEffect(DynamicValue amount, boolean showEffectHint) { super(Outcome.Benefit); - this.scryNumber = scryNumber; + this.amount = amount.copy(); this.showEffectHint = showEffectHint; - this.setText(); + this.staticText = generateText(); } protected ScryEffect(final ScryEffect effect) { super(effect); - this.scryNumber = effect.scryNumber; + this.amount = effect.amount.copy(); this.showEffectHint = effect.showEffectHint; } @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - return player.scry(scryNumber, source, game); + if (player == null) { + return false; } - return false; + return player.scry(this.amount.calculate(game, source, this), source, game); } @Override @@ -46,19 +56,24 @@ public class ScryEffect extends OneShotEffect { return new ScryEffect(this); } - private void setText() { - StringBuilder sb = new StringBuilder("scry ").append(scryNumber); - if (!showEffectHint) { - staticText = sb.toString(); - return; + private String generateText() { + StringBuilder sb = new StringBuilder("scry "); + String value = amount.toString(); + sb.append(value); + String message = amount.getMessage(); + if (!message.isEmpty()) { + sb.append(value.equals("X") ? ", where X is " : " for each "); + sb.append(message); } - if (scryNumber == 1) { - sb.append(". (Look at the top card of your library. You may put that card on the bottom.)"); - } else { - sb.append(". (Look at the top "); - sb.append(CardUtil.numberToText(scryNumber)); - sb.append(" cards of your library, then put any number of them on the bottom and the rest on top in any order.)"); + if (showEffectHint) { + if (value == "1") { + sb.append(". (Look at the top card of your library. You may put that card on the bottom.)"); + } else { + sb.append(". (Look at the top "); + sb.append(CardUtil.numberToText(value)); + sb.append(" cards of your library, then put any number of them on the bottom and the rest on top in any order.)"); + } } - staticText = sb.toString(); + return sb.toString(); } } diff --git a/Mage/src/main/java/mage/abilities/hint/common/GateYouControlHint.java b/Mage/src/main/java/mage/abilities/hint/common/GatesYouControlHint.java similarity index 75% rename from Mage/src/main/java/mage/abilities/hint/common/GateYouControlHint.java rename to Mage/src/main/java/mage/abilities/hint/common/GatesYouControlHint.java index 5a2498624aa..f6f7bc93084 100644 --- a/Mage/src/main/java/mage/abilities/hint/common/GateYouControlHint.java +++ b/Mage/src/main/java/mage/abilities/hint/common/GatesYouControlHint.java @@ -9,11 +9,11 @@ import mage.game.Game; /** * @author JayDi85 */ -public enum GateYouControlHint implements Hint { +public enum GatesYouControlHint implements Hint { instance; - private static final Hint hint = new ValueHint("Gate you control", GateYouControlCount.instance); + private static final Hint hint = new ValueHint("Gates you control", GateYouControlCount.instance); @Override public String getText(Game game, Ability ability) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/AffinityEffect.java b/Mage/src/main/java/mage/abilities/keyword/AffinityAbility.java similarity index 50% rename from Mage/src/main/java/mage/abilities/effects/common/AffinityEffect.java rename to Mage/src/main/java/mage/abilities/keyword/AffinityAbility.java index ebf43626e2c..4d3f1ca0853 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/AffinityEffect.java +++ b/Mage/src/main/java/mage/abilities/keyword/AffinityAbility.java @@ -1,11 +1,10 @@ -package mage.abilities.effects.common; +package mage.abilities.keyword; import mage.abilities.Ability; import mage.abilities.SpellAbility; +import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.cost.CostModificationEffectImpl; -import mage.constants.CostModificationType; -import mage.constants.Duration; -import mage.constants.Outcome; +import mage.constants.*; import mage.filter.common.FilterControlledPermanent; import mage.game.Game; import mage.util.CardUtil; @@ -15,15 +14,45 @@ import mage.util.CardUtil; * 702.40a Affinity is a static ability that functions while the spell with affinity is on the stack. * “Affinity for [text]” means “This spell costs you {1} less to cast for each [text] you control.” * 702.40b If a spell has multiple instances of affinity, each of them applies. + * + * @author Loki, TheElk801 */ -public class AffinityEffect extends CostModificationEffectImpl { +public class AffinityAbility extends SimpleStaticAbility { + + private AffinityType affinityType; + + public AffinityAbility(AffinityType affinityType) { + super(Zone.ALL, new AffinityEffect(affinityType.getFilter())); + setRuleAtTheTop(true); + this.addHint(affinityType.getHint()); + this.affinityType = affinityType; + } + + protected AffinityAbility(final AffinityAbility ability) { + super(ability); + this.affinityType = ability.affinityType; + } + + @Override + public AffinityAbility copy() { + return new AffinityAbility(this); + } + + @Override + public String getRule() { + return "Affinity for " + affinityType.getFilter().getMessage() + + " (This spell costs {1} less to cast for each " + + affinityType.getSingularName() + " you control.)"; + } +} + +class AffinityEffect extends CostModificationEffectImpl { private final FilterControlledPermanent filter; public AffinityEffect(FilterControlledPermanent affinityFilter) { super(Duration.WhileOnStack, Outcome.Benefit, CostModificationType.REDUCE_COST); this.filter = affinityFilter; - staticText = "Affinity for " + filter.getMessage(); } protected AffinityEffect(final AffinityEffect effect) { @@ -34,14 +63,18 @@ public class AffinityEffect extends CostModificationEffectImpl { @Override public boolean apply(Game game, Ability source, Ability abilityToModify) { // abilityToModify.getControllerId() works with Sen Triplets and in multiplayer games, see https://github.com/magefree/mage/issues/5931 - int count = game.getBattlefield().getActivePermanents(filter, abilityToModify.getControllerId(), source, game).size(); - CardUtil.reduceCost(abilityToModify, count); + CardUtil.reduceCost( + abilityToModify, game.getBattlefield().count( + filter, abilityToModify.getControllerId(), source, game + ) + ); return true; } @Override public boolean applies(Ability abilityToModify, Ability source, Game game) { - return abilityToModify instanceof SpellAbility && abilityToModify.getSourceId().equals(source.getSourceId()); + return abilityToModify instanceof SpellAbility + && abilityToModify.getSourceId().equals(source.getSourceId()); } @Override diff --git a/Mage/src/main/java/mage/abilities/keyword/AffinityForArtifactsAbility.java b/Mage/src/main/java/mage/abilities/keyword/AffinityForArtifactsAbility.java index d5e5d5b203a..1b6793dd945 100644 --- a/Mage/src/main/java/mage/abilities/keyword/AffinityForArtifactsAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/AffinityForArtifactsAbility.java @@ -1,21 +1,14 @@ package mage.abilities.keyword; -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.common.AffinityEffect; -import mage.abilities.hint.common.ArtifactYouControlHint; -import mage.constants.Zone; -import mage.filter.StaticFilters; +import mage.constants.AffinityType; /** * Affinity for artifacts */ -public class AffinityForArtifactsAbility extends SimpleStaticAbility { +public class AffinityForArtifactsAbility extends AffinityAbility { public AffinityForArtifactsAbility() { - super(Zone.ALL, new AffinityEffect(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT)); - setRuleAtTheTop(true); - - this.addHint(ArtifactYouControlHint.instance); + super(AffinityType.ARTIFACTS); } protected AffinityForArtifactsAbility(final AffinityForArtifactsAbility ability) { @@ -26,9 +19,4 @@ public class AffinityForArtifactsAbility extends SimpleStaticAbility { public AffinityForArtifactsAbility copy() { return new AffinityForArtifactsAbility(this); } - - @Override - public String getRule() { - return "Affinity for artifacts (This spell costs {1} less to cast for each artifact you control.)"; - } } diff --git a/Mage/src/main/java/mage/abilities/keyword/AffinityForLandTypeAbility.java b/Mage/src/main/java/mage/abilities/keyword/AffinityForLandTypeAbility.java deleted file mode 100644 index cc63bd9249d..00000000000 --- a/Mage/src/main/java/mage/abilities/keyword/AffinityForLandTypeAbility.java +++ /dev/null @@ -1,42 +0,0 @@ -package mage.abilities.keyword; - -import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.common.AffinityEffect; -import mage.abilities.hint.ValueHint; -import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterControlledPermanent; - -/** - * @author LevelX2 - */ - -public class AffinityForLandTypeAbility extends SimpleStaticAbility { - private final String rulesText; - - public AffinityForLandTypeAbility(SubType landType, String pluralName) { - super(Zone.ALL, null); - rulesText = "Affinity for " + pluralName + " (This spell costs {1} less to cast for each " + landType + " you control.)"; - setRuleAtTheTop(true); - - FilterControlledPermanent filter = new FilterControlledPermanent(landType); - addEffect(new AffinityEffect(filter)); - addHint(new ValueHint(pluralName + " you control", new PermanentsOnBattlefieldCount(filter))); - } - - protected AffinityForLandTypeAbility(final AffinityForLandTypeAbility ability) { - super(ability); - this.rulesText = ability.rulesText; - } - - @Override - public AffinityForLandTypeAbility copy() { - return new AffinityForLandTypeAbility(this); - } - - @Override - public String getRule() { - return rulesText; - } -} diff --git a/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java b/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java index 1fd68ab47dc..6349655d944 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java @@ -21,7 +21,6 @@ import mage.players.Player; import mage.target.common.TargetControlledPermanent; import mage.util.CardUtil; -import java.util.Objects; import java.util.UUID; /* @@ -188,13 +187,9 @@ class ConspireTriggeredAbility extends CastSourceTriggeredAbility { return false; } Spell spell = game.getStack().getSpell(event.getSourceId()); - return spell != null - && spell - .getSpellAbility() - .getAllEffects() - .stream() - .map(effect -> effect.getValue("ConspireActivation" + conspireId + addedById)) - .anyMatch(Objects::nonNull); + return spell != null && CardUtil.getEffectValueFromAbility( + spell.getSpellAbility(), "ConspireActivation" + conspireId + addedById, Boolean.class + ).orElse(false); } @Override diff --git a/Mage/src/main/java/mage/abilities/keyword/EquipAbility.java b/Mage/src/main/java/mage/abilities/keyword/EquipAbility.java index 76b469d2e59..9c55d877d04 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EquipAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EquipAbility.java @@ -88,7 +88,7 @@ public class EquipAbility extends ActivatedAbilityImpl { String targetText = getTargets().get(0) != null ? getTargets().get(0).getFilter().getMessage() : "creature"; String reminderText = " (" + getManaCosts().getText() + ": Attach to target " + targetText + ". Equip only as a sorcery.)"; - StringBuilder sb = new StringBuilder("Equip"); + StringBuilder sb = new StringBuilder(addRulePrefix("Equip")); if (!targetText.equals("creature you control")) { sb.append(' ').append(targetText); } @@ -109,6 +109,6 @@ public class EquipAbility extends ActivatedAbilityImpl { if (showAbilityHint) { sb.append(reminderText); } - return sb.toString(); + return sb.toString().replace("..", "."); } } diff --git a/Mage/src/main/java/mage/abilities/keyword/EscalateAbility.java b/Mage/src/main/java/mage/abilities/keyword/EscalateAbility.java index 38db09dc2a0..4fe3b2cacb9 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EscalateAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EscalateAbility.java @@ -2,15 +2,18 @@ package mage.abilities.keyword; import mage.abilities.Ability; +import mage.abilities.Mode; import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.Cost; +import mage.abilities.costs.mana.ManaCost; import mage.abilities.effects.common.cost.CostModificationEffectImpl; import mage.constants.CostModificationType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; +import mage.util.CardUtil; /** * @author emerald000 @@ -39,7 +42,6 @@ class EscalateEffect extends CostModificationEffectImpl { EscalateEffect(Cost cost) { super(Duration.WhileOnBattlefield, Outcome.Detriment, CostModificationType.INCREASE_COST); this.cost = cost; - this.staticText = "Escalate " + cost.getText() + " (Pay this cost for each mode chosen beyond the first.)"; } private EscalateEffect(final EscalateEffect effect) { @@ -68,4 +70,19 @@ class EscalateEffect extends CostModificationEffectImpl { public EscalateEffect copy() { return new EscalateEffect(this); } + + @Override + public String getText(Mode mode) { + StringBuilder sb = new StringBuilder("Escalate"); + if (cost instanceof ManaCost) { + sb.append(' '); + sb.append(cost.getText()); + } else { + sb.append("—"); + sb.append(CardUtil.getTextWithFirstCharUpperCase(cost.getText())); + sb.append('.'); + } + sb.append(" (Pay this cost for each mode chosen beyond the first.)"); + return sb.toString(); + } } diff --git a/Mage/src/main/java/mage/abilities/keyword/EvokeAbility.java b/Mage/src/main/java/mage/abilities/keyword/EvokeAbility.java index c14e5336849..4ca62852bbc 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EvokeAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EvokeAbility.java @@ -1,12 +1,10 @@ package mage.abilities.keyword; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.EvokedCondition; import mage.abilities.costs.AlternativeSourceCostsImpl; import mage.abilities.costs.Cost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; /** @@ -24,11 +22,9 @@ public class EvokeAbility extends AlternativeSourceCostsImpl { public EvokeAbility(Cost cost) { super(EVOKE_KEYWORD, REMINDER_TEXT, cost); - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new SacrificeSourceEffect(true)), - EvokedCondition.instance, "When this permanent enters the battlefield, if its evoke cost was paid, its controller sacrifices it."); - ability.setRuleVisible(false); - addSubAbility(ability); + this.addSubAbility(new EntersBattlefieldTriggeredAbility( + new SacrificeSourceEffect(true).setText("its controller sacrifices it") + ).setTriggerPhrase("When this permanent enters, ").withInterveningIf(EvokedCondition.instance).setRuleVisible(false)); } private EvokeAbility(final EvokeAbility ability) { @@ -40,7 +36,7 @@ public class EvokeAbility extends AlternativeSourceCostsImpl { return new EvokeAbility(this); } - public static String getActivationKey(){ + public static String getActivationKey() { return getActivationKey(EVOKE_KEYWORD); } } diff --git a/Mage/src/main/java/mage/abilities/keyword/EvolveAbility.java b/Mage/src/main/java/mage/abilities/keyword/EvolveAbility.java index 09fb8dee62a..a80bb2c43ce 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EvolveAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EvolveAbility.java @@ -10,6 +10,7 @@ import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import mage.util.CardUtil; /** * FAQ 2013/01/11 @@ -75,18 +76,14 @@ public class EvolveAbility extends EntersBattlefieldAllTriggeredAbility { @Override public boolean checkInterveningIfClause(Game game) { Permanent sourcePermanent = getSourcePermanentOrLKI(game); - Permanent permanentEntering = (Permanent) this - .getEffects() - .stream() - .map(effect -> effect.getValue("permanentEnteringBattlefield")) - .findFirst() - .orElse(null); return sourcePermanent != null - && permanentEntering != null && sourcePermanent.isCreature(game) - && permanentEntering.isCreature(game) - && (permanentEntering.getPower().getValue() > sourcePermanent.getPower().getValue() - || permanentEntering.getToughness().getValue() > sourcePermanent.getToughness().getValue()); + && CardUtil + .getEffectValueFromAbility(this, "permanentEnteringBattlefield", Permanent.class) + .filter(permanent -> permanent.isCreature(game)) + .filter(permanent -> sourcePermanent.getPower().getValue() < permanent.getPower().getValue() + || sourcePermanent.getToughness().getValue() < permanent.getToughness().getValue()) + .isPresent(); } @Override diff --git a/Mage/src/main/java/mage/abilities/keyword/ExhaustAbility.java b/Mage/src/main/java/mage/abilities/keyword/ExhaustAbility.java index f8cd4e3f532..c34fcfc2a6c 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ExhaustAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ExhaustAbility.java @@ -12,15 +12,14 @@ import mage.game.Game; */ public class ExhaustAbility extends ActivatedAbilityImpl { - private boolean withReminderText = true; + private final boolean withReminderText; public ExhaustAbility(Effect effect, Cost cost) { - super(Zone.BATTLEFIELD, effect, cost); + this(effect, cost, true); } public ExhaustAbility(Effect effect, Cost cost, boolean withReminderText) { super(Zone.BATTLEFIELD, effect, cost); - this.setRuleVisible(false); this.withReminderText = withReminderText; } @@ -30,11 +29,6 @@ public class ExhaustAbility extends ActivatedAbilityImpl { this.withReminderText = ability.withReminderText; } - public ExhaustAbility withReminderText(boolean withReminderText) { - this.withReminderText = withReminderText; - return this; - } - @Override public ExhaustAbility copy() { return new ExhaustAbility(this); @@ -45,8 +39,8 @@ public class ExhaustAbility extends ActivatedAbilityImpl { ActivationInfo info = getActivationInfo(game); if (info != null && info.totalActivations >= maxActivationsPerGame) { boolean canActivate = !game.getContinuousEffects() - .asThough(sourceId, AsThoughEffectType.ALLOW_EXHAUST_PER_TURN, this, controllerId, game) - .isEmpty(); + .asThough(sourceId, AsThoughEffectType.ALLOW_EXHAUST_PER_TURN, this, controllerId, game) + .isEmpty(); if (canActivate) { return true; } diff --git a/Mage/src/main/java/mage/abilities/keyword/GiftAbility.java b/Mage/src/main/java/mage/abilities/keyword/GiftAbility.java index 7070eb9bd89..f0909d8cb47 100644 --- a/Mage/src/main/java/mage/abilities/keyword/GiftAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/GiftAbility.java @@ -6,7 +6,6 @@ import mage.abilities.StaticAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.GiftWasPromisedCondition; import mage.abilities.costs.*; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.constants.GiftType; @@ -53,11 +52,10 @@ public class GiftAbility extends StaticAbility implements OptionalAdditionalSour this.rule = additionalCost.getName() + ' ' + additionalCost.getReminderText(); this.setRuleAtTheTop(true); if (card.isPermanent()) { - this.addSubAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new PromiseGiftEffect(giftType)), - GiftWasPromisedCondition.TRUE, "When this permanent enters, " + - "if the gift was promised, they " + giftType.getDescription() + '.' - ).setRuleVisible(false)); + this.addSubAbility(new EntersBattlefieldTriggeredAbility(new PromiseGiftEffect(giftType)) + .setTriggerPhrase("When this permanent enters, ") + .withInterveningIf(GiftWasPromisedCondition.TRUE) + .setRuleVisible(false)); } else { card.getSpellAbility().addEffect(new PromiseGiftEffect(giftType)); } @@ -154,6 +152,7 @@ class PromiseGiftEffect extends OneShotEffect { PromiseGiftEffect(GiftType giftType) { super(Outcome.Benefit); this.giftType = giftType; + staticText = "they " + giftType.getDescription(); } private PromiseGiftEffect(final PromiseGiftEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/keyword/ImpendingAbility.java b/Mage/src/main/java/mage/abilities/keyword/ImpendingAbility.java index 63cf3019712..647cff7afd4 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ImpendingAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ImpendingAbility.java @@ -1,7 +1,6 @@ package mage.abilities.keyword; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; @@ -13,6 +12,7 @@ import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.AddContinuousEffectToGame; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.constants.*; import mage.counters.CounterType; import mage.game.Game; @@ -37,7 +37,7 @@ public class ImpendingAbility extends AlternativeSourceCostsImpl { private static final String IMPENDING_REMINDER = "If you cast this spell for its impending cost, " + "it enters with %s time counters and isn't a creature until the last is removed. " + "At the beginning of your end step, remove a time counter from it."; - private static final Condition counterCondition = new SourceHasCounterCondition(CounterType.TIME, 0, 0); + private static final Condition counterCondition = new SourceHasCounterCondition(CounterType.TIME, ComparisonType.EQUAL_TO, 0); public ImpendingAbility(int amount, String manaString) { super(IMPENDING_KEYWORD + ' ' + amount, String.format(IMPENDING_REMINDER, CardUtil.numberToText(amount)), new ManaCostsImpl<>(manaString), IMPENDING_KEYWORD); diff --git a/Mage/src/main/java/mage/abilities/keyword/LevelerCardBuilder.java b/Mage/src/main/java/mage/abilities/keyword/LevelerCardBuilder.java index 7c28881cfdf..cf5bf334ca5 100644 --- a/Mage/src/main/java/mage/abilities/keyword/LevelerCardBuilder.java +++ b/Mage/src/main/java/mage/abilities/keyword/LevelerCardBuilder.java @@ -4,14 +4,15 @@ import mage.abilities.Abilities; import mage.abilities.AbilitiesImpl; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.CompoundCondition; import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.abilities.effects.common.continuous.SetBasePowerToughnessSourceEffect; +import mage.constants.ComparisonType; import mage.constants.Duration; -import mage.constants.Zone; import mage.counters.CounterType; import java.util.ArrayList; @@ -46,7 +47,11 @@ public class LevelerCardBuilder { public List build() { List constructed = new ArrayList<>(); - Condition condition = new SourceHasCounterCondition(CounterType.LEVEL, level1, level2); + Condition condition = new CompoundCondition( + "", + new SourceHasCounterCondition(CounterType.LEVEL, ComparisonType.OR_GREATER, level1), + new SourceHasCounterCondition(CounterType.LEVEL, ComparisonType.OR_LESS, level2) + ); for (Ability ability : abilities) { ContinuousEffect effect = new GainAbilitySourceEffect(ability); ConditionalContinuousEffect abEffect = new ConditionalContinuousEffect(effect, condition, ""); diff --git a/Mage/src/main/java/mage/abilities/keyword/ModularAbility.java b/Mage/src/main/java/mage/abilities/keyword/ModularAbility.java index 30d4caf5fdd..f11bb84d88c 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ModularAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ModularAbility.java @@ -17,7 +17,7 @@ import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.common.TargetArtifactPermanent; +import mage.target.TargetPermanent; import mage.util.CardUtil; import java.util.ArrayList; @@ -53,7 +53,7 @@ public class ModularAbility extends DiesSourceTriggeredAbility { public ModularAbility(Card card, int amount, boolean sunburst) { super(new ModularDistributeCounterEffect(), true); - this.addTarget(new TargetArtifactPermanent(filter)); + this.addTarget(new TargetPermanent(filter)); this.amount = amount; this.sunburst = sunburst; if (sunburst) { @@ -91,7 +91,7 @@ public class ModularAbility extends DiesSourceTriggeredAbility { public String getRule() { if (sunburst) { return "Modular—Sunburst (This enters the battlefield with a +1/+1 counter on it for each" - + " color of mana spent to cast it. When it dies, you may put its +1/+1 counters on target artifact creature.)"; + + " color of mana spent to cast it. When it dies, you may put its +1/+1 counters on target artifact creature.)"; } else { return "Modular " + amount + " (This creature enters the battlefield with " + CardUtil.getOneOneCountersText(amount) + diff --git a/Mage/src/main/java/mage/abilities/keyword/MonstrosityAbility.java b/Mage/src/main/java/mage/abilities/keyword/MonstrosityAbility.java index e7555cc659e..66dc7b6f87d 100644 --- a/Mage/src/main/java/mage/abilities/keyword/MonstrosityAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/MonstrosityAbility.java @@ -4,6 +4,8 @@ import mage.abilities.Ability; import mage.abilities.ActivatedAbilityImpl; import mage.abilities.costs.CostAdjuster; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.hint.common.MonstrousHint; import mage.constants.Outcome; @@ -14,6 +16,8 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.util.CardUtil; +import java.util.Optional; + /** * Monstrosity @@ -43,7 +47,7 @@ import mage.util.CardUtil; public class MonstrosityAbility extends ActivatedAbilityImpl { - private final int monstrosityValue; + private final DynamicValue monstrosityValue; public MonstrosityAbility(String manaString, int monstrosityValue) { this(manaString, monstrosityValue, null, ""); @@ -56,6 +60,10 @@ public class MonstrosityAbility extends ActivatedAbilityImpl { * @param costAdjusterText Clarifies the cost adjusting condition(s). */ public MonstrosityAbility(String manaString, int monstrosityValue, CostAdjuster costAdjuster, String costAdjusterText) { + this(manaString, StaticValue.get(monstrosityValue), costAdjuster, costAdjusterText); + } + + public MonstrosityAbility(String manaString, DynamicValue monstrosityValue, CostAdjuster costAdjuster, String costAdjusterText) { super(Zone.BATTLEFIELD, new BecomeMonstrousSourceEffect(monstrosityValue, costAdjusterText), new ManaCostsImpl<>(manaString)); this.monstrosityValue = monstrosityValue; this.addHint(MonstrousHint.instance); @@ -72,7 +80,7 @@ public class MonstrosityAbility extends ActivatedAbilityImpl { return new MonstrosityAbility(this); } - public int getMonstrosityValue() { + public DynamicValue getMonstrosityValue() { return monstrosityValue; } } @@ -80,11 +88,11 @@ public class MonstrosityAbility extends ActivatedAbilityImpl { class BecomeMonstrousSourceEffect extends OneShotEffect { - public BecomeMonstrousSourceEffect(int monstrosityValue) { + BecomeMonstrousSourceEffect(DynamicValue monstrosityValue) { this(monstrosityValue, ""); } - public BecomeMonstrousSourceEffect(int monstrosityValue, String costAdjusterText) { + BecomeMonstrousSourceEffect(DynamicValue monstrosityValue, String costAdjusterText) { super(Outcome.BoostCreature); this.staticText = setText(monstrosityValue, costAdjusterText); } @@ -104,11 +112,14 @@ class BecomeMonstrousSourceEffect extends OneShotEffect { if (permanent == null || permanent.isMonstrous()) { return false; } - int monstrosityValue = ((MonstrosityAbility) source).getMonstrosityValue(); - // handle monstrosity = X - if (monstrosityValue == Integer.MAX_VALUE) { - monstrosityValue = CardUtil.getSourceCostsTag(game, source, "X", 0); - } + int monstrosityValue = Optional + .ofNullable(source) + .map(MonstrosityAbility.class::cast) + .map(MonstrosityAbility::getMonstrosityValue) + .map(dynamicValue -> dynamicValue.calculate(game, source, this)) + // handle monstrosity = X + .map(i -> i == Integer.MAX_VALUE ? CardUtil.getSourceCostsTag(game, source, "X", 0) : i) + .orElse(0); permanent.addCounters( CounterType.P1P1.createInstance(monstrosityValue), source.getControllerId(), source, game @@ -121,10 +132,15 @@ class BecomeMonstrousSourceEffect extends OneShotEffect { return true; } - private String setText(int monstrosityValue, String costAdjusterText) { - return "Monstrosity " + (monstrosityValue == Integer.MAX_VALUE ? "X" : monstrosityValue) + + private String setText(DynamicValue monstrosityValue, String costAdjusterText) { + if (!(monstrosityValue instanceof StaticValue)) { + return "Monstrosity X, where X is " + monstrosityValue.getMessage() + ". " + costAdjusterText + + "(If this creature isn't monstrous, put X +1/+1 counters on it and it becomes monstrous.)"; + } + int value = ((StaticValue) monstrosityValue).getValue(); + return "Monstrosity " + (value == Integer.MAX_VALUE ? "X" : value) + ". " + costAdjusterText + "(If this creature isn't monstrous, put " + - (monstrosityValue == Integer.MAX_VALUE ? "X" : CardUtil.numberToText(monstrosityValue)) + + (value == Integer.MAX_VALUE ? "X" : CardUtil.numberToText(value)) + " +1/+1 counters on it and it becomes monstrous.)"; } } diff --git a/Mage/src/main/java/mage/abilities/keyword/OffspringAbility.java b/Mage/src/main/java/mage/abilities/keyword/OffspringAbility.java index 9888c995dde..8be10098297 100644 --- a/Mage/src/main/java/mage/abilities/keyword/OffspringAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/OffspringAbility.java @@ -7,7 +7,6 @@ import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.costs.*; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenCopyTargetEffect; import mage.constants.Outcome; @@ -43,10 +42,8 @@ public class OffspringAbility extends StaticAbility implements OptionalAdditiona this.additionalCost.setRepeatable(false); this.rule = additionalCost.getName() + ' ' + additionalCost.getReminderText(); this.setRuleAtTheTop(true); - this.addSubAbility(new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new OffspringEffect()), OffspringCondition.instance, - "When this creature enters, if its offspring cost was paid, create a 1/1 token copy of it." - ).setRuleVisible(false)); + this.addSubAbility(new EntersBattlefieldTriggeredAbility(new OffspringEffect()) + .withInterveningIf(OffspringCondition.instance).setRuleVisible(false)); } private OffspringAbility(final OffspringAbility ability) { @@ -96,6 +93,7 @@ class OffspringEffect extends OneShotEffect { OffspringEffect() { super(Outcome.Benefit); + staticText = "create a 1/1 token copy of it"; } private OffspringEffect(final OffspringEffect effect) { @@ -127,6 +125,6 @@ enum OffspringCondition implements Condition { @Override public String toString() { - return "Offspring cost was paid"; + return "its offspring cost was paid"; } } diff --git a/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java b/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java index 913b9d7c9d1..3fc0f7e1335 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java @@ -81,8 +81,8 @@ public class ProtectionAbility extends StaticAbility { if (this.staticText != null && !this.staticText.isEmpty()) { return this.staticText; } - return (flavorWord == null ? "protection from " : CardUtil.italicizeWithEmDash(flavorWord) + "Protection from ") - + filter.getMessage() + (removeAuras ? "" : ". This effect doesn't remove Auras."); + return addRulePrefix("protection from ") + filter.getMessage() + + (removeAuras ? "" : ". This effect doesn't remove Auras."); } public ProtectionAbility setText(String text) { diff --git a/Mage/src/main/java/mage/abilities/keyword/RavenousAbility.java b/Mage/src/main/java/mage/abilities/keyword/RavenousAbility.java index 77cbbdc64e0..5d98d9b150b 100644 --- a/Mage/src/main/java/mage/abilities/keyword/RavenousAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/RavenousAbility.java @@ -4,7 +4,6 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.dynamicvalue.common.GetXValue; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.EntersBattlefieldWithXCountersEffect; @@ -18,13 +17,8 @@ public class RavenousAbility extends EntersBattlefieldAbility { public RavenousAbility() { super(new EntersBattlefieldWithXCountersEffect(CounterType.P1P1.createInstance())); - Ability ability = new ConditionalInterveningIfTriggeredAbility( - new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1)), - RavenousAbilityCondition.instance, "When this creature enters, " + - "if X is 5 or more, draw a card" - ); - ability.setRuleVisible(false); - this.addSubAbility(ability); + this.addSubAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1)) + .withInterveningIf(RavenousAbilityCondition.instance).setRuleVisible(false)); } private RavenousAbility(final RavenousAbility ability) { @@ -50,4 +44,9 @@ enum RavenousAbilityCondition implements Condition { public boolean apply(Game game, Ability source) { return GetXValue.instance.calculate(game, source, null) >= 5; } + + @Override + public String toString() { + return "X is 5 or more"; + } } diff --git a/Mage/src/main/java/mage/abilities/keyword/VanishingAbility.java b/Mage/src/main/java/mage/abilities/keyword/VanishingAbility.java index d414fce5881..1a726422c35 100644 --- a/Mage/src/main/java/mage/abilities/keyword/VanishingAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/VanishingAbility.java @@ -1,14 +1,13 @@ package mage.abilities.keyword; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; @@ -27,12 +26,10 @@ public class VanishingAbility extends EntersBattlefieldAbility { public VanishingAbility(int amount) { super(new AddCountersSourceEffect(CounterType.TIME.createInstance(amount))); this.amount = amount; - this.addSubAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfUpkeepTriggeredAbility( - new RemoveCounterSourceEffect(CounterType.TIME.createInstance()), false - ), condition, "At the beginning of your upkeep, if this permanent " + - "has a time counter on it, remove a time counter from it." - ).setRuleVisible(false)); + this.addSubAbility(new BeginningOfUpkeepTriggeredAbility( + new RemoveCounterSourceEffect(CounterType.TIME.createInstance()) + .setText("remove a time counter from it"), false + ).withInterveningIf(condition).setRuleVisible(false)); this.addSubAbility(new VanishingTriggeredAbility()); } diff --git a/Mage/src/main/java/mage/abilities/meta/OrTriggeredAbility.java b/Mage/src/main/java/mage/abilities/meta/OrTriggeredAbility.java index 2383d0bbf9a..55d98a00b13 100644 --- a/Mage/src/main/java/mage/abilities/meta/OrTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/meta/OrTriggeredAbility.java @@ -93,7 +93,7 @@ public class OrTriggeredAbility extends TriggeredAbilityImpl { for (Effect e : getEffects()) { //Add effects to the sub-abilities so that they can set target pointers ability.addEffect(e); } - if (ability.checkEventType(event, game) && ability.checkTrigger(event, game)) { + if (ability.checkEventType(event, game) && ability.checkTrigger(event, game) && ability.checkTriggerCondition(game)) { toRet = true; } ability.getEffects().clear(); //Remove afterwards, ensures that they remain synced even with copying diff --git a/Mage/src/main/java/mage/cards/Card.java b/Mage/src/main/java/mage/cards/Card.java index 21776e6be3a..8882c14869d 100644 --- a/Mage/src/main/java/mage/cards/Card.java +++ b/Mage/src/main/java/mage/cards/Card.java @@ -250,6 +250,16 @@ public interface Card extends MageObject, Ownerable { List getAttachments(); + /** + * @param attachment can be any object: card, permanent, token + * @param source can be null for default checks like state base + * @param game + * @param silentMode - use it to ignore warning message for users (e.g. for + * checking only) + * @return + */ + boolean cantBeAttachedBy(MageObject attachment, Ability source, Game game, boolean silentMode); + boolean addAttachment(UUID permanentId, Ability source, Game game); boolean removeAttachment(UUID permanentId, Ability source, Game game); diff --git a/Mage/src/main/java/mage/cards/CardImpl.java b/Mage/src/main/java/mage/cards/CardImpl.java index 27d65fe059f..2edbf43c918 100644 --- a/Mage/src/main/java/mage/cards/CardImpl.java +++ b/Mage/src/main/java/mage/cards/CardImpl.java @@ -7,10 +7,7 @@ import mage.abilities.*; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.continuous.HasSubtypesSourceEffect; -import mage.abilities.keyword.ChangelingAbility; -import mage.abilities.keyword.FlashbackAbility; -import mage.abilities.keyword.ReconfigureAbility; -import mage.abilities.keyword.SunburstAbility; +import mage.abilities.keyword.*; import mage.abilities.mana.ActivatedManaAbilityImpl; import mage.cards.mock.MockableCard; import mage.cards.repository.PluginClassloaderRegistery; @@ -919,6 +916,32 @@ public abstract class CardImpl extends MageObjectImpl implements Card { return attachments; } + @Override + public boolean cantBeAttachedBy(MageObject attachment, Ability source, Game game, boolean silentMode) { + for (ProtectionAbility ability : this.getAbilities(game).getProtectionAbilities()) { + if ((!attachment.hasSubtype(SubType.AURA, game) || ability.removesAuras()) + && (!attachment.hasSubtype(SubType.EQUIPMENT, game) || ability.removesEquipment()) + && !attachment.getId().equals(ability.getAuraIdNotToBeRemoved()) + && !ability.canTarget(attachment, game)) { + return !ability.getDoesntRemoveControlled() || Objects.equals(getControllerOrOwnerId(), game.getControllerId(attachment.getId())); + } + } + + boolean canAttach = true; + Permanent attachmentPermanent = game.getPermanent(attachment.getId()); + // If attachment is an aura, ensures this permanent can still be legally enchanted, according to the enchantment's Enchant ability + if (attachment.hasSubtype(SubType.AURA, game) + && attachmentPermanent != null + && attachmentPermanent.getSpellAbility() != null + && !attachmentPermanent.getSpellAbility().getTargets().isEmpty()) { + // Line of code below functionally gets the target of the aura's Enchant ability, then compares to this permanent. Enchant improperly implemented in XMage, see #9583 + // Note: stillLegalTarget used exclusively to account for Dream Leash. Can be made canTarget in the event that that card is rewritten (and "stillLegalTarget" removed from TargetImpl). + canAttach = attachmentPermanent.getSpellAbility().getTargets().get(0).copy().withNotTarget(true).stillLegalTarget(attachmentPermanent.getControllerId(), this.getId(), source, game); + } + + return !canAttach || game.getContinuousEffects().preventedByRuleModification(new StayAttachedEvent(this.getId(), attachment.getId(), source), null, game, silentMode); + } + @Override public boolean addAttachment(UUID permanentId, Ability source, Game game) { if (permanentId == null @@ -942,6 +965,9 @@ public abstract class CardImpl extends MageObjectImpl implements Card { && (attachment.isCreature(game) || !this.isLand(game))) { return false; } + if (this.cantBeAttachedBy(attachment, source, game, false)) { + return false; + } if (game.replaceEvent(new AttachEvent(objectId, attachment, source))) { return false; } diff --git a/Mage/src/main/java/mage/cards/CardsImpl.java b/Mage/src/main/java/mage/cards/CardsImpl.java index b4f748b29a4..92205a199c3 100644 --- a/Mage/src/main/java/mage/cards/CardsImpl.java +++ b/Mage/src/main/java/mage/cards/CardsImpl.java @@ -99,7 +99,7 @@ public class CardsImpl extends LinkedHashSet implements Cards, Serializabl @Override public int count(FilterCard filter, UUID playerId, Game game) { - return (int) this.stream().filter(card -> filter.match(game.getCard(card), playerId, game)).count(); + return (int) this.stream().filter(card -> filter.match(game.getCard(card), playerId, null, game)).count(); } diff --git a/Mage/src/main/java/mage/cards/decks/DeckValidator.java b/Mage/src/main/java/mage/cards/decks/DeckValidator.java index 6a9772c4ae5..a80ff3d7444 100644 --- a/Mage/src/main/java/mage/cards/decks/DeckValidator.java +++ b/Mage/src/main/java/mage/cards/decks/DeckValidator.java @@ -174,7 +174,7 @@ public abstract class DeckValidator implements Serializable { } } - public int getEdhPowerLevel(Deck deck) { + public int getEdhPowerLevel(Deck deck, List foundPowerCards, List foundInfo) { return 0; } diff --git a/Mage/src/main/java/mage/cards/repository/CardInfo.java b/Mage/src/main/java/mage/cards/repository/CardInfo.java index d870673e76d..e9b8a2aab24 100644 --- a/Mage/src/main/java/mage/cards/repository/CardInfo.java +++ b/Mage/src/main/java/mage/cards/repository/CardInfo.java @@ -167,7 +167,9 @@ public class CardInfo { this.modalDoubleFacedSecondSideName = ((ModalDoubleFacedCard) card).getRightHalfCard().getName(); } - this.frameStyle = card.getFrameStyle().toString(); + if (card.getFrameStyle() != null) { + this.frameStyle = card.getFrameStyle().toString(); + } this.frameColor = card.getFrameColor(null).toString(); this.variousArt = card.getUsesVariousArt(); this.blue = card.getColor(null).isBlue(); diff --git a/Mage/src/main/java/mage/constants/AffinityType.java b/Mage/src/main/java/mage/constants/AffinityType.java new file mode 100644 index 00000000000..a49fe5f9bfc --- /dev/null +++ b/Mage/src/main/java/mage/constants/AffinityType.java @@ -0,0 +1,119 @@ +package mage.constants; + +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.hint.common.ArtifactYouControlHint; +import mage.abilities.hint.common.CreaturesYouControlHint; +import mage.abilities.hint.common.GatesYouControlHint; +import mage.filter.common.*; +import mage.filter.predicate.mageobject.HistoricPredicate; +import mage.filter.predicate.mageobject.OutlawPredicate; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.util.CardUtil; + +/** + * @author TheElk801 + */ +public enum AffinityType { + ARTIFACTS(new FilterControlledArtifactPermanent("artifacts"), ArtifactYouControlHint.instance), + CREATURES(new FilterControlledCreaturePermanent("creatures"), CreaturesYouControlHint.instance), + ARTIFACT_CREATURES(AffinityFilters.ARTIFACT_CREATURES), + ENCHANTMENTS(new FilterControlledEnchantmentPermanent("enchantments")), + PLANESWALKERS(new FilterControlledPlaneswalkerPermanent("planeswalker")), + + EQUIPMENT(new FilterControlledPermanent(SubType.EQUIPMENT, "Equipment"), "Equipment"), + AURAS(new FilterControlledPermanent(SubType.AURA, "Auras")), + FOOD(new FilterControlledPermanent(SubType.FOOD, "Food"), "Food"), + TOKENS(AffinityFilters.TOKENS), + + PLAINS(new FilterControlledPermanent(SubType.PLAINS, "Plains")), + ISLANDS(new FilterControlledPermanent(SubType.ISLAND, "Islands")), + SWAMPS(new FilterControlledPermanent(SubType.SWAMP, "Swamps")), + MOUNTAINS(new FilterControlledPermanent(SubType.MOUNTAIN, "Mountains")), + FORESTS(new FilterControlledPermanent(SubType.FOREST, "Forests")), + + SPIRITS(new FilterControlledPermanent(SubType.SPIRIT, "Spirits")), + HUMANS(new FilterControlledPermanent(SubType.HUMAN, "Humans")), + KNIGHTS(new FilterControlledPermanent(SubType.KNIGHT, "Knights")), + DALEKS(new FilterControlledPermanent(SubType.DALEK, "Daleks")), + FROGS(new FilterControlledPermanent(SubType.FROG, "Frogs")), + LIZARDS(new FilterControlledPermanent(SubType.LIZARD, "Lizards")), + BIRDS(new FilterControlledPermanent(SubType.BIRD, "Birds")), + CITIZENS(new FilterControlledPermanent(SubType.CITIZEN, "Citizens")), + TOWNS(new FilterControlledPermanent(SubType.TOWN, "Towns")), + GATES(new FilterControlledPermanent(SubType.GATE, "Gates"), GatesYouControlHint.instance), + SNOW_LANDS(AffinityFilters.SNOW_LANDS), + HISTORIC(AffinityFilters.HISTORIC), + OUTLAWS(AffinityFilters.OUTLAWS); + + private final FilterControlledPermanent filter; + private final Hint hint; + private final String singularName; + + AffinityType(FilterControlledPermanent filter) { + this(filter, filter.getMessage()); + } + + AffinityType(FilterControlledPermanent filter, String singularName) { + this(filter, new ValueHint( + CardUtil.getTextWithFirstCharUpperCase(filter.getMessage()) + " you control", + new PermanentsOnBattlefieldCount(filter) + ), singularName); + } + + AffinityType(FilterControlledPermanent filter, Hint hint) { + this(filter, hint, filter.getMessage().substring(0, filter.getMessage().length() - 1)); + } + + AffinityType(FilterControlledPermanent filter, Hint hint, String singularName) { + this.filter = filter; + this.hint = hint; + this.singularName = singularName; + } + + public FilterControlledPermanent getFilter() { + return filter; + } + + public Hint getHint() { + return hint; + } + + public String getSingularName() { + return singularName; + } +} + +class AffinityFilters { + static final FilterControlledPermanent TOKENS = new FilterControlledPermanent("tokens"); + + static { + TOKENS.add(TokenPredicate.TRUE); + } + + static final FilterControlledPermanent SNOW_LANDS = new FilterControlledLandPermanent("snow lands"); + + static { + SNOW_LANDS.add(SuperType.SNOW.getPredicate()); + } + + static final FilterControlledPermanent OUTLAWS = new FilterControlledPermanent("outlaws"); + + static { + OUTLAWS.add(OutlawPredicate.instance); + } + + static final FilterControlledPermanent HISTORIC = new FilterControlledPermanent("historic permanents"); + + static { + HISTORIC.add(HistoricPredicate.instance); + } + + static final FilterControlledPermanent ARTIFACT_CREATURES = new FilterControlledPermanent("artifact creatures"); + + static { + ARTIFACT_CREATURES.add(CardType.ARTIFACT.getPredicate()); + ARTIFACT_CREATURES.add(CardType.CREATURE.getPredicate()); + } +} diff --git a/Mage/src/main/java/mage/constants/SubType.java b/Mage/src/main/java/mage/constants/SubType.java index ec9aff18548..b8a14045d1d 100644 --- a/Mage/src/main/java/mage/constants/SubType.java +++ b/Mage/src/main/java/mage/constants/SubType.java @@ -331,6 +331,7 @@ public enum SubType { PUREBLOOD("Pureblood", SubTypeSet.CreatureType, true), // Q QUARREN("Quarren", SubTypeSet.CreatureType, true), // Star Wars + QU("Qu", SubTypeSet.CreatureType), // R RABBIT("Rabbit", SubTypeSet.CreatureType), RACCOON("Raccoon", SubTypeSet.CreatureType), diff --git a/Mage/src/main/java/mage/counters/Counters.java b/Mage/src/main/java/mage/counters/Counters.java index dd7948c4681..50b1f1ca770 100644 --- a/Mage/src/main/java/mage/counters/Counters.java +++ b/Mage/src/main/java/mage/counters/Counters.java @@ -14,7 +14,10 @@ import java.util.stream.Collectors; */ public class Counters extends HashMap implements Serializable, Copyable { - public Counters() { + public Counters(Counter... counters) { + for (Counter counter : counters) { + this.addCounter(counter); + } } protected Counters(final Counters counters) { diff --git a/Mage/src/main/java/mage/filter/FilterCard.java b/Mage/src/main/java/mage/filter/FilterCard.java index ccdb72f6e52..c4eb8cf57ef 100644 --- a/Mage/src/main/java/mage/filter/FilterCard.java +++ b/Mage/src/main/java/mage/filter/FilterCard.java @@ -11,9 +11,7 @@ import mage.game.Game; import java.util.ArrayList; import java.util.List; -import java.util.Set; import java.util.UUID; -import java.util.stream.Collectors; /** * Works with cards only. For objects like commanders you must override your canTarget method. @@ -56,10 +54,6 @@ public class FilterCard extends FilterObject { return super.match(card, game); } - public boolean match(Card card, UUID playerId, Game game) { - return match(card, playerId, null, game); - } - public boolean match(Card card, UUID playerId, Ability source, Game game) { if (!this.match(card, game)) { return false; @@ -80,10 +74,6 @@ public class FilterCard extends FilterObject { extraPredicates.add(predicate); } - public Set filter(Set cards, Game game) { - return cards.stream().filter(card -> match(card, game)).collect(Collectors.toSet()); - } - public boolean hasPredicates() { return !predicates.isEmpty() || !extraPredicates.isEmpty(); } diff --git a/Mage/src/main/java/mage/filter/FilterSource.java b/Mage/src/main/java/mage/filter/FilterSource.java new file mode 100644 index 00000000000..335e30b4f9f --- /dev/null +++ b/Mage/src/main/java/mage/filter/FilterSource.java @@ -0,0 +1,77 @@ + +package mage.filter; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.cards.Card; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.filter.predicate.Predicate; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.command.CommandObject; +import mage.game.permanent.Permanent; +import mage.game.stack.StackObject; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * @author Susucr + */ +public class FilterSource extends FilterObject { + + protected final List> extraPredicates = new ArrayList<>(); + + public FilterSource() { + super("source"); + } + + public FilterSource(String name) { + super(name); + } + + private FilterSource(final FilterSource filter) { + super(filter); + this.extraPredicates.addAll(filter.extraPredicates); + } + + @Override + public FilterSource copy() { + return new FilterSource(this); + } + + public FilterSource add(ObjectSourcePlayerPredicate predicate) { + if (isLockedFilter()) { + throw new UnsupportedOperationException("You may not modify a locked filter"); + } + + // verify check -- make sure predicates work with all 3 Class that could be a Source + Predicates.makeSurePredicateCompatibleWithFilter(predicate, Permanent.class, Card.class, StackObject.class, CommandObject.class); + + extraPredicates.add(predicate); + return this; + } + + @Override + public boolean checkObjectClass(Object object) { + return object instanceof Permanent + || object instanceof Card + || object instanceof StackObject + || object instanceof CommandObject; + } + + public boolean match(MageObject object, UUID sourceControllerId, Ability source, Game game) { + if (!this.match(object, game)) { + return false; + } + ObjectSourcePlayer osp = new ObjectSourcePlayer<>(object, sourceControllerId, source); + return extraPredicates.stream().allMatch(p -> p.apply(osp, game)); + } + + @Override + public List getExtraPredicates() { + return new ArrayList<>(extraPredicates); + } +} diff --git a/Mage/src/main/java/mage/filter/StaticFilters.java b/Mage/src/main/java/mage/filter/StaticFilters.java index 31d2277e82c..201c60fc726 100644 --- a/Mage/src/main/java/mage/filter/StaticFilters.java +++ b/Mage/src/main/java/mage/filter/StaticFilters.java @@ -7,10 +7,7 @@ import mage.filter.common.*; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.*; import mage.filter.predicate.other.AnotherTargetPredicate; -import mage.filter.predicate.permanent.AttachedOrShareCreatureTypePredicate; -import mage.filter.predicate.permanent.RingBearerPredicate; -import mage.filter.predicate.permanent.TappedPredicate; -import mage.filter.predicate.permanent.TokenPredicate; +import mage.filter.predicate.permanent.*; /** * A class that holds Filter objects that may not be modified without copying @@ -27,12 +24,6 @@ public final class StaticFilters { private StaticFilters() { } - public static final FilterSpiritOrArcaneCard FILTER_SPIRIT_OR_ARCANE_CARD = new FilterSpiritOrArcaneCard(); - - static { - FILTER_SPIRIT_OR_ARCANE_CARD.setLockedFilter(true); - } - public static final FilterCard FILTER_CARD = new FilterCard("card"); static { @@ -100,6 +91,13 @@ public final class StaticFilters { FILTER_CARD_CREATURE_A.setLockedFilter(true); } + // for checks on cards to be cast as "a creature spell", this is a FilterCard, but the text is about spell + public static final FilterCreatureCard FILTER_CARD_A_CREATURE_SPELL = new FilterCreatureCard("a creature spell"); + + static { + FILTER_CARD_A_CREATURE_SPELL.setLockedFilter(true); + } + public static final FilterCreatureCard FILTER_CARD_CREATURE_YOUR_HAND = new FilterCreatureCard("a creature card from your hand"); static { @@ -154,24 +152,20 @@ public final class StaticFilters { FILTER_CARD_CREATURE_A_GRAVEYARD.setLockedFilter(true); } - public static final FilterNoncreatureCard FILTER_CARD_NON_CREATURE = new FilterNoncreatureCard(); + public static final FilterCard FILTER_CARD_NON_CREATURE = new FilterCard(); static { + FILTER_CARD_NON_CREATURE.add(Predicates.not(CardType.CREATURE.getPredicate())); FILTER_CARD_NON_CREATURE.setLockedFilter(true); } - public static final FilterNoncreatureCard FILTER_CARD_A_NON_CREATURE = new FilterNoncreatureCard("a noncreature card"); + public static final FilterCard FILTER_CARD_A_NON_CREATURE = new FilterCard("a noncreature card"); static { + FILTER_CARD_A_NON_CREATURE.add(Predicates.not(CardType.CREATURE.getPredicate())); FILTER_CARD_A_NON_CREATURE.setLockedFilter(true); } - public static final FilterNoncreatureCard FILTER_CARDS_NON_CREATURE = new FilterNoncreatureCard("noncreature cards"); - - static { - FILTER_CARDS_NON_CREATURE.setLockedFilter(true); - } - public static final FilterLandCard FILTER_CARD_LAND = new FilterLandCard(); static { @@ -516,9 +510,13 @@ public final class StaticFilters { FILTER_CONTROLLED_PERMANENT_LANDS.setLockedFilter(true); } - public static final FilterPermanent FILTER_CONTROLLED_PERMANENT_CREATURE_OR_PLANESWALKER = new FilterControlledCreatureOrPlaneswalkerPermanent("creature or planeswalker you control"); + public static final FilterPermanent FILTER_CONTROLLED_PERMANENT_CREATURE_OR_PLANESWALKER = new FilterControlledPermanent("creature or planeswalker you control"); static { + FILTER_CONTROLLED_PERMANENT_CREATURE_OR_PLANESWALKER.add(Predicates.or( + CardType.CREATURE.getPredicate(), + CardType.PLANESWALKER.getPredicate() + )); FILTER_CONTROLLED_PERMANENT_CREATURE_OR_PLANESWALKER.setLockedFilter(true); } @@ -643,6 +641,20 @@ public final class StaticFilters { FILTER_OTHER_CONTROLLED_CREATURES.setLockedFilter(true); } + public static final FilterControlledPermanent FILTER_OTHER_CONTROLLED_PERMANENTS = new FilterControlledPermanent("other permanents you control"); + + static { + FILTER_OTHER_CONTROLLED_PERMANENTS.add(AnotherPredicate.instance); + FILTER_OTHER_CONTROLLED_PERMANENTS.setLockedFilter(true); + } + + public static final FilterControlledArtifactPermanent FILTER_OTHER_CONTROLLED_ARTIFACTS = new FilterControlledArtifactPermanent("other artifacts you control"); + + static { + FILTER_OTHER_CONTROLLED_ARTIFACTS.add(AnotherPredicate.instance); + FILTER_OTHER_CONTROLLED_ARTIFACTS.setLockedFilter(true); + } + public static final FilterControlledCreaturePermanent FILTER_CONTROLLED_A_CREATURE = new FilterControlledCreaturePermanent("a creature you control"); static { @@ -1029,12 +1041,30 @@ public final class StaticFilters { FILTER_SPELL_AN_ENCHANTMENT.setLockedFilter(true); } - public static final FilterSpell FILTER_SPELL_AN_ARTIFACT = new FilterArtifactSpell("an artifact spell"); + public static final FilterSpell FILTER_SPELL_AN_ARTIFACT = new FilterSpell("an artifact spell"); static { + FILTER_SPELL_AN_ARTIFACT.add(CardType.ARTIFACT.getPredicate()); FILTER_SPELL_AN_ARTIFACT.setLockedFilter(true); } + public static final FilterSpell FILTER_SPELL_SPIRIT_OR_ARCANE = new FilterSpell("a Spirit or Arcane spell"); + + static { + FILTER_SPELL_SPIRIT_OR_ARCANE.add(Predicates.or( + SubType.SPIRIT.getPredicate(), + SubType.ARCANE.getPredicate() + )); + FILTER_SPELL_SPIRIT_OR_ARCANE.setLockedFilter(true); + } + + public static final FilterSpell FILTER_SPELL_HISTORIC = new FilterSpell("a historic spell"); + + static { + FILTER_SPELL_HISTORIC.add(HistoricPredicate.instance); + FILTER_SPELL_HISTORIC.setLockedFilter(true); + } + public static final FilterSpell FILTER_SPELL_KICKED_A = new FilterSpell("a kicked spell"); static { @@ -1202,6 +1232,22 @@ public final class StaticFilters { FILTER_BLOCKING_CREATURES.setLockedFilter(true); } + public static final FilterPermanent FILTER_CREATURE_DAMAGED_THIS_TURN = new FilterCreaturePermanent("creature that was dealt damage this turn"); + + static { + FILTER_CREATURE_DAMAGED_THIS_TURN.add(WasDealtDamageThisTurnPredicate.instance); + FILTER_CREATURE_DAMAGED_THIS_TURN.setLockedFilter(true); + } + + + public static final FilterPermanent FILTER_OPPONENTS_CREATURE_DAMAGED_THIS_TURN = new FilterCreaturePermanent("creature an opponent controls that was dealt damage this turn"); + + static { + FILTER_OPPONENTS_CREATURE_DAMAGED_THIS_TURN.add(TargetController.OPPONENT.getControllerPredicate()); + FILTER_OPPONENTS_CREATURE_DAMAGED_THIS_TURN.add(WasDealtDamageThisTurnPredicate.instance); + FILTER_OPPONENTS_CREATURE_DAMAGED_THIS_TURN.setLockedFilter(true); + } + public static final FilterPermanent FILTER_PERMANENT_AURAS = new FilterEnchantmentPermanent("Auras"); static { @@ -1209,12 +1255,18 @@ public final class StaticFilters { FILTER_PERMANENT_AURAS.setLockedFilter(true); } - public static final FilterPermanent FILTER_PERMANENT_EQUIPMENT = new FilterEquipmentPermanent(); + public static final FilterPermanent FILTER_PERMANENT_EQUIPMENT = new FilterPermanent(SubType.EQUIPMENT, "Equipment"); static { FILTER_PERMANENT_EQUIPMENT.setLockedFilter(true); } + public static final FilterPermanent FILTER_CONTROLLED_PERMANENT_EQUIPMENT = new FilterControlledPermanent(SubType.EQUIPMENT); + + static { + FILTER_CONTROLLED_PERMANENT_EQUIPMENT.setLockedFilter(true); + } + public static final FilterPermanent FILTER_PERMANENT_FORTIFICATION = new FilterPermanent(); static { @@ -1290,5 +1342,4 @@ public final class StaticFilters { static { FILTER_CONTROLLED_CLUE.setLockedFilter(true); } - } diff --git a/Mage/src/main/java/mage/filter/common/FilterArtifactSpell.java b/Mage/src/main/java/mage/filter/common/FilterArtifactSpell.java deleted file mode 100644 index dda5d74a7fb..00000000000 --- a/Mage/src/main/java/mage/filter/common/FilterArtifactSpell.java +++ /dev/null @@ -1,21 +0,0 @@ - -package mage.filter.common; - -import mage.constants.CardType; -import mage.filter.FilterSpell; - -/** - * - * @author Jgod - */ -public class FilterArtifactSpell extends FilterSpell { - - public FilterArtifactSpell() { - this("artifact spell"); - } - - public FilterArtifactSpell(String name) { - super(name); - this.add(CardType.ARTIFACT.getPredicate()); - } -} diff --git a/Mage/src/main/java/mage/filter/common/FilterControlledCreatureOrPlaneswalkerPermanent.java b/Mage/src/main/java/mage/filter/common/FilterControlledCreatureOrPlaneswalkerPermanent.java deleted file mode 100644 index 44c62344baf..00000000000 --- a/Mage/src/main/java/mage/filter/common/FilterControlledCreatureOrPlaneswalkerPermanent.java +++ /dev/null @@ -1,46 +0,0 @@ -package mage.filter.common; - -import mage.constants.CardType; -import mage.constants.SubType; -import mage.filter.predicate.Predicates; - -/** - * @author LevelX2 - */ -public class FilterControlledCreatureOrPlaneswalkerPermanent extends FilterControlledPermanent { - - public FilterControlledCreatureOrPlaneswalkerPermanent() { - this("creature or planeswalker you control"); - } - - public FilterControlledCreatureOrPlaneswalkerPermanent(SubType subType) { - this(subType, "a " + subType + " creature or a " + subType + " planeswalker"); - } - - public FilterControlledCreatureOrPlaneswalkerPermanent(SubType subType, String name) { - super(name); - this.add(Predicates.or( - CardType.CREATURE.getPredicate(), - CardType.PLANESWALKER.getPredicate() - )); - this.add(subType.getPredicate()); - } - - public FilterControlledCreatureOrPlaneswalkerPermanent(String name) { - super(name); - this.add(Predicates.or( - CardType.CREATURE.getPredicate(), - CardType.PLANESWALKER.getPredicate() - )); - } - - protected FilterControlledCreatureOrPlaneswalkerPermanent(final FilterControlledCreatureOrPlaneswalkerPermanent filter) { - super(filter); - } - - @Override - public FilterControlledCreatureOrPlaneswalkerPermanent copy() { - return new FilterControlledCreatureOrPlaneswalkerPermanent(this); - } - -} diff --git a/Mage/src/main/java/mage/filter/common/FilterCreatureForAttack.java b/Mage/src/main/java/mage/filter/common/FilterCreatureForAttack.java deleted file mode 100644 index 358d4771c13..00000000000 --- a/Mage/src/main/java/mage/filter/common/FilterCreatureForAttack.java +++ /dev/null @@ -1,55 +0,0 @@ - - -package mage.filter.common; - -import mage.abilities.keyword.DefenderAbility; -import mage.filter.predicate.Predicate; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.AbilityPredicate; -import mage.filter.predicate.permanent.AttackingPredicate; -import mage.filter.predicate.permanent.BlockingPredicate; -import mage.filter.predicate.permanent.TappedPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; - -/** - * @author BetaSteward_at_googlemail.com - * @author North - */ -public class FilterCreatureForAttack extends FilterCreaturePermanent { - - public FilterCreatureForAttack() { - this(""); - } - - public FilterCreatureForAttack(String name) { - super(name); - this.add(Predicates.not(AttackingPredicate.instance)); - this.add(Predicates.not(BlockingPredicate.instance)); - this.add(TappedPredicate.UNTAPPED); - this.add(Predicates.not(new AbilityPredicate(DefenderAbility.class))); - this.add(new CanTapPredicate()); - } - - protected FilterCreatureForAttack(final FilterCreatureForAttack filter) { - super(filter); - } - - @Override - public FilterCreatureForAttack copy() { - return new FilterCreatureForAttack(this); - } -} - -class CanTapPredicate implements Predicate { - - @Override - public boolean apply(Permanent input, Game game) { - return input.canTap(game); - } - - @Override - public String toString() { - return "CanTap"; - } -} diff --git a/Mage/src/main/java/mage/filter/common/FilterEquipmentPermanent.java b/Mage/src/main/java/mage/filter/common/FilterEquipmentPermanent.java deleted file mode 100644 index bd5b02747cb..00000000000 --- a/Mage/src/main/java/mage/filter/common/FilterEquipmentPermanent.java +++ /dev/null @@ -1,31 +0,0 @@ - - - -package mage.filter.common; - -import mage.constants.SubType; -import mage.filter.FilterPermanent; - -/** - * @author TheElk801 - */ -public class FilterEquipmentPermanent extends FilterPermanent { - - public FilterEquipmentPermanent() { - this("Equipment"); - } - - public FilterEquipmentPermanent(String name) { - super(name); - this.add(SubType.EQUIPMENT.getPredicate()); - } - - protected FilterEquipmentPermanent(final FilterEquipmentPermanent filter) { - super(filter); - } - - @Override - public FilterEquipmentPermanent copy() { - return new FilterEquipmentPermanent(this); - } -} diff --git a/Mage/src/main/java/mage/filter/common/FilterHistoricCard.java b/Mage/src/main/java/mage/filter/common/FilterHistoricCard.java deleted file mode 100644 index 159fef4a66b..00000000000 --- a/Mage/src/main/java/mage/filter/common/FilterHistoricCard.java +++ /dev/null @@ -1,28 +0,0 @@ -package mage.filter.common; - -import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.HistoricPredicate; - -/** - * @author LevelX2 - */ -public class FilterHistoricCard extends FilterCard { - - public FilterHistoricCard() { - this("historic card"); - } - - public FilterHistoricCard(String name) { - super(name); - this.add(HistoricPredicate.instance); - } - - protected FilterHistoricCard(final FilterHistoricCard filter) { - super(filter); - } - - @Override - public FilterHistoricCard copy() { - return new FilterHistoricCard(this); - } -} diff --git a/Mage/src/main/java/mage/filter/common/FilterHistoricSpell.java b/Mage/src/main/java/mage/filter/common/FilterHistoricSpell.java deleted file mode 100644 index df3cb607d1a..00000000000 --- a/Mage/src/main/java/mage/filter/common/FilterHistoricSpell.java +++ /dev/null @@ -1,24 +0,0 @@ -package mage.filter.common; - -import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.HistoricPredicate; - -/** - * @author igoudt - */ -public class FilterHistoricSpell extends FilterSpell { - - public FilterHistoricSpell() { - super("a historic spell"); - this.add(HistoricPredicate.instance); - } - - protected FilterHistoricSpell(final FilterHistoricSpell filter) { - super(filter); - } - - @Override - public FilterHistoricSpell copy() { - return new FilterHistoricSpell(this); - } -} diff --git a/Mage/src/main/java/mage/filter/common/FilterInstantSpell.java b/Mage/src/main/java/mage/filter/common/FilterInstantSpell.java deleted file mode 100644 index 0982049952b..00000000000 --- a/Mage/src/main/java/mage/filter/common/FilterInstantSpell.java +++ /dev/null @@ -1,26 +0,0 @@ -package mage.filter.common; - -import mage.constants.CardType; -import mage.filter.FilterSpell; - -public class FilterInstantSpell extends FilterSpell { - - public FilterInstantSpell() { - this("instant spell"); - } - - public FilterInstantSpell(String name) { - super(name); - this.add(CardType.INSTANT.getPredicate()); - } - - protected FilterInstantSpell(final FilterInstantSpell filter) { - super(filter); - } - - @Override - public FilterInstantSpell copy() { - return new FilterInstantSpell(this); - } - -} \ No newline at end of file diff --git a/Mage/src/main/java/mage/filter/common/FilterNoncreatureCard.java b/Mage/src/main/java/mage/filter/common/FilterNoncreatureCard.java deleted file mode 100644 index 496b26863c2..00000000000 --- a/Mage/src/main/java/mage/filter/common/FilterNoncreatureCard.java +++ /dev/null @@ -1,29 +0,0 @@ -package mage.filter.common; - -import mage.constants.CardType; -import mage.filter.FilterCard; -import mage.filter.predicate.Predicates; - -/** - * @author ssouders412 - */ -public class FilterNoncreatureCard extends FilterCard { - - public FilterNoncreatureCard() { - this("noncreature card"); - } - - public FilterNoncreatureCard(String name) { - super(name); - this.add(Predicates.not(CardType.CREATURE.getPredicate())); - } - - protected FilterNoncreatureCard(final FilterNoncreatureCard filter) { - super(filter); - } - - @Override - public FilterNoncreatureCard copy() { - return new FilterNoncreatureCard(this); - } -} diff --git a/Mage/src/main/java/mage/filter/common/FilterSpiritOrArcaneCard.java b/Mage/src/main/java/mage/filter/common/FilterSpiritOrArcaneCard.java deleted file mode 100644 index ce187aefdc1..00000000000 --- a/Mage/src/main/java/mage/filter/common/FilterSpiritOrArcaneCard.java +++ /dev/null @@ -1,26 +0,0 @@ -package mage.filter.common; - -import mage.constants.SubType; -import mage.filter.FilterSpell; -import mage.filter.predicate.Predicates; - -public class FilterSpiritOrArcaneCard extends FilterSpell { - - public FilterSpiritOrArcaneCard() { - this("a Spirit or Arcane spell"); - } - - public FilterSpiritOrArcaneCard(String name) { - super(name); - this.add(Predicates.or(SubType.SPIRIT.getPredicate(), SubType.ARCANE.getPredicate())); - } - - protected FilterSpiritOrArcaneCard(final FilterSpiritOrArcaneCard filter) { - super(filter); - } - - @Override - public FilterSpiritOrArcaneCard copy() { - return new FilterSpiritOrArcaneCard(this); - } -} diff --git a/Mage/src/main/java/mage/filter/predicate/card/CardTextPredicate.java b/Mage/src/main/java/mage/filter/predicate/card/CardTextPredicate.java index dc15882ba86..a1cfb2ce14e 100644 --- a/Mage/src/main/java/mage/filter/predicate/card/CardTextPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/card/CardTextPredicate.java @@ -1,19 +1,27 @@ package mage.filter.predicate.card; -import mage.cards.*; +import mage.cards.Card; +import mage.cards.CardWithSpellOption; +import mage.cards.ModalDoubleFacedCard; +import mage.cards.SplitCard; import mage.cards.mock.MockCard; import mage.constants.SubType; import mage.constants.SuperType; import mage.filter.predicate.Predicate; import mage.game.Game; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; /** * Special predicate to search cards in deck editor * - * @author North + * @author North, JayDi85 */ public class CardTextPredicate implements Predicate { @@ -22,7 +30,10 @@ public class CardTextPredicate implements Predicate { private final boolean inTypes; private final boolean inRules; private final boolean isUnique; - private HashMap seenCards = null; + private HashMap seenCards; + private final Pattern pattern; + private final Matcher matcher; + private final List textTokens; public CardTextPredicate(String text, boolean inNames, boolean inTypes, boolean inRules, boolean isUnique) { this.text = text; @@ -30,22 +41,33 @@ public class CardTextPredicate implements Predicate { this.inTypes = inTypes; this.inRules = inRules; this.isUnique = isUnique; - seenCards = new HashMap<>(); + this.seenCards = new HashMap<>(); + + // regexp to find texts inside "xxx" like + // "123 345" → ["123", "345"] + // "123 345 678" → ["123", "345", "678"] + // "123 "345 678"" → ["123", "345 678"] + this.textTokens = new ArrayList<>(); + this.pattern = Pattern.compile("\"([^\"]*)\"|(\\S+)"); + this.matcher = this.pattern.matcher(text.toLowerCase(Locale.ENGLISH)); + while (matcher.find()) { + if (matcher.group(1) != null) { + // inside "xxx" + this.textTokens.add(matcher.group(1)); + } else { + // normal xxx + this.textTokens.add(matcher.group(2)); + } + } } @Override public boolean apply(Card input, Game game) { - if (text.isEmpty() && !isUnique) { - return true; + if (this.textTokens.isEmpty()) { + return saveAndReturnUniqueFind(input); } - if (text.isEmpty() && isUnique) { - boolean found = !seenCards.containsKey(input.getName()); - seenCards.put(input.getName(), true); - return found; - } - - // first check in card name + // name: need all tokens if (inNames) { String fullName = input.getName(); if (input instanceof MockCard) { @@ -55,99 +77,74 @@ public class CardTextPredicate implements Predicate { } else if (input instanceof CardWithSpellOption) { fullName = input.getName() + MockCard.CARD_WITH_SPELL_OPTION_NAME_SEPARATOR + ((CardWithSpellOption) input).getSpellCard().getName(); } - - if (fullName.toLowerCase(Locale.ENGLISH).contains(text.toLowerCase(Locale.ENGLISH))) { - if (isUnique && seenCards.containsKey(input.getName())) { - return false; - } - if (isUnique) { - seenCards.put(input.getName(), true); - } - return true; + if (textHasTokens(fullName, true)) { + return saveAndReturnUniqueFind(input); } } - // separate by spaces - String[] tokens = text.toLowerCase(Locale.ENGLISH).split(" "); - for (String token : tokens) { - boolean found = false; - if (!token.isEmpty()) { - // then try to find in rules - if (inRules) { - if (input instanceof SplitCard) { - for (String rule : ((SplitCard) input).getLeftHalfCard().getRules(game)) { - if (rule.toLowerCase(Locale.ENGLISH).contains(token)) { - found = true; - break; - } - } - for (String rule : ((SplitCard) input).getRightHalfCard().getRules(game)) { - if (rule.toLowerCase(Locale.ENGLISH).contains(token)) { - found = true; - break; - } - } - } - - if (input instanceof ModalDoubleFacedCard) { - for (String rule : ((ModalDoubleFacedCard) input).getLeftHalfCard().getRules(game)) { - if (rule.toLowerCase(Locale.ENGLISH).contains(token)) { - found = true; - break; - } - } - for (String rule : ((ModalDoubleFacedCard) input).getRightHalfCard().getRules(game)) { - if (rule.toLowerCase(Locale.ENGLISH).contains(token)) { - found = true; - break; - } - } - } - - if (input instanceof CardWithSpellOption) { - for (String rule : ((CardWithSpellOption) input).getSpellCard().getRules(game)) { - if (rule.toLowerCase(Locale.ENGLISH).contains(token)) { - found = true; - break; - } - } - } - - for (String rule : input.getRules(game)) { - if (rule.toLowerCase(Locale.ENGLISH).contains(token)) { - found = true; - break; - } - } - } - if (inTypes) { - for (SubType subType : input.getSubtype(game)) { - if (subType.toString().equalsIgnoreCase(token)) { - found = true; - break; - } - } - for (SuperType superType : input.getSuperType(game)) { - if (superType.toString().equalsIgnoreCase(token)) { - found = true; - break; - } - } - } + // rules: need all tokens + if (inRules) { + List fullRules = new ArrayList<>(input.getRules(game)); + if (input instanceof SplitCard) { + fullRules.addAll(((SplitCard) input).getLeftHalfCard().getRules(game)); + fullRules.addAll(((SplitCard) input).getRightHalfCard().getRules(game)); } - - if (found && isUnique && seenCards.containsKey(input.getName())) { - found = false; + if (input instanceof ModalDoubleFacedCard) { + fullRules.addAll(((ModalDoubleFacedCard) input).getLeftHalfCard().getRules(game)); + fullRules.addAll(((ModalDoubleFacedCard) input).getRightHalfCard().getRules(game)); } - if (!found) { - return false; + if (input instanceof CardWithSpellOption) { + fullRules.addAll(((CardWithSpellOption) input).getSpellCard().getRules(game)); + } + if (textHasTokens(String.join("@", fullRules), true)) { + return saveAndReturnUniqueFind(input); } } + // types: need all tokens + if (inTypes) { + List fullTypes = new ArrayList<>(); + fullTypes.addAll(input.getSubtype(game).stream().map(SubType::toString).collect(Collectors.toList())); + fullTypes.addAll(input.getSuperType(game).stream().map(SuperType::toString).collect(Collectors.toList())); + if (textHasTokens(String.join("@", fullTypes), true)) { + return saveAndReturnUniqueFind(input); + } + } + + // not found + return false; + } + + private boolean saveAndReturnUniqueFind(Card card) { if (isUnique) { - seenCards.put(input.getName(), true); + boolean found = !seenCards.containsKey(card.getName()); + seenCards.put(card.getName(), true); + return found; + } else { + return true; } - return true; + } + + private boolean textHasTokens(String text, boolean needAllTokens) { + String searchingText = text + "@" + text.replace(", ", " "); + searchingText += "@" + searchingText.toLowerCase(Locale.ENGLISH); + + boolean found = false; + for (String token : this.textTokens) { + if (searchingText.contains(token)) { + found = true; + if (!needAllTokens) { + break; + } + } else { + if (needAllTokens) { + found = false; + break; + } + } + } + + return found; } @Override diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/ManaValueEqualToCountersSourceCountPredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/ManaValueEqualToCountersSourceCountPredicate.java new file mode 100644 index 00000000000..cd5ce658e8b --- /dev/null +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/ManaValueEqualToCountersSourceCountPredicate.java @@ -0,0 +1,36 @@ +package mage.filter.predicate.mageobject; + +import mage.MageObject; +import mage.counters.CounterType; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; + +import java.util.Optional; + +/** + * @author xenohedron + */ +public class ManaValueEqualToCountersSourceCountPredicate implements ObjectSourcePlayerPredicate { + + private final CounterType counterType; + + public ManaValueEqualToCountersSourceCountPredicate(CounterType counterType) { + this.counterType = counterType; + } + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + return Optional + .ofNullable(input.getSource().getSourcePermanentOrLKI(game)) + .map(permanent -> permanent.getCounters(game)) + .map(counters -> counters.getCount(counterType)) + .orElse(-1) // always false + .equals(input.getObject().getManaValue()); + } + + @Override + public String toString() { + return "mana value equal to the number of " + counterType.getName() + " counters on {this}"; + } +} diff --git a/Mage/src/main/java/mage/filter/predicate/card/ManaValueLessThanControlledLandCountPredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/ManaValueLessThanControlledLandCountPredicate.java similarity index 94% rename from Mage/src/main/java/mage/filter/predicate/card/ManaValueLessThanControlledLandCountPredicate.java rename to Mage/src/main/java/mage/filter/predicate/mageobject/ManaValueLessThanControlledLandCountPredicate.java index 4d0da691a9a..c6f96bd981d 100644 --- a/Mage/src/main/java/mage/filter/predicate/card/ManaValueLessThanControlledLandCountPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/ManaValueLessThanControlledLandCountPredicate.java @@ -1,4 +1,4 @@ -package mage.filter.predicate.card; +package mage.filter.predicate.mageobject; import mage.MageObject; import mage.filter.StaticFilters; diff --git a/Mage/src/main/java/mage/filter/predicate/permanent/AttachedToPredicate.java b/Mage/src/main/java/mage/filter/predicate/permanent/AttachedToPredicate.java index bc12b7b00fd..5b73eb57667 100644 --- a/Mage/src/main/java/mage/filter/predicate/permanent/AttachedToPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/permanent/AttachedToPredicate.java @@ -1,17 +1,17 @@ - package mage.filter.predicate.permanent; -import java.util.UUID; import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicate; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.Optional; + /** - * * @author LoneFox */ -public class AttachedToPredicate implements Predicate { +public class AttachedToPredicate implements ObjectSourcePlayerPredicate { private final FilterPermanent filter; @@ -20,10 +20,14 @@ public class AttachedToPredicate implements Predicate { } @Override - public boolean apply(Permanent input, Game game) { - UUID attachedTo = input.getAttachedTo(); - Permanent permanent = game.getPermanent(attachedTo); - return filter.match(permanent, game); + public boolean apply(ObjectSourcePlayer input, Game game) { + return Optional + .ofNullable(input) + .map(ObjectSourcePlayer::getObject) + .map(Permanent::getAttachedTo) + .map(game::getPermanent) + .map(permanent -> filter.match(permanent, input.getPlayerId(), input.getSource(), game)) + .orElse(false); } @Override diff --git a/Mage/src/main/java/mage/filter/predicate/permanent/AttachedToSourcePredicate.java b/Mage/src/main/java/mage/filter/predicate/permanent/AttachedToSourcePredicate.java new file mode 100644 index 00000000000..d5268eefc18 --- /dev/null +++ b/Mage/src/main/java/mage/filter/predicate/permanent/AttachedToSourcePredicate.java @@ -0,0 +1,24 @@ +package mage.filter.predicate.permanent; + +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * @author Susucr + */ +public enum AttachedToSourcePredicate implements ObjectSourcePlayerPredicate { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + return input.getObject().isAttachedTo(input.getSourceId()); + } + + @Override + public String toString() { + return "attached to {this}"; + } + +} diff --git a/Mage/src/main/java/mage/game/FakeMatch.java b/Mage/src/main/java/mage/game/FakeMatch.java index 44cb401b961..44dbf6a7281 100644 --- a/Mage/src/main/java/mage/game/FakeMatch.java +++ b/Mage/src/main/java/mage/game/FakeMatch.java @@ -11,7 +11,7 @@ import mage.game.match.MatchOptions; public class FakeMatch extends MatchImpl { public FakeMatch() { - super(new MatchOptions("fake match", "fake game type", true, 2)); + super(new MatchOptions("fake match", "fake game type", false)); } @Override diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index fc4269a71e9..06932787520 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -1852,7 +1852,7 @@ public abstract class GameImpl implements Game { if (turnController != null) { Player targetPlayer = getPlayer(spellControllerId); if (targetPlayer != null) { - targetPlayer.setGameUnderYourControl(true, false); + targetPlayer.setGameUnderYourControl(this, true, false); informPlayers(turnController.getLogName() + " lost control over " + targetPlayer.getLogName()); if (targetPlayer.getTurnControlledBy().equals(turnController.getId())) { turnController.getPlayersUnderYourControl().remove(targetPlayer.getId()); @@ -1960,7 +1960,7 @@ public abstract class GameImpl implements Game { @Override public void addEffect(ContinuousEffect continuousEffect, Ability source) { Ability newAbility = source.copy(); - newAbility.setSourceObjectZoneChangeCounter(getState().getZoneChangeCounter(source.getSourceId())); + newAbility.initSourceObjectZoneChangeCounter(this, true); ContinuousEffect newEffect = continuousEffect.copy(); newEffect.newId(); @@ -2159,18 +2159,14 @@ public abstract class GameImpl implements Game { // countered, or otherwise responded to. Rather, it resolves immediately after the mana // ability that triggered it, without waiting for priority. Ability manaAbility = ability.copy(); - if (manaAbility.getSourceObjectZoneChangeCounter() == 0) { - manaAbility.setSourceObjectZoneChangeCounter(getState().getZoneChangeCounter(ability.getSourceId())); - } + manaAbility.initSourceObjectZoneChangeCounter(this, false); if (manaAbility.activate(this, false)) { manaAbility.resolve(this); } } else { TriggeredAbility newAbility = ability.copy(); newAbility.newId(); - if (newAbility.getSourceObjectZoneChangeCounter() == 0) { - newAbility.setSourceObjectZoneChangeCounter(getState().getZoneChangeCounter(ability.getSourceId())); - } + newAbility.initSourceObjectZoneChangeCounter(this, false); if (!(newAbility instanceof DelayedTriggeredAbility)) { newAbility.setSourcePermanentTransformCount(this); } @@ -2715,9 +2711,9 @@ public abstract class GameImpl implements Game { .add(perm); } } - // 704.5s If the number of lore counters on a Saga permanent is greater than or equal to its final chapter number + // 704.5s If the number of lore counters on a Saga permanent with one or more chapter abilities is greater than or equal to its final chapter number // and it isn't the source of a chapter ability that has triggered but not yet left the stack, that Saga's controller sacrifices it. - if (perm.hasSubtype(SubType.SAGA, this)) { + if (perm.hasSubtype(SubType.SAGA, this) && perm.getAbilities(this).containsClass(SagaAbility.class)) { int maxChapter = perm .getAbilities(this) .stream() diff --git a/Mage/src/main/java/mage/game/command/dungeons/UndercityDungeon.java b/Mage/src/main/java/mage/game/command/dungeons/UndercityDungeon.java index f69bf70fdd5..80eed0d95ce 100644 --- a/Mage/src/main/java/mage/game/command/dungeons/UndercityDungeon.java +++ b/Mage/src/main/java/mage/game/command/dungeons/UndercityDungeon.java @@ -30,6 +30,7 @@ import mage.target.TargetPlayer; import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; import mage.util.RandomUtil; /** @@ -158,7 +159,7 @@ class ThroneOfTheDeadThreeEffect extends OneShotEffect { } if (card != null) { player.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); if (permanent != null) { permanent.addCounters(CounterType.P1P1.createInstance(3), source, game); game.addEffect(new GainAbilityTargetEffect(HexproofAbility.getInstance(), Duration.UntilYourNextTurn) diff --git a/Mage/src/main/java/mage/game/command/emblems/ArlinnEmbracedByTheMoonEmblem.java b/Mage/src/main/java/mage/game/command/emblems/ArlinnEmbracedByTheMoonEmblem.java index d722d1e07ce..5606f00850c 100644 --- a/Mage/src/main/java/mage/game/command/emblems/ArlinnEmbracedByTheMoonEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/ArlinnEmbracedByTheMoonEmblem.java @@ -33,7 +33,7 @@ public final class ArlinnEmbracedByTheMoonEmblem extends Emblem { Ability ability2 = new SimpleActivatedAbility(effect2, new TapSourceCost()); ability2.addTarget(new TargetAnyTarget()); effect = new GainAbilityControlledEffect(ability2, Duration.EndOfGame, filter); - effect.setText("and '{T}: This creature deals damage equal to its power to any target"); + effect.setText("and '{T}: This creature deals damage equal to its power to any target'"); ability.addEffect(effect); this.getAbilities().add(ability); } diff --git a/Mage/src/main/java/mage/game/command/emblems/LolthSpiderQueenEmblem.java b/Mage/src/main/java/mage/game/command/emblems/LolthSpiderQueenEmblem.java index b45be6875a4..9e775d88d92 100644 --- a/Mage/src/main/java/mage/game/command/emblems/LolthSpiderQueenEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/LolthSpiderQueenEmblem.java @@ -3,7 +3,6 @@ package mage.game.command.emblems; import mage.abilities.Ability; import mage.abilities.common.OneOrMoreCombatDamagePlayerTriggeredAbility; import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; @@ -25,13 +24,9 @@ public final class LolthSpiderQueenEmblem extends Emblem { // −8: You get an emblem with "Whenever an opponent is dealt combat damage by one or more creatures you control, if that player lost less than 8 life this turn, they lose life equal to the difference." public LolthSpiderQueenEmblem() { super("Emblem Lolth"); - this.getAbilities().add(new ConditionalInterveningIfTriggeredAbility( - new OneOrMoreCombatDamagePlayerTriggeredAbility( - Zone.COMMAND, new LolthSpiderQueenEmblemEffect(), StaticFilters.FILTER_PERMANENT_CREATURES, SetTargetPointer.PLAYER, false - ), LolthSpiderQueenEmblemCondition.instance, "Whenever an opponent " + - "is dealt combat damage by one or more creatures you control, " + - "if that player lost less than 8 life this turn, they lose life equal to the difference." - )); + this.getAbilities().add(new OneOrMoreCombatDamagePlayerTriggeredAbility( + Zone.COMMAND, new LolthSpiderQueenEmblemEffect(), StaticFilters.FILTER_PERMANENT_CREATURES, SetTargetPointer.PLAYER, false + ).withInterveningIf(LolthSpiderQueenEmblemCondition.instance).setTriggerPhrase("Whenever an opponent is dealt combat damage by one or more creatures you control, ")); } private LolthSpiderQueenEmblem(final LolthSpiderQueenEmblem card) { @@ -61,12 +56,18 @@ enum LolthSpiderQueenEmblemCondition implements Condition { PlayerLostLifeWatcher watcher = game.getState().getWatcher(PlayerLostLifeWatcher.class); return player != null && watcher != null && watcher.getLifeLost(player.getId()) < 8; } + + @Override + public String toString() { + return "that player lost less than 8 life this turn"; + } } class LolthSpiderQueenEmblemEffect extends OneShotEffect { LolthSpiderQueenEmblemEffect() { super(Outcome.Benefit); + staticText = "they lose life equal to the difference"; } private LolthSpiderQueenEmblemEffect(final LolthSpiderQueenEmblemEffect effect) { diff --git a/Mage/src/main/java/mage/game/command/emblems/RadiationEmblem.java b/Mage/src/main/java/mage/game/command/emblems/RadiationEmblem.java index 9c2fcd54a1a..7f72bf4b36c 100644 --- a/Mage/src/main/java/mage/game/command/emblems/RadiationEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/RadiationEmblem.java @@ -1,10 +1,9 @@ package mage.game.command.emblems; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfFirstMainTriggeredAbility; import mage.abilities.condition.Condition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.OneShotEffect; +import mage.abilities.triggers.BeginningOfFirstMainTriggeredAbility; import mage.cards.Cards; import mage.cards.FrameStyle; import mage.cards.repository.TokenInfo; @@ -31,12 +30,9 @@ public class RadiationEmblem extends Emblem { super("Radiation"); this.frameStyle = FrameStyle.M15_NORMAL; - this.getAbilities().add(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfFirstMainTriggeredAbility(Zone.ALL, TargetController.YOU, new RadiationEffect(), false), - RadiationCondition.instance, - "At the beginning of your precombat main phase, if you have any rad counters, " - + "mill that many cards. For each nonland card milled this way, you lose 1 life and a rad counter." - )); + this.getAbilities().add(new BeginningOfFirstMainTriggeredAbility( + Zone.ALL, TargetController.YOU, new RadiationEffect(), false + ).withInterveningIf(RadiationCondition.instance).setTriggerPhrase("At the beginning of each player's precombat main phase, ")); TokenInfo foundInfo = TokenRepository.instance.findPreferredTokenInfoForXmage(TokenRepository.XMAGE_IMAGE_NAME_RADIATION, null); if (foundInfo != null) { @@ -69,6 +65,11 @@ enum RadiationCondition implements Condition { Player player = game.getPlayer(source.getControllerId()); return player != null && player.getCountersCount(CounterType.RAD) > 0; } + + @Override + public String toString() { + return "that player has one or more rad counters"; + } } /** diff --git a/Mage/src/main/java/mage/game/draft/DraftCube.java b/Mage/src/main/java/mage/game/draft/DraftCube.java index d01888a5c39..b9bbbe560ce 100644 --- a/Mage/src/main/java/mage/game/draft/DraftCube.java +++ b/Mage/src/main/java/mage/game/draft/DraftCube.java @@ -1,20 +1,28 @@ package mage.game.draft; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; import mage.cards.Card; +import mage.cards.ExpansionSet; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; import mage.util.RandomUtil; import org.apache.log4j.Logger; +import java.text.SimpleDateFormat; +import java.util.*; + /** + * Data sources by priority: + * - official mtgo website + * - cubecobra (big amount of cubes but can be outdated) * - * @author LevelX2 + * @author LevelX2, JayDi85 */ public abstract class DraftCube { + SimpleDateFormat UPDATE_DATE_FORMAT = new SimpleDateFormat("yyyy MMMM", Locale.ENGLISH); // 2025 April + private static final Logger logger = Logger.getLogger(DraftCube.class); + + public static class CardIdentity { private final String name; @@ -38,36 +46,78 @@ public abstract class DraftCube { public String getName() { return name; } + public String getExtension() { return extension; } + public String getCardNumber() { return number; } } - private static final Logger logger = Logger.getLogger(DraftCube.class); - private final String name; private final String code; + private String updateInfo; + private final Date updateDate; private static final int boosterSize = 15; protected List cubeCards = new ArrayList<>(); protected List leftCubeCards = new ArrayList<>(); protected DraftCube(String name) { + this(name, "", 0, 0, 0); + } + + public DraftCube(String name, String updateInfo, int updateYear, int updateMonth, int updateDay) { this.name = name; this.code = getClass().getSimpleName(); + this.updateInfo = updateInfo; + this.updateDate = updateYear == 0 ? null : ExpansionSet.buildDate(updateYear, updateMonth, updateDay); } public String getName() { - return name; + String res = this.name; + + List extra = new ArrayList<>(); + if (this.updateInfo != null && !this.updateInfo.isEmpty()) { + extra.add(this.updateInfo); + } + if (this.updateDate != null) { + extra.add(UPDATE_DATE_FORMAT.format(this.updateDate)); + } + if (!extra.isEmpty()) { + res += String.format(" (%s)", String.join(", ", extra)); + } + + return res; + } + + /** + * Validate inner data - is it fine to start (example: cube from deck must has loaded cards) + */ + public void validateData() { + if (cubeCards.isEmpty()) { + throw new IllegalArgumentException("Can't create cube draft - found empty cards list: " + this.getName()); + } } public String getCode() { return code; } + public String getUpdateInfo() { + return updateInfo; + } + + public void setUpdateInfo(String info) { + this.updateInfo = info; + } + + public Date getUpdateDate() { + return updateDate; + } + public List getCubeCards() { return cubeCards; } diff --git a/Mage/src/main/java/mage/game/events/FlipCoinEvent.java b/Mage/src/main/java/mage/game/events/FlipCoinEvent.java index 493eb24e651..2194aef0d28 100644 --- a/Mage/src/main/java/mage/game/events/FlipCoinEvent.java +++ b/Mage/src/main/java/mage/game/events/FlipCoinEvent.java @@ -12,6 +12,7 @@ public class FlipCoinEvent extends GameEvent { private boolean result; private final boolean chosen; private final boolean winnable; + private boolean autoWin = false; private int flipCount = 1; public FlipCoinEvent(UUID playerId, Ability source, boolean result, boolean chosen, boolean winnable) { @@ -53,6 +54,14 @@ public class FlipCoinEvent extends GameEvent { this.flipCount = flipCount; } + public void setAutoWin(boolean autoWin) { + this.autoWin = autoWin; + } + + public boolean isAutoWin() { + return autoWin; + } + public CoinFlippedEvent createFlippedEvent() { return new CoinFlippedEvent(playerId, sourceId, flipCount, result, chosen, winnable); } diff --git a/Mage/src/main/java/mage/game/events/FlipCoinsEvent.java b/Mage/src/main/java/mage/game/events/FlipCoinsEvent.java new file mode 100644 index 00000000000..ac9a54199e3 --- /dev/null +++ b/Mage/src/main/java/mage/game/events/FlipCoinsEvent.java @@ -0,0 +1,25 @@ +package mage.game.events; + +import mage.abilities.Ability; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public class FlipCoinsEvent extends GameEvent { + + private boolean isHeadsAndWon = false; + + public FlipCoinsEvent(UUID playerId, int amount, Ability source) { + super(EventType.FLIP_COINS, playerId, source, playerId, amount, false); + } + + public void setHeadsAndWon(boolean headsAndWon) { + isHeadsAndWon = headsAndWon; + } + + public boolean isHeadsAndWon() { + return isHeadsAndWon; + } +} diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java index 77f1fd0ea22..9a12cc41e44 100644 --- a/Mage/src/main/java/mage/game/events/GameEvent.java +++ b/Mage/src/main/java/mage/game/events/GameEvent.java @@ -173,7 +173,7 @@ public class GameEvent implements Serializable { amount amount of life loss flag true = from combat damage - other from non combat damage */ - + LOSE_LIFE, LOST_LIFE, /* LOST_LIFE_BATCH_FOR_ONE_PLAYER combines all life lost events for a player to a single batch (event) @@ -401,7 +401,7 @@ public class GameEvent implements Serializable { SURVEIL, SURVEILED, PROLIFERATE, PROLIFERATED, FATESEALED, - FLIP_COIN, COIN_FLIPPED, + FLIP_COIN, FLIP_COINS, COIN_FLIPPED, REPLACE_ROLLED_DIE, // for Clam-I-Am workaround only ROLL_DIE, DIE_ROLLED, ROLL_DICE, DICE_ROLLED, @@ -509,6 +509,7 @@ public class GameEvent implements Serializable { combines all permanent damage events to a single batch (event) and split it per damaged permanent */ DAMAGED_BATCH_FOR_ONE_PERMANENT(true), + REMOVE_DAMAGE_EOT, DESTROY_PERMANENT, /* DESTROYED_PERMANENT diff --git a/Mage/src/main/java/mage/game/match/MatchOptions.java b/Mage/src/main/java/mage/game/match/MatchOptions.java index a878a911f35..55e0eca3063 100644 --- a/Mage/src/main/java/mage/game/match/MatchOptions.java +++ b/Mage/src/main/java/mage/game/match/MatchOptions.java @@ -31,8 +31,7 @@ public class MatchOptions implements Serializable { protected String deckType; protected boolean limited; protected List playerTypes = new ArrayList<>(); - protected boolean multiPlayer; - protected int numSeats; + protected boolean multiPlayer; // allow to play single game with all tourney's players protected String password; protected SkillLevel skillLevel = SkillLevel.CASUAL; protected boolean rollbackTurnsAllowed; @@ -51,27 +50,14 @@ public class MatchOptions implements Serializable { protected Collection perPlayerEmblemCards = Collections.emptySet(); protected Collection globalEmblemCards = Collections.emptySet(); - public MatchOptions(String name, String gameType, boolean multiPlayer, int numSeats) { + public MatchOptions(String name, String gameType, boolean multiPlayer) { this.name = name; this.gameType = gameType; this.password = ""; this.multiPlayer = multiPlayer; - this.numSeats = numSeats; } - public void setNumSeats(int numSeats) { - this.numSeats = numSeats; - } - - public int getNumSeats() { - return numSeats; - } - - public void setMultiPlayer(boolean multiPlayer) { - this.multiPlayer = multiPlayer; - } - - public boolean getMultiPlayer() { + public boolean isSingleGameTourney() { return multiPlayer; } diff --git a/Mage/src/main/java/mage/game/permanent/Permanent.java b/Mage/src/main/java/mage/game/permanent/Permanent.java index 5df20522f2f..1447879b362 100644 --- a/Mage/src/main/java/mage/game/permanent/Permanent.java +++ b/Mage/src/main/java/mage/game/permanent/Permanent.java @@ -132,16 +132,6 @@ public interface Permanent extends Card, Controllable { boolean hasProtectionFrom(MageObject source, Game game); - /** - * @param attachment can be any object: card, permanent, token - * @param source can be null for default checks like state base - * @param game - * @param silentMode - use it to ignore warning message for users (e.g. for - * checking only) - * @return - */ - boolean cantBeAttachedBy(MageObject attachment, Ability source, Game game, boolean silentMode); - boolean wasControlledFromStartOfControllerTurn(); boolean hasSummoningSickness(); diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index 409ace36912..6a74a11ad23 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -524,7 +524,11 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { @Override public void endOfTurn(Game game) { - this.damage = 0; + if (!game.replaceEvent(GameEvent.getEvent( + EventType.REMOVE_DAMAGE_EOT, this.getId(), null, this.getControllerId() + ))) { + this.damage = 0; + } this.timesLoyaltyUsed = 0; this.turnsOnBattlefield++; this.deathtouched = false; @@ -1363,32 +1367,6 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { return false; } - @Override - public boolean cantBeAttachedBy(MageObject attachment, Ability source, Game game, boolean silentMode) { - for (ProtectionAbility ability : this.getAbilities(game).getProtectionAbilities()) { - if ((!attachment.hasSubtype(SubType.AURA, game) || ability.removesAuras()) - && (!attachment.hasSubtype(SubType.EQUIPMENT, game) || ability.removesEquipment()) - && !attachment.getId().equals(ability.getAuraIdNotToBeRemoved()) - && !ability.canTarget(attachment, game)) { - return !ability.getDoesntRemoveControlled() || isControlledBy(game.getControllerId(attachment.getId())); - } - } - - boolean canAttach = true; - Permanent attachmentPermanent = game.getPermanent(attachment.getId()); - // If attachment is an aura, ensures this permanent can still be legally enchanted, according to the enchantment's Enchant ability - if (attachment.hasSubtype(SubType.AURA, game) - && attachmentPermanent != null - && attachmentPermanent.getSpellAbility() != null - && !attachmentPermanent.getSpellAbility().getTargets().isEmpty()) { - // Line of code below functionally gets the target of the aura's Enchant ability, then compares to this permanent. Enchant improperly implemented in XMage, see #9583 - // Note: stillLegalTarget used exclusively to account for Dream Leash. Can be made canTarget in the event that that card is rewritten (and "stillLegalTarget" removed from TargetImpl). - canAttach = attachmentPermanent.getSpellAbility().getTargets().get(0).copy().withNotTarget(true).stillLegalTarget(attachmentPermanent.getControllerId(), this.getId(), source, game); - } - - return !canAttach || game.getContinuousEffects().preventedByRuleModification(new StayAttachedEvent(this.getId(), attachment.getId(), source), null, game, silentMode); - } - @Override public boolean destroy(Ability source, Game game) { return destroy(source, game, false); diff --git a/Mage/src/main/java/mage/game/permanent/token/AshiokWickedManipulatorNightmareToken.java b/Mage/src/main/java/mage/game/permanent/token/AshiokWickedManipulatorNightmareToken.java index 4e2812f731c..afa3fcf91cd 100644 --- a/Mage/src/main/java/mage/game/permanent/token/AshiokWickedManipulatorNightmareToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/AshiokWickedManipulatorNightmareToken.java @@ -1,12 +1,11 @@ package mage.game.permanent.token; import mage.MageInt; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.abilities.condition.common.WasCardExiledThisTurnCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.hint.ConditionHint; import mage.abilities.hint.Hint; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; @@ -22,21 +21,16 @@ public final class AshiokWickedManipulatorNightmareToken extends TokenImpl { * /!\ You need to add CardsExiledThisTurnWatcher to any card using this token */ public AshiokWickedManipulatorNightmareToken() { - super("Nightmare Token", "1/1 black Nightmare creature tokens with \"At the beginning of combat on your turn, if a card was put into exile this turn, put a +1/+1 counter on this creature.\""); + super("Nightmare Token", "1/1 black Nightmare creature token with \"At the beginning of combat on your turn, if a card was put into exile this turn, put a +1/+1 counter on this token.\""); cardType.add(CardType.CREATURE); color.setBlack(true); subtype.add(SubType.NIGHTMARE); power = new MageInt(1); toughness = new MageInt(1); - this.addAbility(new ConditionalInterveningIfTriggeredAbility( - new BeginningOfCombatTriggeredAbility( - new AddCountersSourceEffect(CounterType.P1P1.createInstance()) - ), - WasCardExiledThisTurnCondition.instance, - "At the beginning of combat on your turn, if a card was put into exile " - + "this turn, put a +1/+1 counter on this creature." - ).addHint(hint)); + this.addAbility(new BeginningOfCombatTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()) + ).withInterveningIf(WasCardExiledThisTurnCondition.instance).addHint(hint)); } private AshiokWickedManipulatorNightmareToken(final AshiokWickedManipulatorNightmareToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/BeastieToken.java b/Mage/src/main/java/mage/game/permanent/token/BeastieToken.java index 7bd07279026..b5e4f4fe03d 100644 --- a/Mage/src/main/java/mage/game/permanent/token/BeastieToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/BeastieToken.java @@ -12,7 +12,7 @@ import mage.constants.SubType; public final class BeastieToken extends TokenImpl { public BeastieToken() { - super("Beast Token", "4/4 white Beast creature token with \"This creature can't attack or block alone.\""); + super("Beast Token", "4/4 white Beast creature token with \"This token can't attack or block alone.\""); cardType.add(CardType.CREATURE); color.setWhite(true); subtype.add(SubType.BEAST); diff --git a/Mage/src/main/java/mage/game/permanent/token/ChocoboToken.java b/Mage/src/main/java/mage/game/permanent/token/ChocoboToken.java new file mode 100644 index 00000000000..d51b49e0e92 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/ChocoboToken.java @@ -0,0 +1,34 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.common.LandfallAbility; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class ChocoboToken extends TokenImpl { + + public ChocoboToken() { + super("Bird Token", "2/2 green Bird creature token with \"Whenever a land you control enters, this token gets +1/+0 until end of turn.\""); + cardType.add(CardType.CREATURE); + color.setGreen(true); + subtype.add(SubType.BIRD); + power = new MageInt(2); + toughness = new MageInt(2); + + addAbility(new LandfallAbility(new BoostSourceEffect(1, 0, Duration.EndOfTurn)).setAbilityWord(null)); + } + + private ChocoboToken(final ChocoboToken token) { + super(token); + } + + @Override + public ChocoboToken copy() { + return new ChocoboToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/DarkstarToken.java b/Mage/src/main/java/mage/game/permanent/token/DarkstarToken.java new file mode 100644 index 00000000000..d6cb4038687 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/DarkstarToken.java @@ -0,0 +1,32 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; + +/** + * @author TheElk801 + */ +public final class DarkstarToken extends TokenImpl { + + public DarkstarToken() { + super("Darkstar", "Darkstar, a legendary 2/2 white and black Dog creature token"); + supertype.add(SuperType.LEGENDARY); + cardType.add(CardType.CREATURE); + color.setWhite(true); + color.setBlack(true); + subtype.add(SubType.DOG); + power = new MageInt(2); + toughness = new MageInt(2); + } + + private DarkstarToken(final DarkstarToken token) { + super(token); + } + + @Override + public DarkstarToken copy() { + return new DarkstarToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/DragonMenaceAndStealArtifactToken.java b/Mage/src/main/java/mage/game/permanent/token/DragonMenaceAndStealArtifactToken.java index 09906bdad14..6b47dd7b914 100644 --- a/Mage/src/main/java/mage/game/permanent/token/DragonMenaceAndStealArtifactToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/DragonMenaceAndStealArtifactToken.java @@ -11,7 +11,7 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.common.FilterArtifactPermanent; import mage.target.TargetPermanent; -import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; public class DragonMenaceAndStealArtifactToken extends TokenImpl { @@ -31,7 +31,7 @@ public class DragonMenaceAndStealArtifactToken extends TokenImpl { Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new GainControlTargetEffect(Duration.EndOfGame), false, true); ability.addTarget(new TargetPermanent(filter)); - ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); addAbility(ability); } diff --git a/Mage/src/main/java/mage/game/permanent/token/ElementalAllColorsToken.java b/Mage/src/main/java/mage/game/permanent/token/ElementalAllColorsToken.java new file mode 100644 index 00000000000..0d42f387201 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/ElementalAllColorsToken.java @@ -0,0 +1,33 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class ElementalAllColorsToken extends TokenImpl { + + public ElementalAllColorsToken() { + super("Elemental Token", "2/2 Elemental creature token that's all colors"); + cardType.add(CardType.CREATURE); + color.setWhite(true); + color.setBlue(true); + color.setBlack(true); + color.setRed(true); + color.setGreen(true); + subtype.add(SubType.ELEMENTAL); + power = new MageInt(2); + toughness = new MageInt(2); + } + + private ElementalAllColorsToken(final ElementalAllColorsToken token) { + super(token); + } + + @Override + public ElementalAllColorsToken copy() { + return new ElementalAllColorsToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/FishToken.java b/Mage/src/main/java/mage/game/permanent/token/FishToken.java index 89c2170c151..38b409c0851 100644 --- a/Mage/src/main/java/mage/game/permanent/token/FishToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/FishToken.java @@ -11,14 +11,14 @@ import mage.constants.SubType; public final class FishToken extends TokenImpl { public FishToken() { - super("Fish Token", "1/1 blue Fish creature token with \"This creature can't be blocked.\""); + super("Fish Token", "1/1 blue Fish creature token with \"This token can't be blocked.\""); cardType.add(CardType.CREATURE); subtype.add(SubType.FISH); color.setBlue(true); power = new MageInt(1); toughness = new MageInt(1); - addAbility(new CantBeBlockedSourceAbility("this creature can't be blocked")); + addAbility(new CantBeBlockedSourceAbility("this token can't be blocked")); } private FishToken(final FishToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/FrogGreenToken.java b/Mage/src/main/java/mage/game/permanent/token/FrogGreenToken.java new file mode 100644 index 00000000000..300b48fa428 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/FrogGreenToken.java @@ -0,0 +1,28 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class FrogGreenToken extends TokenImpl { + + public FrogGreenToken() { + super("Frog Token", "1/1 green Frog creature token"); + cardType.add(CardType.CREATURE); + color.setGreen(true); + subtype.add(SubType.FROG); + power = new MageInt(1); + toughness = new MageInt(1); + } + + private FrogGreenToken(final FrogGreenToken token) { + super(token); + } + + public FrogGreenToken copy() { + return new FrogGreenToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/GarrukCursedHuntsmanToken.java b/Mage/src/main/java/mage/game/permanent/token/GarrukCursedHuntsmanToken.java index 87b3014c61d..6a73f0fba05 100644 --- a/Mage/src/main/java/mage/game/permanent/token/GarrukCursedHuntsmanToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/GarrukCursedHuntsmanToken.java @@ -18,7 +18,7 @@ public final class GarrukCursedHuntsmanToken extends TokenImpl { = new FilterControlledPermanent(SubType.GARRUK, "Garruk you control"); public GarrukCursedHuntsmanToken() { - super("Wolf Token", "2/2 black and green Wolf creature token with \"When this creature dies, put a loyalty counter on each Garruk you control.\""); + super("Wolf Token", "2/2 black and green Wolf creature token with \"When this token dies, put a loyalty counter on each Garruk you control.\""); cardType.add(CardType.CREATURE); color.setBlack(true); color.setGreen(true); diff --git a/Mage/src/main/java/mage/game/permanent/token/Horror3Token.java b/Mage/src/main/java/mage/game/permanent/token/Horror3Token.java new file mode 100644 index 00000000000..a18c2b74d35 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/Horror3Token.java @@ -0,0 +1,28 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class Horror3Token extends TokenImpl { + + public Horror3Token() { + super("Horror Token", "2/2 black Horror creature token"); + cardType.add(CardType.CREATURE); + color.setBlack(true); + subtype.add(SubType.HORROR); + power = new MageInt(2); + toughness = new MageInt(2); + } + + private Horror3Token(final Horror3Token token) { + super(token); + } + + public Horror3Token copy() { + return new Horror3Token(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/KarnConstructToken.java b/Mage/src/main/java/mage/game/permanent/token/KarnConstructToken.java index 6b721307cf9..edae1dc7c4e 100644 --- a/Mage/src/main/java/mage/game/permanent/token/KarnConstructToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/KarnConstructToken.java @@ -15,7 +15,7 @@ import mage.constants.Zone; public final class KarnConstructToken extends TokenImpl { public KarnConstructToken() { - super("Construct Token", "0/0 colorless Construct artifact creature token with \"This creature gets +1/+1 for each artifact you control.\""); + super("Construct Token", "0/0 colorless Construct artifact creature token with \"This token gets +1/+1 for each artifact you control.\""); cardType.add(CardType.ARTIFACT); cardType.add(CardType.CREATURE); subtype.add(SubType.CONSTRUCT); @@ -24,7 +24,7 @@ public final class KarnConstructToken extends TokenImpl { this.addAbility(new SimpleStaticAbility( new BoostSourceEffect(ArtifactYouControlCount.instance, ArtifactYouControlCount.instance, Duration.WhileOnBattlefield) - .setText("This creature gets +1/+1 for each artifact you control") + .setText("This token gets +1/+1 for each artifact you control") )); } diff --git a/Mage/src/main/java/mage/game/permanent/token/Mutant33DeathtouchToken.java b/Mage/src/main/java/mage/game/permanent/token/Mutant33DeathtouchToken.java new file mode 100644 index 00000000000..bfd77473771 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/Mutant33DeathtouchToken.java @@ -0,0 +1,34 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.DeathtouchAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author padfoothelix + */ + + +public final class Mutant33DeathtouchToken extends TokenImpl { + + public Mutant33DeathtouchToken() { + super("Mutant Token", "3/3 green mutant creature token with deathtouch"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.MUTANT); + color.setGreen(true); + power = new MageInt(3); + toughness = new MageInt(3); + + addAbility(DeathtouchAbility.getInstance()); + } + + private Mutant33DeathtouchToken(final Mutant33DeathtouchToken token) { + super(token); + } + + public Mutant33DeathtouchToken copy() { + return new Mutant33DeathtouchToken(this); + } +} + diff --git a/Mage/src/main/java/mage/game/permanent/token/NymphToken.java b/Mage/src/main/java/mage/game/permanent/token/NymphToken.java new file mode 100644 index 00000000000..e64712e0b26 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/NymphToken.java @@ -0,0 +1,29 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class NymphToken extends TokenImpl { + + public NymphToken() { + super("Nymph Token", "2/2 white Nymph enchantment creature token"); + cardType.add(CardType.ENCHANTMENT); + cardType.add(CardType.CREATURE); + color.setWhite(true); + subtype.add(SubType.NYMPH); + power = new MageInt(2); + toughness = new MageInt(2); + } + + private NymphToken(final NymphToken token) { + super(token); + } + + public NymphToken copy() { + return new NymphToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/Pest11GainLifeToken.java b/Mage/src/main/java/mage/game/permanent/token/Pest11GainLifeToken.java index 1ab3238b88e..93833f09d98 100644 --- a/Mage/src/main/java/mage/game/permanent/token/Pest11GainLifeToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/Pest11GainLifeToken.java @@ -12,7 +12,7 @@ import mage.constants.SubType; public final class Pest11GainLifeToken extends TokenImpl { public Pest11GainLifeToken() { - super("Pest Token", "1/1 black and green Pest creature token with \"When this creature dies, you gain 1 life.\""); + super("Pest Token", "1/1 black and green Pest creature token with \"When this token dies, you gain 1 life.\""); cardType.add(CardType.CREATURE); color.setBlack(true); color.setGreen(true); diff --git a/Mage/src/main/java/mage/game/permanent/token/PilotCrewToken.java b/Mage/src/main/java/mage/game/permanent/token/PilotCrewToken.java index 4955c7c6d84..59c49d79df7 100644 --- a/Mage/src/main/java/mage/game/permanent/token/PilotCrewToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/PilotCrewToken.java @@ -11,7 +11,7 @@ import mage.constants.SubType; public final class PilotCrewToken extends TokenImpl { public PilotCrewToken() { - super("Pilot Token", "1/1 colorless Pilot creature token with \"This creature crews Vehicles as though its power were 2 greater.\""); + super("Pilot Token", "1/1 colorless Pilot creature token with \"This token crews Vehicles as though its power were 2 greater.\""); cardType.add(CardType.CREATURE); subtype.add(SubType.PILOT); power = new MageInt(1); diff --git a/Mage/src/main/java/mage/game/permanent/token/SeizeTheStormElementalToken.java b/Mage/src/main/java/mage/game/permanent/token/SeizeTheStormElementalToken.java index 908e4c7f8f2..00b0721fc7c 100644 --- a/Mage/src/main/java/mage/game/permanent/token/SeizeTheStormElementalToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/SeizeTheStormElementalToken.java @@ -22,7 +22,7 @@ public final class SeizeTheStormElementalToken extends TokenImpl { public SeizeTheStormElementalToken(DynamicValue xValue, Hint hint) { super("Elemental Token", "red Elemental creature token with trample and " + - "\"This creature's power and toughness are each equal to the number of instant " + + "\"This token's power and toughness are each equal to the number of instant " + "and sorcery cards in your graveyard plus the number of cards with flashback you own in exile.\""); cardType.add(CardType.CREATURE); color.setRed(true); @@ -32,7 +32,7 @@ public final class SeizeTheStormElementalToken extends TokenImpl { this.addAbility(TrampleAbility.getInstance()); this.addAbility(new SimpleStaticAbility(new SetBasePowerToughnessSourceEffect( xValue - ).setText("this creature's power and toughness are each equal to the number of " + + ).setText("this token's power and toughness are each equal to the number of " + "instant and sorcery cards in your graveyard, plus the number of cards with flashback you own in exile") ).addHint(hint)); } diff --git a/Mage/src/main/java/mage/game/permanent/token/VrenRatToken.java b/Mage/src/main/java/mage/game/permanent/token/VrenRatToken.java index 5925705fc98..c7d1f8932d0 100644 --- a/Mage/src/main/java/mage/game/permanent/token/VrenRatToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/VrenRatToken.java @@ -2,12 +2,13 @@ package mage.game.permanent.token; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; +import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.AnotherPredicate; @@ -16,26 +17,23 @@ import mage.filter.predicate.mageobject.AnotherPredicate; */ public final class VrenRatToken extends TokenImpl { - private static final FilterControlledPermanent filter = new FilterControlledPermanent("other Rat you control"); + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.RAT, "other Rat you control"); static { - filter.add(SubType.RAT.getPredicate()); filter.add(AnotherPredicate.instance); } + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + public VrenRatToken() { - super("Rat Token", "1/1 black Rat creature tokens with \"This creature gets +1/+1 for each other Rat you control\""); + super("Rat Token", "1/1 black Rat creature token with \"This token gets +1/+1 for each other Rat you control.\""); cardType.add(CardType.CREATURE); color.setBlack(true); subtype.add(SubType.RAT); power = new MageInt(1); toughness = new MageInt(1); - this.addAbility(new SimpleStaticAbility( - new BoostSourceEffect(new PermanentsOnBattlefieldCount(filter), - new PermanentsOnBattlefieldCount(filter), Duration.WhileOnBattlefield - ) - )); + this.addAbility(new SimpleStaticAbility(new BoostSourceEffect(xValue, xValue, Duration.WhileOnBattlefield))); } private VrenRatToken(final VrenRatToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/WrennAndSevenTreefolkToken.java b/Mage/src/main/java/mage/game/permanent/token/WrennAndSevenTreefolkToken.java index a25f5aaa8c7..ac2080ca59a 100644 --- a/Mage/src/main/java/mage/game/permanent/token/WrennAndSevenTreefolkToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/WrennAndSevenTreefolkToken.java @@ -15,7 +15,7 @@ import mage.constants.Zone; public final class WrennAndSevenTreefolkToken extends TokenImpl { public WrennAndSevenTreefolkToken() { - super("Treefolk Token", "green Treefolk creature token with reach and \"This creature's power and toughness are each equal to the number of lands you control.\""); + super("Treefolk Token", "green Treefolk creature token with reach and \"This token's power and toughness are each equal to the number of lands you control.\""); cardType.add(CardType.CREATURE); color.setGreen(true); subtype.add(SubType.TREEFOLK); @@ -24,7 +24,7 @@ public final class WrennAndSevenTreefolkToken extends TokenImpl { this.addAbility(ReachAbility.getInstance()); this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetBasePowerToughnessSourceEffect( LandsYouControlCount.instance - ).setText("this creature's power and toughness are each equal to the number of lands you control"))); + ).setText("this token's power and toughness are each equal to the number of lands you control"))); } private WrennAndSevenTreefolkToken(final WrennAndSevenTreefolkToken token) { diff --git a/Mage/src/main/java/mage/game/permanent/token/YoungHeroRoleToken.java b/Mage/src/main/java/mage/game/permanent/token/YoungHeroRoleToken.java index d73aaf07a81..04eeb1a0090 100644 --- a/Mage/src/main/java/mage/game/permanent/token/YoungHeroRoleToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/YoungHeroRoleToken.java @@ -5,7 +5,6 @@ import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceMatchesFilterCondition; -import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; @@ -23,7 +22,7 @@ import mage.target.common.TargetCreaturePermanent; */ public final class YoungHeroRoleToken extends TokenImpl { - private static final FilterPermanent filter = new FilterCreaturePermanent(); + private static final FilterPermanent filter = new FilterCreaturePermanent("its toughness is 3 or less"); static { filter.add(new ToughnessPredicate(ComparisonType.FEWER_THAN, 4)); @@ -45,10 +44,10 @@ public final class YoungHeroRoleToken extends TokenImpl { // Enchanted creature has "Whenever this creature attacks, if its toughness is 3 or less, put a +1/+1 counter on it." this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect( - new ConditionalInterveningIfTriggeredAbility( - new AttacksTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance())), - condition, "Whenever this creature attacks, if its toughness is 3 or less, put a +1/+1 counter on it." - ), AttachmentType.AURA + new AttacksTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()) + .setText("put a +1/+1 counter on it") + ).withInterveningIf(condition), AttachmentType.AURA ))); } diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java index a4939295c25..7cc905df6b6 100644 --- a/Mage/src/main/java/mage/game/stack/Spell.java +++ b/Mage/src/main/java/mage/game/stack/Spell.java @@ -1169,6 +1169,11 @@ public class Spell extends StackObjectImpl implements Card { game.fireEvent(new CopiedStackObjectEvent(this, spellCopy, newControllerId)); } + @Override + public boolean canBeCopied() { + return this.getSpellAbility().canBeCopied(); + } + @Override public boolean isAllCreatureTypes(Game game) { return card.isAllCreatureTypes(game); @@ -1204,6 +1209,11 @@ public class Spell extends StackObjectImpl implements Card { throw new UnsupportedOperationException("Not supported."); } + @Override + public boolean cantBeAttachedBy(MageObject attachment, Ability source, Game game, boolean silentMode) { + throw new UnsupportedOperationException("Not supported."); + } + @Override public boolean addAttachment(UUID permanentId, Ability source, Game game) { throw new UnsupportedOperationException("Not supported."); diff --git a/Mage/src/main/java/mage/game/stack/StackAbility.java b/Mage/src/main/java/mage/game/stack/StackAbility.java index 422e3c79585..f83a90a5697 100644 --- a/Mage/src/main/java/mage/game/stack/StackAbility.java +++ b/Mage/src/main/java/mage/game/stack/StackAbility.java @@ -583,6 +583,11 @@ public class StackAbility extends StackObjectImpl implements Ability { throw new UnsupportedOperationException("Not supported."); } + @Override + public String addRulePrefix(String rule) { + throw new UnsupportedOperationException("Not supported."); + } + @Override public Ability withFirstModeFlavorWord(String flavorWord) { throw new UnsupportedOperationException("Not supported."); @@ -668,6 +673,10 @@ public class StackAbility extends StackObjectImpl implements Ability { ability.setSourceObjectZoneChangeCounter(zoneChangeCounter); } + @Override + public void initSourceObjectZoneChangeCounter(Game game, boolean force) { + ability.initSourceObjectZoneChangeCounter(game, force); + } @Override public int getSourceObjectZoneChangeCounter() { return ability.getSourceObjectZoneChangeCounter(); @@ -718,6 +727,16 @@ public class StackAbility extends StackObjectImpl implements Ability { throw new UnsupportedOperationException("Not supported."); } + @Override + public boolean canBeCopied() { + return ability.canBeCopied(); + } + + @Override + public Ability withCanBeCopied(boolean canBeCopied) { + throw new UnsupportedOperationException("Not supported."); + } + @Override public void createSingleCopy(UUID newControllerId, StackObjectCopyApplier applier, MageObjectReferencePredicate newTargetFilterPredicate, Game game, Ability source, boolean chooseNewTargets) { Ability newAbility = this.ability.copy(); diff --git a/Mage/src/main/java/mage/game/stack/StackObject.java b/Mage/src/main/java/mage/game/stack/StackObject.java index 7c183e78a29..fe53ba09a7b 100644 --- a/Mage/src/main/java/mage/game/stack/StackObject.java +++ b/Mage/src/main/java/mage/game/stack/StackObject.java @@ -40,6 +40,8 @@ public interface StackObject extends MageObject, Controllable { void createSingleCopy(UUID newControllerId, StackObjectCopyApplier applier, MageObjectReferencePredicate newTargetFilterPredicate, Game game, Ability source, boolean chooseNewTargets); + boolean canBeCopied(); + boolean isTargetChanged(); void setTargetChanged(boolean targetChanged); diff --git a/Mage/src/main/java/mage/game/stack/StackObjectImpl.java b/Mage/src/main/java/mage/game/stack/StackObjectImpl.java index 5bd3621b749..fdf38192f3a 100644 --- a/Mage/src/main/java/mage/game/stack/StackObjectImpl.java +++ b/Mage/src/main/java/mage/game/stack/StackObjectImpl.java @@ -154,6 +154,9 @@ public abstract class StackObjectImpl implements StackObject { @Override public void createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets, int amount, StackObjectCopyApplier applier) { + if (!this.canBeCopied()) { + return; + } GameEvent gameEvent = new CopyStackObjectEvent(source, this, newControllerId, amount); if (game.replaceEvent(gameEvent)) { return; diff --git a/Mage/src/main/java/mage/game/tournament/MultiplayerRound.java b/Mage/src/main/java/mage/game/tournament/MultiplayerRound.java index 6c9ba9f0b78..2f19c2c2d97 100644 --- a/Mage/src/main/java/mage/game/tournament/MultiplayerRound.java +++ b/Mage/src/main/java/mage/game/tournament/MultiplayerRound.java @@ -15,28 +15,19 @@ public class MultiplayerRound { private final int roundNum; private final Tournament tournament; - private final int numSeats; private final List allPlayers = new ArrayList<>(); private Match match; private UUID tableId; - public MultiplayerRound(int roundNum, Tournament tournament, int numSeats) { + public MultiplayerRound(int roundNum, Tournament tournament) { this.roundNum = roundNum; this.tournament = tournament; - this.numSeats = numSeats; } public List getAllPlayers () { return allPlayers; } - - public TournamentPlayer getPlayer (int i) { - if (i >= 0 && i < numSeats && i < allPlayers.size()) { - return allPlayers.get(i); - } - return null; - } public void addPairing(TournamentPairing match) { this.allPlayers.add(match.getPlayer1()); diff --git a/Mage/src/main/java/mage/game/tournament/TournamentImpl.java b/Mage/src/main/java/mage/game/tournament/TournamentImpl.java index 3872189d581..1af54ac63ce 100644 --- a/Mage/src/main/java/mage/game/tournament/TournamentImpl.java +++ b/Mage/src/main/java/mage/game/tournament/TournamentImpl.java @@ -25,6 +25,8 @@ import java.util.concurrent.CopyOnWriteArrayList; */ public abstract class TournamentImpl implements Tournament { + private static final Logger logger = Logger.getLogger(TournamentImpl.class); + protected UUID id = UUID.randomUUID(); protected UUID tableId = null; // assign on table create protected List rounds = new CopyOnWriteArrayList<>(); @@ -223,9 +225,11 @@ public abstract class TournamentImpl implements Tournament { } protected void playMultiplayerRound(MultiplayerRound round) { + // it's a single game, so tourney will be ends immediately + // and will keep one game active for better performance + // and less max tourney restrictions on the server + updateResults(); playMultiPlayerMatch(round); - - updateResults(); // show points from byes } protected List getActivePlayers() { @@ -248,6 +252,8 @@ public abstract class TournamentImpl implements Tournament { player.setPoints(0); player.setStateInfo(""); } + + // multiple games tourney for (Round round : rounds) { for (TournamentPairing pair : round.getPairs()) { Match match = pair.getMatch(); @@ -256,8 +262,10 @@ public abstract class TournamentImpl implements Tournament { TournamentPlayer tp2 = pair.getPlayer2(); MatchPlayer mp1 = match.getPlayer(pair.getPlayer1().getPlayer().getId()); MatchPlayer mp2 = match.getPlayer(pair.getPlayer2().getPlayer().getId()); + // set player state if they finished the round - if (round.getRoundNumber() == rounds.size()) { // for elimination getRoundNumber = 0 so never true here + // for elimination getRoundNumber = 0 so never true here + if (round.getRoundNumber() == rounds.size()) { match.setTournamentRound(round.getRoundNumber()); if (tp1.getState() == TournamentPlayerState.DUELING) { if (round.getRoundNumber() == getNumberRounds()) { @@ -274,6 +282,7 @@ public abstract class TournamentImpl implements Tournament { } } } + // Add round result tp1.setResults(addRoundResult(round.getRoundNumber(), pair, tp1, tp2)); tp2.setResults(addRoundResult(round.getRoundNumber(), pair, tp2, tp1)); diff --git a/Mage/src/main/java/mage/game/tournament/TournamentOptions.java b/Mage/src/main/java/mage/game/tournament/TournamentOptions.java index c81294ce3ee..4822ba1d583 100644 --- a/Mage/src/main/java/mage/game/tournament/TournamentOptions.java +++ b/Mage/src/main/java/mage/game/tournament/TournamentOptions.java @@ -26,9 +26,9 @@ public class TournamentOptions implements Serializable { protected int quitRatio; protected int minimumRating; - public TournamentOptions(String name, String matchType, int numSeats) { + public TournamentOptions(String name, String matchType, boolean isSingleMultiplayerGame) { this.name = name; - this.matchOptions = new MatchOptions("", matchType, numSeats > 2, numSeats); + this.matchOptions = new MatchOptions("", matchType, isSingleMultiplayerGame); } public String getName() { diff --git a/Mage/src/main/java/mage/game/tournament/TournamentSealedOptions.java b/Mage/src/main/java/mage/game/tournament/TournamentSealedOptions.java index 19d9ab3d6b5..5df7d96cf43 100644 --- a/Mage/src/main/java/mage/game/tournament/TournamentSealedOptions.java +++ b/Mage/src/main/java/mage/game/tournament/TournamentSealedOptions.java @@ -8,8 +8,8 @@ package mage.game.tournament; */ public class TournamentSealedOptions extends TournamentOptions { - public TournamentSealedOptions(String name, String matchType, int numSeats) { - super(name, matchType, numSeats); + public TournamentSealedOptions(String name, String matchType, boolean isSingleMultiplayerGame) { + super(name, matchType, isSingleMultiplayerGame); } } diff --git a/Mage/src/main/java/mage/game/tournament/TournamentSingleElimination.java b/Mage/src/main/java/mage/game/tournament/TournamentSingleElimination.java index 8d366a2136c..e2499db4344 100644 --- a/Mage/src/main/java/mage/game/tournament/TournamentSingleElimination.java +++ b/Mage/src/main/java/mage/game/tournament/TournamentSingleElimination.java @@ -2,13 +2,13 @@ package mage.game.tournament; -import java.util.Map; -import java.util.UUID; import mage.constants.MultiplayerAttackOption; import mage.game.events.TableEvent; +import java.util.Map; +import java.util.UUID; + /** - * * @author BetaSteward_at_googlemail.com */ public abstract class TournamentSingleElimination extends TournamentImpl { @@ -19,38 +19,39 @@ public abstract class TournamentSingleElimination extends TournamentImpl { @Override protected void runTournament() { - for (Map.Entry entry: players.entrySet()) { + for (Map.Entry entry : players.entrySet()) { if (entry.getValue().getPlayer().autoLoseGame()) { entry.getValue().setEliminated(); entry.getValue().setResults("Auto Eliminated"); } - } - if (options.matchOptions.getNumSeats() == 2) { + } + if (options.matchOptions.isSingleGameTourney()) { + // one game with all players + options.matchOptions.setAttackOption(MultiplayerAttackOption.MULTIPLE); + MultiplayerRound round = new MultiplayerRound(0, this); + for (TournamentPlayer player : getActivePlayers()) { + round.addPlayer(player); + } + playMultiplayerRound(round); + } else { + // split to multiple rounds/games while (this.getActivePlayers().size() > 1) { // check if some player got killed / disconnected meanwhile and update their state tableEventSource.fireTableEvent(TableEvent.EventType.CHECK_STATE_PLAYERS); Round round = createRoundRandom(); playRound(round); eliminatePlayers(round); - } - } else { - options.matchOptions.setAttackOption(MultiplayerAttackOption.MULTIPLE); - MultiplayerRound round = new MultiplayerRound(0, this, options.matchOptions.getNumSeats()); - for (TournamentPlayer player : getActivePlayers()) { - round.addPlayer(player); } - playMultiplayerRound(round); } - + nextStep(); } - + private void eliminatePlayers(Round round) { - for (TournamentPairing pair: round.getPairs()) { + for (TournamentPairing pair : round.getPairs()) { pair.eliminatePlayers(); } } - } diff --git a/Mage/src/main/java/mage/game/tournament/TournamentSwiss.java b/Mage/src/main/java/mage/game/tournament/TournamentSwiss.java index 714305a73af..1870098ee87 100644 --- a/Mage/src/main/java/mage/game/tournament/TournamentSwiss.java +++ b/Mage/src/main/java/mage/game/tournament/TournamentSwiss.java @@ -1,19 +1,18 @@ package mage.game.tournament; -import java.util.List; -import java.util.Map.Entry; -import java.util.UUID; import mage.constants.MultiplayerAttackOption; - import mage.constants.TournamentPlayerState; import mage.game.events.TableEvent; import mage.game.tournament.pairing.RoundPairings; import mage.game.tournament.pairing.SwissPairingMinimalWeightMatching; import mage.game.tournament.pairing.SwissPairingSimple; +import java.util.List; +import java.util.Map.Entry; +import java.util.UUID; + /** - * * @author BetaSteward_at_googlemail.com */ public abstract class TournamentSwiss extends TournamentImpl { @@ -31,82 +30,65 @@ public abstract class TournamentSwiss extends TournamentImpl { } } - if (options.matchOptions.getNumSeats() == 2) { + if (options.matchOptions.isSingleGameTourney()) { + // one game with all players (it's same as normal tourney's mode) + options.matchOptions.setAttackOption(MultiplayerAttackOption.MULTIPLE); + MultiplayerRound round = new MultiplayerRound(0, this); + for (TournamentPlayer player : getActivePlayers()) { + round.addPlayer(player); + } + playMultiplayerRound(round); + } else { + // split to multiple rounds/games while (this.getActivePlayers().size() > 1 && this.getNumberRounds() > this.getRounds().size()) { // check if some player got killed / disconnected meanwhile and update their state tableEventSource.fireTableEvent(TableEvent.EventType.CHECK_STATE_PLAYERS); - // Swiss pairing Round round = createRoundSwiss(); playRound(round); } - } else { - options.matchOptions.setAttackOption(MultiplayerAttackOption.MULTIPLE); - MultiplayerRound round = createMultiplayerRound(); - playMultiplayerRound(round); } - + nextStep(); } protected Round createRoundSwiss() { + // multiple rounds, can be called multiple times per tourney + List roundPlayers = getActivePlayers(); boolean isLastRound = (rounds.size() + 1 == getNumberRounds()); + if (options.matchOptions.isSingleGameTourney()) { + throw new IllegalStateException("Wrong code usage: multi rounds for non single game tourneys only (e.g. with two seats)"); + } Round round = null; - if (options.matchOptions.getNumSeats() == 2) { - RoundPairings roundPairings; - if (roundPlayers.size() <= 16) { - SwissPairingMinimalWeightMatching swissPairing = new SwissPairingMinimalWeightMatching(roundPlayers, rounds, isLastRound); - roundPairings = swissPairing.getRoundPairings(); - } else { - SwissPairingSimple swissPairing = new SwissPairingSimple(roundPlayers, rounds); - roundPairings = swissPairing.getRoundPairings(); - } - round = new Round(rounds.size() + 1, this); - rounds.add(round); - for (TournamentPairing pairing : roundPairings.getPairings()) { - round.addPairing(pairing); - } - for (TournamentPlayer playerBye : roundPairings.getPlayerByes()) { - // player free round - add to bye players of this round - round.getPlayerByes().add(playerBye); - if (isLastRound) { - playerBye.setState(TournamentPlayerState.FINISHED); - } else { - playerBye.setState(TournamentPlayerState.WAITING); - } - playerBye.setStateInfo("Round Bye"); - updateResults(); + RoundPairings roundPairings; + if (roundPlayers.size() <= 16) { + SwissPairingMinimalWeightMatching swissPairing = new SwissPairingMinimalWeightMatching(roundPlayers, rounds, isLastRound); + roundPairings = swissPairing.getRoundPairings(); + } else { + SwissPairingSimple swissPairing = new SwissPairingSimple(roundPlayers, rounds); + roundPairings = swissPairing.getRoundPairings(); + } + + round = new Round(rounds.size() + 1, this); + rounds.add(round); + for (TournamentPairing pairing : roundPairings.getPairings()) { + round.addPairing(pairing); + } + + for (TournamentPlayer playerBye : roundPairings.getPlayerByes()) { + // player free round - add to bye players of this round + round.getPlayerByes().add(playerBye); + if (isLastRound) { + playerBye.setState(TournamentPlayerState.FINISHED); + } else { + playerBye.setState(TournamentPlayerState.WAITING); } + playerBye.setStateInfo("Round Bye"); + updateResults(); } return round; } - - public MultiplayerRound createMultiplayerRound() { - List roundPlayers = getActivePlayers(); - boolean isLastRound = (rounds.size() + 1 == getNumberRounds()); - - MultiplayerRound round = null; - if (options.matchOptions.getNumSeats() > 2) { - options.matchOptions.setAttackOption(MultiplayerAttackOption.MULTIPLE); - RoundPairings roundPairings; - if (roundPlayers.size() <= 16) { - SwissPairingMinimalWeightMatching swissPairing = new SwissPairingMinimalWeightMatching(roundPlayers, rounds, isLastRound); - roundPairings = swissPairing.getRoundPairings(); - } else { - SwissPairingSimple swissPairing = new SwissPairingSimple(roundPlayers, rounds); - roundPairings = swissPairing.getRoundPairings(); - } - - round = new MultiplayerRound(rounds.size() + 1, this, options.matchOptions.getNumSeats()); - for (TournamentPairing pairing : roundPairings.getPairings()) { - round.addPairing(pairing); - } - - } - return round; - - } } diff --git a/Mage/src/main/java/mage/game/turn/PreCombatMainStep.java b/Mage/src/main/java/mage/game/turn/PreCombatMainStep.java index 35d40f1d56e..4567c64731f 100644 --- a/Mage/src/main/java/mage/game/turn/PreCombatMainStep.java +++ b/Mage/src/main/java/mage/game/turn/PreCombatMainStep.java @@ -1,9 +1,11 @@ package mage.game.turn; +import mage.abilities.common.SagaAbility; import mage.constants.PhaseStep; import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.FilterPermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; import mage.game.Game; import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; @@ -19,6 +21,7 @@ public class PreCombatMainStep extends Step { static { filter.add(SubType.SAGA.getPredicate()); + filter.add(new AbilityPredicate(SagaAbility.class)); } public PreCombatMainStep() { diff --git a/Mage/src/main/java/mage/game/turn/Turn.java b/Mage/src/main/java/mage/game/turn/Turn.java index 3aa40f53653..995c0b3c48f 100644 --- a/Mage/src/main/java/mage/game/turn/Turn.java +++ b/Mage/src/main/java/mage/game/turn/Turn.java @@ -233,7 +233,7 @@ public class Turn implements Serializable { if (player != controllingPlayer && controllingPlayer != null) { game.informPlayers(controllingPlayer.getLogName() + " lost control over " + player.getLogName()); } - player.setGameUnderYourControl(true); + player.setGameUnderYourControl(game, true); } }); diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java index a0bc38d90ff..1028d914a0c 100644 --- a/Mage/src/main/java/mage/players/Player.java +++ b/Mage/src/main/java/mage/players/Player.java @@ -388,7 +388,7 @@ public interface Player extends MageItem, Copyable { * * @param value */ - void setGameUnderYourControl(boolean value); + void setGameUnderYourControl(Game game, boolean value); /** * Return player's turn control to prev player @@ -396,7 +396,7 @@ public interface Player extends MageItem, Copyable { * @param value * @param fullRestore return turn control to own */ - void setGameUnderYourControl(boolean value, boolean fullRestore); + void setGameUnderYourControl(Game game, boolean value, boolean fullRestore); void setTestMode(boolean value); @@ -533,6 +533,8 @@ public interface Player extends MageItem, Copyable { boolean hasProtectionFrom(MageObject source, Game game); + List flipCoins(Ability source, Game game, int amount, boolean winnable); + boolean flipCoin(Ability source, Game game, boolean winnable); boolean flipCoinResult(Game game); @@ -715,7 +717,7 @@ public interface Player extends MageItem, Copyable { */ boolean putCardsOnBottomOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder); - boolean putCardsOnBottomOfLibrary(Card card, Game game, Ability source, boolean anyOrder); + boolean putCardsOnBottomOfLibrary(Card card, Game game, Ability source); /** * Moves the card to the top x position of the library @@ -748,11 +750,12 @@ public interface Player extends MageItem, Copyable { /** * Set the value for X in spells and abilities + * * @param isManaPay helper param for better AI logic */ int announceX(int min, int max, String message, Game game, Ability source, boolean isManaPay); - // TODO: rework to use pair's list of effect + ability instead string's map + // TODO: rework to use pair's list of effect + ability instead string's map int chooseReplacementEffect(Map effectsMap, Map objectsMap, Game game); TriggeredAbility chooseTriggeredAbility(List abilities, Game game); @@ -764,7 +767,6 @@ public interface Player extends MageItem, Copyable { void selectBlockers(Ability source, Game game, UUID defendingPlayerId); /** - * * @param source can be null for system actions like define damage */ int getAmount(int min, int max, String message, Ability source, Game game); @@ -782,12 +784,18 @@ public interface Player extends MageItem, Copyable { * @return List of integers with size equal to messages.size(). The sum of the integers is equal to max. */ default List getMultiAmount(Outcome outcome, List messages, int optionMin, int totalMin, int totalMax, MultiAmountType type, Game game) { + // do not override it + + // runtime check: make sure all default values are valid + // TODO: add default check inside getMultiAmountWithIndividualConstraints if (optionMin > totalMax || optionMin * messages.size() > totalMin) { throw new IllegalArgumentException(String.format("Wrong code usage: getMultiAmount found bad option min/max values: %d/%d", optionMin, totalMax)); } + List constraints = messages.stream() .map(s -> new MultiAmountMessage(s, optionMin, totalMax)) .collect(Collectors.toList()); + return getMultiAmountWithIndividualConstraints(outcome, constraints, totalMin, totalMax, type, game); } diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 02f2f2db436..33227e25d36 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -160,6 +160,7 @@ public abstract class PlayerImpl implements Player, Serializable { protected List alternativeSourceCosts = new ArrayList<>(); // TODO: rework turn controller to use single list (see other todos) + // see PlayerUnderControlTest //protected Stack allTurnControllers = new Stack<>(); protected boolean isGameUnderControl = true; // TODO: replace with allTurnControllers.isEmpty protected UUID turnController; // null on own control TODO: replace with allTurnControllers.last @@ -619,7 +620,7 @@ public abstract class PlayerImpl implements Player, Serializable { if (!playerUnderControlId.equals(this.getId())) { this.playersUnderYourControl.add(playerUnderControlId); if (!playerUnderControl.hasLeft() && !playerUnderControl.hasLost()) { - playerUnderControl.setGameUnderYourControl(false); + playerUnderControl.setGameUnderYourControl(game, false); } // control will reset on start of the turn } @@ -663,14 +664,15 @@ public abstract class PlayerImpl implements Player, Serializable { } @Override - public void setGameUnderYourControl(boolean value) { - setGameUnderYourControl(value, true); + public void setGameUnderYourControl(Game game, boolean value) { + setGameUnderYourControl(game, value, true); } @Override - public void setGameUnderYourControl(boolean value, boolean fullRestore) { + public void setGameUnderYourControl(Game game, boolean value, boolean fullRestore) { this.isGameUnderControl = value; if (isGameUnderControl) { + removeMeFromPlayersUnderControl(game); if (fullRestore) { // to own this.turnControllers.clear(); @@ -687,11 +689,26 @@ public abstract class PlayerImpl implements Player, Serializable { } else { this.turnController = turnControllers.get(turnControllers.size() - 1); isGameUnderControl = false; + addMeToPlayersUnderControl(game, this.turnController); } } } } + private void removeMeFromPlayersUnderControl(Game game) { + game.getPlayers().values().forEach(p -> { + p.getPlayersUnderYourControl().remove(this.getId()); + }); + } + + private void addMeToPlayersUnderControl(Game game, UUID newTurnController) { + game.getPlayers().values().forEach(p -> { + if (p.getId().equals(newTurnController)) { + p.getPlayersUnderYourControl().add(this.getId()); + } + }); + } + @Override public void endOfTurn(Game game) { this.passedTurn = false; @@ -1034,8 +1051,8 @@ public abstract class PlayerImpl implements Player, Serializable { } @Override - public boolean putCardsOnBottomOfLibrary(Card card, Game game, Ability source, boolean anyOrder) { - return putCardsOnBottomOfLibrary(new CardsImpl(card), game, source, anyOrder); + public boolean putCardsOnBottomOfLibrary(Card card, Game game, Ability source) { + return putCardsOnBottomOfLibrary(new CardsImpl(card), game, source, false); } @Override @@ -1289,7 +1306,7 @@ public abstract class PlayerImpl implements Player, Serializable { SpellAbility ability = originalAbility.copy(); Set allowedIdentifiers = originalAbility.spellCanBeActivatedNow(getId(), game); ability.setControllerId(getId()); - ability.setSourceObjectZoneChangeCounter(game.getState().getZoneChangeCounter(ability.getSourceId())); + ability.initSourceObjectZoneChangeCounter(game, true); //20091005 - 601.2a if (ability.getSourceId() == null) { @@ -1315,7 +1332,7 @@ public abstract class PlayerImpl implements Player, Serializable { spell.setCopy(true, null); } // Update the zcc to the stack - ability.setSourceObjectZoneChangeCounter(game.getState().getZoneChangeCounter(ability.getSourceId())); + ability.initSourceObjectZoneChangeCounter(game, true); // ALTERNATIVE COST from dynamic effects // some effects set sourceId to cast without paying mana costs or other costs @@ -3054,44 +3071,66 @@ public abstract class PlayerImpl implements Player, Serializable { */ @Override public boolean flipCoin(Ability source, Game game, boolean winnable) { - boolean chosen = false; - if (winnable) { - chosen = this.chooseUse(Outcome.Benefit, "Heads or tails?", "", "Heads", "Tails", source, game); - game.informPlayers(getLogName() + " chose " + CardUtil.booleanToFlipName(chosen)); - } - boolean result = this.flipCoinResult(game); - FlipCoinEvent event = new FlipCoinEvent(playerId, source, result, chosen, winnable); - game.replaceEvent(event); - game.informPlayers(getLogName() + " flipped " + CardUtil.booleanToFlipName(event.getResult()) - + CardUtil.getSourceLogName(game, source)); - if (event.getFlipCount() > 1) { - boolean canChooseHeads = event.getResult(); - boolean canChooseTails = !event.getResult(); - for (int i = 1; i < event.getFlipCount(); i++) { - boolean tempFlip = this.flipCoinResult(game); - canChooseHeads = canChooseHeads || tempFlip; - canChooseTails = canChooseTails || !tempFlip; - game.informPlayers(getLogName() + " flipped " + CardUtil.booleanToFlipName(tempFlip)); + return flipCoins(source, game, 1, winnable).get(0); + } + + @Override + public List flipCoins(Ability source, Game game, int amount, boolean winnable) { + List results = new ArrayList<>(); + FlipCoinsEvent flipsEvent = new FlipCoinsEvent(this.getId(), amount, source); + game.replaceEvent(flipsEvent); + for (int i = 0; i < flipsEvent.getAmount(); i++) { + if (flipsEvent.isHeadsAndWon()) { + if (winnable) { + game.informPlayers(getLogName() + " chose " + CardUtil.booleanToFlipName(true)); + } + game.informPlayers(getLogName() + " flipped " + CardUtil.booleanToFlipName(true) + CardUtil.getSourceLogName(game, source)); + if (winnable) { + game.informPlayers(getLogName() + " won the flip" + CardUtil.getSourceLogName(game, source)); + } + game.fireEvent(new FlipCoinEvent(playerId, source, true, true, true).createFlippedEvent()); + results.add(true); + continue; } - if (canChooseHeads && canChooseTails) { - event.setResult(chooseUse(Outcome.Benefit, "Choose which flip to keep", - (event.isWinnable() ? "(You called " + event.getChosenName() + ")" : null), - "Heads", "Tails", source, game - )); + boolean chosen; + if (winnable) { + chosen = this.chooseUse(Outcome.Benefit, "Heads or tails?", "", "Heads", "Tails", source, game); + game.informPlayers(getLogName() + " chose " + CardUtil.booleanToFlipName(chosen)); } else { - event.setResult(canChooseHeads); + chosen = false; } - game.informPlayers(getLogName() + " chose to keep " + CardUtil.booleanToFlipName(event.getResult())); - } - if (event.isWinnable()) { - game.informPlayers(getLogName() + " " + (event.getResult() == event.getChosen() ? "won" : "lost") + " the flip" + boolean result = this.flipCoinResult(game); + FlipCoinEvent event = new FlipCoinEvent(playerId, source, result, chosen, winnable); + game.replaceEvent(event); + game.informPlayers(getLogName() + " flipped " + CardUtil.booleanToFlipName(event.getResult()) + CardUtil.getSourceLogName(game, source)); + if (event.getFlipCount() > 1) { + boolean canChooseHeads = event.getResult(); + boolean canChooseTails = !event.getResult(); + for (int j = 1; j < event.getFlipCount(); j++) { + boolean tempFlip = this.flipCoinResult(game); + canChooseHeads = canChooseHeads || tempFlip; + canChooseTails = canChooseTails || !tempFlip; + game.informPlayers(getLogName() + " flipped " + CardUtil.booleanToFlipName(tempFlip)); + } + if (canChooseHeads && canChooseTails) { + event.setResult(chooseUse(Outcome.Benefit, "Choose which flip to keep", + (event.isWinnable() ? "(You called " + event.getChosenName() + ")" : null), + "Heads", "Tails", source, game + )); + } else { + event.setResult(canChooseHeads); + } + game.informPlayers(getLogName() + " chose to keep " + CardUtil.booleanToFlipName(event.getResult())); + } + if (event.isWinnable()) { + game.informPlayers(getLogName() + " " + (event.getResult() == event.getChosen() ? "won" : "lost") + " the flip" + + CardUtil.getSourceLogName(game, source)); + } + game.fireEvent(event.createFlippedEvent()); + results.add(event.isWinnable() ? event.getResult() == event.getChosen() : event.getResult()); } - game.fireEvent(event.createFlippedEvent()); - if (event.isWinnable()) { - return event.getResult() == event.getChosen(); - } - return event.getResult(); + return results; } /** diff --git a/Mage/src/main/java/mage/players/PlayerType.java b/Mage/src/main/java/mage/players/PlayerType.java index 75bd6058227..4a8fccd8922 100644 --- a/Mage/src/main/java/mage/players/PlayerType.java +++ b/Mage/src/main/java/mage/players/PlayerType.java @@ -8,15 +8,19 @@ package mage.players; * @author IGOUDT */ public enum PlayerType { - HUMAN("Human"), - COMPUTER_DRAFT_BOT("Computer - draftbot"), - COMPUTER_MONTE_CARLO("Computer - monte carlo"), - COMPUTER_MAD("Computer - mad"); + HUMAN("Human", false, true), + COMPUTER_DRAFT_BOT("Computer - draftbot", true, false), + COMPUTER_MONTE_CARLO("Computer - monte carlo", true, true), + COMPUTER_MAD("Computer - mad", true, true); final String description; + final boolean isAI; + final boolean isWorkablePlayer; // false for draft bots cause it does nothing in real game and just loose a game - PlayerType(String description) { + PlayerType(String description, boolean isAI, boolean isWorkablePlayer) { this.description = description; + this.isAI = isAI; + this.isWorkablePlayer = isWorkablePlayer; } @Override @@ -24,6 +28,14 @@ public enum PlayerType { return description; } + public boolean isAI() { + return this.isAI; + } + + public boolean isWorkablePlayer() { + return this.isWorkablePlayer; + } + public static PlayerType getByDescription(String description) { for (PlayerType type : values()) { if (type.description.equals(description)) { diff --git a/Mage/src/main/java/mage/target/TargetCard.java b/Mage/src/main/java/mage/target/TargetCard.java index b5322d2782d..118ca88ed9e 100644 --- a/Mage/src/main/java/mage/target/TargetCard.java +++ b/Mage/src/main/java/mage/target/TargetCard.java @@ -56,6 +56,18 @@ public class TargetCard extends TargetObject { return this; } + /** + * Checks if there are enough {@link Card} that can be selected. + * + * @param sourceControllerId - controller of the select event + * @param game + * @return - true if enough valid {@link Card} exist + */ + @Override + public boolean canChoose(UUID sourceControllerId, Game game) { + return canChoose(sourceControllerId, null, game); + } + /** * Checks if there are enough {@link Card cards} in the appropriate zone that the player can choose from among them * or if they are autochosen since there are fewer than the minimum number. @@ -67,7 +79,6 @@ public class TargetCard extends TargetObject { */ @Override public boolean canChoose(UUID sourceControllerId, Ability source, Game game) { - UUID sourceId = source != null ? source.getSourceId() : null; int possibleTargets = 0; if (getMinNumberOfTargets() == 0) { // if 0 target is valid, the canChoose is always true return true; @@ -80,150 +91,245 @@ public class TargetCard extends TargetObject { } switch (zone) { case HAND: - for (Card card : player.getHand().getCards(filter, sourceControllerId, source, game)) { - if (sourceId == null || isNotTarget() || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { - possibleTargets++; - if (possibleTargets >= this.minNumberOfTargets) { - return true; - } - } - } + possibleTargets += countPossibleTargetInHand(game, player, sourceControllerId, source, + filter, isNotTarget(), this.minNumberOfTargets - possibleTargets); break; case GRAVEYARD: - for (Card card : player.getGraveyard().getCards(filter, sourceControllerId, source, game)) { - if (sourceId == null || isNotTarget() || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { - possibleTargets++; - if (possibleTargets >= this.minNumberOfTargets) { - return true; - } - } - } + possibleTargets += countPossibleTargetInGraveyard(game, player, sourceControllerId, source, + filter, isNotTarget(), this.minNumberOfTargets - possibleTargets); break; case LIBRARY: - for (Card card : player.getLibrary().getUniqueCards(game)) { - if (sourceId == null || isNotTarget() || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { - if (filter.match(card, game)) { - possibleTargets++; - if (possibleTargets >= this.minNumberOfTargets) { - return true; - } - } - } - } + possibleTargets += countPossibleTargetInLibrary(game, player, sourceControllerId, source, + filter, isNotTarget(), this.minNumberOfTargets - possibleTargets); break; case EXILED: - for (Card card : game.getExile().getPermanentExile().getCards(game)) { - if (sourceId == null || isNotTarget() || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { - if (filter.match(card, player.getId(), game)) { - possibleTargets++; - if (possibleTargets >= this.minNumberOfTargets) { - return true; - } - } - } - } + possibleTargets += countPossibleTargetInExile(game, player, sourceControllerId, source, + filter, isNotTarget(), this.minNumberOfTargets - possibleTargets); break; case COMMAND: - List possibleCards = game.getCommandersIds(player, CommanderCardType.ANY, false).stream() - .map(game::getCard) - .filter(Objects::nonNull) - .filter(card -> game.getState().getZone(card.getId()).equals(Zone.COMMAND)) - .filter(card -> filter.match(card, sourceControllerId, source, game)) - .collect(Collectors.toList()); - for (Card card : possibleCards) { - if (sourceId == null || isNotTarget() || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { - possibleTargets++; - if (possibleTargets >= this.minNumberOfTargets) { - return true; - } - } - } + possibleTargets += countPossibleTargetInCommandZone(game, player, sourceControllerId, source, + filter, isNotTarget(), this.minNumberOfTargets - possibleTargets); break; } + if (possibleTargets >= this.minNumberOfTargets) { + return true; + } } } return false; } /** - * Checks if there are enough {@link Card} that can be selected. - * - * @param sourceControllerId - controller of the select event - * @param game - * @return - true if enough valid {@link Card} exist + * count up to N possible target cards in a player's hand matching the filter */ - @Override - public boolean canChoose(UUID sourceControllerId, Game game) { - return canChoose(sourceControllerId, null, game); - } - - @Override - public Set possibleTargets(UUID sourceControllerId, Ability source, Game game) { - Set possibleTargets = new HashSet<>(); + protected static int countPossibleTargetInHand(Game game, Player player, UUID sourceControllerId, Ability source, FilterCard filter, boolean isNotTarget, int countUpTo) { UUID sourceId = source != null ? source.getSourceId() : null; - for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - switch (zone) { - case HAND: - for (Card card : player.getHand().getCards(filter, sourceControllerId, source, game)) { - // TODO: Why for sourceId == null? - if (sourceId == null || isNotTarget() || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { - possibleTargets.add(card.getId()); - } - } - break; - case GRAVEYARD: - for (Card card : player.getGraveyard().getCards(filter, sourceControllerId, source, game)) { - if (sourceId == null || isNotTarget() || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { - possibleTargets.add(card.getId()); - } - } - break; - case LIBRARY: - for (Card card : player.getLibrary().getUniqueCards(game)) { - if (sourceId == null || isNotTarget() || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { - if (filter.match(card, sourceControllerId, source, game)) { - possibleTargets.add(card.getId()); - } - } - } - break; - case EXILED: - for (Card card : game.getExile().getPermanentExile().getCards(game)) { - if (sourceId == null || isNotTarget() || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { - if (filter.match(card, sourceControllerId, source, game)) { - possibleTargets.add(card.getId()); - } - } - } - break; - case COMMAND: - List possibleCards = game.getCommandersIds(player, CommanderCardType.ANY, false).stream() - .map(game::getCard) - .filter(Objects::nonNull) - .filter(card -> game.getState().getZone(card.getId()).equals(Zone.COMMAND)) - .filter(card -> filter.match(card, sourceControllerId, source, game)) - .collect(Collectors.toList()); - for (Card card : possibleCards) { - if (sourceId == null || isNotTarget() || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { - possibleTargets.add(card.getId()); - } - } - break; + int possibleTargets = 0; + for (Card card : player.getHand().getCards(filter, sourceControllerId, source, game)) { + if (sourceId == null || isNotTarget || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { + possibleTargets++; + if (possibleTargets >= countUpTo) { + return possibleTargets; // early return for faster computation. } } } return possibleTargets; } + /** + * count up to N possible target cards in a player's graveyard matching the filter + */ + protected static int countPossibleTargetInGraveyard(Game game, Player player, UUID sourceControllerId, Ability source, FilterCard filter, boolean isNotTarget, int countUpTo) { + UUID sourceId = source != null ? source.getSourceId() : null; + int possibleTargets = 0; + for (Card card : player.getGraveyard().getCards(filter, sourceControllerId, source, game)) { + if (sourceId == null || isNotTarget || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { + possibleTargets++; + if (possibleTargets >= countUpTo) { + return possibleTargets; // early return for faster computation. + } + } + } + return possibleTargets; + } + + /** + * count up to N possible target cards in a player's library matching the filter + */ + protected static int countPossibleTargetInLibrary(Game game, Player player, UUID sourceControllerId, Ability source, FilterCard filter, boolean isNotTarget, int countUpTo) { + UUID sourceId = source != null ? source.getSourceId() : null; + int possibleTargets = 0; + for (Card card : player.getLibrary().getUniqueCards(game)) { + if (sourceId == null || isNotTarget || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { + if (filter.match(card, game)) { + possibleTargets++; + if (possibleTargets >= countUpTo) { + return possibleTargets; // early return for faster computation. + } + } + } + } + return possibleTargets; + } + + /** + * count up to N possible target cards in a player's exile matching the filter + */ + protected static int countPossibleTargetInExile(Game game, Player player, UUID sourceControllerId, Ability source, FilterCard filter, boolean isNotTarget, int countUpTo) { + UUID sourceId = source != null ? source.getSourceId() : null; + int possibleTargets = 0; + for (Card card : game.getExile().getPermanentExile().getCards(game)) { + if (sourceId == null || isNotTarget || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { + if (filter.match(card, player.getId(), source, game)) { + possibleTargets++; + if (possibleTargets >= countUpTo) { + return possibleTargets; // early return for faster computation. + } + } + } + } + return possibleTargets; + } + + /** + * count up to N possible target cards in a player's command zone matching the filter + */ + protected static int countPossibleTargetInCommandZone(Game game, Player player, UUID sourceControllerId, Ability source, FilterCard filter, boolean isNotTarget, int countUpTo) { + UUID sourceId = source != null ? source.getSourceId() : null; + int possibleTargets = 0; + List possibleCards = game.getCommandersIds(player, CommanderCardType.ANY, false).stream() + .map(game::getCard) + .filter(Objects::nonNull) + .filter(card -> game.getState().getZone(card.getId()).equals(Zone.COMMAND)) + .filter(card -> filter.match(card, sourceControllerId, source, game)) + .collect(Collectors.toList()); + for (Card card : possibleCards) { + if (sourceId == null || isNotTarget || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { + possibleTargets++; + if (possibleTargets >= countUpTo) { + return possibleTargets; // early return for faster computation. + } + } + } + return possibleTargets; + } + + @Override + public Set possibleTargets(UUID sourceControllerId, Game game) { + return possibleTargets(sourceControllerId, null, game); + } + public Set possibleTargets(UUID sourceControllerId, Cards cards, Ability source, Game game) { return cards.getCards(filter, sourceControllerId, source, game).stream().map(MageItem::getId).collect(Collectors.toSet()); } @Override - public Set possibleTargets(UUID sourceControllerId, Game game) { - return possibleTargets(sourceControllerId, (Ability) null, game); + public Set possibleTargets(UUID sourceControllerId, Ability source, Game game) { + Set possibleTargets = new HashSet<>(); + for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + switch (zone) { + case HAND: + possibleTargets.addAll(getAllPossibleTargetInHand(game, player, sourceControllerId, source, filter, isNotTarget())); + break; + case GRAVEYARD: + possibleTargets.addAll(getAllPossibleTargetInGraveyard(game, player, sourceControllerId, source, filter, isNotTarget())); + break; + case LIBRARY: + possibleTargets.addAll(getAllPossibleTargetInLibrary(game, player, sourceControllerId, source, filter, isNotTarget())); + break; + case EXILED: + possibleTargets.addAll(getAllPossibleTargetInExile(game, player, sourceControllerId, source, filter, isNotTarget())); + break; + case COMMAND: + possibleTargets.addAll(getAllPossibleTargetInCommandZone(game, player, sourceControllerId, source, filter, isNotTarget())); + break; + } + } + } + return possibleTargets; + } + + /** + * set of all matching target in a player's hand + */ + protected static Set getAllPossibleTargetInHand(Game game, Player player, UUID sourceControllerId, Ability source, FilterCard filter, boolean isNotTarget) { + Set possibleTargets = new HashSet<>(); + UUID sourceId = source != null ? source.getSourceId() : null; + for (Card card : player.getHand().getCards(filter, sourceControllerId, source, game)) { + // TODO: Why for sourceId == null? + if (sourceId == null || isNotTarget || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { + possibleTargets.add(card.getId()); + } + } + return possibleTargets; + } + + /** + * set of all matching target in a player's hand + */ + protected static Set getAllPossibleTargetInGraveyard(Game game, Player player, UUID sourceControllerId, Ability source, FilterCard filter, boolean isNotTarget) { + Set possibleTargets = new HashSet<>(); + UUID sourceId = source != null ? source.getSourceId() : null; + for (Card card : player.getGraveyard().getCards(filter, sourceControllerId, source, game)) { + if (sourceId == null || isNotTarget || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { + possibleTargets.add(card.getId()); + } + } + return possibleTargets; + } + + /** + * set of all matching target in a player's hand + */ + protected static Set getAllPossibleTargetInLibrary(Game game, Player player, UUID sourceControllerId, Ability source, FilterCard filter, boolean isNotTarget) { + Set possibleTargets = new HashSet<>(); + UUID sourceId = source != null ? source.getSourceId() : null; + for (Card card : player.getLibrary().getUniqueCards(game)) { + if (sourceId == null || isNotTarget || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { + if (filter.match(card, sourceControllerId, source, game)) { + possibleTargets.add(card.getId()); + } + } + } + return possibleTargets; + } + + /** + * set of all matching target for a player in exile + */ + protected static Set getAllPossibleTargetInExile(Game game, Player player, UUID sourceControllerId, Ability source, FilterCard filter, boolean isNotTarget) { + Set possibleTargets = new HashSet<>(); + UUID sourceId = source != null ? source.getSourceId() : null; + for (Card card : game.getExile().getPermanentExile().getCards(game)) { + if (sourceId == null || isNotTarget || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { + if (filter.match(card, sourceControllerId, source, game)) { + possibleTargets.add(card.getId()); + } + } + } + return possibleTargets; + } + + /** + * set of all matching target in a player's command zone + */ + protected static Set getAllPossibleTargetInCommandZone(Game game, Player player, UUID sourceControllerId, Ability source, FilterCard filter, boolean isNotTarget) { + Set possibleTargets = new HashSet<>(); + UUID sourceId = source != null ? source.getSourceId() : null; + List possibleCards = game.getCommandersIds(player, CommanderCardType.ANY, false).stream() + .map(game::getCard) + .filter(Objects::nonNull) + .filter(card -> game.getState().getZone(card.getId()).equals(Zone.COMMAND)) + .filter(card -> filter.match(card, sourceControllerId, source, game)) + .collect(Collectors.toList()); + for (Card card : possibleCards) { + if (sourceId == null || isNotTarget || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { + possibleTargets.add(card.getId()); + } + } + return possibleTargets; } // TODO: check all class targets, if it override canTarget then make sure it override ALL 3 METHODS with canTarget and possibleTargets (method with cards doesn't need) diff --git a/Mage/src/main/java/mage/target/TargetSource.java b/Mage/src/main/java/mage/target/TargetSource.java index 84da2f19245..171e6b88236 100644 --- a/Mage/src/main/java/mage/target/TargetSource.java +++ b/Mage/src/main/java/mage/target/TargetSource.java @@ -2,40 +2,52 @@ package mage.target; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; - import mage.MageObject; import mage.abilities.Ability; import mage.cards.Card; import mage.constants.Zone; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.game.Game; +import mage.game.command.CommandObject; import mage.game.permanent.Permanent; import mage.game.stack.StackObject; import mage.players.Player; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** + * 609.7a. If an effect requires a player to choose a source of damage, they may choose a permanent; + * a spell on the stack (including a permanent spell); any object referred to by an object on the stack, + * by a replacement or prevention effect that's waiting to apply, or by a delayed triggered ability + * that's waiting to trigger (even if that object is no longer in the zone it used to be in); or a + * face-up object in the command zone. A source doesn't need to be capable of dealing damage to be + * a legal choice. The source is chosen when the effect is created. If the player chooses a permanent, + * the effect will apply to the next damage dealt by that permanent, regardless of whether it's combat + * damage or damage dealt as the result of a spell or ability. If the player chooses a permanent spell, + * the effect will apply to any damage dealt by that spell and any damage dealt by the permanent that + * spell becomes when it resolves. + * * @author BetaSteward_at_googlemail.com */ public class TargetSource extends TargetObject { - protected final FilterObject filter; + protected final FilterSource filter; public TargetSource() { - this(1, 1, new FilterObject("source of your choice")); + this(1, 1, new FilterSource("source of your choice")); } - public TargetSource(FilterObject filter) { + public TargetSource(FilterSource filter) { this(1, 1, filter); } - public TargetSource(int numTargets, FilterObject filter) { + public TargetSource(int numTargets, FilterSource filter) { this(numTargets, numTargets, filter); } - public TargetSource(int minNumTargets, int maxNumTargets, FilterObject filter) { + public TargetSource(int minNumTargets, int maxNumTargets, FilterSource filter) { super(minNumTargets, maxNumTargets, Zone.ALL, true); this.filter = filter; this.targetName = filter.getMessage(); @@ -47,7 +59,7 @@ public class TargetSource extends TargetObject { } @Override - public FilterObject getFilter() { + public FilterSource getFilter() { return filter; } @@ -78,15 +90,16 @@ public class TargetSource extends TargetObject { } @Override - public boolean canChoose(UUID sourceControllerId, Ability source, Game game) { - return canChoose(sourceControllerId, game); + public boolean canChoose(UUID sourceControllerId, Game game) { + return canChoose(sourceControllerId, (Ability) null, game); } @Override - public boolean canChoose(UUID sourceControllerId, Game game) { + public boolean canChoose(UUID sourceControllerId, Ability source, Game game) { int count = 0; for (StackObject stackObject : game.getStack()) { - if (game.getState().getPlayersInRange(sourceControllerId, game).contains(stackObject.getControllerId()) && filter.match(stackObject, game)) { + if (game.getState().getPlayersInRange(sourceControllerId, game).contains(stackObject.getControllerId()) + && filter.match(stackObject, sourceControllerId, source, game)) { count++; if (count >= this.minNumberOfTargets) { return true; @@ -94,7 +107,7 @@ public class TargetSource extends TargetObject { } } for (Permanent permanent : game.getBattlefield().getActivePermanents(sourceControllerId, game)) { - if (filter.match(permanent, game)) { + if (filter.match(permanent, sourceControllerId, source, game)) { count++; if (count >= this.minNumberOfTargets) { return true; @@ -103,7 +116,7 @@ public class TargetSource extends TargetObject { } for (Player player : game.getPlayers().values()) { for (Card card : player.getGraveyard().getCards(game)) { - if (filter.match(card, game)) { + if (filter.match(card, sourceControllerId, source, game)) { count++; if (count >= this.minNumberOfTargets) { return true; @@ -112,7 +125,15 @@ public class TargetSource extends TargetObject { } } for (Card card : game.getExile().getAllCards(game)) { - if (filter.match(card, game)) { + if (filter.match(card, sourceControllerId, source, game)) { + count++; + if (count >= this.minNumberOfTargets) { + return true; + } + } + } + for (CommandObject commandObject : game.getState().getCommand()) { + if (filter.match(commandObject, sourceControllerId, source, game)) { count++; if (count >= this.minNumberOfTargets) { return true; @@ -123,35 +144,41 @@ public class TargetSource extends TargetObject { } @Override - public Set possibleTargets(UUID sourceControllerId, Ability source, Game game) { - return possibleTargets(sourceControllerId, game); + public Set possibleTargets(UUID sourceControllerId, Game game) { + return possibleTargets(sourceControllerId, (Ability) null, game); } @Override - public Set possibleTargets(UUID sourceControllerId, Game game) { + public Set possibleTargets(UUID sourceControllerId, Ability source, Game game) { Set possibleTargets = new HashSet<>(); for (StackObject stackObject : game.getStack()) { - if (game.getState().getPlayersInRange(sourceControllerId, game).contains(stackObject.getControllerId()) && filter.match(stackObject, game)) { + if (game.getState().getPlayersInRange(sourceControllerId, game).contains(stackObject.getControllerId()) + && filter.match(stackObject, sourceControllerId, source, game)) { possibleTargets.add(stackObject.getId()); } } for (Permanent permanent : game.getBattlefield().getActivePermanents(sourceControllerId, game)) { - if (filter.match(permanent, game)) { + if (filter.match(permanent, sourceControllerId, source, game)) { possibleTargets.add(permanent.getId()); } } for (Player player : game.getPlayers().values()) { for (Card card : player.getGraveyard().getCards(game)) { - if (filter.match(card, game)) { + if (filter.match(card, sourceControllerId, source, game)) { possibleTargets.add(card.getId()); } } } for (Card card : game.getExile().getAllCards(game)) { - if (filter.match(card, game)) { + if (filter.match(card, sourceControllerId, source, game)) { possibleTargets.add(card.getId()); } } + for (CommandObject commandObject : game.getState().getCommand()) { + if (filter.match(commandObject, sourceControllerId, source, game)) { + possibleTargets.add(commandObject.getId()); + } + } return possibleTargets; } diff --git a/Mage/src/main/java/mage/target/Targets.java b/Mage/src/main/java/mage/target/Targets.java index 3be408aa038..3ae7326bcdc 100644 --- a/Mage/src/main/java/mage/target/Targets.java +++ b/Mage/src/main/java/mage/target/Targets.java @@ -40,6 +40,18 @@ public class Targets extends ArrayList implements Copyable { return this; } + public Target getByTag(int tag) { + return this.stream().filter(t -> t.getTargetTag() == tag).findFirst().orElse(null); + } + + public List getTargetsByTag(int tag) { + Target target = getByTag(tag); + if (target == null) { + return new ArrayList<>(); + } + return target.getTargets(); + } + public Target getNextUnchosen(Game game) { return getNextUnchosen(game, 0); } diff --git a/Mage/src/main/java/mage/target/common/TargetArtifactPermanent.java b/Mage/src/main/java/mage/target/common/TargetArtifactPermanent.java index 38387696f36..bdb192d9c57 100644 --- a/Mage/src/main/java/mage/target/common/TargetArtifactPermanent.java +++ b/Mage/src/main/java/mage/target/common/TargetArtifactPermanent.java @@ -1,7 +1,6 @@ package mage.target.common; import mage.filter.StaticFilters; -import mage.filter.common.FilterArtifactPermanent; import mage.target.TargetPermanent; /** @@ -18,19 +17,11 @@ public class TargetArtifactPermanent extends TargetPermanent { } public TargetArtifactPermanent(int minNumTargets, int maxNumTargets) { - this(minNumTargets, maxNumTargets, + super(minNumTargets, maxNumTargets, (maxNumTargets > 1 ? StaticFilters.FILTER_PERMANENT_ARTIFACTS : StaticFilters.FILTER_PERMANENT_ARTIFACT), false); } - public TargetArtifactPermanent(FilterArtifactPermanent filter) { - this(1, 1, filter, false); - } - - public TargetArtifactPermanent(int minNumTargets, int maxNumTargets, FilterArtifactPermanent filter, boolean notTarget) { - super(minNumTargets, maxNumTargets, filter, notTarget); - } - protected TargetArtifactPermanent(final TargetArtifactPermanent target) { super(target); } diff --git a/Mage/src/main/java/mage/target/common/TargetCardInExile.java b/Mage/src/main/java/mage/target/common/TargetCardInExile.java index 50a71d6636b..2c8cba1d09a 100644 --- a/Mage/src/main/java/mage/target/common/TargetCardInExile.java +++ b/Mage/src/main/java/mage/target/common/TargetCardInExile.java @@ -75,7 +75,7 @@ public class TargetCardInExile extends TargetCard { ExileZone exileZone = game.getExile().getExileZone(zoneId); if (exileZone != null) { for (Card card : exileZone.getCards(game)) { - if (filter.match(card, sourceControllerId, game)) { + if (filter.match(card, sourceControllerId, source, game)) { possibleTargets.add(card.getId()); } } @@ -96,11 +96,8 @@ public class TargetCardInExile extends TargetCard { } } else { ExileZone exileZone = game.getExile().getExileZone(zoneId); - if (exileZone != null) { - if (exileZone.count(filter, sourceControllerId, source, game) >= this.minNumberOfTargets) { - return true; - } - } + return exileZone != null && exileZone.count(filter, sourceControllerId, source, game) >= this.minNumberOfTargets; + } return false; } diff --git a/Mage/src/main/java/mage/target/common/TargetCardInYourGraveyardOrExile.java b/Mage/src/main/java/mage/target/common/TargetCardInYourGraveyardOrExile.java new file mode 100644 index 00000000000..fd5b6816627 --- /dev/null +++ b/Mage/src/main/java/mage/target/common/TargetCardInYourGraveyardOrExile.java @@ -0,0 +1,104 @@ +package mage.target.common; + +import mage.abilities.Ability; +import mage.cards.Card; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author Susucr + */ +public class TargetCardInYourGraveyardOrExile extends TargetCard { + + private final FilterCard filterGraveyard; + private final FilterCard filterExile; + + public TargetCardInYourGraveyardOrExile(FilterCard filterGraveyard, FilterCard filterExile) { + super(1, 1, Zone.GRAVEYARD, filterGraveyard); // we do not actually use those, as all is overwritten + this.filterGraveyard = filterGraveyard; + this.filterExile = filterExile; + this.targetName = filterGraveyard.getMessage() + " or " + filterExile.getMessage(); + } + + protected TargetCardInYourGraveyardOrExile(final TargetCardInYourGraveyardOrExile target) { + super(target); + this.filterGraveyard = target.filterGraveyard; + this.filterExile = target.filterExile; + } + + @Override + public boolean canChoose(UUID sourceControllerId, Ability source, Game game) { + Player player = game.getPlayer(sourceControllerId); + if (player == null) { + return false; + } + int possibleTargets = 0; + // in your graveyard: + possibleTargets += countPossibleTargetInGraveyard(game, player, sourceControllerId, source, + filterGraveyard, isNotTarget(), this.minNumberOfTargets - possibleTargets); + if (possibleTargets >= this.minNumberOfTargets) { + return true; + } + // in exile: + possibleTargets += countPossibleTargetInExile(game, player, sourceControllerId, source, + filterExile, isNotTarget(), this.minNumberOfTargets - possibleTargets); + return possibleTargets >= this.minNumberOfTargets; + } + + @Override + public Set possibleTargets(UUID sourceControllerId, Game game) { + return this.possibleTargets(sourceControllerId, (Ability) null, game); + } + + @Override + public Set possibleTargets(UUID sourceControllerId, Ability source, Game game) { + Set possibleTargets = new HashSet<>(); + Player player = game.getPlayer(sourceControllerId); + if (player == null) { + return possibleTargets; + } + // in your graveyard: + possibleTargets.addAll(getAllPossibleTargetInGraveyard(game, player, sourceControllerId, source, filterGraveyard, isNotTarget())); + // in exile: + possibleTargets.addAll(getAllPossibleTargetInExile(game, player, sourceControllerId, source, filterExile, isNotTarget())); + return possibleTargets; + } + + @Override + public boolean canTarget(UUID id, Ability source, Game game) { + return this.canTarget(source.getControllerId(), id, source, game); + } + + @Override + public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) { + Card card = game.getCard(id); + if (card == null) { + return false; + } + switch (game.getState().getZone(id)) { + case GRAVEYARD: + return filterGraveyard.match(card, playerId, source, game); + case EXILED: + return filterExile.match(card, playerId, source, game); + default: + return false; + } + } + + @Override + public boolean canTarget(UUID id, Game game) { + return this.canTarget(null, id, null, game); + } + + @Override + public TargetCardInYourGraveyardOrExile copy() { + return new TargetCardInYourGraveyardOrExile(this); + } +} diff --git a/Mage/src/main/java/mage/target/common/TargetControlledCreaturePermanent.java b/Mage/src/main/java/mage/target/common/TargetControlledCreaturePermanent.java index 6c323bfd701..784e5e3d2a0 100644 --- a/Mage/src/main/java/mage/target/common/TargetControlledCreaturePermanent.java +++ b/Mage/src/main/java/mage/target/common/TargetControlledCreaturePermanent.java @@ -17,7 +17,7 @@ public class TargetControlledCreaturePermanent extends TargetControlledPermanent } public TargetControlledCreaturePermanent(int minNumTargets, int maxNumTargets) { - this(minNumTargets, maxNumTargets, StaticFilters.FILTER_CONTROLLED_CREATURE, false); + this(minNumTargets, maxNumTargets, maxNumTargets > 1 ? StaticFilters.FILTER_CONTROLLED_CREATURES : StaticFilters.FILTER_CONTROLLED_CREATURE, false); } public TargetControlledCreaturePermanent(FilterControlledCreaturePermanent filter) { diff --git a/Mage/src/main/java/mage/target/common/TargetCreatureOrPlaneswalkerAmount.java b/Mage/src/main/java/mage/target/common/TargetCreatureOrPlaneswalkerAmount.java index a508ead9533..6a8ffe544b7 100644 --- a/Mage/src/main/java/mage/target/common/TargetCreatureOrPlaneswalkerAmount.java +++ b/Mage/src/main/java/mage/target/common/TargetCreatureOrPlaneswalkerAmount.java @@ -1,6 +1,5 @@ package mage.target.common; -import mage.filter.common.FilterControlledCreatureOrPlaneswalkerPermanent; import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; /** @@ -53,22 +52,6 @@ public class TargetCreatureOrPlaneswalkerAmount extends TargetPermanentAmount { super(amount, minNumberOfTargets, maxNumberOfTargets, filter); } - /** - * IMPORTANT: Use more specific constructor if {@code amount} is not always the same number! - * - * @see TargetCreatureOrPlaneswalkerAmount#TargetCreatureOrPlaneswalkerAmount(int, FilterCreatureOrPlaneswalkerPermanent) - */ - public TargetCreatureOrPlaneswalkerAmount(int amount, FilterControlledCreatureOrPlaneswalkerPermanent filter) { - this(amount, amount > 3 ? 0 : 1, amount, filter); - } - - /** - * @see TargetCreatureOrPlaneswalkerAmount#TargetCreatureOrPlaneswalkerAmount(int, int, int, FilterCreatureOrPlaneswalkerPermanent) - */ - public TargetCreatureOrPlaneswalkerAmount(int amount, int minNumberOfTargets, int maxNumberOfTargets, FilterControlledCreatureOrPlaneswalkerPermanent filter) { - super(amount, minNumberOfTargets, maxNumberOfTargets, filter); - } - private TargetCreatureOrPlaneswalkerAmount(final TargetCreatureOrPlaneswalkerAmount target) { super(target); } diff --git a/Mage/src/main/java/mage/target/common/TargetDiscard.java b/Mage/src/main/java/mage/target/common/TargetDiscard.java index 854230c9eaf..4e732d8ba09 100644 --- a/Mage/src/main/java/mage/target/common/TargetDiscard.java +++ b/Mage/src/main/java/mage/target/common/TargetDiscard.java @@ -47,7 +47,7 @@ public class TargetDiscard extends TargetCard { @Override public boolean canTarget(UUID id, Ability source, Game game) { Card card = game.getPlayer(playerId).getHand().get(id, game); - return filter.match(card, source.getControllerId(), game); + return filter.match(card, source.getControllerId(), source, game); } @Override diff --git a/Mage/src/main/java/mage/target/common/TargetEnchantmentPermanent.java b/Mage/src/main/java/mage/target/common/TargetEnchantmentPermanent.java index a7640b8e077..4181b614232 100644 --- a/Mage/src/main/java/mage/target/common/TargetEnchantmentPermanent.java +++ b/Mage/src/main/java/mage/target/common/TargetEnchantmentPermanent.java @@ -1,7 +1,6 @@ package mage.target.common; import mage.filter.StaticFilters; -import mage.filter.common.FilterEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -13,20 +12,12 @@ public class TargetEnchantmentPermanent extends TargetPermanent { this(1); } - public TargetEnchantmentPermanent(FilterEnchantmentPermanent filter) { - this(1, 1, filter, false); - } - public TargetEnchantmentPermanent(int numTargets) { this(numTargets, numTargets); } public TargetEnchantmentPermanent(int minNumTargets, int maxNumTargets) { - this(minNumTargets, maxNumTargets, StaticFilters.FILTER_PERMANENT_ENCHANTMENT, false); - } - - public TargetEnchantmentPermanent(int minNumTargets, int maxNumTargets, FilterEnchantmentPermanent filter, boolean notTarget) { - super(minNumTargets, maxNumTargets, filter, notTarget); + super(minNumTargets, maxNumTargets, StaticFilters.FILTER_PERMANENT_ENCHANTMENT, false); } protected TargetEnchantmentPermanent(final TargetEnchantmentPermanent target) { diff --git a/Mage/src/main/java/mage/target/common/TargetEquipmentPermanent.java b/Mage/src/main/java/mage/target/common/TargetEquipmentPermanent.java deleted file mode 100644 index 2fe9733ec75..00000000000 --- a/Mage/src/main/java/mage/target/common/TargetEquipmentPermanent.java +++ /dev/null @@ -1,37 +0,0 @@ - - -package mage.target.common; - -import mage.filter.common.FilterEquipmentPermanent; -import mage.target.TargetPermanent; - -/** - * @author TheElk801 - */ -public class TargetEquipmentPermanent extends TargetPermanent { - - public TargetEquipmentPermanent() { - this(1, 1, new FilterEquipmentPermanent(), false); - } - - public TargetEquipmentPermanent(FilterEquipmentPermanent filter) { - this(1, 1, filter, false); - } - - public TargetEquipmentPermanent(int numTargets) { - this(numTargets, numTargets, new FilterEquipmentPermanent(), false); - } - - public TargetEquipmentPermanent(int minNumTargets, int maxNumTargets, FilterEquipmentPermanent filter, boolean notTarget) { - super(minNumTargets, maxNumTargets, filter, notTarget); - } - - protected TargetEquipmentPermanent(final TargetEquipmentPermanent target) { - super(target); - } - - @Override - public TargetEquipmentPermanent copy() { - return new TargetEquipmentPermanent(this); - } -} diff --git a/Mage/src/main/java/mage/target/common/TargetLandPermanent.java b/Mage/src/main/java/mage/target/common/TargetLandPermanent.java index 1311728ea8f..90baa11f606 100644 --- a/Mage/src/main/java/mage/target/common/TargetLandPermanent.java +++ b/Mage/src/main/java/mage/target/common/TargetLandPermanent.java @@ -2,7 +2,6 @@ package mage.target.common; import mage.filter.StaticFilters; -import mage.filter.common.FilterLandPermanent; import mage.target.TargetPermanent; /** @@ -14,20 +13,12 @@ public class TargetLandPermanent extends TargetPermanent { this(1); } - public TargetLandPermanent(FilterLandPermanent filter) { - this(1, 1, filter, false); - } - public TargetLandPermanent(int numTargets) { this(numTargets, numTargets); } public TargetLandPermanent(int numTargets, int maxNumTargets) { - this(numTargets, maxNumTargets, maxNumTargets > 1 ? StaticFilters.FILTER_LANDS : StaticFilters.FILTER_LAND, false); - } - - public TargetLandPermanent(int minNumTargets, int maxNumTargets, FilterLandPermanent filter, boolean notTarget) { - super(minNumTargets, maxNumTargets, filter, notTarget); + super(numTargets, maxNumTargets, maxNumTargets > 1 ? StaticFilters.FILTER_LANDS : StaticFilters.FILTER_LAND, false); } protected TargetLandPermanent(final TargetLandPermanent target) { diff --git a/Mage/src/main/java/mage/target/common/TargetNonBasicLandPermanent.java b/Mage/src/main/java/mage/target/common/TargetNonBasicLandPermanent.java index 8d131f27fd2..f1264c265c2 100644 --- a/Mage/src/main/java/mage/target/common/TargetNonBasicLandPermanent.java +++ b/Mage/src/main/java/mage/target/common/TargetNonBasicLandPermanent.java @@ -5,11 +5,12 @@ package mage.target.common; import mage.constants.SuperType; import mage.filter.common.FilterLandPermanent; import mage.filter.predicate.Predicates; +import mage.target.TargetPermanent; /** * @author BetaSteward_at_googlemail.com */ -public class TargetNonBasicLandPermanent extends TargetLandPermanent { +public class TargetNonBasicLandPermanent extends TargetPermanent { private static final FilterLandPermanent filter = new FilterLandPermanent("nonbasic land"); diff --git a/Mage/src/main/java/mage/target/common/TargetNonlandPermanent.java b/Mage/src/main/java/mage/target/common/TargetNonlandPermanent.java index b5333668777..b9eac0f230c 100644 --- a/Mage/src/main/java/mage/target/common/TargetNonlandPermanent.java +++ b/Mage/src/main/java/mage/target/common/TargetNonlandPermanent.java @@ -1,7 +1,6 @@ package mage.target.common; import mage.filter.StaticFilters; -import mage.filter.common.FilterNonlandPermanent; import mage.target.TargetPermanent; /** @@ -13,10 +12,6 @@ public class TargetNonlandPermanent extends TargetPermanent { this(1); } - public TargetNonlandPermanent(FilterNonlandPermanent filter) { - this(1, 1, filter, false); - } - public TargetNonlandPermanent(int numTargets) { this(numTargets, numTargets); } @@ -26,11 +21,7 @@ public class TargetNonlandPermanent extends TargetPermanent { } public TargetNonlandPermanent(int minNumTargets, int maxNumTargets, boolean notTarget) { - this(minNumTargets, maxNumTargets, (maxNumTargets > 1 ? StaticFilters.FILTER_PERMANENTS_NON_LAND : StaticFilters.FILTER_PERMANENT_NON_LAND), notTarget); - } - - public TargetNonlandPermanent(int minNumTargets, int maxNumTargets, FilterNonlandPermanent filter, boolean notTarget) { - super(minNumTargets, maxNumTargets, filter, notTarget); + super(minNumTargets, maxNumTargets, maxNumTargets > 1 ? StaticFilters.FILTER_PERMANENTS_NON_LAND : StaticFilters.FILTER_PERMANENT_NON_LAND, notTarget); } protected TargetNonlandPermanent(final TargetNonlandPermanent target) { diff --git a/Mage/src/main/java/mage/target/common/TargetPlaneswalkerPermanent.java b/Mage/src/main/java/mage/target/common/TargetPlaneswalkerPermanent.java index c8163e33c80..f0f5c9a50bf 100644 --- a/Mage/src/main/java/mage/target/common/TargetPlaneswalkerPermanent.java +++ b/Mage/src/main/java/mage/target/common/TargetPlaneswalkerPermanent.java @@ -1,7 +1,6 @@ package mage.target.common; import mage.filter.StaticFilters; -import mage.filter.common.FilterPlaneswalkerPermanent; import mage.target.TargetPermanent; /** @@ -10,23 +9,15 @@ import mage.target.TargetPermanent; public class TargetPlaneswalkerPermanent extends TargetPermanent { public TargetPlaneswalkerPermanent() { - this(1, 1, StaticFilters.FILTER_PERMANENT_PLANESWALKER, false); - } - - public TargetPlaneswalkerPermanent(FilterPlaneswalkerPermanent filter) { - this(1, 1, filter, false); + this(1); } public TargetPlaneswalkerPermanent(int numTargets) { - this(numTargets, numTargets, StaticFilters.FILTER_PERMANENT_PLANESWALKER, false); + this(numTargets, numTargets); } public TargetPlaneswalkerPermanent(int minNumTargets, int maxNumTargets) { - this(minNumTargets, maxNumTargets, StaticFilters.FILTER_PERMANENT_PLANESWALKER, false); - } - - public TargetPlaneswalkerPermanent(int minNumTargets, int maxNumTargets, FilterPlaneswalkerPermanent filter, boolean notTarget) { - super(minNumTargets, maxNumTargets, filter, notTarget); + super(minNumTargets, maxNumTargets, StaticFilters.FILTER_PERMANENT_PLANESWALKER, false); } private TargetPlaneswalkerPermanent(final TargetPlaneswalkerPermanent target) { diff --git a/Mage/src/main/java/mage/target/targetadjustment/ConditionalTargetAdjuster.java b/Mage/src/main/java/mage/target/targetadjustment/ConditionalTargetAdjuster.java index 0a3f94ee8e8..9bc39becaaf 100644 --- a/Mage/src/main/java/mage/target/targetadjustment/ConditionalTargetAdjuster.java +++ b/Mage/src/main/java/mage/target/targetadjustment/ConditionalTargetAdjuster.java @@ -71,6 +71,10 @@ public class ConditionalTargetAdjuster implements TargetAdjuster { blueprintTarget = ability.getTargets().get(0).copy(); } } + @Override + public void clearDefaultTargets() { + blueprintTarget = null; + } @Override public void adjustTargets(Ability ability, Game game) { diff --git a/Mage/src/main/java/mage/target/targetadjustment/ForEachOpponentTargetsAdjuster.java b/Mage/src/main/java/mage/target/targetadjustment/ForEachPlayerTargetsAdjuster.java similarity index 66% rename from Mage/src/main/java/mage/target/targetadjustment/ForEachOpponentTargetsAdjuster.java rename to Mage/src/main/java/mage/target/targetadjustment/ForEachPlayerTargetsAdjuster.java index 638e3c36497..e688a74ec47 100644 --- a/Mage/src/main/java/mage/target/targetadjustment/ForEachOpponentTargetsAdjuster.java +++ b/Mage/src/main/java/mage/target/targetadjustment/ForEachPlayerTargetsAdjuster.java @@ -11,24 +11,24 @@ import mage.target.TargetCard; import mage.util.CardUtil; import java.util.UUID; +import java.util.stream.Stream; /** * @author notgreat */ -public class ForEachOpponentTargetsAdjuster extends GenericTargetAdjuster { +public class ForEachPlayerTargetsAdjuster extends GenericTargetAdjuster { private final boolean owner; + private final boolean onlyOpponents; //Makes this a "For Each Opponent" adjuster /** - * Duplicates the permanent target for each opponent. + * Duplicates the permanent target for each player (or opponent). * Filtering of permanent's controllers will be handled inside, so * do not pass a blueprint target with a controller restriction filter/predicate. */ - public ForEachOpponentTargetsAdjuster() { - this(false); - } - public ForEachOpponentTargetsAdjuster(boolean owner) { + public ForEachPlayerTargetsAdjuster(boolean owner, boolean onlyOpponents) { this.owner = owner; + this.onlyOpponents = onlyOpponents; } @Override @@ -43,21 +43,27 @@ public class ForEachOpponentTargetsAdjuster extends GenericTargetAdjuster { @Override public void adjustTargets(Ability ability, Game game) { ability.getTargets().clear(); - for (UUID opponentId : game.getOpponents(ability.getControllerId())) { - Player opponent = game.getPlayer(opponentId); + Stream ids; + if (onlyOpponents) { + ids = game.getOpponents(ability.getControllerId()).stream(); + } else { + ids = game.getState().getPlayersInRange(ability.getControllerId(), game).stream(); + } + ids.forEach( id -> { + Player opponent = game.getPlayer(id); if (opponent == null) { - continue; + return; } Target newTarget = blueprintTarget.copy(); Filter filter = newTarget.getFilter(); if (owner) { - filter.add(new OwnerIdPredicate(opponentId)); + filter.add(new OwnerIdPredicate(id)); newTarget.withTargetName(filter.getMessage() + " (owned by " + opponent.getLogName() + ")"); } else { - filter.add(new ControllerIdPredicate(opponentId)); + filter.add(new ControllerIdPredicate(id)); newTarget.withTargetName(filter.getMessage() + " (controlled by " + opponent.getLogName() + ")"); } ability.addTarget(newTarget); - } + }); } } diff --git a/Mage/src/main/java/mage/target/targetadjustment/GenericTargetAdjuster.java b/Mage/src/main/java/mage/target/targetadjustment/GenericTargetAdjuster.java index 83184a706ec..89162d21524 100644 --- a/Mage/src/main/java/mage/target/targetadjustment/GenericTargetAdjuster.java +++ b/Mage/src/main/java/mage/target/targetadjustment/GenericTargetAdjuster.java @@ -14,4 +14,8 @@ public abstract class GenericTargetAdjuster implements TargetAdjuster { throw new IllegalStateException("Wrong code usage: target adjuster already has blueprint target - " + blueprintTarget); } } + @Override + public void clearDefaultTargets() { + blueprintTarget = null; + } } diff --git a/Mage/src/main/java/mage/target/targetadjustment/TargetAdjuster.java b/Mage/src/main/java/mage/target/targetadjustment/TargetAdjuster.java index 5b4020e1e70..bd93e6ea8d5 100644 --- a/Mage/src/main/java/mage/target/targetadjustment/TargetAdjuster.java +++ b/Mage/src/main/java/mage/target/targetadjustment/TargetAdjuster.java @@ -20,4 +20,7 @@ public interface TargetAdjuster extends Serializable { */ default void addDefaultTargets(Ability ability) { } + + default void clearDefaultTargets() { + } } diff --git a/Mage/src/main/java/mage/target/targetadjustment/DamagedPlayerControlsTargetAdjuster.java b/Mage/src/main/java/mage/target/targetadjustment/ThatPlayerControlsTargetAdjuster.java similarity index 80% rename from Mage/src/main/java/mage/target/targetadjustment/DamagedPlayerControlsTargetAdjuster.java rename to Mage/src/main/java/mage/target/targetadjustment/ThatPlayerControlsTargetAdjuster.java index 73694ca6d41..eacea107664 100644 --- a/Mage/src/main/java/mage/target/targetadjustment/DamagedPlayerControlsTargetAdjuster.java +++ b/Mage/src/main/java/mage/target/targetadjustment/ThatPlayerControlsTargetAdjuster.java @@ -1,7 +1,6 @@ package mage.target.targetadjustment; import mage.abilities.Ability; -import mage.abilities.common.OneOrMoreDamagePlayerTriggeredAbility; import mage.filter.Filter; import mage.filter.predicate.card.OwnerIdPredicate; import mage.filter.predicate.permanent.ControllerIdPredicate; @@ -17,20 +16,20 @@ import java.util.UUID; /** * @author notgreat */ -public class DamagedPlayerControlsTargetAdjuster extends GenericTargetAdjuster { +public class ThatPlayerControlsTargetAdjuster extends GenericTargetAdjuster { private final boolean owner; /** * Use with {@link mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility} with setTargetPointer enabled, - * or {@link OneOrMoreDamagePlayerTriggeredAbility} with "SetTargetPointer.PLAYER" or similar. - * Adjusts the target to only target something the damaged player controls (or owns with alternative constructor) - * And then removes the effects' target pointer that the triggered ability set + * or {@link mage.abilities.common.OneOrMoreDamagePlayerTriggeredAbility} with "SetTargetPointer.PLAYER" or similar. + * Adjusts the target to only target something the damaged/attacked/etc. player controls (or owns with alternative constructor) + * And then removes the effects' target pointer that the triggered ability set, replacing it with the standard {@link FirstTargetPointer} */ - public DamagedPlayerControlsTargetAdjuster() { + public ThatPlayerControlsTargetAdjuster() { this(false); } - public DamagedPlayerControlsTargetAdjuster(boolean owner) { + public ThatPlayerControlsTargetAdjuster(boolean owner) { this.owner = owner; } diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index 5c32c6b6d3a..8cd26e37641 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -85,7 +85,7 @@ public final class CardUtil { public static final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS"); private static final List costWords = Arrays.asList( - "put", "return", "exile", "discard", "sacrifice", "remove", "tap", "reveal", "pay", "collect" + "put", "return", "exile", "discard", "sacrifice", "remove", "tap", "reveal", "pay", "collect", "forage" ); // search set code in commands like "set_code-card_name" @@ -979,7 +979,11 @@ public final class CardUtil { sb.append(counter.getDescription()); } if (!targetPlayerGets) { - sb.append(add ? " on " : " from ").append(description); + sb.append(add ? " on " : " from "); + if (description.contains("up to") && !description.contains("up to one")) { + sb.append("each of "); + } + sb.append(description); } if (!amount.getMessage().isEmpty()) { sb.append(xValue ? ", where X is " : " for each ").append(amount.getMessage()); @@ -1315,6 +1319,15 @@ public final class CardUtil { return permCard; } + /** + * If a card object is moved to the battlefield, object id can be different (e.g. MDFC). + * Use this method to get the permanent object from the card object after move to battlefield. + * Can return null if not found on the battlefield. + */ + public static Permanent getPermanentFromCardPutToBattlefield(Card card, Game game) { + return game.getPermanent(CardUtil.getDefaultCardSideForBattlefield(game, card).getId()); + } + /** * Return card name for same name searching * @@ -1421,7 +1434,7 @@ public final class CardUtil { * @param playerUnderControl */ public static void takeControlUnderPlayerEnd(Game game, Ability source, Player controller, Player playerUnderControl) { - playerUnderControl.setGameUnderYourControl(true, false); + playerUnderControl.setGameUnderYourControl(game, true, false); if (!playerUnderControl.getTurnControlledBy().equals(controller.getId())) { game.informPlayers(controller.getLogName() + " return control of the turn to " + playerUnderControl.getLogName() + CardUtil.getSourceLogName(game, source)); controller.getPlayersUnderYourControl().remove(playerUnderControl.getId()); @@ -1627,15 +1640,15 @@ public final class CardUtil { } } - public static void castSingle(Player player, Ability source, Game game, Card card) { - castSingle(player, source, game, card, null); + public static boolean castSingle(Player player, Ability source, Game game, Card card) { + return castSingle(player, source, game, card, null); } - public static void castSingle(Player player, Ability source, Game game, Card card, ManaCostsImpl manaCost) { - castSingle(player, source, game, card, false, manaCost); + public static boolean castSingle(Player player, Ability source, Game game, Card card, ManaCostsImpl manaCost) { + return castSingle(player, source, game, card, false, manaCost); } - public static void castSingle(Player player, Ability source, Game game, Card card, boolean noMana, ManaCostsImpl manaCost) { + public static boolean castSingle(Player player, Ability source, Game game, Card card, boolean noMana, ManaCostsImpl manaCost) { // handle split-cards if (card instanceof SplitCard) { SplitCardHalf leftHalfCard = ((SplitCard) card).getLeftHalfCard(); @@ -1701,8 +1714,10 @@ public final class CardUtil { player.setCastSourceIdWithAlternateMana(card.getMainCard().getId(), manaCost, additionalCostsNormalCard, MageIdentifier.Default); } + game.getState().setValue("PlayFromNotOwnHandZone" + card.getMainCard().getId(), Boolean.TRUE); + // cast it - player.cast(player.chooseAbilityForCast(card.getMainCard(), game, noMana), + boolean result = player.cast(player.chooseAbilityForCast(card.getMainCard(), game, noMana), game, noMana, new ApprovingObject(source, game)); // turn off effect after cast on every possible card-face @@ -1726,6 +1741,7 @@ public final class CardUtil { } // turn off effect on a normal card game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); + return result; } /** @@ -1758,8 +1774,9 @@ public final class CardUtil { // Waiting on actual ruling of Ashiok, Wicked Manipulator. return true; } - if (player.loseLife(lifeToPay, game, source, false) >= lifeToPay) { - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.LIFE_PAID, player.getId(), source, player.getId(), lifeToPay)); + int lostLife = player.loseLife(lifeToPay, game, source, false); + if (lostLife > 0) { + game.fireEvent(GameEvent.getEvent(GameEvent.EventType.LIFE_PAID, player.getId(), source, player.getId(), lostLife)); return true; } diff --git a/Mage/src/main/java/mage/util/MultiAmountMessage.java b/Mage/src/main/java/mage/util/MultiAmountMessage.java index 3749c20c408..ce4f72f98a9 100644 --- a/Mage/src/main/java/mage/util/MultiAmountMessage.java +++ b/Mage/src/main/java/mage/util/MultiAmountMessage.java @@ -5,9 +5,10 @@ import java.io.Serializable; /** * A helper class for facilitating the multi-choose dialog * - * @author alexander-novo + * @author alexander-novo, JayDi85 */ -public class MultiAmountMessage implements Serializable { +public class MultiAmountMessage implements Serializable, Copyable { + public String message; public int min; public int max; @@ -24,8 +25,20 @@ public class MultiAmountMessage implements Serializable { this.defaultValue = defaultValue; } + private MultiAmountMessage(final MultiAmountMessage mes) { + this.message = mes.message; + this.min = mes.min; + this.max = mes.max; + this.defaultValue = mes.defaultValue; + } + @Override public String toString() { return String.format("%s - from %d to %d - default %d", message, min, max, defaultValue); } + + @Override + public MultiAmountMessage copy() { + return new MultiAmountMessage(this); + } } diff --git a/Mage/src/main/java/mage/watchers/common/CountersAddedFirstTimeWatcher.java b/Mage/src/main/java/mage/watchers/common/CountersAddedFirstTimeWatcher.java new file mode 100644 index 00000000000..c59205a4410 --- /dev/null +++ b/Mage/src/main/java/mage/watchers/common/CountersAddedFirstTimeWatcher.java @@ -0,0 +1,58 @@ +package mage.watchers.common; + +import mage.MageObjectReference; +import mage.constants.WatcherScope; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * @author padfoot + */ +// copied almost entirely from BoostCountersAddedFirstTimeWatcher +public class CountersAddedFirstTimeWatcher extends Watcher { + + private final Map map = new HashMap<>(); + + public CountersAddedFirstTimeWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.COUNTERS_ADDED) { + return; + } + Permanent permanent = game.getPermanent(event.getTargetId()); + int offset = 0; + if (permanent == null) { + permanent = game.getPermanentEntering(event.getTargetId()); + offset++; + } + if (permanent != null) { + map.putIfAbsent(new MageObjectReference(permanent, game, offset), event.getId()); + } + } + + @Override + public void reset() { + super.reset(); + map.clear(); + } + + public static boolean checkEvent(GameEvent event, Permanent permanent, Game game, int offset) { + return event + .getId() + .equals(game + .getState() + .getWatcher(CountersAddedFirstTimeWatcher.class) + .map + .getOrDefault(new MageObjectReference(permanent, game, offset), null)); + } +} diff --git a/Mage/src/main/java/mage/watchers/common/DealtDamageThisGameWatcher.java b/Mage/src/main/java/mage/watchers/common/DealtDamageThisGameWatcher.java new file mode 100644 index 00000000000..a537a64c7b5 --- /dev/null +++ b/Mage/src/main/java/mage/watchers/common/DealtDamageThisGameWatcher.java @@ -0,0 +1,52 @@ +package mage.watchers.common; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.watchers.Watcher; + +import java.util.HashSet; +import java.util.Set; + +/** + * @author TheElk801 + */ +public class DealtDamageThisGameWatcher extends Watcher { + + private final Set damagers = new HashSet<>(); + + public DealtDamageThisGameWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + switch (event.getType()) { + case BEGINNING_PHASE_PRE: + // keep the stored values from getting too big, especially since it doesn't reset between games + damagers.removeIf(mor -> !mor.zoneCounterIsCurrent(game)); + return; + case DAMAGED_PERMANENT: + case DAMAGED_PLAYER: + break; + default: + return; + } + Permanent permanent = game.getPermanent(event.getSourceId()); + if (permanent != null) { + damagers.add(new MageObjectReference(permanent, game)); + } + } + + public static boolean checkCreature(Game game, Ability source) { + return game + .getState() + .getWatcher(DealtDamageThisGameWatcher.class) + .damagers + .stream() + .noneMatch(mor -> mor.refersTo(source.getSourcePermanentOrLKI(game), game)); + } +} diff --git a/Mage/src/main/resources/brackets/infinite-combos.txt b/Mage/src/main/resources/brackets/infinite-combos.txt new file mode 100644 index 00000000000..534ba1e1e05 --- /dev/null +++ b/Mage/src/main/resources/brackets/infinite-combos.txt @@ -0,0 +1,1994 @@ +# Infinite x2 cards combos list for Commander Brackets calculation +# Format: name1@name2 - any order, any card names, can duplicates +# Generated by verify test VerifyCardDataTest.downloadAndPrepareCommanderBracketsData +# Source: https://commanderspellbook.com +# Updated: 2025-06-06 +# Found: 1988 +Bloodchief Ascension@Mindcrank +Zaxara, the Exemplary@Freed from the Real +Brallin, Skyshark Rider@Ophidian Eye +Krosan Restorer@Ashaya, Soul of the Wild +Animar, Soul of Elements@Ancestral Statue +Leonin Relic-Warder@Animate Dead +Hapatra, Vizier of Poisons@Blowfly Infestation +Tooth and Claw@Primal Vigor +Twincast@Reverberate +Stormfront Riders@Cryptic Gateway +Molten Echoes@Felidar Guardian +Palinchron@Flameshadow Conjuring +Painter's Servant@Grindstone +Intruder Alarm@Krenko, Mob Boss +Conspiracy@Turntimber Ranger +Opalescence@Parallax Wave +Erratic Portal@Emrakul, the Aeons Torn +Pili-Pala@Grand Architect +Freed from the Real@Ley Weaver +Faeburrow Elder@Freed from the Real +Vraska, Swarm's Eminence@Walking Ballista +Staff of Domination@Selvala, Heart of the Wilds +Molten Echoes@Wispweaver Angel +Devoted Druid@One with the Stars +Dramatic Reversal@Isochron Scepter +Peregrine Drake@Deadeye Navigator +Sekki, Seasons' Guide@Warstorm Surge +Worldgorger Dragon@Molten Echoes +Niv-Mizzet, the Firemind@Tandem Lookout +The Locust God@Sage of the Falls +Glint-Horn Buccaneer@Ophidian Eye +Godo, Bandit Warlord@Helm of the Host +Muldrotha, the Gravetide@Second Chance +Boros Reckoner@Avacyn, Angel of Hope +Venser, Shaper Savant@Intruder Alarm +The Locust God@Beck // Call +Nekusar, the Mindrazer@Peer into the Abyss +Temple Bell@Mind Over Matter +Guile@Void Mirror +Famished Paladin@Sorcerer's Wand +Mephidross Vampire@Walking Ballista +Panoptic Mirror@Time Warp +Thousand-Year Storm@Spelljack +Intruder Alarm@Whitemane Lion +Grumgully, the Generous@Aerie Ouphes +Goblin Sharpshooter@Splinter Twin +Animate Dead@Worldgorger Dragon +Seedcradle Witch@Wirewood Channeler +Mephidross Vampire@Triskelion +Great Whale@Deadeye Navigator +Guile@Cephalid Shrine +Molten Echoes@Wanderwine Prophets +Scourge of the Skyclaves@Archfiend of Despair +Cryptic Trilobite@Enduring Renewal +Walking Ballista@Mikaeus, the Unhallowed +Stonecoil Serpent@Enduring Renewal +Bladewing the Risen@Heat Shimmer +Archon of Sun's Grace@Enchanted Evening +Leonin Relic-Warder@Phyrexian Metamorph +Necrotic Ooze@Kiki-Jiki, Mirror Breaker +Aminatou, the Fateshifter@Felidar Guardian +Azami, Lady of Scrolls@Mind Over Matter +Reset@Reiterate +Dockside Extortionist@Barrin, Master Wizard +Sea Gate Loremaster@Mind Over Matter +Spawnsire of Ulamog@Training Grounds +Aggravated Assault@Nature's Will +Kiki-Jiki, Mirror Breaker@Deceiver Exarch +Kiki-Jiki, Mirror Breaker@Trumpeting Gnarr +Walking Ballista@Enduring Renewal +Kiki-Jiki, Mirror Breaker@Hyrax Tower Scout +Basalt Monolith@Forsaken Monument +Rakdos, Lord of Riots@Ancestral Statue +Hangarback Walker@Enduring Renewal +Charmbreaker Devils@Time Warp +Ral, Storm Conduit@Chain of Smog +Leonin Relic-Warder@Dance of the Dead +Naru Meha, Master Wizard@Sublime Epiphany +Dockside Extortionist@Temur Sabertooth +Kiki-Jiki, Mirror Breaker@Dirge Bat +Brallin, Skyshark Rider@Curiosity +Ilharg, the Raze-Boar@Medomai the Ageless +Intruder Alarm@Drana's Chosen +Filigree Sages@Chromatic Orrery +Palinchron@Caged Sun +Combat Celebrant@Splinter Twin +Jolrael, Empress of Beasts@Aggravated Assault +Saheeli Rai@Felidar Guardian +Zaxara, the Exemplary@Pemmin's Aura +Dualcaster Mage@Release to the Wind +Heliod, Sun-Crowned@Walking Ballista +Splinter Twin@Zealous Conscripts +Leonin Relic-Warder@Mycosynth Lattice +Neheb, Dreadhorde Champion@Aggravated Assault +Nivix Guildmage@Brass's Bounty +Pemmin's Aura@Krosan Restorer +Aluren@Cloudstone Curio +Brallin, Skyshark Rider@Keen Sense +Mana Geyser@Reiterate +Brudiclad, Telchor Engineer@Breath of Fury +Mana Geyser@Reiterate +Devoted Druid@One with the Stars +Archon of Sun's Grace@Enchanted Evening +Kiki-Jiki, Mirror Breaker@Trumpeting Gnarr +Earthcraft@Spawning Grounds +Aluren@Cloudstone Curio +Scourge of the Skyclaves@Archfiend of Despair +Twincast@Reverberate +Cryptic Trilobite@Enduring Renewal +Reality Spasm@Reiterate +Walking Ballista@Mikaeus, the Unhallowed +Sekki, Seasons' Guide@Warstorm Surge +Basalt Monolith@Mesmeric Orb +Spawnsire of Ulamog@Parallel Lives +Seedcradle Witch@Wirewood Channeler +Intruder Alarm@Whitemane Lion +Wispweaver Angel@Felidar Guardian +Faeburrow Elder@Freed from the Real +Grumgully, the Generous@Aerie Ouphes +Boros Reckoner@Avacyn, Angel of Hope +Intruder Alarm@Krenko, Mob Boss +Kiki-Jiki, Mirror Breaker@Deceiver Exarch +Guile@Void Mirror +Freed from the Real@Ley Weaver +Kiki-Jiki, Mirror Breaker@Dirge Bat +Niv-Mizzet, Parun@Ophidian Eye +Walking Ballista@Enduring Renewal +Reset@Reiterate +Dockside Extortionist@Barrin, Master Wizard +Heliod, Sun-Crowned@Walking Ballista +Pemmin's Aura@Krosan Restorer +Burnt Offering@Reiterate +Opalescence@Parallax Wave +Goblin Sharpshooter@Splinter Twin +Hapatra, Vizier of Poisons@Blowfly Infestation +Rakdos, Lord of Riots@Ancestral Statue +Dramatic Reversal@Isochron Scepter +Peregrine Drake@Deadeye Navigator +Kiki-Jiki, Mirror Breaker@Hyrax Tower Scout +Sydri, Galvanic Genius@Aetherflux Reservoir +Chain of Acid@Ral, Storm Conduit +Stonecoil Serpent@Enduring Renewal +Leonin Relic-Warder@Animate Dead +Conspiracy@Turntimber Ranger +Spike Feeder@Sunbond +Mephidross Vampire@Triskelion +Dockside Extortionist@Temur Sabertooth +Venser, Shaper Savant@Intruder Alarm +Azami, Lady of Scrolls@Mind Over Matter +Hangarback Walker@Enduring Renewal +Charmbreaker Devils@Time Warp +Molten Echoes@Wispweaver Angel +Godo, Bandit Warlord@Helm of the Host +Mephidross Vampire@Walking Ballista +Leonin Relic-Warder@Dance of the Dead +Animate Dead@Worldgorger Dragon +Temple Bell@Mind Over Matter +Naru Meha, Master Wizard@Sublime Epiphany +Parallax Wave@Felidar Guardian +Wakeroot Elemental@Growing Rites of Itlimoc // Itlimoc, Cradle of the Sun +Zaxara, the Exemplary@Freed from the Real +Hellkite Charger@Bear Umbra +Brallin, Skyshark Rider@Curiosity +Worldgorger Dragon@Molten Echoes +Spawnsire of Ulamog@Training Grounds +The Locust God@Sage of the Falls +Intruder Alarm@Drana's Chosen +Stormfront Riders@Cryptic Gateway +Vraska, Swarm's Eminence@Walking Ballista +Great Whale@Deadeye Navigator +Thousand-Year Storm@Spelljack +Filigree Sages@Chromatic Orrery +Staff of Domination@Selvala, Heart of the Wilds +Necrotic Ooze@Kiki-Jiki, Mirror Breaker +Jolrael, Empress of Beasts@Aggravated Assault +Muldrotha, the Gravetide@Second Chance +Saheeli Rai@Felidar Guardian +Famished Paladin@Sorcerer's Wand +Lion's Eye Diamond@Auriok Salvagers +Painter's Servant@Grindstone +Dualcaster Mage@Release to the Wind +Charmbreaker Devils@Time Walk +Ceaseless Searblades@Lavaclaw Reaches +Combat Celebrant@Splinter Twin +Splinter Twin@Zealous Conscripts +Bladewing the Risen@Heat Shimmer +Basalt Monolith@Forsaken Monument +Leonin Relic-Warder@Mycosynth Lattice +Leonin Relic-Warder@Phyrexian Metamorph +Neheb, Dreadhorde Champion@Aggravated Assault +Nivix Guildmage@Brass's Bounty +Molten Echoes@Wanderwine Prophets +Aminatou, the Fateshifter@Felidar Guardian +Ral, Storm Conduit@Chain of Smog +Panoptic Mirror@Time Warp +Reset@Izzet Guildmage +Brallin, Skyshark Rider@Keen Sense +Zaxara, the Exemplary@Pemmin's Aura +Sea Gate Loremaster@Mind Over Matter +Boros Reckoner@Gift of Doom +Panoptic Mirror@Time Warp +Niv-Mizzet, the Firemind@Tandem Lookout +Mana Geyser@Reiterate +Palinchron@Flameshadow Conjuring +Guile@Void Mirror +Freed from the Real@Gyre Engineer +Archon of Sun's Grace@Enchanted Evening +Devoted Druid@One with the Stars +Kiki-Jiki, Mirror Breaker@Trumpeting Gnarr +Staff of Domination@Selvala, Heart of the Wilds +Vraska, Swarm's Eminence@Triskelion +Worldgorger Dragon@Molten Echoes +Aggravated Assault@Nature's Will +Aluren@Cloudstone Curio +Scourge of the Skyclaves@Archfiend of Despair +Tooth and Claw@Primal Vigor +Sekki, Seasons' Guide@Warstorm Surge +Endless One@Enduring Renewal +Cryptic Trilobite@Enduring Renewal +Boros Reckoner@Boros Charm +Glarecaster@Volcano Hellion +Spawnsire of Ulamog@Parallel Lives +Walking Ballista@Mikaeus, the Unhallowed +Seeker of Skybreak@Wake Thrasher +Grumgully, the Generous@Aerie Ouphes +Dramatic Reversal@Isochron Scepter +Grim Monolith@Mana Reflection +Wakeroot Elemental@Growing Rites of Itlimoc // Itlimoc, Cradle of the Sun +Ral, Storm Conduit@Chain of Smog +Soulfire Grand Master@Temporal Manipulation +Aminatou, the Fateshifter@Felidar Guardian +Molten Echoes@Wanderwine Prophets +Arixmethes, Slumbering Isle@Freed from the Real +Basalt Monolith@Mana Reflection +Enduring Renewal@Ashnod's Altar +Lithoform Engine@Sands of Time +Intruder Alarm@Krenko, Mob Boss +Heliod, Sun-Crowned@Walking Ballista +Hellkite Charger@Bear Umbra +Shrieking Drake@Earthcraft +Spawnsire of Ulamog@Training Grounds +Molten Echoes@Felidar Guardian +Boros Reckoner@Avacyn, Angel of Hope +Freed from the Real@Argothian Elder +Kiki-Jiki, Mirror Breaker@Deceiver Exarch +The Locust God@Sage of the Falls +Earthcraft@Spawning Grounds +Freed from the Real@Ley Weaver +Molten Echoes@Wispweaver Angel +Muldrotha, the Gravetide@Second Chance +Mephidross Vampire@Walking Ballista +Charmbreaker Devils@Time Walk +Timestream Navigator@Grenzo, Dungeon Warden +Spawnsire of Ulamog@Illusionist's Bracers +Walking Ballista@Enduring Renewal +Scourge of the Skyclaves@Wound Reflection +Reset@Reiterate +Lion's Eye Diamond@Auriok Salvagers +Dockside Extortionist@Barrin, Master Wizard +Kiki-Jiki, Mirror Breaker@Cavern Whisperer +Spike Feeder@Sunbond +Krosan Restorer@Ashaya, Soul of the Wild +Niv-Mizzet, Parun@Ophidian Eye +Vizkopa Guildmage@Revival // Revenge +Ceaseless Searblades@Lavaclaw Reaches +Famished Paladin@Sorcerer's Wand +Burnt Offering@Reiterate +Seedcradle Witch@Wirewood Channeler +Godo, Bandit Warlord@Helm of the Host +Goblin Sharpshooter@Splinter Twin +The Locust God@Beck // Call +Kiki-Jiki, Mirror Breaker@Hyrax Tower Scout +Zaxara, the Exemplary@Pemmin's Aura +Rakdos, Lord of Riots@Ancestral Statue +Nekusar, the Mindrazer@Peer into the Abyss +Wakeroot Elemental@Nykthos, Shrine to Nyx +Peregrine Drake@Deadeye Navigator +Intruder Alarm@Whitemane Lion +Naru Meha, Master Wizard@Ghostly Flicker +Venser, Shaper Savant@Intruder Alarm +Soulfire Grand Master@Walk the Aeons +Stormfront Riders@Cryptic Gateway +Soulfire Grand Master@Time Walk +Tooth and Claw@Parallel Lives +Stonecoil Serpent@Enduring Renewal +Zaxara, the Exemplary@Freed from the Real +Parallax Wave@Felidar Guardian +Ley Weaver@Maze of Ith +Faeburrow Elder@Freed from the Real +Kiki-Jiki, Mirror Breaker@Wispweaver Angel +Crackdown Construct@Chimeric Idol +Kiki-Jiki, Mirror Breaker@Dirge Bat +Mephidross Vampire@Triskelion +Polyraptor@Forerunner of the Empire +Dockside Extortionist@Temur Sabertooth +Brudiclad, Telchor Engineer@Breath of Fury +Brago, King Eternal@Strionic Resonator +Palinchron@Nyxbloom Ancient +Azami, Lady of Scrolls@Mind Over Matter +Najeela, the Blade-Blossom@Derevi, Empyrial Tactician +Hangarback Walker@Enduring Renewal +Basalt Monolith@Nyxbloom Ancient +Pemmin's Aura@Krosan Restorer +Charmbreaker Devils@Time Warp +Dualcaster Mage@Twinflame +Basalt Monolith@Mesmeric Orb +Raptor Hatchling@Warstorm Surge +Animate Dead@Worldgorger Dragon +Muldrotha, the Gravetide@Second Chance +Naru Meha, Master Wizard@Ghostly Flicker +Leonin Relic-Warder@Dance of the Dead +Molten Echoes@Wispweaver Angel +Heliod, Sun-Crowned@Walking Ballista +Freed from the Real@Ley Weaver +Temple Bell@Mind Over Matter +Mesmeric Orb@Seeker of Skybreak +The Locust God@Beck // Call +Naru Meha, Master Wizard@Sublime Epiphany +Boros Reckoner@Gift of Doom +Nexus of Fate@Spellbinder +Siona, Captain of the Pyleas@Shielded by Faith +Sydri, Galvanic Genius@Aetherflux Reservoir +Rings of Brighthearth@Basalt Monolith +Brallin, Skyshark Rider@Tandem Lookout +Tooth and Claw@Primal Vigor +Bloodchief Ascension@Mindcrank +Vraska, Swarm's Eminence@Walking Ballista +Brallin, Skyshark Rider@Curiosity +Burnt Offering@Reiterate +Brudiclad, Telchor Engineer@Breath of Fury +Arcanis the Omnipotent@Mind Over Matter +Neheb, the Eternal@Aggravated Assault +Parallax Wave@Felidar Guardian +Reality Spasm@Reiterate +Guile@Void Mirror +Grumgully, the Generous@Aerie Ouphes +Battered Golem@Splinter Twin +Boros Reckoner@Indestructibility +Inalla, Archmage Ritualist@Wanderwine Prophets +Zaxara, the Exemplary@Freed from the Real +Great Whale@Deadeye Navigator +Animar, Soul of Elements@Ancestral Statue +Thousand-Year Storm@Spelljack +Molten Echoes@Wanderwine Prophets +Filigree Sages@Chromatic Orrery +Kiki-Jiki, Mirror Breaker@Wispweaver Angel +Glint-Horn Buccaneer@Ophidian Eye +Planar Portal@Nexus of Fate +Famished Paladin@Sorcerer's Wand +Necrotic Ooze@Kiki-Jiki, Mirror Breaker +Jolrael, Empress of Beasts@Aggravated Assault +Polyraptor@Forerunner of the Empire +Kiki-Jiki, Mirror Breaker@Spark Double +Gravecrawler@Phyrexian Altar +Saheeli Rai@Felidar Guardian +Faeburrow Elder@Freed from the Real +Worldgorger Dragon@Molten Echoes +Kiki-Jiki, Mirror Breaker@Breaching Hippocamp +Maddening Cacophony@Bruvac the Grandiloquent +Seedcradle Witch@Selvala, Heart of the Wilds +Intruder Alarm@Kor Skyfisher +Staff of Domination@Selvala, Heart of the Wilds +Dualcaster Mage@Release to the Wind +Lithoform Engine@Sands of Time +Soulfire Grand Master@Capture of Jingzhou +Zacama, Primal Calamity@Temur Sabertooth +Charmbreaker Devils@Walk the Aeons +Aetherflux Reservoir@March of the Machines +Combat Celebrant@Splinter Twin +Soulfire Grand Master@Temporal Manipulation +Molten Echoes@Felidar Guardian +Splinter Twin@Zealous Conscripts +Bladewing the Risen@Heat Shimmer +Stormfront Riders@Cryptic Gateway +Basalt Monolith@Forsaken Monument +Kiki-Jiki, Mirror Breaker@Timestream Navigator +Leonin Relic-Warder@Mycosynth Lattice +Reset@Nivix Guildmage +Leonin Relic-Warder@Phyrexian Metamorph +Nekusar, the Mindrazer@Peer into the Abyss +Neheb, Dreadhorde Champion@Aggravated Assault +Fury Storm@Ral, Storm Conduit +Dramatic Reversal@Isochron Scepter +Nivix Guildmage@Brass's Bounty +Timestream Navigator@Grenzo, Dungeon Warden +Conspiracy@Turntimber Ranger +Intruder Alarm@Shrieking Drake +Brallin, Skyshark Rider@Ophidian Eye +Painter's Servant@Grindstone +Niv-Mizzet, Parun@Ophidian Eye +Mycosynth Golem@Ancestral Statue +Wakeroot Elemental@Growing Rites of Itlimoc // Itlimoc, Cradle of the Sun +Erratic Portal@Emrakul, the Aeons Torn +Hapatra, Vizier of Poisons@Blowfly Infestation +Intruder Alarm@Krenko, Mob Boss +Mephidross Vampire@Walking Ballista +Opalescence@Parallax Wave +Palinchron@Flameshadow Conjuring +Sea Gate Loremaster@Mind Over Matter +Dance of the Dead@Worldgorger Dragon +Zacama, Primal Calamity@Sanctum of Eternity +Palinchron@Flameshadow Conjuring +Panoptic Mirror@Time Warp +Ilharg, the Raze-Boar@Medomai the Ageless +Freed from the Real@Ley Weaver +Hapatra, Vizier of Poisons@Blowfly Infestation +Mana Geyser@Reiterate +Faeburrow Elder@Freed from the Real +Dockside Extortionist@Deadeye Navigator +Twincast@Reverberate +Chain of Acid@Ral, Storm Conduit +Kiki-Jiki, Mirror Breaker@Sparring Mummy +Najeela, the Blade-Blossom@Sword of Feast and Famine +Najeela, the Blade-Blossom@Bear Umbra +Sigil Tracer@Dramatic Reversal +Meletis Charlatan@Dramatic Reversal +Turnabout@Mirror Sheen +Famished Paladin@Resplendent Mentor +Cephalid Illusionist@Shuko +Cephalid Illusionist@Lightning Greaves +Lich@Repay in Kind +Brago, King Eternal@Aurelia, the Warleader +Naru Meha, Master Wizard@Displace +Squee, the Immortal@Food Chain +Eternal Scourge@Food Chain +Filigree Sages@Doubling Cube +Pitiless Plunderer@March of the Machines +Pitiless Plunderer@Titania's Song +Village Bell-Ringer@Temur Sabertooth +Kiki-Jiki, Mirror Breaker@Aphetto Alchemist +Kiki-Jiki, Mirror Breaker@Fatestitcher +Kiki-Jiki, Mirror Breaker@Kiora's Follower +Kiki-Jiki, Mirror Breaker@Seeker of Skybreak +Kiki-Jiki, Mirror Breaker@Vizier of Tumbling Sands +Kiki-Jiki, Mirror Breaker@Tidewater Minion +Karakas@Emrakul, the Aeons Torn +Time Vault@Voltaic Key +Time Vault@Manifold Key +Time Vault@Galvanic Key +Time Vault@Clock of Omens +Time Vault@Aphetto Alchemist +Time Vault@Vizier of Tumbling Sands +Time Vault@Fatestitcher +Time Vault@Tidewater Minion +Time Vault@Captain of the Mists +Time Vault@Elder Druid +Time Vault@Kiora's Follower +Time Vault@Unbender Tine +Time Vault@Ral Zarek +Time Vault@Tezzeret the Seeker +Time Vault@Teferi, Timebender +Time Vault@Frenzied Fugue +Time Vault@Filigree Sages +Time Vault@Voltaic Servant +Time Vault@Mind Over Matter +Time Vault@Trickster Mage +Time Vault@Telekinetic Bonds +Summon the School@Paradox Engine +Time Vault@Derevi, Empyrial Tactician +Crackdown Construct@Mobilized District +Crackdown Construct@Wandering Fumarole +Crackdown Construct@Lavaclaw Reaches +Loyal Retainers@Saffi Eriksdotter +Swans of Bryn Argoll@Chain of Plasma +Thromok the Insatiable@Chandra's Ignition +Nexus of Fate@Divining Witch +Nexus of Fate@Thought Lash +Heartless Hidetsugu@Vizkopa Guildmage +Niv-Mizzet, the Firemind@Swans of Bryn Argoll +Sneak Attack@Palinchron +Karakas@Zacama, Primal Calamity +Ceaseless Searblades@Wandering Fumarole +Drogskol Reaver@Horizon Chimera +Wheel of Misfortune@Cho-Arrim Alchemist +Wheel of Misfortune@Hallow +Wheel of Misfortune@Intervention Pact +Wheel of Misfortune@Purity +Wheel of Misfortune@Samite Ministration +Wheel of Misfortune@Reverse Damage +Kiki-Jiki, Mirror Breaker@Port Razer +Port Razer@Helm of the Host +Port Razer@Brago, King Eternal +Livio, Oathsworn Sentinel@Village Bell-Ringer +Livio, Oathsworn Sentinel@Intruder Alarm +Intruder Alarm@Flamewright +Intruder Alarm@Ghoulcaller Gisa +Intruder Alarm@Jund Battlemage +Intruder Alarm@Imperious Perfect +Intruder Alarm@Kazandu Tuskcaller +Intruder Alarm@Lich Lord of Unx +Intruder Alarm@Puppet Conjurer +Intruder Alarm@Selesnya Evangel +Intruder Alarm@Skirsdag High Priest +Intruder Alarm@Tolsimir Wolfblood +Intruder Alarm@Wall of Kelp +Emiel the Blessed@Workhorse +Cadaverous Bloom@Oath of Lim-Dûl +Peer into the Abyss@Psychic Corrosion +Enter the Infinite@Psychic Corrosion +Cadaverous Bloom@Well of Knowledge +Cadaverous Bloom@Idle Thoughts +Ashaya, Soul of the Wild@Realm Razer +Malcolm, Keen-Eyed Navigator@Glint-Horn Buccaneer +Midnight Guard@Splinter Twin +Kiki-Jiki, Mirror Breaker@Coercive Recruiter +Port Razer@Splinter Twin +Second Chance@Treasury Thrull +Second Chance@Sun Titan +Second Chance@Skull of Orm +Second Chance@Silent Sentinel +Second Chance@Hanna, Ship's Navigator +Second Chance@Dowsing Shaman +Jeska's Will@Reiterate +Oath of Druids@Jace, Wielder of Mysteries +Kodama of the East Tree@Meloku the Clouded Mirror +Dargo, the Shipwrecker@Phyrexian Altar +Dargo, the Shipwrecker@Thermopod +Spawnsire of Ulamog@Ashnod's Altar +Wildwood Scourge@Enduring Scalelord +Fleet Swallower@Fraying Sanity +Tawnos, Urza's Apprentice@Sands of Time +Gray Merchant of Asphodel@Rite of Replication +Cephalid Aristocrat@Lancers en-Kor +Cephalid Aristocrat@Reconnaissance +Cephalid Illusionist@Lancers en-Kor +Cephalid Illusionist@Nomads en-Kor +Cephalid Illusionist@Outrider en-Kor +Cephalid Illusionist@Shaman en-Kor +Cephalid Illusionist@Soltari Guerrillas +Cephalid Illusionist@Spirit en-Kor +Cephalid Illusionist@Warrior en-Kor +Cephalid Illusionist@Reconnaissance +Polyraptor@Aether Flash +Opalescence@Day of the Dragons +Marrow-Gnawer@Thornbite Staff +Hamza, Guardian of Arashin@Ancestral Statue +Metalworker@Voltaic Construct +Magda, Brazen Outlaw@Aggravated Assault +Kaya the Inexorable@Karn's Temporal Sundering +Crackdown Construct@Dark Maze +Crackdown Construct@Blinking Spirit +Crackdown Construct@Shuko +Crackdown Construct@Grafted Wargear +Crackdown Construct@Shaman en-Kor +Crackdown Construct@Flowstone Hellion +Crackdown Construct@Hyalopterous Lemure +Crackdown Construct@Jodah's Avenger +Crackdown Construct@Frenetic Efreet +Crackdown Construct@Urza's Avenger +Crackdown Construct@Lancers en-Kor +Crackdown Construct@Soltari Guerrillas +Crackdown Construct@Warrior en-Kor +Crackdown Construct@Knowledge Vault +Crackdown Construct@Mist Dragon +Crackdown Construct@Viscid Lemures +Crackdown Construct@Spirit en-Kor +Crackdown Construct@Hopping Automaton +Crackdown Construct@Frenetic Sliver +Crackdown Construct@Outrider en-Kor +Crackdown Construct@Varchild's Crusader +Crackdown Construct@Personal Incarnation +Crackdown Construct@Nomads en-Kor +Triskelion@Scythe of the Wretched +Sliver Queen@Basal Sliver +Sliver Queen@Ashnod's Altar +Power Artifact@Grim Monolith +Power Artifact@Basalt Monolith +Wanderwine Prophets@Followed Footsteps +Wanderwine Prophets@Stonybrook Schoolmaster +Bellowing Aegisaur@Walking Ballista +Bellowing Aegisaur@Triskelion +Spike Feeder@Light of Promise +Minion Reflector@Worldgorger Dragon +Mischievous Quanar@Turnabout +Earthcraft@Grinning Ignus +Goldspan Dragon@Crown of Flames +Goldspan Dragon@Flickering Ward +Goldspan Dragon@Shimmering Wings +Goldspan Dragon@Whip Silk +Rasputin Dreamweaver@Emiel the Blessed +Body of Knowledge@Niv-Mizzet, the Firemind +Body of Knowledge@Niv-Mizzet, Parun +Tidewater Minion@Illusionist's Bracers +Liliana, Untouched by Death@Phyrexian Altar +Lightning Greaves@Crackdown Construct +Animar, Soul of Elements@Palinchron +Hellkite Charger@Sword of Feast and Famine +Conspiracy@Glorious Protector +Conspiracy@Restoration Angel +Blazing Sunsteel@Brash Taunter +Nevinyrral, Urborg Tyrant@Phyrexian Altar +Polyraptor@Goblin Bombardment +Intruder Alarm@Hell's Caretaker +Reverberate@Dual Strike +Leonin Relic-Warder@Dance of Many +Day of the Dragons@Starfield of Nyx +Hullbreacher@Teferi's Puzzle Box +Dockside Extortionist@Capsize +Sharuum the Hegemon@Body Double +Sharuum the Hegemon@Clever Impersonator +Sharuum the Hegemon@Clone +Sharuum the Hegemon@Copy Artifact +Sharuum the Hegemon@Evil Twin +Sharuum the Hegemon@Gigantoplasm +Sharuum the Hegemon@Glasspool Mimic // Glasspool Shore +Sharuum the Hegemon@Mercurial Pretender +Sharuum the Hegemon@Mirror Image +Sharuum the Hegemon@Mirrormade +Sharuum the Hegemon@Mirror of the Forebears +Sharuum the Hegemon@Phantasmal Image +Sharuum the Hegemon@Phyrexian Metamorph +Sharuum the Hegemon@Quicksilver Gargantuan +Sharuum the Hegemon@Reflections of Littjara +Sharuum the Hegemon@Sakashima's Protege +Sharuum the Hegemon@Sakashima's Student +Sharuum the Hegemon@Sculpting Steel +Sharuum the Hegemon@Stunt Double +Sharuum the Hegemon@Vesuvan Doppelganger +Sharuum the Hegemon@Vesuvan Shapeshifter +Sharuum the Hegemon@Vizier of Many Faces +Sharuum the Hegemon@Wall of Stolen Identity +Filigree Sages@Empowered Autogenerator +Elemental Mastery@Midnight Guard +Nadier, Agent of the Duskenel@Food Chain +Professor Onyx@Chain of Smog +Birgi, God of Storytelling // Harnfel, Horn of Bounty@Grinning Ignus +Chain of Acid@Witherbloom Apprentice +Chain of Acid@Professor Onyx +Aggravated Assault@Selvala, Heart of the Wilds +Proteus Staff@Nexus of Fate +Proteus Staff@Beacon of Tomorrows +Karrthus, Tyrant of Jund@Splinter Twin +Splinter Twin@Derevi, Empyrial Tactician +Auriok Salvagers@Black Lotus +Plague of Vermin@Corpse Knight +Rionya, Fire Dancer@Combat Celebrant +Wake Thrasher@Aphetto Alchemist +Prossh, Skyraider of Kher@Food Chain +Dual Strike@Fork +Dual Strike@Increasing Vengeance +Dual Strike@Insidious Will +Dual Strike@Reiterate +Dual Strike@Twincast +Expansion // Explosion@Twincast +Expansion // Explosion@Fork +Twincast@Fork +Twincast@Increasing Vengeance +Fraying Omnipotence@Wound Reflection +Fury Storm@Deekah, Fractal Theorist +Fury Storm@Archmage Emeritus +Fury Storm@Professor Onyx +Fury Storm@Sedgemoor Witch +Fury Storm@Storm-Kiln Artist +Fury Storm@Witherbloom Apprentice +Fury Storm@Witherbloom Pledgemage +Twinning Staff@Narset's Reversal +Double Major@Vadrok, Apex of Thunder +Dualcaster Mage@Ghostly Flicker +Turntimber Ranger@Maskwood Nexus +Morophon, the Boundless@Shrieking Drake +Rionya, Fire Dancer@Godo, Bandit Warlord +Herd Baloth@Ivy Lane Denizen +Herd Baloth@Cathars' Crusade +Emiel the Blessed@Priest of Urabrask +Emiel the Blessed@Priest of Gix +Arcbond@Pariah +Scurry Oak@Cathars' Crusade +Scurry Oak@Ivy Lane Denizen +Vrondiss, Rage of Ancients@Terror of the Peaks +Old Gnawbone@Hellkite Charger +Delina, Wild Mage@Medomai the Ageless +Cleric Class@Spike Feeder +Aggravated Assault@Old Gnawbone +Najeela, the Blade-Blossom@Old Gnawbone +Grimgrin, Corpse-Born@Elemental Mastery +Doubling Season@Jace, Cunning Castaway +Aluren@Aether Adept +Aluren@Cavern Harpy +Aluren@Fleetfoot Panther +Aluren@Horned Kavu +Aluren@Lava Zombie +Aluren@Man-o'-War +Aluren@Shrieking Drake +Aluren@Silver Drake +Aluren@Stonecloaker +Aluren@Whitemane Lion +Kroxa, Titan of Death's Hunger@Worldfire +Selvala, Heart of the Wilds@Gauntlets of Light +Dual Casting@Traitorous Greed +Teferi, Who Slows the Sunset@The Chain Veil +Halana and Alena, Partners@Sage of Hours +Dominating Vampire@Splinter Twin +Weaver of Blossoms // Blossom-Clad Werewolf@Freed from the Real +Weaver of Blossoms // Blossom-Clad Werewolf@Pemmin's Aura +Wedding Ring@Consecrated Sphinx +Chain of Smog@Witherbloom Apprentice +Brass's Bounty@Reiterate +Ill-Tempered Loner // Howlpack Avenger@Stuffy Doll +Medomai the Ageless@Phantom Steed +Brion Stoutarm@Enduring Angel // Angelic Enforcer +Brion Stoutarm@Soul of Eternity +Brion Stoutarm@Serra Avatar +Fling@Enduring Angel // Angelic Enforcer +Fling@Soul of Eternity +Fling@Serra Avatar +Grand Warlord Radha@Aggravated Assault +Goblin Welder@Thornbite Staff +Goblin Welder@Intruder Alarm +Workhorse@Eldrazi Displacer +Traxos, Scourge of Kroog@Banishing Knack +Ranar the Ever-Watchful@Portcullis +Acererak the Archlich@Aluren +Acererak the Archlich@Rooftop Storm +Myr Welder@Time Vault +Mairsil, the Pretender@Time Vault +Enduring Angel // Angelic Enforcer@Vito, Thorn of the Dusk Rose +Zethi, Arcane Blademaster@Savage Beating +Devoted Druid@Swift Reconfiguration +Sanctum Weaver@Pemmin's Aura +Sanctum Weaver@Freed from the Real +Bladewing the Risen@Reflections of Littjara +Enter the Infinite@Thassa's Oracle +Village Bell-Ringer@Deadeye Navigator +Incubation Druid@Pemmin's Aura +Incubation Druid@Freed from the Real +Village Bell-Ringer@Emiel the Blessed +Soulfire Grand Master@Reset +Soulfire Grand Master@Dramatic Reversal +Village Bell-Ringer@Eldrazi Displacer +Storm-Kiln Artist@Haze of Rage +Splinter Twin@Faces of the Past +Vigean Graftmage@Incubation Druid +Vigean Graftmage@Bloom Tender +Vigean Graftmage@Faeburrow Elder +Blinkmoth Infusion@Lithoform Engine +Blinkmoth Infusion@Reiterate +Go-Shintai of Life's Origin@Second Chance +Rionya, Fire Dancer@Lightning Runner +Kazuul's Fury // Kazuul's Cliffs@Serra Avatar +Crystalline Crawler@Evolution Vat +Thud@Enduring Angel // Angelic Enforcer +Shabraz, the Skyshark@Lich's Mastery +Shabraz, the Skyshark@Lich +Xathrid Demon@Enduring Angel // Angelic Enforcer +Thud@Soul of Eternity +Xathrid Demon@Soul of Eternity +Xathrid Demon@Serra Avatar +Lava Zombie@Rooftop Storm +Sedraxis Alchemist@Rooftop Storm +Chulane, Teller of Tales@Intruder Alarm +Voice of the Woods@Intruder Alarm +Thud@Serra Avatar +Light of Promise@Triskelion +Light of Promise@Walking Ballista +Fathom Mage@Wizard Class +Intruder Alarm@Elemental Mastery +Frenetic Efreet@Tavern Scoundrel +Fiendlash@Guilty Conscience +Elemental Mastery@Black Carriage +Enduring Angel // Angelic Enforcer@Vizkopa Guildmage +Elemental Mastery@Altar Golem +Axebane Guardian@High Alert +Slimefoot, the Stowaway@Mana Echoes +Myr Matrix@Mana Echoes +Lord Xander, the Collector@Bruvac the Grandiloquent +Sky Hussar@Eldrazi Displacer +Grimgrin, Corpse-Born@Presence of Gond +Selvala, Heart of the Wilds@High Alert +Sky Hussar@Emiel the Blessed +Faeburrow Elder@High Alert +Bloom Tender@High Alert +Rapacious Dragon@Deadeye Navigator +Faeburrow Elder@Gauntlets of Light +Prosperous Pirates@Deadeye Navigator +Bloom Tender@Gauntlets of Light +Eloise, Nephalia Sleuth@March of the Machines +Sky Hussar@Deadeye Navigator +Kamahl, Pit Fighter@Scythe of the Wretched +Fable of the Mirror-Breaker // Reflection of Kiki-Jiki@Timestream Navigator +Kiki-Jiki, Mirror Breaker@Helm of the Host +Feldon of the Third Path@Timestream Navigator +Kiki-Jiki, Mirror Breaker@Double Major +Kiki-Jiki, Mirror Breaker@Augmenter Pugilist // Echoing Equation +Teferi, Who Slows the Sunset@Lithoform Engine +Bruvac the Grandiloquent@Traumatize +Lord Xander, the Collector@Fraying Sanity +Chandra's Ignition@Enduring Angel // Angelic Enforcer +Chandra's Ignition@Soul of Eternity +Chandra's Ignition@Serra Avatar +Grab the Reins@Enduring Angel // Angelic Enforcer +Grab the Reins@Soul of Eternity +Grab the Reins@Serra Avatar +Kazuul's Fury // Kazuul's Cliffs@Enduring Angel // Angelic Enforcer +Kazuul's Fury // Kazuul's Cliffs@Soul of Eternity +Ghave, Guru of Spores@Mana Echoes +Accomplished Alchemist@Freed from the Real +Accomplished Alchemist@Pemmin's Aura +Scourge of the Skyclaves@Warlock Class +Niv-Mizzet, Parun@Helm of the Ghastlord +Niv-Mizzet, the Firemind@Helm of the Ghastlord +Famished Foragers@Emiel the Blessed +Benthic Biomancer@Wizard Class +The Locust God@Dire Undercurrents +Cut Your Losses@Bruvac the Grandiloquent +Cut Your Losses@Fraying Sanity +Crackleburr@Cryptolith Rite +Crackleburr@Song of Freyalise +Devoted Druid@Luxior, Giada's Gift +Leonin Relic-Warder@Enchanted Evening +Bootleggers' Stash@Time Sieve +Queza, Augur of Agonies@Lich +Queza, Augur of Agonies@Lich's Mastery +Tivit, Seller of Secrets@Deadeye Navigator +Vigean Graftmage@Krosan Restorer +Mikaeus, the Unhallowed@Cinderhaze Wretch +Mikaeus, the Unhallowed@Barrenton Medic +Mikaeus, the Unhallowed@Devoted Druid +Kiki-Jiki, Mirror Breaker@Teardrop Kami +Splinter Twin@Teardrop Kami +Shrieking Drake@Chakram Retriever +Stuffy Doll@Blazing Sunsteel +Boros Reckoner@Blazing Sunsteel +Boros Reckoner@Fiendlash +Spitemare@Blazing Sunsteel +Spitemare@Fiendlash +Truefire Captain@Blazing Sunsteel +Sun-Crowned Hunters@Blazing Sunsteel +Ill-Tempered Loner // Howlpack Avenger@Blazing Sunsteel +Ill-Tempered Loner // Howlpack Avenger@Fiendlash +Mogg Maniac@Blazing Sunsteel +Broodhatch Nantuko@Terror of the Peaks +Broodhatch Nantuko@Warstorm Surge +Saber Ants@Terror of the Peaks +Saber Ants@Warstorm Surge +Hornet Nest@Terror of the Peaks +Hornet Nest@Warstorm Surge +Overgrown Armasaur@Terror of the Peaks +Overgrown Armasaur@Warstorm Surge +Jadzi, Oracle of Arcavios // Journey to the Oracle@Uyo, Silent Prophet +Sphinx of the Second Sun@Aggravated Assault +Chatterfang, Squirrel General@Pitiless Plunderer +Storm-Kiln Artist@Chain of Smog +Storm-Kiln Artist@Chain of Acid +Adarkar Valkyrie@Teardrop Kami +Enduring Scalelord@Spark Double +Enduring Scalelord@Altered Ego +Enduring Scalelord@Moritte of the Frost +Sedgemoor Witch@Chain of Smog +Sedgemoor Witch@Chain of Acid +Jarad, Golgari Lich Lord@Serra Avatar +Jarad, Golgari Lich Lord@Soul of Eternity +Jarad, Golgari Lich Lord@Enduring Angel // Angelic Enforcer +Jarad, Golgari Lich Lord@Wall of Blood +Jarad, Golgari Lich Lord@Hatred +Gisela, Blade of Goldnight@Heartless Hidetsugu +Varragoth, Bloodsky Sire@Nexus of Fate +Varragoth, Bloodsky Sire@Beacon of Tomorrows +Sage of Hours@Clockspinning +Lich@Soul Conduit +Dualcaster Mage@Saw in Half +Naru Meha, Master Wizard@Saw in Half +Lich@Axis of Mortality +Morophon, the Boundless@Cavern Harpy +Worldfire@Mogis, God of Slaughter +Worldfire@Nekusar, the Mindrazer +Worldfire@Marath, Will of the Wild +Worldfire@Outpost Siege +Splinter Twin@Intruder Alarm +Mana Echoes@Emiel the Blessed +Mana Echoes@Eldrazi Displacer +Niv-Mizzet, Parun@Snake Umbra +Niv-Mizzet, the Firemind@Snake Umbra +Aurelia, the Warleader@Sword of Hearth and Home +Sanctum Weaver@Gauntlets of Light +Phantom Steed@Port Razer +Selvala, Heart of the Wilds@Freed from the Real +Selvala, Heart of the Wilds@Pemmin's Aura +Wanderwine Prophets@Maskwood Nexus +Kiki-Jiki, Mirror Breaker@Kelpie Guide +Metalworker@Cogwork Assembler +Ashaya, Soul of the Wild@Quirion Ranger +Sasaya, Orochi Ascendant // Sasaya's Essence@Wakeroot Elemental +Blood Pet@Enduring Renewal +Wild Cantor@Enduring Renewal +Immaculate Magistrate@Sage of Hours +Sanctum Weaver@High Alert +Astarion, the Decadent@Peer into the Abyss +Astarion, the Decadent@Fraying Omnipotence +Astarion, the Decadent@Quietus Spike +Astarion, the Decadent@Raving Dead +Astarion, the Decadent@Scourge of the Skyclaves +Astarion, the Decadent@Virtus the Veiled +Astarion, the Decadent@Scytheclaw +Silvanus's Invoker@Gaea's Cradle +Silvanus's Invoker@Growing Rites of Itlimoc // Itlimoc, Cradle of the Sun +Silvanus's Invoker@Serra's Sanctum +Silvanus's Invoker@Storm the Vault // Vault of Catlacan +Silvanus's Invoker@Tolarian Academy +Myrkul, Lord of Bones@Devoted Druid +Abdel Adrian, Gorion's Ward@Animate Dead +Naru Meha, Master Wizard@Blur +Dualcaster Mage@Blur +Polyraptor@Where Ancients Tread +Lord of the Forsaken@Cut // Ribbons +Endless Horizons@Goblin Charbelcher +Tooth and Claw@Chatterfang, Squirrel General +Grand Warlord Radha@Hellkite Charger +Icewind Stalwart@Conspiracy +Vhal, Candlekeep Researcher@Staff of Domination +Vhal, Candlekeep Researcher@Umbral Mantle +Vhal, Candlekeep Researcher@Sword of the Paruns +Miirym, Sentinel Wyrm@Worldgorger Dragon +Reckless Barbarian@Enduring Renewal +Abdel Adrian, Gorion's Ward@Dance of the Dead +Abdel Adrian, Gorion's Ward@Necromancy +Wedding Ring@Psychic Possession +Astral Dragon@Cursed Mirror +Astral Dragon@Journey to Nowhere +Astral Dragon@Oblivion Ring +Astral Dragon@Parallax Wave +Myrkul, Lord of Bones@Astral Dragon +Astral Dragon@Dance of Many +Jan Jansen, Chaos Crafter@Intruder Alarm +Vhal, Candlekeep Researcher@Singing Bell Strike +Abdel Adrian, Gorion's Ward@Deadeye Navigator +Abdel Adrian, Gorion's Ward@Emiel the Blessed +Abdel Adrian, Gorion's Ward@Eldrazi Displacer +Rionya, Fire Dancer@Port Razer +Delina, Wild Mage@Port Razer +Marwyn, the Nurturer@Singing Bell Strike +Selvala, Heart of the Wilds@Singing Bell Strike +Circle of Dreams Druid@Singing Bell Strike +Heronblade Elite@Singing Bell Strike +Viridian Joiner@Singing Bell Strike +Alena, Kessig Trapper@Singing Bell Strike +Kiki-Jiki, Mirror Breaker@Delina, Wild Mage +Nested Ghoul@Terror of the Peaks +Nested Ghoul@Warstorm Surge +Nested Ghoul@Pandemonium +Thrasta, Tempest's Roar@Food Chain +Slogurk, the Overslime@Mind Over Matter +Combat Celebrant@Mirage Phalanx +Living Plane@Massacre Wurm +Dualcaster Mage@Settle Beyond Reality +Naru Meha, Master Wizard@Settle Beyond Reality +Dualcaster Mage@Siren's Ruse +Naru Meha, Master Wizard@Siren's Ruse +Myojin of Cryptic Dreams@Dramatist's Puppet +Myojin of Cryptic Dreams@Throne of Geth +Lurking Roper@Sorcerer's Wand +Breath of Fury@Goblin Rabblemaster +Crypt Champion@Phantasmal Image +Crypt Champion@Glasspool Mimic // Glasspool Shore +Crypt Champion@Mirror Image +Chain of Acid@Archmage Emeritus +Chain of Smog@Deekah, Fractal Theorist +Chain of Acid@Deekah, Fractal Theorist +Volcano Hellion@True Conviction +Volcano Hellion@Heliod, Sun-Crowned +Goldspan Dragon@View from Above +Traxos, Scourge of Kroog@Retraction Helix +Scytheclaw@Wound Reflection +Scytheclaw@Archfiend of Despair +Scytheclaw@Warlock Class +Quietus Spike@Wound Reflection +Quietus Spike@Archfiend of Despair +Quietus Spike@Warlock Class +Virtus the Veiled@Archfiend of Despair +Virtus the Veiled@Warlock Class +Raving Dead@Wound Reflection +Raving Dead@Archfiend of Despair +Raving Dead@Warlock Class +Ruin Ghost@Retreat to Coralhelm +Rakka Mar@Intruder Alarm +Ripjaw Raptor@Niv-Mizzet, Parun +Ripjaw Raptor@Niv-Mizzet, the Firemind +Kelpie Guide@Freed from the Real +Kelpie Guide@Pemmin's Aura +Medomai the Ageless@Satoru Umezawa +Minsc & Boo, Timeless Heroes@Serra Avatar +Minsc & Boo, Timeless Heroes@Soul of Eternity +Minsc & Boo, Timeless Heroes@Enduring Angel // Angelic Enforcer +Frilled Deathspitter@Guilty Conscience +Sun-Crowned Hunters@Guilty Conscience +Spelljack@Swarm Intelligence +Spelljack@Bonus Round +Aeve, Progenitor Ooze@Food Chain +Martyrdom@Arcbond +Felidar Guardian@Flameshadow Conjuring +Sakashima of a Thousand Faces@Felidar Guardian +Animar, Soul of Elements@Primordial Mist +The Locust God@Rite of Harmony +Heartless Hidetsugu@Curse of Bloodletting +Heartless Hidetsugu@Bitter Feud +Heartless Hidetsugu@Goblin Goliath +Satoru Umezawa@Master of Cruelties +Toralf, God of Fury // Toralf's Hammer@Star of Extinction +Black Carriage@Presence of Gond +Black Carriage@Splinter Twin +Phantom Steed@Dual Nature +Orah, Skyclave Hierophant@Infinite Reflection +Pili-Pala@Careful Cultivation +Farmstead Gleaner@Careful Cultivation +Composite Golem@Fool's Demise +Intruder Alarm@Jade Mage +Intruder Alarm@Biogenic Ooze +Morality Shift@Mortal Combat +Ivy Lane Denizen@Aerie Ouphes +Cathars' Crusade@Aerie Ouphes +Niv-Mizzet, the Firemind@Illusory Ambusher +Niv-Mizzet, Parun@Illusory Ambusher +Walking Ballista@Sunbond +Triskelion@Sunbond +Lightning Runner@Infinite Reflection +Old Gnawbone@Time Sieve +Jace, Cunning Castaway@Vorinclex, Monstrous Raider +Worldgorger Dragon@Worldfire +Wrathful Red Dragon@Star of Extinction +Drogskol Reaver@Confessor +Drogskol Reaver@Feast of Sanity +Jace, Wielder of Mysteries@Enter the Infinite +Mind Over Matter@Idol of Oblivion +Terror of the Peaks@Rite of Replication +Scourge of Valkas@Rite of Replication +Druids' Repository@Aggravated Assault +Hellkite Charger@Druids' Repository +Weaver of Harmony@Intruder Alarm +Baral's Expertise@Dualcaster Mage +Naru Meha, Master Wizard@Baral's Expertise +Magar of the Magic Strings@Time Warp +Magar of the Magic Strings@Temporal Manipulation +Olivia's Attendants@Time Sieve +Sophina, Spearsage Deserter@Time Sieve +Surge to Victory@Savage Beating +Dominating Vampire@Kiki-Jiki, Mirror Breaker +Duke Ulder Ravengard@Port Razer +Archmage Ascension@Nexus of Fate +Archmage Ascension@Beacon of Tomorrows +Emrakul's Hatcher@Emiel the Blessed +Emrakul's Hatcher@Eldrazi Displacer +Mercurial Pretender@Dockside Extortionist +Disciple of Bolas@Body of Research +Greater Good@Body of Research +Sheoldred, the Apocalypse@Peer into the Abyss +Drogskol Reaver@Sheoldred, the Apocalypse +Sheoldred, the Apocalypse@Lich +Sheoldred, the Apocalypse@Lich's Mastery +Sheoldred, the Apocalypse@Nefarious Lich +The Reaver Cleaver@Time Sieve +The Reaver Cleaver@Aggravated Assault +Najeela, the Blade-Blossom@The Reaver Cleaver +The Reaver Cleaver@Hellkite Charger +Filigree Sages@Timeless Lotus +Estrid, the Masked@The Peregrine Dynamo +Gideon, Martial Paragon@The Peregrine Dynamo +Teferi, Who Slows the Sunset@The Peregrine Dynamo +Robaran Mercenaries@Kiki-Jiki, Mirror Breaker +Jodah's Codex@Mind Over Matter +Briar Hydra@Sage of Hours +Magar of the Magic Strings@Savage Beating +Sevinne, the Chronoclasm@Increasing Vengeance +Sevinne, the Chronoclasm@Refuse // Cooperate +Brash Taunter@Aetherflux Reservoir +Ashaya, Soul of the Wild@Petravark +Fury Storm@Twinning Staff +Kianne, Dean of Substance // Imbraham, Dean of Theory@Intruder Alarm +Najeela, the Blade-Blossom@Esika, God of the Tree // The Prismatic Bridge +Duskmantle Guildmage@Maddening Cacophony +Feast of Sanity@Peer into the Abyss +Vrondiss, Rage of Ancients@Warstorm Surge +Vrondiss, Rage of Ancients@Pandemonium +Archangel of Thune@Walking Ballista +Archangel of Thune@Triskelion +Marneus Calgar@The Locust God +Leonin Relic-Warder@Biotransference +Beacon of Immortality@Plague Drone +Trazyn the Infinite@Time Vault +Shard of the Nightbringer@Wound Reflection +Shard of the Nightbringer@Archfiend of Despair +Shard of the Nightbringer@Warlock Class +Shard of the Nightbringer@Sanguine Bond +Vito, Thorn of the Dusk Rose@Shard of the Nightbringer +Rhys the Redeemed@Intruder Alarm +Palinchron@Reflections of Littjara +Belisarius Cawl@Intruder Alarm +Cybernetica Datasmith@Intruder Alarm +Time Sieve@Inquisitor Eisenhorn +Blazing Sunsteel@Mirrormade +Blazing Sunsteel@Guilty Conscience +Blazing Sunsteel@Mirage Mirror +Body of Research@Thud +Body of Research@Kazuul's Fury // Kazuul's Cliffs +Body of Research@Fling +Body of Research@Grab the Reins +Body of Research@Chandra's Ignition +Palisade Giant@Arcbond +Intruder Alarm@Ancestral Statue +Rootha, Mercurial Artist@Refuse // Cooperate +Rootha, Mercurial Artist@Twincast +Rootha, Mercurial Artist@Fork +Rootha, Mercurial Artist@Reverberate +Rootha, Mercurial Artist@Increasing Vengeance +Vadrok, Apex of Thunder@Chef's Kiss +Scurry Oak@Coat of Arms +Sharuum the Hegemon@Infinite Reflection +Guile@Flusterstorm +Extus, Oriq Overlord // Awaken the Blood Avatar@Chain of Smog +Young Necromancer@Altar of Dementia +Murderous Redcap@Solemnity +Mirror-Mad Phantasm@Phantasmal Image +Mirror-Mad Phantasm@Renegade Doppelganger +Flumph@Niv-Mizzet, the Firemind +Flumph@Niv-Mizzet, Parun +Volo, Guide to Monsters@Palinchron +Last Voyage of the _____@Worldgorger Dragon +Leonin Relic-Warder@Last Voyage of the _____ +Abdel Adrian, Gorion's Ward@Last Voyage of the _____ +Devoted Druid@Captain Rex Nebula +Bladewing the Risen@Cursed Mirror +Bladewing the Risen@Clone +Shabraz, the Skyshark@Nefarious Lich +Sliver Queen@Intruder Alarm +Myr Matrix@Intruder Alarm +Bladewing the Risen@Necroduality +Bladewing the Risen@Dual Nature +Apex Altisaur@Outmuscle +Apex Altisaur@Blizzard Brawl +Apex Altisaur@Eldrazi Monument +Apex Altisaur@Hammer of Nazahn +Apex Altisaur@Darksteel Plate +Vrondiss, Rage of Ancients@Dragon Tempest +Flameshadow Conjuring@Worldgorger Dragon +Rousing Refrain@Rift Elemental +Sprouting Phytohydra@Blasting Station +Sprouting Phytohydra@Goblin Bombardment +Exchange of Words@Scute Swarm +Vrondiss, Rage of Ancients@Scourge of Valkas +Tooth and Claw@Adrix and Nev, Twincasters +Arcbond@Pariah's Shield +Tezzeret, Master of the Bridge@Ancestral Statue +Fraying Omnipotence@Warlock Class +Fraying Omnipotence@Archfiend of Despair +Dualcaster Mage@Ephemerate +Dualcaster Mage@Cloudshift +Dualcaster Mage@Essence Flux +Naru Meha, Master Wizard@Ephemerate +Naru Meha, Master Wizard@Cloudshift +Dualcaster Mage@Momentary Blink +Dualcaster Mage@Justiciar's Portal +Dualcaster Mage@Flicker of Fate +Naru Meha, Master Wizard@Momentary Blink +Naru Meha, Master Wizard@Justiciar's Portal +Naru Meha, Master Wizard@Flicker of Fate +Dualcaster Mage@Displace +Machine God's Effigy@Devoted Druid +Machine God's Effigy@Barrenton Medic +Machine God's Effigy@Cinderhaze Wretch +Hexavus@Devoted Druid +Bruvac the Grandiloquent@Terisian Mindbreaker +Terisian Mindbreaker@Fraying Sanity +Drafna, Founder of Lat-Nam@Ugin's Nexus +Deathbloom Ritualist@Freed from the Real +Deathbloom Ritualist@Pemmin's Aura +Dockside Extortionist@Meticulous Excavation +Kiki-Jiki, Mirror Breaker@Corridor Monitor +Volcano Hellion@Sorin, Vengeful Bloodlord +Volcano Hellion@Whip of Erebos +Sanctum Weaver@Thassa's Ire +Murderous Redcap@Cathars' Crusade +Port Razer@Mirage Phalanx +Inner Fire@Reiterate +Auntie Blyte, Bad Influence@Volcano Hellion +Lita, Mechanical Engineer@The Peregrine Dynamo +Nest of Scarabs@Blowfly Infestation +Sporeweb Weaver@Blazing Sunsteel +Tolsimir Wolfblood@Thornbite Staff +Astral Dragon@Machine God's Effigy +Palinchron@Phantasmal Image +Teysa, Orzhov Scion@Sleight of Mind +Teysa, Orzhov Scion@Whim of Volrath +Teysa, Orzhov Scion@Alter Reality +Teysa, Orzhov Scion@Crystal Spray +Teysa, Orzhov Scion@Mind Bend +Rionya, Fire Dancer@Bloodthirster +Ill-Tempered Loner // Howlpack Avenger@Angelfire Ignition +Spitemare@Angelfire Ignition +Wirewood Symbiote@Maskwood Nexus +Sharuum the Hegemon@Machine God's Effigy +Norika Yamazaki, the Poet@Second Chance +Kaya, Intangible Slayer@Felidar Guardian +Guilty Conscience@Phyrexian Vindicator +Conduit of Worlds@Second Chance +All Will Be One@Quest for Pure Flame +All Will Be One@The Red Terror +Urabrask's Forge@Breath of Fury +Henzie "Toolbox" Torre@Ancestral Statue +Queza, Augur of Agonies@Nefarious Lich +Jace's Archivist@Mind Over Matter +Kwain, Itinerant Meddler@Mind Over Matter +Magistrate's Scepter@Clock of Omens +Neera, Wild Mage@Displacer Kitten +Ith, High Arcanist@Mesmeric Orb +Against All Odds@Dualcaster Mage +Against All Odds@Naru Meha, Master Wizard +Solphim, Mayhem Dominus@Heartless Hidetsugu +All Will Be One@Night Dealings +All Will Be One@Talon of Pain +All Will Be One@War Elemental +Unctus, Grand Metatect@Aphetto Alchemist +Emiel the Blessed@Intruder Alarm +Eldrazi Displacer@Intruder Alarm +Deadeye Navigator@Intruder Alarm +Zephyr Scribe@Retraction Helix +Zephyr Scribe@Banishing Knack +Replication Technique@Parallel Lives +Replication Technique@Primal Vigor +Replication Technique@Doubling Season +Replication Technique@Anointed Procession +Zalto, Fire Giant Duke@Blazing Sunsteel +Loran of the Third Path@Mind Over Matter +Urban Daggertooth@Walking Ballista +Urban Daggertooth@Triskelion +Urban Daggertooth@Deathbringer Thoctar +Unctus, Grand Metatect@Pemmin's Aura +Unctus, Grand Metatect@Freed from the Real +Opalescence@Journey to Nowhere +Blitzwing, Cruel Tormentor // Blitzwing, Adaptive Assailant@Peer into the Abyss +Blitzwing, Cruel Tormentor // Blitzwing, Adaptive Assailant@Fraying Omnipotence +Blitzwing, Cruel Tormentor // Blitzwing, Adaptive Assailant@Quietus Spike +Blitzwing, Cruel Tormentor // Blitzwing, Adaptive Assailant@Raving Dead +Blitzwing, Cruel Tormentor // Blitzwing, Adaptive Assailant@Scourge of the Skyclaves +Blitzwing, Cruel Tormentor // Blitzwing, Adaptive Assailant@Virtus the Veiled +Blitzwing, Cruel Tormentor // Blitzwing, Adaptive Assailant@Scytheclaw +Riku of Two Reflections@Worldgorger Dragon +Ith, High Arcanist@Wake Thrasher +Aurelia, the Warleader@Rionya, Fire Dancer +Niv-Mizzet, Parun@Swans of Bryn Argoll +Brion Stoutarm@Body of Research +Sunstrike Legionnaire@Splinter Twin +Sunstrike Legionnaire@Presence of Gond +Sunstrike Legionnaire@Elemental Mastery +Otherworld Atlas@Mind Over Matter +Primordial Mist@Hamza, Guardian of Arashin +Primordial Mist@Rakdos, Lord of Riots +Time Sieve@Ria Ivor, Bane of Bladehold +Unctus, Grand Metatect@Tidewater Minion +Ith, High Arcanist@Unctus, Grand Metatect +All Will Be One@Body of Research +All Will Be One@Eternity Vessel +Lagomos, Hand of Hatred@Breath of Fury +Morophon, the Boundless@Horned Kavu +Shadowheart, Dark Justiciar@Body of Research +Enduring Renewal@Thermopod +Goro-Goro and Satoru@Breath of Fury +Mirrodin Besieged@Morality Shift +Crackdown Construct@Kazuul's Toll Collector +Crackdown Construct@Eater of the Dead +Crackdown Construct@Aphetto Alchemist +Lu Su, Wu Advisor@Mind Over Matter +Mind Over Matter@Talas Researcher +Mind Over Matter@Enclave Cryptologist +Mind Over Matter@Urza's Blueprints +Silvanus's Invoker@Nykthos, Shrine to Nyx +Enduring Renewal@Skirk Prospector +Myrel, Shield of Argive@Time Sieve +Breath of Fury@Determined Iteration +Kiki-Jiki, Mirror Breaker@Breath of Fury +Breath of Fury@Splinter Twin +Rionya, Fire Dancer@Breath of Fury +Breath of Fury@Daring Piracy +Breath of Fury@Loyal Apprentice +Valduk, Keeper of the Flame@Breath of Fury +Blessed Wind@Archfiend of Despair +Blessed Wind@Wound Reflection +Blessed Wind@Warlock Class +Magister Sphinx@Archfiend of Despair +Magister Sphinx@Wound Reflection +Magister Sphinx@Warlock Class +Magar of the Magic Strings@Capture of Jingzhou +Magar of the Magic Strings@Walk the Aeons +Tainted Sigil@Aetherflux Reservoir +Akki Battle Squad@Sword of Hearth and Home +Brago, King Eternal@Akki Battle Squad +Kiki-Jiki, Mirror Breaker@Janjeet Sentry +Crackdown Construct@Martyrdom +Dockside Extortionist@Disappear +Sanctum Weaver@Seedcradle Witch +The One Ring@Mind Over Matter +Volo, Itinerant Scholar@Mind Over Matter +Volrath, the Shapestealer@Patron of the Orochi +Unctus, Grand Metatect@Tolarian Kraken +Restoration Angel@Icewind Stalwart +Ad Nauseam@Cloudsteel Kirin +Magistrate's Scepter@Ichormoon Gauntlet +Najeela, the Blade-Blossom@Cyclonus, the Saboteur // Cyclonus, Cybertronian Fighter +Cyclonus, the Saboteur // Cyclonus, Cybertronian Fighter@Aggravated Assault +Hermit Druid@Nexus of Fate +Hermit Druid@Beacon of Tomorrows +Leveler@Nexus of Fate +Leveler@Beacon of Tomorrows +Surrak and Goreclaw@Aerie Ouphes +Kami of Whispered Hopes@Freed from the Real +Kami of Whispered Hopes@Pemmin's Aura +Kami of Whispered Hopes@Crab Umbra +Kami of Whispered Hopes@High Alert +Tribute to the World Tree@Aerie Ouphes +Xerex Strobe-Knight@Intruder Alarm +Omen Hawker@Pemmin's Aura +Omen Hawker@Freed from the Real +Sprouting Phytohydra@Marauding Raptor +Sprouting Phytohydra@Aether Flash +Kroxa and Kunoros@Altar of Dementia +Shalai and Hallar@Heliod, Sun-Crowned +Shalai and Hallar@Cleric Class +Shalai and Hallar@Ajani's Pridemate +Shalai and Hallar@Voice of the Blessed +Shalai and Hallar@Celestial Unicorn +Shalai and Hallar@Twinblade Paladin +Shalai and Hallar@Ageless Entity +Shalai and Hallar@Gideon's Company +Shalai and Hallar@Trelasarra, Moon Dancer +Shalai and Hallar@Archangel of Thune +Weaver of Harmony@Awakening +Weaver of Harmony@Curse of Bounty +Shalai and Hallar@The Red Terror +Hangar Scrounger@Aphetto Alchemist +Shalai and Hallar@Phyrexian Devourer +All Will Be One@Phyrexian Devourer +Scourge of the Throne@Helm of the Host +Rionya, Fire Dancer@Scourge of the Throne +All Will Be One@Soul-Scar Mage +Intruder Alarm@Aethersnipe +Mirror-Mad Phantasm@Sakashima's Will +Shalai and Hallar@War Elemental +Ob Nixilis, Captive Kingpin@All Will Be One +Ob Nixilis, Captive Kingpin@Shalai and Hallar +Harnessed Snubhorn@Second Chance +All Will Be One@Everlasting Torment +Timestream Navigator@The Temporal Anchor +Ashaya, Soul of the Wild@Wormfang Turtle +Ashaya, Soul of the Wild@Wormfang Newt +Chain Stasis@Faeburrow Elder +Underworld Breach@Path of the Pyromancer +Volrath, the Shapestealer@Akki Battle Squad +Selfless Squire@Wheel of Misfortune +Kaya, Intangible Slayer@Enchanted Evening +Urabrask // The Great Work@Resourceful Defense +Exalted Flamer of Tzeentch@Time Warp +Exalted Flamer of Tzeentch@Temporal Manipulation +Exalted Flamer of Tzeentch@Capture of Jingzhou +Exalted Flamer of Tzeentch@Walk the Aeons +Kiki-Jiki, Mirror Breaker@Akki Battle Squad +Rionya, Fire Dancer@Akki Battle Squad +Leonin Relic-Warder@Encroaching Mycosynth +Fable of the Mirror-Breaker // Reflection of Kiki-Jiki@Village Bell-Ringer +Fable of the Mirror-Breaker // Reflection of Kiki-Jiki@Intruder Alarm +Akki Battle Squad@Helm of the Host +Bladewing the Risen@Saw in Half +Bladewing the Risen@Activated Sleeper +Body of Research@Soul's Fire +Famished Paladin@Porcuparrot +Lurking Roper@Porcuparrot +Famished Paladin@Viridian Longbow +Famished Paladin@Burning Anger +Famished Paladin@Hermetic Study +Famished Paladin@Fire Whip +Famished Paladin@Arcane Teachings +Lurking Roper@Viridian Longbow +Lurking Roper@Burning Anger +Lurking Roper@Hermetic Study +Lurking Roper@Fire Whip +Lurking Roper@Arcane Teachings +Ratadrabik of Urborg@Boromir, Warden of the Tower +Rosie Cotton of South Lane@Herd Baloth +Rosie Cotton of South Lane@Scurry Oak +One Ring to Rule Them All@Body of Research +Sakashima of a Thousand Faces@Ioreth of the Healing House +Éomer, Marshal of Rohan@Blade of Selves +Éomer, Marshal of Rohan@Legion Loyalty +Meneldor, Swift Savior@Aurelia, the Warleader +The Watcher in the Water@Greater Good +Orcish Bowmasters@Peer into the Abyss +Fall of Cair Andros@Star of Extinction +Fall of Cair Andros@Blasphemous Act +Marneus Calgar@The Watcher in the Water +Heliod, the Radiant Dawn // Heliod, the Warped Eclipse@Ancestral Statue +Ezzaroot Channeler@Ancestral Statue +Feast of Sanity@Flumph +Feast of Sanity@Illusory Ambusher +Feast of Sanity@Ripjaw Raptor +Charnelhoard Wurm@Time Warp +Charnelhoard Wurm@Capture of Jingzhou +Charnelhoard Wurm@Temporal Manipulation +Charnelhoard Wurm@Walk the Aeons +Charnelhoard Wurm@Second Chance +Oviya Pashiri, Sage Lifecrafter@Intruder Alarm +Rootha, Mercurial Artist@Expansion // Explosion +Peer into the Abyss@Alhammarret's Archive +Ith, High Arcanist@Ioreth of the Healing House +Mirage Phalanx@Bloodthirster +Archmage Emeritus@Chain of Smog +Mirkwood Bats@Plague of Vermin +Orthion, Hero of Lavabrink@Intruder Alarm +There and Back Again@Vitu-Ghazi Guildmage +Hangar Scrounger@Seeker of Skybreak +Gilraen, Dúnedain Protector@Village Bell-Ringer +The Watcher in the Water@Dire Undercurrents +Peregrin Took@Summoning Station +Intruder Alarm@Banishing Knack +Intruder Alarm@Retraction Helix +Ugin's Nexus@Masterful Replication +Syr Gwyn, Hero of Ashvale@Crackdown Construct +Crackdown Construct@Puresteel Paladin +Duke Ulder Ravengard@Éomer, Marshal of Rohan +Kaya, Intangible Slayer@Luxior, Giada's Gift +Ill-Tempered Loner // Howlpack Avenger@Soul Link +Spitemare@Soul Link +Boros Reckoner@Soul Link +Hoard Hauler@Aggravated Assault +Hoard Hauler@Hellkite Charger +Najeela, the Blade-Blossom@Hoard Hauler +Hoard Hauler@Time Sieve +Blazing Sunsteel@Metropolis Reformer +Vodalian Wave-Knight@Benthic Biomancer +Brinelin, the Moon Kraken@Mycosynth Golem +Crackdown Construct@Belt of Giant Strength +Mishra, Eminent One@Aetherflux Reservoir +Heartless Hidetsugu@Archfiend of Despair +Heartless Hidetsugu@Wound Reflection +Heartless Hidetsugu@Warlock Class +Orthion, Hero of Lavabrink@Fanatic of Mogis +Orthion, Hero of Lavabrink@Gray Merchant of Asphodel +Soulfire Grand Master@Jeska's Will +Rionya, Fire Dancer@Terror of the Peaks +Orthion, Hero of Lavabrink@Terror of the Peaks +Rukarumel, Biologist@Intruder Alarm +Mind Over Matter@Oracle's Insight +Mind Over Matter@Ocular Halo +Mind Over Matter@Quicksilver Dagger +Protector of the Crown@Arcbond +Lurking Roper@Heavy Arbalest +Lurking Roper@Hypervolt Grasp +Lurking Roper@Psionic Gift +Lurking Roper@Power of Fire +Lurking Roper@Wolfhunter's Quiver +Lurking Roper@Lightning Prowess +Lurking Roper@Quicksilver Dagger +Famished Paladin@Heavy Arbalest +Famished Paladin@Hypervolt Grasp +Famished Paladin@Psionic Gift +Famished Paladin@Power of Fire +Famished Paladin@Wolfhunter's Quiver +Famished Paladin@Lightning Prowess +Famished Paladin@Quicksilver Dagger +Aetherflux Reservoir@Myr Welder +Trazyn the Infinite@Aetherflux Reservoir +Aetherflux Reservoir@Skilled Animator +Aetherflux Reservoir@Cyberdrive Awakener +Aetherflux Reservoir@Animate Artifact +Selvala, Explorer Returned@Tolarian Kraken +Renata, Called to the Hunt@Aerie Ouphes +Moritte of the Frost@Aerie Ouphes +Ondu Spiritdancer@Enchanted Evening +The Tenth Doctor@Rousing Refrain +Éowyn, Shieldmaiden@Breath of Fury +Body of Research@Altar of Dementia +Overgrown Battlement@Singing Bell Strike +Intruder Alarm@Jungle Patrol +Dualcaster Mage@Scrollshift +Naru Meha, Master Wizard@Scrollshift +Breath of Fury@Helm of the Host +Mishra, Eminent One@Breath of Fury +Dualcaster Mage@Mirage Mockery +River Song@Timestream Navigator +Ashiok, Wicked Manipulator@Wall of Blood +Orcish Bowmasters@Flumph +Composer of Spring@Cloudstone Curio +Mischievous Quanar@Brass's Bounty +Mischievous Quanar@Reset +Mischievous Quanar@Rude Awakening +Phyrexian Devourer@Nexus of Fate +Phyrexian Devourer@Beacon of Tomorrows +Teach by Example@Increasing Vengeance +Naru Meha, Master Wizard@Necromantic Selection +Dualcaster Mage@Necromantic Selection +Omarthis, Ghostfire Initiate@Spark Double +Timestream Navigator@Planar Bridge +Selvala, Heart of the Wilds@Crab Umbra +Enduring Scalelord@Kiki-Jiki, Mirror Breaker +Palinchron@Panharmonicon +Palinchron@Elesh Norn, Mother of Machines +Palinchron@Gauntlet of Power +Palinchron@Mirari's Wake +Palinchron@Mana Reflection +Palinchron@Zendikar Resurgent +Palinchron@Dictate of Karametra +Enduring Scalelord@Splinter Twin +Enduring Scalelord@Cackling Counterpart +Enduring Scalelord@Quasiduplicate +Enduring Scalelord@Croaking Counterpart +Enduring Scalelord@Helm of the Host +Enduring Scalelord@Lithoform Engine +Felidar Guardian@Cursed Mirror +Felidar Guardian@Machine God's Effigy +Wispweaver Angel@Cursed Mirror +Chain of Smog@Clever Lumimancer +Chain of Smog@Dragonsguard Elite +Chain of Smog@Eager First-Year +Chain of Smog@Veyran, Voice of Duality +Chain of Smog@Karok Wrangler +Chain of Smog@Leonin Lightscribe +Chain of Smog@Lorehold Pledgemage +Chain of Smog@Quandrix Pledgemage +Chain of Smog@Silverquill Apprentice +Chain of Smog@Zaffai, Thunder Conductor +Chain of Smog@Witherbloom Pledgemage +Chain of Acid@Clever Lumimancer +Chain of Acid@Dragonsguard Elite +Chain of Acid@Eager First-Year +Chain of Acid@Karok Wrangler +Chain of Acid@Leonin Lightscribe +Chain of Acid@Lorehold Pledgemage +Chain of Acid@Quandrix Pledgemage +Chain of Acid@Silverquill Apprentice +Chain of Acid@Zaffai, Thunder Conductor +Chain of Acid@Witherbloom Pledgemage +Chain of Acid@Veyran, Voice of Duality +Karn, Silver Golem@Aetherflux Reservoir +Aetherflux Reservoir@Karn's Touch +Aetherflux Reservoir@Tezzeret, Agent of Bolas +Aetherflux Reservoir@Tezzeret, Betrayer of Flesh +Aetherflux Reservoir@Toymaker +Aetherflux Reservoir@Xenic Poltergeist +Aetherflux Reservoir@Yotia Declares War +Katsumasa, the Animator@Aetherflux Reservoir +Deathbringer Thoctar@Heliod, Sun-Crowned +Deathbringer Thoctar@Archangel of Thune +Deathbringer Thoctar@Cleric Class +Deathbringer Thoctar@Light of Promise +Deathbringer Thoctar@Sunbond +Hellkite Charger@Nature's Will +Screams from Within@Warehouse Tabby +Agatha of the Vile Cauldron@Spawnsire of Ulamog +Peregrin Took@Experimental Confectioner +Morophon, the Boundless@Grinning Ignus +Screams from Within@Knight of Doves +The World Tree@Purphoros, God of the Forge +Donna Noble@Guilty Conscience +Donna Noble@Spitemare +Donna Noble@Boros Reckoner +Donna Noble@Ill-Tempered Loner // Howlpack Avenger +Olivia, Crimson Bride@Breath of Fury +Ayara, Widow of the Realm // Ayara, Furnace Queen@Breath of Fury +Ancestral Statue@Mana Echoes +Patrol Signaler@Earthcraft +Tooth and Claw@Mondrak, Glory Dominus +Tooth and Claw@Queen Allenal of Ruadach +Tameshi, Reality Architect@Second Chance +Be'lakor, the Dark Master@Orthion, Hero of Lavabrink +Be'lakor, the Dark Master@Rite of Replication +Body of Research@Doom Weaver +Dualcaster Mage@Illusionist's Stratagem +Sporeweb Weaver@Warstorm Surge +Sporeweb Weaver@Terror of the Peaks +Sporeweb Weaver@Pandemonium +Sporeweb Weaver@Goblin Bombardment +Sporeweb Weaver@Blasting Station +Mirror-Mad Phantasm@Dermotaxi +Mirror-Mad Phantasm@Shameless Charlatan +Mirror-Mad Phantasm@Unstable Shapeshifter +Mirror-Mad Phantasm@Quasiduplicate +Mirror-Mad Phantasm@Polymorphous Rush +Mirror-Mad Phantasm@Absorb Identity +Mirror-Mad Phantasm@Mirror of the Forebears +Mirror-Mad Phantasm@Mirage Mirror +Mirror-Mad Phantasm@Cephalid Facetaker +Mirror-Mad Phantasm@Cursed Mirror +Mirror-Mad Phantasm@Machine God's Effigy +Crackdown Construct@Gigantoplasm +Crackdown Construct@Mirror Entity +Crackdown Construct@Chimeric Staff +Crackdown Construct@Crypt Rats +Crackdown Construct@Demonspine Whip +Crackdown Construct@Illusionary Mask +Crackdown Construct@Vengeful Archon +Valki, God of Lies // Tibalt, Cosmic Impostor@Crackdown Construct +Zacama, Primal Calamity@Stormfront Riders +Polyraptor@Blasting Station +Ojer Axonil, Deepest Might // Temple of Power@Pyrohemia +Ojer Axonil, Deepest Might // Temple of Power@Warmonger +Reins of Power@Sigil Tracer +Reins of Power@Echo Mage +Caesar, Legion's Emperor@Breath of Fury +Paradox Engine@Shrieking Drake +Paradox Engine@Whitemane Lion +Sheoldred, the Apocalypse@Griselbrand +Maze of Ith@Krosan Restorer +Volcano Hellion@Vito, Thorn of the Dusk Rose +Volcano Hellion@Vault of the Archangel +Saheeli, the Sun's Brilliance@Intruder Alarm +Sovereign Okinec Ahau@Sage of Hours +Dino DNA@Dockside Extortionist +Dino DNA@Palinchron +Dino DNA@Great Whale +Dino DNA@Mana Echoes +Breath of Fury@Moira, Urborg Haunt +Timestream Navigator@Leveler +Timestream Navigator@Phyrexian Devourer +Time Sieve@Cybermen Squadron +Time Sieve@Legion Loyalty +Omarthis, Ghostfire Initiate@Double Major +Magmatic Galleon@Havoc Jester +Magmatic Galleon@Mayhem Devil +Time Sieve@Exsanguinator Cavalry +The Master, Formed Anew@Palinchron +Wanderwine Prophets@Deeproot Pilgrimage +Carmen, Cruel Skymarcher@Breath of Fury +Filigree Sages@Nyx Lotus +The Enigma Jewel // Locus of Enlightenment@Basalt Monolith +The Enigma Jewel // Locus of Enlightenment@Grim Monolith +Cradle Clearcutter@Voltaic Construct +Poetic Ingenuity@Aggravated Assault +Peer into the Abyss@Bloodletter of Aclazotz +Pili-Pala@Bigger on the Inside +Farmstead Gleaner@Bigger on the Inside +Felidar Guardian@Icewind Stalwart +Charismatic Conqueror@Fractured Identity +Saheeli, the Sun's Brilliance@Timestream Navigator +Harried Dronesmith@Breath of Fury +Doppelgang@Reiterate +Doppelgang@Eternal Witness +Doppelgang@Archaeomancer +The Pride of Hull Clade@Body of Research +Barbed Servitor@Volcano Hellion +Spawnsire of Ulamog@Pitiless Plunderer +Lonis, Genetics Expert@Extruder +Kate Stewart@Soultether Golem +Lonis, Genetics Expert@Rosie Cotton of South Lane +Bloodletter of Aclazotz@Fraying Omnipotence +Bloodletter of Aclazotz@Revival // Revenge +Bloodletter of Aclazotz@Scourge of the Skyclaves +Bloodletter of Aclazotz@Blood Tribute +Bloodletter of Aclazotz@Quietus Spike +Bloodletter of Aclazotz@Ebonblade Reaper +Bloodletter of Aclazotz@Scytheclaw +Bloodletter of Aclazotz@Virtus the Veiled +Bloodletter of Aclazotz@Shard of the Nightbringer +Basalt Monolith@Forensic Gadgeteer +The Watcher in the Water@Kindred Discovery +Crackdown Construct@Vish Kal, Blood Arbiter +Griselbrand@Laboratory Maniac +Griselbrand@Jace, Wielder of Mysteries +Nick Valentine, Private Eye@March of the Machines +Piper Wright, Publick Reporter@Time Sieve +The Watcher in the Water@Sage of the Falls +Tesak, Judith's Hellhound@Hellkite Charger +Splinter Twin@Thornbite Staff +Swarm Intelligence@Reiterate +Thousand-Year Storm@Reiterate +Starfield of Nyx@Parallax Wave +Ognis, the Dragon's Lash@Time Sieve +Lonis, Genetics Expert@Yotian Dissident +Ojer Taq, Deepest Foundation // Temple of Civilization@Tooth and Claw +Preston Garvey, Minuteman@Aggravated Assault +Preston Garvey, Minuteman@Najeela, the Blade-Blossom +Preston Garvey, Minuteman@Hellkite Charger +Bristly Bill, Spine Sower@Crystalline Crawler +Selvala, Eager Trailblazer@Freed from the Real +Bloodletter of Aclazotz@Rush of Dread +Ghired, Mirror of the Wilds@Midnight Guard +Ghired, Mirror of the Wilds@Intruder Alarm +Imodane, the Pyrohammer@Fire Covenant +Bigger on the Inside@Staff of Domination +Blasting Station@Raptor Hatchling +Goblin Bombardment@Raptor Hatchling +Animar, Soul of Elements@Hullbreaker Horror +Shaun, Father of Synths@Aurelia, the Warleader +Bristly Bill, Spine Sower@Devoted Druid +Sharuum the Hegemon@Synth Infiltrator +Five Hundred Year Diary@Filigree Sages +Timestream Navigator@Planar Portal +The Enigma Jewel // Locus of Enlightenment@Seeker of Skybreak +The Enigma Jewel // Locus of Enlightenment@Aphetto Alchemist +Peregrin Took@Nuka-Cola Vending Machine +Cloud of Faeries@Deadeye Navigator +Shrieking Drake@Oltec Matterweaver +Calamity, Galloping Inferno@Port Razer +Myr Propagator@Intruder Alarm +Torgaar, Famine Incarnate@Bloodletter of Aclazotz +Torgaar, Famine Incarnate@Wound Reflection +Torgaar, Famine Incarnate@Archfiend of Despair +Torgaar, Famine Incarnate@Warlock Class +Marneus Calgar@Mana Echoes +Tivit, Seller of Secrets@Time Sieve +Kami of Whispered Hopes@Vigean Graftmage +Yomiji, Who Bars the Way@Omarthis, Ghostfire Initiate +Enduring Renewal@Omarthis, Ghostfire Initiate +Stella Lee, Wild Card@Stella Lee, Wild Card +Felhide Spiritbinder@Ondu Spiritdancer +Chthonian Nightmare@Dockside Extortionist +Silverclad Ferocidons@Mayhem Devil +Crackdown Construct@Plate Armor +Crackdown Construct@Arm-Mounted Anchor +Ertha Jo, Frontier Mentor@Seeker of Skybreak +Ertha Jo, Frontier Mentor@Aphetto Alchemist +Ertha Jo, Frontier Mentor@Tidewater Minion +Basking Broodscale@Mazirek, Kraul Death Priest +Basking Broodscale@Cathars' Crusade +Basking Broodscale@Rosie Cotton of South Lane +Basking Broodscale@Battle of Hoover Dam +Basking Broodscale@Ghost Lantern // Bind Spirit +Touch the Spirit Realm@Opalescence +Time Vault@Territory Forge +Basking Broodscale@Blade of the Bloodchief +Basking Broodscale@Necrosynthesis +Disciple of Freyalise // Garden of Freyalise@Body of Research +Cayth, Famed Mechanist@Intruder Alarm +Izzet Generatorium@Mind Over Matter +Breath of Fury@Siege-Gang Lieutenant +Satya, Aetherflux Genius@Port Razer +Greenbelt Rampager@Primal Prayers +Chatterfang, Squirrel General@Shilgengar, Sire of Famine +Shilgengar, Sire of Famine@Jinnie Fay, Jetmir's Second +Dualcaster Mage@Eldrazi Confluence +Naru Meha, Master Wizard@Eldrazi Confluence +Ulamog's Dreadsire@Intruder Alarm +Ezio Auditore da Firenze@Sorin Markov +Ezio Auditore da Firenze@Tree of Perdition +Ezio Auditore da Firenze@Magister Sphinx +Lightning Runner@Stone Idol Generator +Lightning Runner@Aetherwind Basker +Sage of the Maze@Pemmin's Aura +Experiment Kraj@Ioreth of the Healing House +Part the Waterveil@Radiate +Barren Glory@Renounce +Breath of Fury@Desert Warfare +Sorin of House Markov // Sorin, Ravenous Neonate@Beacon of Immortality +Sorin of House Markov // Sorin, Ravenous Neonate@Revival // Revenge +Edward Kenway@Time Sieve +Enduring Tenacity@Beacon of Immortality +Enduring Tenacity@Revival // Revenge +Havi, the All-Father@Moritte of the Frost +Enduring Angel // Angelic Enforcer@Enduring Tenacity +Shard of the Nightbringer@Enduring Tenacity +Screaming Nemesis@Blazing Sunsteel +Screaming Nemesis@Guilty Conscience +Timestream Navigator@Progenitor Mimic +Body of Research@Season of Gathering +Naru Meha, Master Wizard@Starfall Invocation +Dualcaster Mage@Starfall Invocation +Kitsa, Otterball Elite@Dramatic Reversal +Twenty-Toed Toad@Peer into the Abyss +Twenty-Toed Toad@Enter the Infinite +Zinnia, Valley's Voice@Palinchron +Wick, the Whorled Mind@Conspiracy +Basking Broodscale@Sadistic Glee +Sanctum Weaver@Aggravated Assault +Weaver of Harmony@Virtue of Loyalty // Ardenvale Fealty +Satya, Aetherflux Genius@Lightning Runner +Charismatic Conqueror@Silverquill Lecturer +Changeling Hero@Dual Nature +Changeling Titan@Dual Nature +Changeling Berserker@Dual Nature +Retrofitter Foundry@Mana Echoes +Chandra's Incinerator@Repercussion +Anzrag, the Quake-Mole@Last Night Together +Ghyrson Starn, Kelermorph@Boros Reckoner +Ghyrson Starn, Kelermorph@Spitemare +Stormsplitter@Sprout Swarm +Hazel's Brewmaster@Devoted Druid +The Jolly Balloon Man@Village Bell-Ringer +The Jolly Balloon Man@Intruder Alarm +Razorkin Needlehead@Peer into the Abyss +Piper Wright, Publick Reporter@Sage of Hours +Najeela, the Blade-Blossom@Shriekwood Devourer +Aggravated Assault@Shriekwood Devourer +Hellkite Charger@Shriekwood Devourer +Alania, Divergent Storm@Flare of Duplication +Uyo, Silent Prophet@Doppelgang +Arabella, Abandoned Doll@Storm Herd +Wedding Ring@Notion Thief +Aggravated Assault@Wirewood Channeler +Enchanted Evening@Ghostly Dancers +Anzrag, the Quake-Mole@Anara, Wolvid Familiar +Blasphemous Act@Repercussion +Soulfire Grand Master@Brass's Bounty +Dragon Tempest@Ancient Gold Dragon +Cavern-Hoard Dragon@Time Sieve +The Mindskinner@Body of Research +The Mindskinner@Serra Avatar +The Mindskinner@Soul of Eternity +The Mindskinner@Enduring Angel // Angelic Enforcer +Grievous Wound@Wound Reflection +Grievous Wound@Archfiend of Despair +Grievous Wound@Warlock Class +Grievous Wound@Bloodletter of Aclazotz +Shaman of the Great Hunt@Mind Over Matter +Marvin, Murderous Mimic@Splinter Twin +Tarrian's Soulcleaver@Basking Broodscale +Ondu Spiritdancer@Secret Arcade // Dusty Parlor +Secret Arcade // Dusty Parlor@Ghostly Dancers +Vito, Thorn of the Dusk Rose@Beacon of Immortality +Vito, Thorn of the Dusk Rose@Revival // Revenge +Marvin, Murderous Mimic@Ioreth of the Healing House +Marina Vendrell's Grimoire@Horizon Chimera +Harabaz Druid@Pemmin's Aura +Twitching Doll@Freed from the Real +Archon of Sun's Grace@Secret Arcade // Dusty Parlor +Secret Arcade // Dusty Parlor@Gremlin Tamer +Enchanted Evening@Gremlin Tamer +Mind Over Matter@Chromatic Orrery +Murderous Redcap@Gev, Scaled Scorch +Anzrag, the Quake-Mole@Darksteel Mutation +The Fifth Doctor@Adric, Mathematical Genius +Aurelia, the Warleader@Gilraen, Dúnedain Protector +Ghired, Mirror of the Wilds@Coercive Recruiter +Ghired, Mirror of the Wilds@Battered Golem +Ghired, Mirror of the Wilds@Steelfin Whale +Ghired, Mirror of the Wilds@Sunstrike Legionnaire +Ghired, Mirror of the Wilds@Grimgrin, Corpse-Born +Touch the Spirit Realm@Starfield of Nyx +Niv-Mizzet, Visionary@Niv-Mizzet, Parun +Niv-Mizzet, Visionary@Niv-Mizzet, the Firemind +Heartless Hidetsugu@Twinflame Tyrant +Aphelia, Viper Whisperer@Bloodletter of Aclazotz +Emiel the Blessed@Dionus, Elvish Archdruid +Quilled Greatwurm@Sage of Hours +Tayam, Luminous Enigma@Devoted Druid +Dionus, Elvish Archdruid@Temur Sabertooth +Mirror Room // Fractured Realm@Palinchron +Orthion, Hero of Lavabrink@Great Oak Guardian +The Mindskinner@Syr Konrad, the Grim +Toralf, God of Fury // Toralf's Hammer@Blasphemous Act +Cavern-Hoard Dragon@Aggravated Assault +Aggravated Assault@Primal Adversary +Kiki-Jiki, Mirror Breaker@Fear of Missing Out +Tyvar, the Pummeler@Devoted Druid +Shadow of the Second Sun@Aggravated Assault +Heartless Hidetsugu@Fiendish Duo +Aggravated Assault@Cryptolith Rite +Fear of Missing Out@Splinter Twin +Shilgengar, Sire of Famine@March of the Machines +Shilgengar, Sire of Famine@Titania's Song +Shilgengar, Sire of Famine@Displaced Dinosaurs +Gremlin Tamer@Screams from Within +Tombstone Stairwell@Poison-Tip Archer +Tombstone Stairwell@Blood Artist +Kiki-Jiki, Mirror Breaker@Hazel's Brewmaster +Kodama of the East Tree@Chthonian Nightmare +Doomsday Excruciator@Phenax, God of Deception +Doomsday Excruciator@Breach the Multiverse +Loot, the Pathfinder@Deadeye Navigator +Aether Syphon@Peer into the Abyss +Body of Research@Peema Trailblazer +Soulfire Grand Master@Mana Geyser +Amalia Benavides Aguirre@Wildgrowth Walker +Riverchurn Monument@Cut Your Losses +Dance of Many@Felidar Guardian +Satya, Aetherflux Genius@Breath of Fury +Sage of the Falls@Cryptcaller Chariot +Intruder Alarm@Nemata, Grove Guardian +The Gitrog Monster@Dakmor Salvage +The Gitrog Monster@The Necrobloom +Baylen, the Haymaker@Aggravated Assault +Time Sieve@Luxurious Locomotive +Hellkite Charger@Luxurious Locomotive +Najeela, the Blade-Blossom@Luxurious Locomotive +Aggravated Assault@Luxurious Locomotive +Conqueror's Galleon // Conqueror's Foothold@Capture of Jingzhou +Conqueror's Galleon // Conqueror's Foothold@Rude Awakening +Conqueror's Galleon // Conqueror's Foothold@Reset +Conqueror's Galleon // Conqueror's Foothold@Turnabout +Tidal Control@Mindslaver +Samut, the Driving Force@Sprout Swarm +Haldir, Lórien Lieutenant@Devoted Druid +Orthion, Hero of Lavabrink@Timestream Navigator +Worldfire@Vela the Night-Clad +Cryptcaller Chariot@Greater Good +Jumbo Cactuar@Selvala, Heart of the Wilds +Jumbo Cactuar@Ruthless Technomancer +Jumbo Cactuar@Swords to Plowshares +Jumbo Cactuar@Windswift Slice +Jumbo Cactuar@Wall of Reverence +Jumbo Cactuar@Bighorner Rancher +Jumbo Cactuar@Infested Thrinax +Jumbo Cactuar@Brightmare +Jumbo Cactuar@Predator's Rapport +Jumbo Cactuar@Dazzling Reflection +Jumbo Cactuar@Peema Aether-Seer +Jumbo Cactuar@Soul's Grace +Jumbo Cactuar@Chastise +Jumbo Cactuar@Altar of Dementia +Jumbo Cactuar@Chandra's Ignition +Redshift, Rocketeer Chief@Aggravated Assault +Marvin, Murderous Mimic@Grinning Ignus +Najeela, the Blade-Blossom@Selvala, Heart of the Wilds +Aggravated Assault@Esika, God of the Tree // The Prismatic Bridge +Astarion, the Decadent@Blood Tribute +Nyxbloom Ancient@Rite of Replication +Isengard Unleashed@Heartless Hidetsugu +Shilgengar, Sire of Famine@Stridehangar Automaton +Saheeli, Radiant Creator@Timestream Navigator +Psychosis Crawler@Peer into the Abyss +Sandstorm Crasher@Aurelia, the Warleader +The Twelfth Doctor@Increasing Vengeance +The Peregrine Dynamo@Plargg, Dean of Chaos // Augusta, Dean of Order +Betor, Ancestor's Voice@Sage of Hours +Betor, Kin to All@Bloodletter of Aclazotz +Shorikai, Genesis Engine@Intruder Alarm +Adaptive Training Post@Flare of Duplication +Aetheric Amplifier@Teferi, Master of Time +Aetheric Amplifier@Magistrate's Scepter +Marina Vendrell's Grimoire@Feast of Sanity +Marrow-Gnawer@Faces of the Past +Kheru Goldkeeper@Tortured Existence +Terisian Mindbreaker@Keening Stone +Maddening Cacophony@Keening Stone +Traumatize@Keening Stone +Cut Your Losses@Keening Stone +Lord Xander, the Collector@Keening Stone +Fleet Swallower@Keening Stone +Magmakin Artillerist@Curiosity +Fortune Teller's Talent@Sensei's Divining Top +Mardu Siegebreaker@Port Razer +Kiki-Jiki, Mirror Breaker@Ratadrabik of Urborg +Mikaeus, the Unhallowed@Canker Abomination +Springheart Nantuko@Nissa, Who Shakes the World +Phelddagrif@Intruder Alarm +Questing Phelddagrif@Intruder Alarm +Necrotic Ooze@Mirror-Mad Phantasm +Tidespout Tyrant@Grim Monolith +Lazav, the Multifarious@Mirror-Mad Phantasm +The Scarab God@Mirror-Mad Phantasm +Mairsil, the Pretender@Mirror-Mad Phantasm +Araumi of the Dead Tide@Mirror-Mad Phantasm +Mirror-Mad Phantasm@Blade of Selves +Mirror-Mad Phantasm@Legion Loyalty +Ludevic, Necrogenius // Olag, Ludevic's Hubris@Mirror-Mad Phantasm +Dance of Many@Mirror-Mad Phantasm +Metamorphic Alteration@Mirror-Mad Phantasm +Anzrag, the Quake-Mole@Anzrag, the Quake-Mole +Patchwork Crawler@Mirror-Mad Phantasm +Mirror-Mad Phantasm@See Double +Chancellor of the Spires@Displacer Kitten +Ancient Copper Dragon@Aggravated Assault +Sway of the Stars@Syr Konrad, the Grim +Sway of the Stars@Vela the Night-Clad +Sway of the Stars@Nadier's Nightblade +Sway of the Stars@Zurgo Stormrender +Felothar the Steadfast@Body of Research +Blazing Sunsteel@The Mycosynth Gardens +Robaran Mercenaries@Ioreth of the Healing House +Demonic Consultation@Timestream Navigator +Soulfire Grand Master@Star of Extinction +Soulfire Grand Master@Blasphemous Act +Cloud, Planet's Champion@Crackdown Construct +Yuna, Grand Summoner@Freed from the Real +Jaws of Defeat@Viscid Lemures +Repercussion@Fear of Burning Alive +Kefka, Court Mage // Kefka, Ruler of Ruin@Queza, Augur of Agonies +Kefka, Court Mage // Kefka, Ruler of Ruin@Starving Revenant +Terra, Magical Adept // Esper Terra@Spark Double +Vivi Ornitier@Deadeye Navigator +Vivi Ornitier@Emiel the Blessed +Kefka, Court Mage // Kefka, Ruler of Ruin@Psychosis Crawler +Maester Seymour@Sage of Hours +General Leo Cristophe@Flesh Duplicate +Vivi Ornitier@Quicksilver Elemental +Kefka, Court Mage // Kefka, Ruler of Ruin@Glint-Horn Buccaneer +Kefka, Court Mage // Kefka, Ruler of Ruin@Feast of Sanity +Protean Thaumaturge@Ondu Spiritdancer +Kefka, Court Mage // Kefka, Ruler of Ruin@Niv-Mizzet, the Firemind +Kefka, Court Mage // Kefka, Ruler of Ruin@Niv-Mizzet, Parun +Spellbinder@Great Train Heist +Terra, Magical Adept // Esper Terra@The Apprentice's Folly +Sharuum the Hegemon@Hashaton, Scarab's Fist +Mardu Siegebreaker@Éomer, Marshal of Rohan +Overkill@Jaws of Defeat +Gogo, Master of Mimicry@Magosi, the Waterveil +Gogo, Master of Mimicry@Magistrate's Scepter +Timestream Navigator@The Fire Crystal +Jenova, Ancient Calamity@Sage of Hours +Hyalopterous Lemure@Jaws of Defeat +Mardu Siegebreaker@Felidar Guardian +Cephalid Aristocrat@Grafted Wargear +Cephalid Aristocrat@Lightning Greaves +Satoru Umezawa@Intruder Alarm +Dissection Tools@One with the Kami diff --git a/Mage/src/main/resources/tokens-database.txt b/Mage/src/main/resources/tokens-database.txt index 696ad31ba28..d7858933b16 100644 --- a/Mage/src/main/resources/tokens-database.txt +++ b/Mage/src/main/resources/tokens-database.txt @@ -2253,6 +2253,7 @@ |Generate|TOK:WHO|Human|2||TheEleventhHourToken| |Generate|TOK:WHO|Human Noble|||TheGirlInTheFireplaceHumanNobleToken| |Generate|TOK:WHO|Mark of the Rani|||MarkOfTheRaniToken| +|Generate|TOK:WHO|Mutant|||Mutant33DeathtouchToken| |Generate|TOK:WHO|Soldier|||SoldierToken| |Generate|TOK:WHO|Treasure|1||TreasureToken| |Generate|TOK:WHO|Treasure|2||TreasureToken| diff --git a/Utils/gen-card.pl b/Utils/gen-card.pl index 8760aba81c2..84f79af28cb 100755 --- a/Utils/gen-card.pl +++ b/Utils/gen-card.pl @@ -19,7 +19,7 @@ my %keywords; sub toCamelCase { my $string = $_[0]; $string =~ s/\b([\w']+)\b/ucfirst($1)/ge; - $string =~ s/[-,\s\':.!]//g; + $string =~ s/[-,\s\':.!\/]//g; $string; } @@ -155,7 +155,7 @@ foreach my $setName (keys %{$cards{$originalName}}) { print $last; } # print card line as last - if (defined($currName) && ($cardName cmp $currName) > 0) { + if (defined($currName) && ($cardName cmp $currName) > -1) { print $line; undef $currName; } diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index 0d939c4b9d5..21675cca7f4 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -53980,282 +53980,424 @@ Jukai Liberator|Alchemy: Kamigawa|27|R|{2}{G}|Creature - Snake Ninja|3|3|Ninjuts Runaway Growth|Alchemy: Kamigawa|28|R|{3}{G}|Enchantment - Aura|||Starting intensity 1$Enchant land$Whenever enchanted land is tapped for mana, its controller adds an additional amount of {G} equal to Runaway Growth's intensity. Then Runaway Growth intensifies by 1.| Forceful Cultivator|Alchemy: Kamigawa|29|M|{2}{G}{G}|Creature - Snake Shaman|2|3|This spell costs {2} less to cast if there are no land cards in your hand.$When Forceful Cultivator enters the battlefield, search your library for a basic land card, put that card onto the battlefield tapped, then shuffle.| Imperial Blademaster|Alchemy: Kamigawa|30|R|{1}{R}{W}|Creature - Human Samurai|2|3|Double strike$Whenever a Samurai or Warrior you control attacks alone, draft a card from Imperial Blademaster's spellbook.| -Acrobatic Cheerleader|Duskmourn: House of Horror|1|C|{1}{W}|Creature - Human Survivor|2|2|Survival -- At the beginning of your second main phase, if Acrobatic Cheerleader is tapped, put a flying counter on it. This ability triggers only once.| -Cult Healer|Duskmourn: House of Horror|2|C|{2}{W}|Creature - Human Doctor|3|3|Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, Cult Healer gains lifelink until end of turn.| -Dazzling Theater // Prop Room|Duskmourn: House of Horror|3|R|{3}{W}|Enchantment - Room|||(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Creature spells you cast have convoke.$Prop Room${2}{W}$Enchantment -- Room$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Untap each creature you control during each other player's untap step.| -Dollmaker's Shop // Porcelain Gallery|Duskmourn: House of Horror|4|M|{1}{W}|Enchantment - Room|||(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Whenever one or more non-Toy creatures you control attack a player, create a 1/1 white Toy artifact creature token.$Porcelain Gallery${4}{W}{W}$Enchantment -- Room$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Creatures you control have base power and toughness each equal to the number of creatures you control.| +Acrobatic Cheerleader|Duskmourn: House of Horror|1|C|{1}{W}|Creature - Human Survivor|2|2|Survival -- At the beginning of your second main phase, if this creature is tapped, put a flying counter on it. This ability triggers only once.| +Cult Healer|Duskmourn: House of Horror|2|C|{2}{W}|Creature - Human Doctor|3|3|Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, this creature gains lifelink until end of turn.| +Dazzling Theater // Prop Room|Duskmourn: House of Horror|3|R|{3}{W}|Enchantment - Room|||Creature spells you cast have convoke.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Prop Room${2}{W}$Enchantment -- Room$Untap each creature you control during each other player's untap step.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| +Dollmaker's Shop // Porcelain Gallery|Duskmourn: House of Horror|4|M|{1}{W}|Enchantment - Room|||Whenever one or more non-Toy creatures you control attack a player, create a 1/1 white Toy artifact creature token.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Porcelain Gallery${4}{W}{W}$Enchantment -- Room$Creatures you control have base power and toughness each equal to the number of creatures you control.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| Emerge from the Cocoon|Duskmourn: House of Horror|5|C|{4}{W}|Sorcery|||Return target creature card from your graveyard to the battlefield. You gain 3 life.| Enduring Innocence|Duskmourn: House of Horror|6|R|{1}{W}{W}|Enchantment Creature - Sheep Glimmer|2|1|Lifelink$Whenever one or more other creatures you control with power 2 or less enter, draw a card. This ability triggers only once each turn.$When Enduring Innocence dies, if it was a creature, return it to the battlefield under its owner's control. It's an enchantment.| Ethereal Armor|Duskmourn: House of Horror|7|U|{W}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +1/+1 for each enchantment you control and has first strike.| Exorcise|Duskmourn: House of Horror|8|U|{1}{W}|Sorcery|||Exile target artifact, enchantment, or creature with power 4 or greater.| -Fear of Abduction|Duskmourn: House of Horror|9|U|{4}{W}{W}|Enchantment Creature - Nightmare|5|5|As an additional cost to cast this spell, exile a creature you control.$Flying$When Fear of Abduction enters, exile target creature an opponent controls.$When Fear of Abduction leaves the battlefield, put each card exiled with it into its owner's hand.| -Fear of Immobility|Duskmourn: House of Horror|10|C|{4}{W}|Enchantment Creature - Nightmare|4|4|When Fear of Immobility enters, tap up to one target creature. If an opponent controls that creature, put a stun counter on it.| -Fear of Surveillance|Duskmourn: House of Horror|11|C|{1}{W}|Enchantment Creature - Nightmare|2|2|Vigilance$Whenever Fear of Surveillance attacks, surveil 1.| -Friendly Ghost|Duskmourn: House of Horror|12|C|{3}{W}|Creature - Spirit|2|4|Flying$When Friendly Ghost enters, target creature gets +2/+4 until end of turn.| -Ghostly Dancers|Duskmourn: House of Horror|13|R|{3}{W}{W}|Creature - Spirit|2|5|Flying$When Ghostly Dancers enters, return an enchantment card from your graveyard to your hand or unlock a locked door of a Room you control.$Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, create a 3/1 white Spirit creature token with flying.| -Glimmer Seeker|Duskmourn: House of Horror|14|U|{2}{W}|Creature - Human Survivor|3|3|Survival -- At the beginning of your second main phase, if Glimmer Seeker is tapped, draw a card if you control a Glimmer creature. If you don't control a Glimmer creature, create a 1/1 white Glimmer enchantment creature token.| -Grand Entryway // Elegant Rotunda|Duskmourn: House of Horror|15|C|{1}{W}|Enchantment - Room|||(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$When you unlock this door, create a 1/1 white Glimmer enchantment creature token.$Elegant Rotunda${2}{W}$Enchantment -- Room$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$When you unlock this door, put a +1/+1 counter on each of up to two target creatures.| -Hardened Escort|Duskmourn: House of Horror|16|C|{2}{W}|Creature - Human Soldier|2|4|Whenever Hardened Escort attacks, another target creature you control gets +1/+0 and gains indestructible until end of turn.| +Fear of Abduction|Duskmourn: House of Horror|9|U|{4}{W}{W}|Enchantment Creature - Nightmare|5|5|As an additional cost to cast this spell, exile a creature you control.$Flying$When this creature enters, exile target creature an opponent controls.$When this creature leaves the battlefield, put each card exiled with it into its owner's hand.| +Fear of Immobility|Duskmourn: House of Horror|10|C|{4}{W}|Enchantment Creature - Nightmare|4|4|When this creature enters, tap up to one target creature. If an opponent controls that creature, put a stun counter on it.| +Fear of Surveillance|Duskmourn: House of Horror|11|C|{1}{W}|Enchantment Creature - Nightmare|2|2|Vigilance$Whenever this creature attacks, surveil 1.| +Friendly Ghost|Duskmourn: House of Horror|12|C|{3}{W}|Creature - Spirit|2|4|Flying$When this creature enters, target creature gets +2/+4 until end of turn.| +Ghostly Dancers|Duskmourn: House of Horror|13|R|{3}{W}{W}|Creature - Spirit|2|5|Flying$When this creature enters, return an enchantment card from your graveyard to your hand or unlock a locked door of a Room you control.$Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, create a 3/1 white Spirit creature token with flying.| +Glimmer Seeker|Duskmourn: House of Horror|14|U|{2}{W}|Creature - Human Survivor|3|3|Survival -- At the beginning of your second main phase, if this creature is tapped, draw a card if you control a Glimmer creature. If you don't control a Glimmer creature, create a 1/1 white Glimmer enchantment creature token.| +Grand Entryway // Elegant Rotunda|Duskmourn: House of Horror|15|C|{1}{W}|Enchantment - Room|||When you unlock this door, create a 1/1 white Glimmer enchantment creature token.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Elegant Rotunda${2}{W}$Enchantment -- Room$When you unlock this door, put a +1/+1 counter on each of up to two target creatures.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| +Hardened Escort|Duskmourn: House of Horror|16|C|{2}{W}|Creature - Human Soldier|2|4|Whenever this creature attacks, another target creature you control gets +1/+0 and gains indestructible until end of turn.| Jump Scare|Duskmourn: House of Horror|17|C|{W}|Instant|||Until end of turn, target creature gets +2/+2, gains flying, and becomes a Horror enchantment creature in addition to its other types.| -Leyline of Hope|Duskmourn: House of Horror|18|R|{2}{W}{W}|Enchantment|||If Leyline of Hope is in your opening hand, you may begin the game with it on the battlefield.$If you would gain life, you gain that much life plus 1 instead.$As long as you have at least 7 life more than your starting life total, creatures you control get +2/+2.| +Leyline of Hope|Duskmourn: House of Horror|18|R|{2}{W}{W}|Enchantment|||If this card is in your opening hand, you may begin the game with it on the battlefield.$If you would gain life, you gain that much life plus 1 instead.$As long as you have at least 7 life more than your starting life total, creatures you control get +2/+2.| Lionheart Glimmer|Duskmourn: House of Horror|19|U|{3}{W}{W}|Enchantment Creature - Cat Glimmer|2|5|Ward {2}$Whenever you attack, creatures you control get +1/+1 until end of turn.| -Living Phone|Duskmourn: House of Horror|20|C|{2}{W}|Artifact Creature - Toy|2|1|When Living Phone dies, look at the top five cards of your library. You may reveal a creature card with power 2 or less from among them and put it into your hand. Put the rest on the bottom of your library in a random order.| +Living Phone|Duskmourn: House of Horror|20|C|{2}{W}|Artifact Creature - Toy|2|1|When this creature dies, look at the top five cards of your library. You may reveal a creature card with power 2 or less from among them and put it into your hand. Put the rest on the bottom of your library in a random order.| Optimistic Scavenger|Duskmourn: House of Horror|21|U|{W}|Creature - Human Scout|1|1|Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, put a +1/+1 counter on target creature.| -Orphans of the Wheat|Duskmourn: House of Horror|22|U|{1}{W}|Creature - Human|2|1|Whenever Orphans of the Wheat attacks, tap any number of untapped creatures you control. Orphans of the Wheat gets +1/+1 until end of turn for each creature tapped this way.| -Overlord of the Mistmoors|Duskmourn: House of Horror|23|M|{5}{W}{W}|Enchantment Creature - Avatar Horror|6|6|Impending 4--{2}{W}{W}$Whenever Overlord of the Mistmoors enters or attacks, create two 2/1 white Insect creature tokens with flying.| -Patched Plaything|Duskmourn: House of Horror|24|U|{2}{W}|Artifact Creature - Toy|4|3|Double strike$Patched Plaything enters with two -1/-1 counters on it if you cast it from your hand.| -Possessed Goat|Duskmourn: House of Horror|25|C|{W}|Creature - Goat|1|1|{3}, Discard a card: Put three +1/+1 counters on Possessed Goat and it becomes a black Demon in addition to its other colors and types. Activate only once.| -Reluctant Role Model|Duskmourn: House of Horror|26|R|{1}{W}|Creature - Human Survivor|2|2|Survival -- At the beginning of your second main phase, if Reluctant Role Model is tapped, put a flying, lifelink, or +1/+1 counter on it.$Whenever Reluctant Role Model or another creature you control dies, if it had counters on it, put those counters on up to one target creature.| -Savior of the Small|Duskmourn: House of Horror|27|U|{3}{W}|Creature - Kor Survivor|3|4|Survival -- At the beginning of your second main phase, if Savior of the Small is tapped, return target creature card with mana value 3 or less from your graveyard to your hand.| +Orphans of the Wheat|Duskmourn: House of Horror|22|U|{1}{W}|Creature - Human|2|1|Whenever this creature attacks, tap any number of untapped creatures you control. This creature gets +1/+1 until end of turn for each creature tapped this way.| +Overlord of the Mistmoors|Duskmourn: House of Horror|23|M|{5}{W}{W}|Enchantment Creature - Avatar Horror|6|6|Impending 4--{2}{W}{W}$Whenever this permanent enters or attacks, create two 2/1 white Insect creature tokens with flying.| +Patched Plaything|Duskmourn: House of Horror|24|U|{2}{W}|Artifact Creature - Toy|4|3|Double strike$This creature enters with two -1/-1 counters on it if you cast it from your hand.| +Possessed Goat|Duskmourn: House of Horror|25|C|{W}|Creature - Goat|1|1|{3}, Discard a card: Put three +1/+1 counters on this creature and it becomes a black Demon in addition to its other colors and types. Activate only once.| +Reluctant Role Model|Duskmourn: House of Horror|26|R|{1}{W}|Creature - Human Survivor|2|2|Survival -- At the beginning of your second main phase, if this creature is tapped, put a flying, lifelink, or +1/+1 counter on it.$Whenever this creature or another creature you control dies, if it had counters on it, put those counters on up to one target creature.| +Savior of the Small|Duskmourn: House of Horror|27|U|{3}{W}|Creature - Kor Survivor|3|4|Survival -- At the beginning of your second main phase, if this creature is tapped, return target creature card with mana value 3 or less from your graveyard to your hand.| Seized from Slumber|Duskmourn: House of Horror|28|C|{4}{W}|Instant|||This spell costs {3} less to cast if it targets a tapped creature.$Destroy target creature.| -Shardmage's Rescue|Duskmourn: House of Horror|29|U|{W}|Enchantment - Aura|||Flash$Enchant creature you control$As long as Shardmage's Rescue entered this turn, enchanted creature has hexproof.$Enchanted creature gets +1/+1.| -Sheltered by Ghosts|Duskmourn: House of Horror|30|U|{1}{W}|Enchantment - Aura|||Enchant creature you control$When Sheltered by Ghosts enters, exile target nonland permanent an opponent controls until Sheltered by Ghosts leaves the battlefield.$Enchanted creature gets +1/+0 and has lifelink and ward {2}.| +Shardmage's Rescue|Duskmourn: House of Horror|29|U|{W}|Enchantment - Aura|||Flash$Enchant creature you control$As long as this Aura entered this turn, enchanted creature has hexproof.$Enchanted creature gets +1/+1.| +Sheltered by Ghosts|Duskmourn: House of Horror|30|U|{1}{W}|Enchantment - Aura|||Enchant creature you control$When this Aura enters, exile target nonland permanent an opponent controls until this Aura leaves the battlefield.$Enchanted creature gets +1/+0 and has lifelink and ward {2}.| Shepherding Spirits|Duskmourn: House of Horror|31|C|{4}{W}{W}|Creature - Spirit|4|5|Flying$Plainscycling {2}| Split Up|Duskmourn: House of Horror|32|R|{1}{W}{W}|Sorcery|||Choose one --$* Destroy all tapped creatures.$* Destroy all untapped creatures.| -Splitskin Doll|Duskmourn: House of Horror|33|U|{1}{W}|Artifact Creature - Toy|2|1|When Splitskin Doll enters, draw a card. Then discard a card unless you control another creature with power 2 or less.| -Surgical Suite // Hospital Room|Duskmourn: House of Horror|34|U|{1}{W}|Enchantment - Room|||(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$When you unlock this door, return target creature card with mana value 3 or less from your graveyard to the battlefield.$Hospital Room${3}{W}$Enchantment -- Room$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Whenever you attack, put a +1/+1 counter on target attacking creature.| -Toby, Beastie Befriender|Duskmourn: House of Horror|35|R|{2}{W}|Legendary Creature - Human Wizard|1|1|When Toby, Beastie Befriender enters, create a 4/4 white Beast creature token with "This creature can't attack or block alone."$As long as you control four or more creature tokens, creature tokens you control have flying.| -Trapped in the Screen|Duskmourn: House of Horror|36|C|{2}{W}|Enchantment|||Ward {2}$When Trapped in the Screen enters, exile target artifact, creature, or enchantment an opponent controls until Trapped in the Screen leaves the battlefield.| -Unidentified Hovership|Duskmourn: House of Horror|37|R|{1}{W}{W}|Artifact - Vehicle|2|2|Flying$When Unidentified Hovership enters, exile up to one target creature with toughness 5 or less.$When Unidentified Hovership leaves the battlefield, the exiled card's owner manifests dread.$Crew 1| -Unsettling Twins|Duskmourn: House of Horror|38|C|{3}{W}|Creature - Human|2|2|When Unsettling Twins enters, manifest dread.| +Splitskin Doll|Duskmourn: House of Horror|33|U|{1}{W}|Artifact Creature - Toy|2|1|When this creature enters, draw a card. Then discard a card unless you control another creature with power 2 or less.| +Surgical Suite // Hospital Room|Duskmourn: House of Horror|34|U|{1}{W}|Enchantment - Room|||When you unlock this door, return target creature card with mana value 3 or less from your graveyard to the battlefield.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Hospital Room${3}{W}$Enchantment -- Room$Whenever you attack, put a +1/+1 counter on target attacking creature.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| +Toby, Beastie Befriender|Duskmourn: House of Horror|35|R|{2}{W}|Legendary Creature - Human Wizard|1|1|When Toby enters, create a 4/4 white Beast creature token with "This token can't attack or block alone."$As long as you control four or more creature tokens, creature tokens you control have flying.| +Trapped in the Screen|Duskmourn: House of Horror|36|C|{2}{W}|Enchantment|||Ward {2}$When this enchantment enters, exile target artifact, creature, or enchantment an opponent controls until this enchantment leaves the battlefield.| +Unidentified Hovership|Duskmourn: House of Horror|37|R|{1}{W}{W}|Artifact - Vehicle|2|2|Flying$When this Vehicle enters, exile up to one target creature with toughness 5 or less.$When this Vehicle leaves the battlefield, the exiled card's owner manifests dread.$Crew 1| +Unsettling Twins|Duskmourn: House of Horror|38|C|{3}{W}|Creature - Human|2|2|When this creature enters, manifest dread.| Unwanted Remake|Duskmourn: House of Horror|39|U|{W}|Instant|||Destroy target creature. Its controller manifests dread.| -Veteran Survivor|Duskmourn: House of Horror|40|U|{W}|Creature - Human Survivor|2|1|Survival -- At the beginning of your second main phase, if Veteran Survivor is tapped, exile up to one target card from a graveyard.$As long as there are three or more cards exiled with Veteran Survivor, it gets +3/+3 and has hexproof.| +Veteran Survivor|Duskmourn: House of Horror|40|U|{W}|Creature - Human Survivor|2|1|Survival -- At the beginning of your second main phase, if this creature is tapped, exile up to one target card from a graveyard.$As long as there are three or more cards exiled with this creature, it gets +3/+3 and has hexproof.| The Wandering Rescuer|Duskmourn: House of Horror|41|M|{3}{W}{W}|Legendary Creature - Human Samurai Noble|3|4|Flash$Convoke$Double strike$Other tapped creatures you control have hexproof.| Abhorrent Oculus|Duskmourn: House of Horror|42|M|{2}{U}|Creature - Eye|5|5|As an additional cost to cast this spell, exile six cards from your graveyard.$Flying$At the beginning of each opponent's upkeep, manifest dread.| -Bottomless Pool // Locker Room|Duskmourn: House of Horror|43|U|{U}|Enchantment - Room|||(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$When you unlock this door, return up to one target creature to its owner's hand.$Locker Room${4}{U}$Enchantment -- Room$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Whenever one or more creatures you control deal combat damage to a player, draw a card.| -Central Elevator // Promising Stairs|Duskmourn: House of Horror|44|R|{3}{U}|Enchantment - Room|||(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$When you unlock this door, search your library for a Room card that doesn't have the same name as a Room you control, reveal it, put it into your hand, then shuffle.$Promising Stairs${2}{U}$Enchantment -- Room$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$At the beginning of your upkeep, surveil 1. You win the game if there are eight or more different names among unlocked doors of Rooms you control.| -Clammy Prowler|Duskmourn: House of Horror|45|C|{3}{U}|Enchantment Creature - Horror|2|5|Whenever Clammy Prowler attacks, another target attacking creature can't be blocked this turn.| +Bottomless Pool // Locker Room|Duskmourn: House of Horror|43|U|{U}|Enchantment - Room|||When you unlock this door, return up to one target creature to its owner's hand.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Locker Room${4}{U}$Enchantment -- Room$Whenever one or more creatures you control deal combat damage to a player, draw a card.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| +Central Elevator // Promising Stairs|Duskmourn: House of Horror|44|R|{3}{U}|Enchantment - Room|||When you unlock this door, search your library for a Room card that doesn't have the same name as a Room you control, reveal it, put it into your hand, then shuffle.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Promising Stairs${2}{U}$Enchantment -- Room$At the beginning of your upkeep, surveil 1. You win the game if there are eight or more different names among unlocked doors of Rooms you control.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| +Clammy Prowler|Duskmourn: House of Horror|45|C|{3}{U}|Enchantment Creature - Horror|2|5|Whenever this creature attacks, another target attacking creature can't be blocked this turn.| Creeping Peeper|Duskmourn: House of Horror|46|C|{1}{U}|Creature - Eye|2|1|{T}: Add {U}. Spend this mana only to cast an enchantment spell, unlock a door, or turn a permanent face up.| -Cursed Windbreaker|Duskmourn: House of Horror|47|U|{2}{U}|Artifact - Equipment|||When Cursed Windbreaker enters, manifest dread, then attach Cursed Windbreaker to that creature.$Equipped creature has flying.$Equip {3}| +Cursed Windbreaker|Duskmourn: House of Horror|47|U|{2}{U}|Artifact - Equipment|||When this Equipment enters, manifest dread, then attach this Equipment to that creature.$Equipped creature has flying.$Equip {3}| Daggermaw Megalodon|Duskmourn: House of Horror|48|C|{4}{U}{U}|Creature - Shark|5|7|Vigilance$Islandcycling {2}| Don't Make a Sound|Duskmourn: House of Horror|49|C|{1}{U}|Instant|||Counter target spell unless its controller pays {2}. If they do, surveil 2.| Duskmourn's Domination|Duskmourn: House of Horror|50|U|{4}{U}{U}|Enchantment - Aura|||Enchant creature$You control enchanted creature.$Enchanted creature gets -3/-0 and loses all abilities.| Enduring Curiosity|Duskmourn: House of Horror|51|R|{2}{U}{U}|Enchantment Creature - Cat Glimmer|4|3|Flash$Whenever a creature you control deals combat damage to a player, draw a card.$When Enduring Curiosity dies, if it was a creature, return it to the battlefield under its owner's control. It's an enchantment.| Enter the Enigma|Duskmourn: House of Horror|52|C|{U}|Sorcery|||Target creature can't be blocked this turn.$Draw a card.| Entity Tracker|Duskmourn: House of Horror|53|R|{2}{U}|Creature - Human Scout|2|3|Flash$Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, draw a card.| -Erratic Apparition|Duskmourn: House of Horror|54|C|{2}{U}|Creature - Spirit|1|3|Flying, vigilance$Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, Erratic Apparition gets +1/+1 until end of turn.| -Fear of Failed Tests|Duskmourn: House of Horror|55|U|{4}{U}|Enchantment Creature - Nightmare|2|7|Whenever Fear of Failed Tests deals combat damage to a player, draw that many cards.| -Fear of Falling|Duskmourn: House of Horror|56|U|{3}{U}{U}|Enchantment Creature - Nightmare|4|4|Flying$Whenever Fear of Falling attacks, target creature defending player controls gets -2/-0 and loses flying until your next turn.| -Fear of Impostors|Duskmourn: House of Horror|57|U|{1}{U}{U}|Enchantment Creature - Nightmare|3|2|Flash$When Fear of Impostors enters, counter target spell. Its controller manifests dread.| +Erratic Apparition|Duskmourn: House of Horror|54|C|{2}{U}|Creature - Spirit|1|3|Flying, vigilance$Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, this creature gets +1/+1 until end of turn.| +Fear of Failed Tests|Duskmourn: House of Horror|55|U|{4}{U}|Enchantment Creature - Nightmare|2|7|Whenever this creature deals combat damage to a player, draw that many cards.| +Fear of Falling|Duskmourn: House of Horror|56|U|{3}{U}{U}|Enchantment Creature - Nightmare|4|4|Flying$Whenever this creature attacks, target creature defending player controls gets -2/-0 and loses flying until your next turn.| +Fear of Impostors|Duskmourn: House of Horror|57|U|{1}{U}{U}|Enchantment Creature - Nightmare|3|2|Flash$When this creature enters, counter target spell. Its controller manifests dread.| Fear of Isolation|Duskmourn: House of Horror|58|U|{1}{U}|Enchantment Creature - Nightmare|2|3|As an additional cost to cast this spell, return a permanent you control to its owner's hand.$Flying| -Floodpits Drowner|Duskmourn: House of Horror|59|U|{1}{U}|Creature - Merfolk|2|1|Flash$Vigilance$When Floodpits Drowner enters, tap target creature an opponent controls and put a stun counter on it.${1}{U}, {T}: Shuffle Floodpits Drowner and target creature with a stun counter on it into their owners' libraries.| +Floodpits Drowner|Duskmourn: House of Horror|59|U|{1}{U}|Creature - Merfolk|2|1|Flash$Vigilance$When this creature enters, tap target creature an opponent controls and put a stun counter on it.${1}{U}, {T}: Shuffle this creature and target creature with a stun counter on it into their owners' libraries.| Get Out|Duskmourn: House of Horror|60|U|{U}{U}|Instant|||Choose one --$* Counter target creature or enchantment spell.$* Return one or two target creatures and/or enchantments you own to your hand.| -Ghostly Keybearer|Duskmourn: House of Horror|61|U|{3}{U}|Creature - Spirit|3|3|Flying$Whenever Ghostly Keybearer deals combat damage to a player, unlock a locked door of up to one target Room you control.| +Ghostly Keybearer|Duskmourn: House of Horror|61|U|{3}{U}|Creature - Spirit|3|3|Flying$Whenever this creature deals combat damage to a player, unlock a locked door of up to one target Room you control.| Glimmerburst|Duskmourn: House of Horror|62|C|{3}{U}|Instant|||Draw two cards. Create a 1/1 white Glimmer enchantment creature token.| -Leyline of Transformation|Duskmourn: House of Horror|63|R|{2}{U}{U}|Enchantment|||If Leyline of Transformation is in your opening hand, you may begin the game with it on the battlefield.$As Leyline of Transformation enters, choose a creature type.$Creatures you control are the chosen type in addition to their other types. The same is true for creature spells you control and creature cards you own that aren't on the battlefield.| +Leyline of Transformation|Duskmourn: House of Horror|63|R|{2}{U}{U}|Enchantment|||If this card is in your opening hand, you may begin the game with it on the battlefield.$As this enchantment enters, choose a creature type.$Creatures you control are the chosen type in addition to their other types. The same is true for creature spells you control and creature cards you own that aren't on the battlefield.| Marina Vendrell's Grimoire|Duskmourn: House of Horror|64|R|{5}{U}|Legendary Artifact|||When Marina Vendrell's Grimoire enters, if you cast it, draw five cards.$You have no maximum hand size and don't lose the game for having 0 or less life.$Whenever you gain life, draw that many cards.$Whenever you lose life, discard that many cards. Then if you have no cards in hand, you lose the game.| -Meat Locker // Drowned Diner|Duskmourn: House of Horror|65|C|{2}{U}|Enchantment - Room|||(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$When you unlock this door, tap up to one target creature and put two stun counters on it.$Drowned Diner${3}{U}{U}$Enchantment -- Room$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$When you unlock this door, draw three cards, then discard a card.| +Meat Locker // Drowned Diner|Duskmourn: House of Horror|65|C|{2}{U}|Enchantment - Room|||When you unlock this door, tap up to one target creature and put two stun counters on it.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Drowned Diner${3}{U}{U}$Enchantment -- Room$When you unlock this door, draw three cards, then discard a card.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| The Mindskinner|Duskmourn: House of Horror|66|R|{U}{U}{U}|Legendary Enchantment Creature - Nightmare|10|1|The Mindskinner can't be blocked.$If a source you control would deal damage to an opponent, prevent that damage and each opponent mills that many cards.| -Mirror Room // Fractured Realm|Duskmourn: House of Horror|67|M|{2}{U}|Enchantment - Room|||(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$When you unlock this door, create a token that's a copy of target creature you control, except it's a Reflection in addition to its other creature types.$Fractured Realm${5}{U}{U}$Enchantment -- Room$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$If a triggered ability of a permanent you control triggers, that ability triggers an additional time.| -Overlord of the Floodpits|Duskmourn: House of Horror|68|M|{3}{U}{U}|Enchantment Creature - Avatar Horror|5|3|Impending 4--{1}{U}{U}$Flying$Whenever Overlord of the Floodpits enters or attacks, draw two cards, then discard a card.| +Mirror Room // Fractured Realm|Duskmourn: House of Horror|67|M|{2}{U}|Enchantment - Room|||When you unlock this door, create a token that's a copy of target creature you control, except it's a Reflection in addition to its other creature types.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Fractured Realm${5}{U}{U}$Enchantment -- Room$If a triggered ability of a permanent you control triggers, that ability triggers an additional time.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| +Overlord of the Floodpits|Duskmourn: House of Horror|68|M|{3}{U}{U}|Enchantment Creature - Avatar Horror|5|3|Impending 4--{1}{U}{U}$Flying$Whenever this permanent enters or attacks, draw two cards, then discard a card.| Paranormal Analyst|Duskmourn: House of Horror|69|U|{1}{U}|Creature - Human Detective|1|3|Whenever you manifest dread, put a card you put into your graveyard this way into your hand.| -Piranha Fly|Duskmourn: House of Horror|70|C|{1}{U}|Creature - Fish Insect|2|1|Flying$Piranha Fly enters tapped.| +Piranha Fly|Duskmourn: House of Horror|70|C|{1}{U}|Creature - Fish Insect|2|1|Flying$This creature enters tapped.| Scrabbling Skullcrab|Duskmourn: House of Horror|71|U|{U}|Creature - Crab Skeleton|0|3|Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, target player mills two cards.| -Silent Hallcreeper|Duskmourn: House of Horror|72|R|{1}{U}|Enchantment Creature - Horror|1|1|Silent Hallcreeper can't be blocked.$Whenever Silent Hallcreeper deals combat damage to a player, choose one that hasn't been chosen --$* Put two +1/+1 counters on Silent Hallcreeper.$* Draw a card.$* Silent Hallcreeper becomes a copy of another target creature you control.| -Stalked Researcher|Duskmourn: House of Horror|73|C|{1}{U}|Creature - Human Wizard|3|3|Defender$Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, Stalked Researcher can attack this turn as though it didn't have defender.| -Stay Hidden, Stay Silent|Duskmourn: House of Horror|74|U|{1}{U}|Enchantment - Aura|||Enchant creature$When Stay Hidden, Stay Silent enters, tap enchanted creature.$Enchanted creature doesn't untap during its controller's untap step.${4}{U}{U}: Shuffle enchanted creature into its owner's library, then manifest dread. Activate only as a sorcery.| +Silent Hallcreeper|Duskmourn: House of Horror|72|R|{1}{U}|Enchantment Creature - Horror|1|1|This creature can't be blocked.$Whenever this creature deals combat damage to a player, choose one that hasn't been chosen --$* Put two +1/+1 counters on this creature.$* Draw a card.$* This creature becomes a copy of another target creature you control.| +Stalked Researcher|Duskmourn: House of Horror|73|C|{1}{U}|Creature - Human Wizard|3|3|Defender$Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, this creature can attack this turn as though it didn't have defender.| +Stay Hidden, Stay Silent|Duskmourn: House of Horror|74|U|{1}{U}|Enchantment - Aura|||Enchant creature$When this Aura enters, tap enchanted creature.$Enchanted creature doesn't untap during its controller's untap step.${4}{U}{U}: Shuffle enchanted creature into its owner's library, then manifest dread. Activate only as a sorcery.| The Tale of Tamiyo|Duskmourn: House of Horror|75|R|{2}{U}|Legendary Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.)$I, II, III -- Mill two cards. If two cards that share a card type were milled this way, draw a card and repeat this process.$IV -- Exile any number of target instant, sorcery, and/or Tamiyo planeswalker cards from your graveyard. Copy them. You may cast any number of the copies.| -Tunnel Surveyor|Duskmourn: House of Horror|76|C|{2}{U}|Creature - Human Detective|2|2|When Tunnel Surveyor enters, create a 1/1 white Glimmer enchantment creature token.| +Tunnel Surveyor|Duskmourn: House of Horror|76|C|{2}{U}|Creature - Human Detective|2|2|When this creature enters, create a 1/1 white Glimmer enchantment creature token.| Twist Reality|Duskmourn: House of Horror|77|C|{1}{U}{U}|Instant|||Choose one --$* Counter target spell.$* Manifest dread.| Unable to Scream|Duskmourn: House of Horror|78|C|{U}|Enchantment - Aura|||Enchant creature$Enchanted creature loses all abilities and is a Toy artifact creature with base power and toughness 0/2 in addition to its other types.$As long as enchanted creature is face down, it can't be turned face up.| -Underwater Tunnel // Slimy Aquarium|Duskmourn: House of Horror|79|C|{U}|Enchantment - Room|||(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$When you unlock this door, surveil 2.$Slimy Aquarium${3}{U}$Enchantment -- Room$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$When you unlock this door, manifest dread, then put a +1/+1 counter on that creature.| +Underwater Tunnel // Slimy Aquarium|Duskmourn: House of Horror|79|C|{U}|Enchantment - Room|||When you unlock this door, surveil 2.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Slimy Aquarium${3}{U}$Enchantment -- Room$When you unlock this door, manifest dread, then put a +1/+1 counter on that creature.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| Unnerving Grasp|Duskmourn: House of Horror|80|U|{2}{U}|Sorcery|||Return up to one target nonland permanent to its owner's hand. Manifest dread.| -Unwilling Vessel|Duskmourn: House of Horror|81|U|{2}{U}|Creature - Human Wizard|3|2|Vigilance$Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, put a possession counter on Unwilling Vessel.$When Unwilling Vessel dies, create an X/X blue Spirit creature token with flying, where X is the number of counters on Unwilling Vessel.| -Vanish from Sight|Duskmourn: House of Horror|82|C|{3}{U}|Instant|||Target nonland permanent's owner puts it on the top or bottom of their library. Surveil 1.| -Appendage Amalgam|Duskmourn: House of Horror|83|C|{2}{B}|Enchantment Creature - Horror|3|2|Flash$Whenever Appendage Amalgam attacks, surveil 1.| +Unwilling Vessel|Duskmourn: House of Horror|81|U|{2}{U}|Creature - Human Wizard|3|2|Vigilance$Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, put a possession counter on this creature.$When this creature dies, create an X/X blue Spirit creature token with flying, where X is the number of counters on this creature.| +Vanish from Sight|Duskmourn: House of Horror|82|C|{3}{U}|Instant|||Target nonland permanent's owner puts it on their choice of the top or bottom of their library. Surveil 1.| +Appendage Amalgam|Duskmourn: House of Horror|83|C|{2}{B}|Enchantment Creature - Horror|3|2|Flash$Whenever this creature attacks, surveil 1.| Balemurk Leech|Duskmourn: House of Horror|84|C|{1}{B}|Creature - Leech|2|2|Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, each opponent loses 1 life.| -Cackling Slasher|Duskmourn: House of Horror|85|C|{3}{B}|Creature - Human Assassin|3|3|Deathtouch$Cackling Slasher enters with a +1/+1 counter on it if a creature died this turn.| +Cackling Slasher|Duskmourn: House of Horror|85|C|{3}{B}|Creature - Human Assassin|3|3|Deathtouch$This creature enters with a +1/+1 counter on it if a creature died this turn.| Come Back Wrong|Duskmourn: House of Horror|86|R|{2}{B}|Sorcery|||Destroy target creature. If a creature card is put into a graveyard this way, return it to the battlefield under your control. Sacrifice it at the beginning of your next end step.| Commune with Evil|Duskmourn: House of Horror|87|U|{2}{B}|Sorcery|||Look at the top four cards of your library. Put one of them into your hand and the rest into your graveyard. You gain 3 life.| -Cracked Skull|Duskmourn: House of Horror|88|C|{2}{B}|Enchantment - Aura|||Enchant creature$When Cracked Skull enters, look at target player's hand. You may choose a nonland card from it. That player discards that card.$When enchanted creature is dealt damage, destroy it.| -Cynical Loner|Duskmourn: House of Horror|89|U|{1}{B}|Creature - Human Survivor|3|1|Cynical Loner can't be blocked by Glimmers.$Survival -- At the beginning of your second main phase, if Cynical Loner is tapped, you may search your library for a card, put it into your graveyard, then shuffle.| -Dashing Bloodsucker|Duskmourn: House of Horror|90|U|{3}{B}|Creature - Vampire Warrior|2|5|Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, Dashing Bloodsucker gets +2/+0 and gains lifelink until end of turn.| -Defiled Crypt // Cadaver Lab|Duskmourn: House of Horror|91|U|{3}{B}|Enchantment - Room|||(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Whenever one or more cards leave your graveyard, create a 2/2 black Horror enchantment creature token. This ability triggers only once each turn.$Cadaver Lab${B}$Enchantment -- Room$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$When you unlock this door, return target creature card from your graveyard to your hand.| +Cracked Skull|Duskmourn: House of Horror|88|C|{2}{B}|Enchantment - Aura|||Enchant creature$When this Aura enters, look at target player's hand. You may choose a nonland card from it. That player discards that card.$When enchanted creature is dealt damage, destroy it.| +Cynical Loner|Duskmourn: House of Horror|89|U|{1}{B}|Creature - Human Survivor|3|1|This creature can't be blocked by Glimmers.$Survival -- At the beginning of your second main phase, if this creature is tapped, you may search your library for a card, put it into your graveyard, then shuffle.| +Dashing Bloodsucker|Duskmourn: House of Horror|90|U|{3}{B}|Creature - Vampire Warrior|2|5|Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, this creature gets +2/+0 and gains lifelink until end of turn.| +Defiled Crypt // Cadaver Lab|Duskmourn: House of Horror|91|U|{3}{B}|Enchantment - Room|||Whenever one or more cards leave your graveyard, create a 2/2 black Horror enchantment creature token. This ability triggers only once each turn.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Cadaver Lab${B}$Enchantment -- Room$When you unlock this door, return target creature card from your graveyard to your hand.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| Demonic Counsel|Duskmourn: House of Horror|92|R|{1}{B}|Sorcery|||Search your library for a Demon card, reveal it, put it into your hand, then shuffle.$Delirium -- If there are four or more card types among cards in your graveyard, instead search your library for any card, put it into your hand, then shuffle.| -Derelict Attic // Widow's Walk|Duskmourn: House of Horror|93|C|{2}{B}|Enchantment - Room|||(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$When you unlock this door, you draw two cards and you lose 2 life.$Widow's Walk${3}{B}$Enchantment -- Room$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Whenever a creature you control attacks alone, it gets +1/+0 and gains deathtouch until end of turn.| -Doomsday Excruciator|Duskmourn: House of Horror|94|R|{B}{B}{B}{B}{B}{B}|Creature - Demon|6|6|Flying$When Doomsday Excruciator enters, if it was cast, each player exiles all but the bottom six cards of their library face down.$At the beginning of your upkeep, draw a card.| +Derelict Attic // Widow's Walk|Duskmourn: House of Horror|93|C|{2}{B}|Enchantment - Room|||When you unlock this door, you draw two cards and you lose 2 life.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Widow's Walk${3}{B}$Enchantment -- Room$Whenever a creature you control attacks alone, it gets +1/+0 and gains deathtouch until end of turn.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| +Doomsday Excruciator|Duskmourn: House of Horror|94|R|{B}{B}{B}{B}{B}{B}|Creature - Demon|6|6|Flying$When this creature enters, if it was cast, each player exiles all but the bottom six cards of their library face down.$At the beginning of your upkeep, draw a card.| Enduring Tenacity|Duskmourn: House of Horror|95|R|{2}{B}{B}|Enchantment Creature - Snake Glimmer|4|3|Whenever you gain life, target opponent loses that much life.$When Enduring Tenacity dies, if it was a creature, return it to the battlefield under its owner's control. It's an enchantment.| -Fanatic of the Harrowing|Duskmourn: House of Horror|96|C|{3}{B}|Creature - Human Cleric|2|2|When Fanatic of the Harrowing enters, each player discards a card. If you discarded a card this way, draw a card.| -Fear of Lost Teeth|Duskmourn: House of Horror|97|C|{B}|Enchantment Creature - Nightmare|1|1|When Fear of Lost Teeth dies, it deals 1 damage to any target and you gain 1 life.| -Fear of the Dark|Duskmourn: House of Horror|98|C|{4}{B}|Enchantment Creature - Nightmare|5|5|Whenever Fear of the Dark attacks, if defending player controls no Glimmer creatures, it gains menace and deathtouch until end of turn.| +Fanatic of the Harrowing|Duskmourn: House of Horror|96|C|{3}{B}|Creature - Human Cleric|2|2|When this creature enters, each player discards a card. If you discarded a card this way, draw a card.| +Fear of Lost Teeth|Duskmourn: House of Horror|97|C|{B}|Enchantment Creature - Nightmare|1|1|When this creature dies, it deals 1 damage to any target and you gain 1 life.| +Fear of the Dark|Duskmourn: House of Horror|98|C|{4}{B}|Enchantment Creature - Nightmare|5|5|Whenever this creature attacks, if defending player controls no Glimmer creatures, it gains menace and deathtouch until end of turn.| Final Vengeance|Duskmourn: House of Horror|99|C|{B}|Sorcery|||As an additional cost to cast this spell, sacrifice a creature or enchantment.$Exile target creature.| -Funeral Room // Awakening Hall|Duskmourn: House of Horror|100|M|{2}{B}|Enchantment - Room|||(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Whenever a creature you control dies, each opponent loses 1 life and you gain 1 life.$Awakening Hall${6}{B}{B}$Enchantment -- Room$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$When you unlock this door, return all creature cards from your graveyard to the battlefield.| +Funeral Room // Awakening Hall|Duskmourn: House of Horror|100|M|{2}{B}|Enchantment - Room|||Whenever a creature you control dies, each opponent loses 1 life and you gain 1 life.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Awakening Hall${6}{B}{B}$Enchantment -- Room$When you unlock this door, return all creature cards from your graveyard to the battlefield.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| Give In to Violence|Duskmourn: House of Horror|101|C|{1}{B}|Instant|||Target creature gets +2/+2 and gains lifelink until end of turn.| Grievous Wound|Duskmourn: House of Horror|102|R|{3}{B}{B}|Enchantment - Aura|||Enchant player$Enchanted player can't gain life.$Whenever enchanted player is dealt damage, they lose half their life, rounded up.| -Innocuous Rat|Duskmourn: House of Horror|103|C|{1}{B}|Creature - Rat|1|1|When Innocuous Rat dies, manifest dread.| -Killer's Mask|Duskmourn: House of Horror|104|U|{2}{B}|Artifact - Equipment|||When Killer's Mask enters, manifest dread, then attach Killer's Mask to that creature.$Equipped creature has menace.$Equip {2}| +Innocuous Rat|Duskmourn: House of Horror|103|C|{1}{B}|Creature - Rat|1|1|When this creature dies, manifest dread.| +Killer's Mask|Duskmourn: House of Horror|104|U|{2}{B}|Artifact - Equipment|||When this Equipment enters, manifest dread, then attach this Equipment to that creature.$Equipped creature has menace.$Equip {2}| Let's Play a Game|Duskmourn: House of Horror|105|U|{3}{B}|Sorcery|||Delirium -- Choose one. If there are four or more card types among cards in your graveyard, choose one or more instead.$* Creatures your opponents control get -1/-1 until end of turn.$* Each opponent discards two cards.$* Each opponent loses 3 life and you gain 3 life.| -Leyline of the Void|Duskmourn: House of Horror|106|R|{2}{B}{B}|Enchantment|||If Leyline of the Void is in your opening hand, you may begin the game with it on the battlefield.$If a card would be put into an opponent's graveyard from anywhere, exile it instead.| +Leyline of the Void|Duskmourn: House of Horror|106|R|{2}{B}{B}|Enchantment|||If this card is in your opening hand, you may begin the game with it on the battlefield.$If a card would be put into an opponent's graveyard from anywhere, exile it instead.| Live or Die|Duskmourn: House of Horror|107|U|{3}{B}{B}|Instant|||Choose one --$* Return target creature card from your graveyard to the battlefield.$* Destroy target creature.| -Meathook Massacre II|Duskmourn: House of Horror|108|M|{X}{X}{B}{B}{B}{B}|Legendary Enchantment|||When Meathook Massacre II enters, each player sacrifices X creatures.$Whenever a creature you control dies, you may pay 3 life. If you do, return that card under your control with a finality counter on it.$Whenever a creature an opponent controls dies, they may pay 3 life. If they don't, return that card under your control with a finality counter on it.| -Miasma Demon|Duskmourn: House of Horror|109|U|{4}{B}{B}|Creature - Demon|5|4|Flying$When Miasma Demon enters, you may discard any number of cards. When you do, up to that many target creatures each get -2/-2 until end of turn.| +Meathook Massacre II|Duskmourn: House of Horror|108|M|{X}{X}{B}{B}{B}{B}|Legendary Enchantment|||When Meathook Massacre II enters, each player sacrifices X creatures of their choice.$Whenever a creature you control dies, you may pay 3 life. If you do, return that card under your control with a finality counter on it.$Whenever a creature an opponent controls dies, they may pay 3 life. If they don't, return that card under your control with a finality counter on it.| +Miasma Demon|Duskmourn: House of Horror|109|U|{4}{B}{B}|Creature - Demon|5|4|Flying$When this creature enters, you may discard any number of cards. When you do, up to that many target creatures each get -2/-2 until end of turn.| Murder|Duskmourn: House of Horror|110|C|{1}{B}{B}|Instant|||Destroy target creature.| -Nowhere to Run|Duskmourn: House of Horror|111|U|{1}{B}|Enchantment|||Flash$When Nowhere to Run enters, target creature an opponent controls gets -3/-3 until end of turn.$Creatures your opponents control can be the targets of spells and abilities as though they didn't have hexproof. Ward abilities of those creatures don't trigger.| -Osseous Sticktwister|Duskmourn: House of Horror|112|U|{1}{B}|Artifact Creature - Scarecrow|2|2|Lifelink$Delirium -- At the beginning of your end step, if there are four or more card types among cards in your graveyard, each opponent may sacrifice a nonland permanent or discard a card. Then Osseous Sticktwister deals damage equal to its power to each opponent who didn't sacrifice a permanent or discard a card this way.| -Overlord of the Balemurk|Duskmourn: House of Horror|113|M|{3}{B}{B}|Enchantment Creature - Avatar Horror|5|5|Impending 5--{1}{B}$Whenever Overlord of the Balemurk enters or attacks, mill four cards, then you may return a non-Avatar creature card or a planeswalker card from your graveyard to your hand.| -Popular Egotist|Duskmourn: House of Horror|114|U|{2}{B}|Creature - Human Rogue|3|2|{1}{B}, Sacrifice another creature or enchantment: Popular Egotist gains indestructible until end of turn. Tap it.$Whenever you sacrifice a permanent, target opponent loses 1 life and you gain 1 life.| -Resurrected Cultist|Duskmourn: House of Horror|115|C|{2}{B}|Creature - Human Cleric|4|1|Delirium -- {2}{B}{B}: Return Resurrected Cultist from your graveyard to the battlefield with a finality counter on it. Activate only if there are four or more card types among cards in your graveyard and only as a sorcery.| +Nowhere to Run|Duskmourn: House of Horror|111|U|{1}{B}|Enchantment|||Flash$When this enchantment enters, target creature an opponent controls gets -3/-3 until end of turn.$Creatures your opponents control can be the targets of spells and abilities as though they didn't have hexproof. Ward abilities of those creatures don't trigger.| +Osseous Sticktwister|Duskmourn: House of Horror|112|U|{1}{B}|Artifact Creature - Scarecrow|2|2|Lifelink$Delirium -- At the beginning of your end step, if there are four or more card types among cards in your graveyard, each opponent may sacrifice a nonland permanent of their choice or discard a card. Then this creature deals damage equal to its power to each opponent who didn't sacrifice a permanent or discard a card this way.| +Overlord of the Balemurk|Duskmourn: House of Horror|113|M|{3}{B}{B}|Enchantment Creature - Avatar Horror|5|5|Impending 5--{1}{B}$Whenever this permanent enters or attacks, mill four cards, then you may return a non-Avatar creature card or a planeswalker card from your graveyard to your hand.| +Popular Egotist|Duskmourn: House of Horror|114|U|{2}{B}|Creature - Human Rogue|3|2|{1}{B}, Sacrifice another creature or enchantment: This creature gains indestructible until end of turn. Tap it.$Whenever you sacrifice a permanent, target opponent loses 1 life and you gain 1 life.| +Resurrected Cultist|Duskmourn: House of Horror|115|C|{2}{B}|Creature - Human Cleric|4|1|Delirium -- {2}{B}{B}: Return this card from your graveyard to the battlefield with a finality counter on it. Activate only if there are four or more card types among cards in your graveyard and only as a sorcery.| Spectral Snatcher|Duskmourn: House of Horror|116|C|{4}{B}{B}|Creature - Spirit|6|5|Ward--Discard a card.$Swampcycling {2}| -Sporogenic Infection|Duskmourn: House of Horror|117|U|{1}{B}|Enchantment - Aura|||Enchant creature$When Sporogenic Infection enters, target player sacrifices a creature other than enchanted creature.$When enchanted creature is dealt damage, destroy it.| -Unholy Annex // Ritual Chamber|Duskmourn: House of Horror|118|R|{2}{B}|Enchantment - Room|||(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$At the beginning of your end step, draw a card. If you control a Demon, each opponent loses 2 life and you gain 2 life. Otherwise, you lose 2 life.$Ritual Chamber${3}{B}{B}$Enchantment -- Room$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$When you unlock this door, create a 6/6 black Demon creature token with flying.| -Unstoppable Slasher|Duskmourn: House of Horror|119|R|{2}{B}|Creature - Zombie Assassin|2|3|Deathtouch$Whenever Unstoppable Slasher deals combat damage to a player, they lose half their life, rounded up.$When Unstoppable Slasher dies, if it had no counters on it, return it to the battlefield tapped under its owner's control with two stun counters on it.| +Sporogenic Infection|Duskmourn: House of Horror|117|U|{1}{B}|Enchantment - Aura|||Enchant creature$When this Aura enters, target player sacrifices a creature of their choice other than enchanted creature.$When enchanted creature is dealt damage, destroy it.| +Unholy Annex // Ritual Chamber|Duskmourn: House of Horror|118|R|{2}{B}|Enchantment - Room|||At the beginning of your end step, draw a card. If you control a Demon, each opponent loses 2 life and you gain 2 life. Otherwise, you lose 2 life.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Ritual Chamber${3}{B}{B}$Enchantment -- Room$When you unlock this door, create a 6/6 black Demon creature token with flying.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| +Unstoppable Slasher|Duskmourn: House of Horror|119|R|{2}{B}|Creature - Zombie Assassin|2|3|Deathtouch$Whenever this creature deals combat damage to a player, they lose half their life, rounded up.$When this creature dies, if it had no counters on it, return it to the battlefield tapped under its owner's control with two stun counters on it.| Valgavoth, Terror Eater|Duskmourn: House of Horror|120|M|{6}{B}{B}{B}|Legendary Creature - Elder Demon|9|9|Flying, lifelink$Ward--Sacrifice three nonland permanents.$If a card you didn't control would be put into an opponent's graveyard from anywhere, exile it instead.$During your turn, you may play cards exiled with Valgavoth. If you cast a spell this way, pay life equal to its mana value rather than pay its mana cost.| -Valgavoth's Faithful|Duskmourn: House of Horror|121|U|{B}|Creature - Human Cleric|1|1|{3}{B}, Sacrifice Valgavoth's Faithful: Return target creature card from your graveyard to the battlefield. Activate only as a sorcery.| -Vile Mutilator|Duskmourn: House of Horror|122|U|{5}{B}{B}|Creature - Demon|6|5|As an additional cost to cast this spell, sacrifice a creature or enchantment.$Flying, trample$When Vile Mutilator enters, each opponent sacrifices a nontoken enchantment, then sacrifices a nontoken creature.| +Valgavoth's Faithful|Duskmourn: House of Horror|121|U|{B}|Creature - Human Cleric|1|1|{3}{B}, Sacrifice this creature: Return target creature card from your graveyard to the battlefield. Activate only as a sorcery.| +Vile Mutilator|Duskmourn: House of Horror|122|U|{5}{B}{B}|Creature - Demon|6|5|As an additional cost to cast this spell, sacrifice a creature or enchantment.$Flying, trample$When this creature enters, each opponent sacrifices a nontoken enchantment of their choice, then sacrifices a nontoken creature of their choice.| Winter's Intervention|Duskmourn: House of Horror|123|C|{1}{B}|Instant|||Winter's Intervention deals 2 damage to target creature. You gain 2 life.| Withering Torment|Duskmourn: House of Horror|124|U|{2}{B}|Instant|||Destroy target creature or enchantment. You lose 2 life.| Bedhead Beastie|Duskmourn: House of Horror|125|C|{4}{R}{R}|Creature - Beast|5|6|Menace$Mountaincycling {2}| Betrayer's Bargain|Duskmourn: House of Horror|126|U|{1}{R}|Instant|||As an additional cost to cast this spell, sacrifice a creature or enchantment or pay {2}.$Betrayer's Bargain deals 5 damage to target creature. If that creature would die this turn, exile it instead.| -Boilerbilges Ripper|Duskmourn: House of Horror|127|C|{4}{R}|Creature - Human Assassin|4|4|When Boilerbilges Ripper enters, you may sacrifice another creature or enchantment. When you do, Boilerbilges Ripper deals 2 damage to any target.| -Chainsaw|Duskmourn: House of Horror|128|R|{1}{R}|Artifact - Equipment|||When Chainsaw enters, it deals 3 damage to up to one target creature.$Whenever one or more creatures die, put a rev counter on Chainsaw.$Equipped creature gets +X/+0, where X is the number of rev counters on Chainsaw.$Equip {3}| -Charred Foyer // Warped Space|Duskmourn: House of Horror|129|M|{3}{R}|Enchantment - Room|||(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$At the beginning of your upkeep, exile the top card of your library. You may play that card this turn.$Warped Space${4}{R}{R}$Enchantment -- Room$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Once each turn, you may pay {0} rather than pay the mana cost for a spell you cast from exile.| -Clockwork Percussionist|Duskmourn: House of Horror|130|C|{R}|Artifact Creature - Monkey Toy|1|1|Haste$When Clockwork Percussionist dies, exile the top card of your library. You may play it until the end of your next turn.| -Cursed Recording|Duskmourn: House of Horror|131|R|{2}{R}{R}|Artifact|||Whenever you cast an instant or sorcery spell, put a time counter on Cursed Recording. Then if there are seven or more time counters on it, remove those counters and it deals 20 damage to you.${T}: When you next cast an instant or sorcery spell this turn, copy that spell. You may choose new targets for the copy.| +Boilerbilges Ripper|Duskmourn: House of Horror|127|C|{4}{R}|Creature - Human Assassin|4|4|When this creature enters, you may sacrifice another creature or enchantment. When you do, this creature deals 2 damage to any target.| +Chainsaw|Duskmourn: House of Horror|128|R|{1}{R}|Artifact - Equipment|||When this Equipment enters, it deals 3 damage to up to one target creature.$Whenever one or more creatures die, put a rev counter on this Equipment.$Equipped creature gets +X/+0, where X is the number of rev counters on this Equipment.$Equip {3}| +Charred Foyer // Warped Space|Duskmourn: House of Horror|129|M|{3}{R}|Enchantment - Room|||At the beginning of your upkeep, exile the top card of your library. You may play it this turn.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Warped Space${4}{R}{R}$Enchantment -- Room$Once each turn, you may pay {0} rather than pay the mana cost for a spell you cast from exile.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| +Clockwork Percussionist|Duskmourn: House of Horror|130|C|{R}|Artifact Creature - Monkey Toy|1|1|Haste$When this creature dies, exile the top card of your library. You may play it until the end of your next turn.| +Cursed Recording|Duskmourn: House of Horror|131|R|{2}{R}{R}|Artifact|||Whenever you cast an instant or sorcery spell, put a time counter on this artifact. Then if there are seven or more time counters on it, remove those counters and it deals 20 damage to you.${T}: When you next cast an instant or sorcery spell this turn, copy that spell. You may choose new targets for the copy.| Diversion Specialist|Duskmourn: House of Horror|132|U|{3}{R}|Creature - Human Warrior|4|3|Menace${1}, Sacrifice another creature or enchantment: Exile the top card of your library. You may play it this turn.| Enduring Courage|Duskmourn: House of Horror|133|R|{2}{R}{R}|Enchantment Creature - Dog Glimmer|3|3|Whenever another creature you control enters, it gets +2/+0 and gains haste until end of turn.$When Enduring Courage dies, if it was a creature, return it to the battlefield under its owner's control. It's an enchantment.| -Fear of Being Hunted|Duskmourn: House of Horror|134|U|{1}{R}{R}|Enchantment Creature - Nightmare|4|2|Haste$Fear of Being Hunted must be blocked if able.| -Fear of Burning Alive|Duskmourn: House of Horror|135|U|{4}{R}{R}|Enchantment Creature - Nightmare|4|4|When Fear of Burning Alive enters, it deals 4 damage to each opponent.$Delirium -- Whenever a source you control deals noncombat damage to an opponent, if there are four or more card types among cards in your graveyard, Fear of Burning Alive deals that amount of damage to target creature that player controls.| -Fear of Missing Out|Duskmourn: House of Horror|136|R|{1}{R}|Enchantment Creature - Nightmare|2|3|When Fear of Missing Out enters, discard a card, then draw a card.$Delirium -- Whenever Fear of Missing Out attacks for the first time each turn, if there are four or more card types among cards in your graveyard, untap target creature. After this phase, there is an additional combat phase.| -Glassworks // Shattered Yard|Duskmourn: House of Horror|137|C|{2}{R}|Enchantment - Room|||(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$When you unlock this door, this Room deals 4 damage to target creature an opponent controls.$Shattered Yard${4}{R}$Enchantment -- Room$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$At the beginning of your end step, this Room deals 1 damage to each opponent.| +Fear of Being Hunted|Duskmourn: House of Horror|134|U|{1}{R}{R}|Enchantment Creature - Nightmare|4|2|Haste$This creature must be blocked if able.| +Fear of Burning Alive|Duskmourn: House of Horror|135|U|{4}{R}{R}|Enchantment Creature - Nightmare|4|4|When this creature enters, it deals 4 damage to each opponent.$Delirium -- Whenever a source you control deals noncombat damage to an opponent, if there are four or more card types among cards in your graveyard, this creature deals that amount of damage to target creature that player controls.| +Fear of Missing Out|Duskmourn: House of Horror|136|R|{1}{R}|Enchantment Creature - Nightmare|2|3|When this creature enters, discard a card, then draw a card.$Delirium -- Whenever this creature attacks for the first time each turn, if there are four or more card types among cards in your graveyard, untap target creature. After this phase, there is an additional combat phase.| +Glassworks // Shattered Yard|Duskmourn: House of Horror|137|C|{2}{R}|Enchantment - Room|||When you unlock this door, this Room deals 4 damage to target creature an opponent controls.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Shattered Yard${4}{R}$Enchantment -- Room$At the beginning of your end step, this Room deals 1 damage to each opponent.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| Grab the Prize|Duskmourn: House of Horror|138|C|{1}{R}|Sorcery|||As an additional cost to cast this spell, discard a card.$Draw two cards. If the discarded card wasn't a land card, Grab the Prize deals 2 damage to each opponent.| -Hand That Feeds|Duskmourn: House of Horror|139|C|{1}{R}|Creature - Mutant|2|2|Delirium -- Whenever Hand That Feeds attacks while there are four or more card types among cards in your graveyard, it gets +2/+0 and gains menace until end of turn.| +Hand That Feeds|Duskmourn: House of Horror|139|C|{1}{R}|Creature - Mutant|2|2|Delirium -- Whenever this creature attacks while there are four or more card types among cards in your graveyard, it gets +2/+0 and gains menace until end of turn.| Impossible Inferno|Duskmourn: House of Horror|140|C|{4}{R}|Instant|||Impossible Inferno deals 6 damage to target creature.$Delirium -- If there are four or more card types among cards in your graveyard, exile the top card of your library. You may play it until the end of your next turn.| -Infernal Phantom|Duskmourn: House of Horror|141|U|{3}{R}|Creature - Spirit|2|3|Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, Infernal Phantom gets +2/+0 until end of turn.$When Infernal Phantom dies, it deals damage equal to its power to any target.| +Infernal Phantom|Duskmourn: House of Horror|141|U|{3}{R}|Creature - Spirit|2|3|Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, this creature gets +2/+0 until end of turn.$When this creature dies, it deals damage equal to its power to any target.| Irreverent Gremlin|Duskmourn: House of Horror|142|U|{1}{R}|Creature - Gremlin|2|2|Menace$Whenever another creature you control with power 2 or less enters, you may discard a card. If you do, draw a card. Do this only once each turn.| -Leyline of Resonance|Duskmourn: House of Horror|143|R|{2}{R}{R}|Enchantment|||If Leyline of Resonance is in your opening hand, you may begin the game with it on the battlefield.$Whenever you cast an instant or sorcery spell that targets only a single creature you control, copy that spell. You may choose new targets for the copy.| +A-Leyline of Resonance|Duskmourn: House of Horror|A-143|R|{2}{R}{R}|Enchantment|||If this card is in your opening hand, you may begin the game with it on the battlefield.$Whenever you cast an instant or sorcery spell that targets only a single creature you control, you may pay {1}. If you do, copy that spell. You may choose new targets for the copy.| +Leyline of Resonance|Duskmourn: House of Horror|143|R|{2}{R}{R}|Enchantment|||If this card is in your opening hand, you may begin the game with it on the battlefield.$Whenever you cast an instant or sorcery spell that targets only a single creature you control, copy that spell. You may choose new targets for the copy.| Most Valuable Slayer|Duskmourn: House of Horror|144|C|{3}{R}|Creature - Human Warrior|2|4|Whenever you attack, target attacking creature gets +1/+0 and gains first strike until end of turn.| -Norin, Swift Survivalist|Duskmourn: House of Horror|145|U|{R}|Legendary Creature - Human Coward|2|1|Norin, Swift Survivalist can't block.$Whenever a creature you control becomes blocked, you may exile it. You may play that card from exile this turn.| -Overlord of the Boilerbilges|Duskmourn: House of Horror|146|M|{4}{R}{R}|Enchantment Creature - Avatar Horror|5|5|Impending 4--{2}{R}{R}$Whenever Overlord of the Boilerbilges enters or attacks, it deals 4 damage to any target.| -Painter's Studio // Defaced Gallery|Duskmourn: House of Horror|147|U|{2}{R}|Enchantment - Room|||(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$When you unlock this door, exile the top two cards of your library. You may play them until the end of your next turn.$Defaced Gallery${1}{R}$Enchantment -- Room$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Whenever you attack, attacking creatures you control get +1/+0 until end of turn.| -Piggy Bank|Duskmourn: House of Horror|148|U|{1}{R}|Artifact Creature - Boar Toy|3|2|When Piggy Bank dies, create a Treasure token.| +Norin, Swift Survivalist|Duskmourn: House of Horror|145|U|{R}|Legendary Creature - Human Coward|2|1|Norin can't block.$Whenever a creature you control becomes blocked, you may exile it. You may play that card from exile this turn.| +Overlord of the Boilerbilges|Duskmourn: House of Horror|146|M|{4}{R}{R}|Enchantment Creature - Avatar Horror|5|5|Impending 4--{2}{R}{R}$Whenever this permanent enters or attacks, it deals 4 damage to any target.| +Painter's Studio // Defaced Gallery|Duskmourn: House of Horror|147|U|{2}{R}|Enchantment - Room|||When you unlock this door, exile the top two cards of your library. You may play them until the end of your next turn.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Defaced Gallery${1}{R}$Enchantment -- Room$Whenever you attack, attacking creatures you control get +1/+0 until end of turn.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| +Piggy Bank|Duskmourn: House of Horror|148|U|{1}{R}|Artifact Creature - Boar Toy|3|2|When this creature dies, create a Treasure token.| Pyroclasm|Duskmourn: House of Horror|149|U|{1}{R}|Sorcery|||Pyroclasm deals 2 damage to each creature.| Ragged Playmate|Duskmourn: House of Horror|150|C|{1}{R}|Artifact Creature - Toy|2|2|{1}, {T}: Target creature with power 2 or less can't be blocked this turn.| -Rampaging Soulrager|Duskmourn: House of Horror|151|C|{2}{R}|Creature - Spirit|1|4|Rampaging Soulrager gets +3/+0 as long as there are two or more unlocked doors among Rooms you control.| +Rampaging Soulrager|Duskmourn: House of Horror|151|C|{2}{R}|Creature - Spirit|1|4|This creature gets +3/+0 as long as there are two or more unlocked doors among Rooms you control.| Razorkin Hordecaller|Duskmourn: House of Horror|152|U|{4}{R}|Creature - Human Clown Berserker|4|4|Haste$Whenever you attack, create a 1/1 red Gremlin creature token.| -Razorkin Needlehead|Duskmourn: House of Horror|153|R|{R}{R}|Creature - Human Assassin|2|2|Razorkin Needlehead has first strike during your turn.$Whenever an opponent draws a card, Razorkin Needlehead deals 1 damage to them.| +Razorkin Needlehead|Duskmourn: House of Horror|153|R|{R}{R}|Creature - Human Assassin|2|2|This creature has first strike during your turn.$Whenever an opponent draws a card, this creature deals 1 damage to them.| Ripchain Razorkin|Duskmourn: House of Horror|154|C|{3}{R}|Creature - Human Berserker|5|3|Reach${2}{R}, Sacrifice a land: Draw a card.| The Rollercrusher Ride|Duskmourn: House of Horror|155|M|{X}{2}{R}|Legendary Enchantment|||Delirium -- If a source you control would deal noncombat damage to a permanent or player while there are four or more card types among cards in your graveyard, it deals double that damage instead.$When The Rollercrusher Ride enters, it deals X damage to each of up to X target creatures.| Scorching Dragonfire|Duskmourn: House of Horror|156|C|{1}{R}|Instant|||Scorching Dragonfire deals 3 damage to target creature or planeswalker. If that creature or planeswalker would die this turn, exile it instead.| -Screaming Nemesis|Duskmourn: House of Horror|157|M|{2}{R}|Creature - Spirit|3|3|Haste$Whenever Screaming Nemesis is dealt damage, it deals that much damage to any other target. If a player is dealt damage this way, they can't gain life for the rest of the game.| -Ticket Booth // Tunnel of Hate|Duskmourn: House of Horror|158|C|{2}{R}|Enchantment - Room|||(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$When you unlock this door, manifest dread.$Tunnel of Hate${4}{R}{R}$Enchantment -- Room$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Whenever you attack, target attacking creature gains double strike until end of turn.| +Screaming Nemesis|Duskmourn: House of Horror|157|M|{2}{R}|Creature - Spirit|3|3|Haste$Whenever this creature is dealt damage, it deals that much damage to any other target. If a player is dealt damage this way, they can't gain life for the rest of the game.| +Ticket Booth // Tunnel of Hate|Duskmourn: House of Horror|158|C|{2}{R}|Enchantment - Room|||When you unlock this door, manifest dread.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Tunnel of Hate${4}{R}{R}$Enchantment -- Room$Whenever you attack, target attacking creature gains double strike until end of turn.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| Trial of Agony|Duskmourn: House of Horror|159|U|{R}|Sorcery|||Choose two target creatures controlled by the same opponent. That player chooses one of those creatures. Trial of Agony deals 5 damage to that creature, and the other can't block this turn.| Turn Inside Out|Duskmourn: House of Horror|160|C|{R}|Instant|||Target creature gets +3/+0 until end of turn. When it dies this turn, manifest dread.| Untimely Malfunction|Duskmourn: House of Horror|161|U|{1}{R}|Instant|||Choose one --$* Destroy target artifact.$* Change the target of target spell or ability with a single target.$* One or two target creatures can't block this turn.| Vengeful Possession|Duskmourn: House of Horror|162|U|{2}{R}|Sorcery|||Gain control of target creature until end of turn. Untap it. It gains haste until end of turn. You may discard a card. If you do, draw a card.| -Vicious Clown|Duskmourn: House of Horror|163|C|{2}{R}|Creature - Human Clown|2|3|Whenever another creature you control with power 2 or less enters, Vicious Clown gets +2/+0 until end of turn.| +Vicious Clown|Duskmourn: House of Horror|163|C|{2}{R}|Creature - Human Clown|2|3|Whenever another creature you control with power 2 or less enters, this creature gets +2/+0 until end of turn.| Violent Urge|Duskmourn: House of Horror|164|U|{R}|Instant|||Target creature gets +1/+0 and gains first strike until end of turn.$Delirium -- If there are four or more card types among cards in your graveyard, that creature gains double strike until end of turn.| Waltz of Rage|Duskmourn: House of Horror|165|R|{3}{R}{R}|Sorcery|||Target creature you control deals damage equal to its power to each other creature. Until end of turn, whenever a creature you control dies, exile the top card of your library. You may play it until the end of your next turn.| -Altanak, the Thrice-Called|Duskmourn: House of Horror|166|U|{5}{G}{G}|Legendary Creature - Insect Beast|9|9|Trample$Whenever Altanak, the Thrice-Called becomes the target of a spell or ability an opponent controls, draw a card.${1}{G}, Discard Altanak, the Thrice-Called: Return target land card from your graveyard to the battlefield tapped.| -Anthropede|Duskmourn: House of Horror|167|C|{3}{G}|Creature - Insect|3|4|Reach$When Anthropede enters, you may discard a card or pay {2}. When you do, destroy target Room.| -Balustrade Wurm|Duskmourn: House of Horror|168|R|{3}{G}{G}|Creature - Wurm|5|5|This spell can't be countered.$Trample, haste$Delirium -- {2}{G}{G}: Return Balustrade Wurm from your graveyard to the battlefield with a finality counter on it. Activate only if there are four or more card types among cards in your graveyard and only as a sorcery.| -Bashful Beastie|Duskmourn: House of Horror|169|C|{4}{G}|Creature - Beast|5|4|When Bashful Beastie dies, manifest dread.| +Altanak, the Thrice-Called|Duskmourn: House of Horror|166|U|{5}{G}{G}|Legendary Creature - Insect Beast|9|9|Trample$Whenever Altanak becomes the target of a spell or ability an opponent controls, draw a card.${1}{G}, Discard this card: Return target land card from your graveyard to the battlefield tapped.| +Anthropede|Duskmourn: House of Horror|167|C|{3}{G}|Creature - Insect|3|4|Reach$When this creature enters, you may discard a card or pay {2}. When you do, destroy target Room.| +Balustrade Wurm|Duskmourn: House of Horror|168|R|{3}{G}{G}|Creature - Wurm|5|5|This spell can't be countered.$Trample, haste$Delirium -- {2}{G}{G}: Return this card from your graveyard to the battlefield with a finality counter on it. Activate only if there are four or more card types among cards in your graveyard and only as a sorcery.| +Bashful Beastie|Duskmourn: House of Horror|169|C|{4}{G}|Creature - Beast|5|4|When this creature dies, manifest dread.| Break Down the Door|Duskmourn: House of Horror|170|U|{2}{G}|Instant|||Choose one --$* Exile target artifact.$* Exile target enchantment.$* Manifest dread.| Cathartic Parting|Duskmourn: House of Horror|171|U|{1}{G}|Sorcery|||The owner of target artifact or enchantment an opponent controls shuffles it into their library. You may shuffle up to four target cards from your graveyard into your library.| -Cautious Survivor|Duskmourn: House of Horror|172|C|{3}{G}|Creature - Elf Survivor|4|4|Survival -- At the beginning of your second main phase, if Cautious Survivor is tapped, you gain 2 life.| +Cautious Survivor|Duskmourn: House of Horror|172|C|{3}{G}|Creature - Elf Survivor|4|4|Survival -- At the beginning of your second main phase, if this creature is tapped, you gain 2 life.| Coordinated Clobbering|Duskmourn: House of Horror|173|U|{G}|Sorcery|||Tap one or two target untapped creatures you control. They each deal damage equal to their power to target creature an opponent controls.| -Cryptid Inspector|Duskmourn: House of Horror|174|C|{2}{G}|Creature - Elf Warrior|2|3|Vigilance$Whenever a face-down permanent you control enters and whenever Cryptid Inspector or another permanent you control is turned face up, put a +1/+1 counter on Cryptid Inspector.| -Defiant Survivor|Duskmourn: House of Horror|175|U|{2}{G}|Creature - Human Survivor|3|2|Survival -- At the beginning of your second main phase, if Defiant Survivor is tapped, manifest dread.| +Cryptid Inspector|Duskmourn: House of Horror|174|C|{2}{G}|Creature - Elf Warrior|2|3|Vigilance$Whenever a face-down permanent you control enters and whenever this creature or another permanent you control is turned face up, put a +1/+1 counter on this creature.| +Defiant Survivor|Duskmourn: House of Horror|175|U|{2}{G}|Creature - Human Survivor|3|2|Survival -- At the beginning of your second main phase, if this creature is tapped, manifest dread.| Enduring Vitality|Duskmourn: House of Horror|176|R|{1}{G}{G}|Enchantment Creature - Elk Glimmer|3|3|Vigilance$Creatures you control have "{T}: Add one mana of any color."$When Enduring Vitality dies, if it was a creature, return it to the battlefield under its owner's control. It's an enchantment.| Fear of Exposure|Duskmourn: House of Horror|177|U|{2}{G}|Enchantment Creature - Nightmare|5|4|As an additional cost to cast this spell, tap two untapped creatures and/or lands you control.$Trample| -Flesh Burrower|Duskmourn: House of Horror|178|C|{1}{G}|Creature - Insect|2|2|Deathtouch$Whenever Flesh Burrower attacks, another target creature you control gains deathtouch until end of turn.| +Flesh Burrower|Duskmourn: House of Horror|178|C|{1}{G}|Creature - Insect|2|2|Deathtouch$Whenever this creature attacks, another target creature you control gains deathtouch until end of turn.| Frantic Strength|Duskmourn: House of Horror|179|C|{2}{G}|Enchantment - Aura|||Flash$Enchant creature$Enchanted creature gets +2/+2 and has trample.| -Grasping Longneck|Duskmourn: House of Horror|180|C|{2}{G}|Enchantment Creature - Horror|4|2|Reach$When Grasping Longneck dies, you gain 2 life.| -Greenhouse // Rickety Gazebo|Duskmourn: House of Horror|181|U|{2}{G}|Enchantment - Room|||(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Lands you control have "{T}: Add one mana of any color."$Rickety Gazebo${3}{G}$Enchantment -- Room$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$When you unlock this door, mill four cards, then return up to two permanent cards from among them to your hand.| -Hauntwoods Shrieker|Duskmourn: House of Horror|182|M|{1}{G}{G}|Creature - Beast Mutant|3|3|Whenever Hauntwoods Shrieker attacks, manifest dread.${1}{G}: Reveal target face-down permanent. If it's a creature card, you may turn it face up.| -Hedge Shredder|Duskmourn: House of Horror|183|R|{2}{G}{G}|Artifact - Vehicle|5|5|Whenever Hedge Shredder attacks, you may mill two cards.$Whenever one or more land cards are put into your graveyard from your library, put them onto the battlefield tapped.$Crew 1| +Grasping Longneck|Duskmourn: House of Horror|180|C|{2}{G}|Enchantment Creature - Horror|4|2|Reach$When this creature dies, you gain 2 life.| +Greenhouse // Rickety Gazebo|Duskmourn: House of Horror|181|U|{2}{G}|Enchantment - Room|||Lands you control have "{T}: Add one mana of any color."$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Rickety Gazebo${3}{G}$Enchantment -- Room$When you unlock this door, mill four cards, then return up to two permanent cards from among them to your hand.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| +Hauntwoods Shrieker|Duskmourn: House of Horror|182|M|{1}{G}{G}|Creature - Beast Mutant|3|3|Whenever this creature attacks, manifest dread.${1}{G}: Reveal target face-down permanent. If it's a creature card, you may turn it face up.| +Hedge Shredder|Duskmourn: House of Horror|183|R|{2}{G}{G}|Artifact - Vehicle|5|5|Whenever this Vehicle attacks, you may mill two cards.$Whenever one or more land cards are put into your graveyard from your library, put them onto the battlefield tapped.$Crew 1| Horrid Vigor|Duskmourn: House of Horror|184|C|{1}{G}|Instant|||Target creature gains deathtouch and indestructible until end of turn.| -House Cartographer|Duskmourn: House of Horror|185|U|{1}{G}|Creature - Human Scout Survivor|2|2|Survival -- At the beginning of your second main phase, if House Cartographer is tapped, reveal cards from the top of your library until you reveal a land card. Put that card into your hand and the rest on the bottom of your library in a random order.| -Insidious Fungus|Duskmourn: House of Horror|186|U|{G}|Creature - Fungus|1|2|{2}, Sacrifice Insidious Fungus: Choose one --$* Destroy target artifact.$* Destroy target enchantment.$* Draw a card. Then you may put a land card from your hand onto the battlefield tapped.| -Kona, Rescue Beastie|Duskmourn: House of Horror|187|R|{3}{G}|Legendary Creature - Beast Survivor|4|3|Survival -- At the beginning of your second main phase, if Kona, Rescue Beastie is tapped, you may put a permanent card from your hand onto the battlefield.| -Leyline of Mutation|Duskmourn: House of Horror|188|R|{2}{G}{G}|Enchantment|||If Leyline of Mutation is in your opening hand, you may begin the game with it on the battlefield.$You may pay {W}{U}{B}{R}{G} rather than pay the mana cost for spells that you cast.| +House Cartographer|Duskmourn: House of Horror|185|U|{1}{G}|Creature - Human Scout Survivor|2|2|Survival -- At the beginning of your second main phase, if this creature is tapped, reveal cards from the top of your library until you reveal a land card. Put that card into your hand and the rest on the bottom of your library in a random order.| +Insidious Fungus|Duskmourn: House of Horror|186|U|{G}|Creature - Fungus|1|2|{2}, Sacrifice this creature: Choose one --$* Destroy target artifact.$* Destroy target enchantment.$* Draw a card. Then you may put a land card from your hand onto the battlefield tapped.| +Kona, Rescue Beastie|Duskmourn: House of Horror|187|R|{3}{G}|Legendary Creature - Beast Survivor|4|3|Survival -- At the beginning of your second main phase, if Kona is tapped, you may put a permanent card from your hand onto the battlefield.| +Leyline of Mutation|Duskmourn: House of Horror|188|R|{2}{G}{G}|Enchantment|||If this card is in your opening hand, you may begin the game with it on the battlefield.$You may pay {W}{U}{B}{R}{G} rather than pay the mana cost for spells you cast.| Manifest Dread|Duskmourn: House of Horror|189|C|{1}{G}|Sorcery|||Manifest dread.| -Moldering Gym // Weight Room|Duskmourn: House of Horror|190|C|{2}{G}|Enchantment - Room|||(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$When you unlock this door, search your library for a basic land card, put it onto the battlefield tapped, then shuffle.$Weight Room${5}{G}$Enchantment -- Room$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$When you unlock this door, manifest dread, then put three +1/+1 counters on that creature.| +Moldering Gym // Weight Room|Duskmourn: House of Horror|190|C|{2}{G}|Enchantment - Room|||When you unlock this door, search your library for a basic land card, put it onto the battlefield tapped, then shuffle.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Weight Room${5}{G}$Enchantment -- Room$When you unlock this door, manifest dread, then put three +1/+1 counters on that creature.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| Monstrous Emergence|Duskmourn: House of Horror|191|C|{1}{G}|Sorcery|||As an additional cost to cast this spell, choose a creature you control or reveal a creature card from your hand.$Monstrous Emergence deals damage equal to the power of the creature you chose or the card you revealed to target creature.| -Omnivorous Flytrap|Duskmourn: House of Horror|192|R|{2}{G}|Creature - Plant|2|4|Delirium -- Whenever Omnivorous Flytrap enters or attacks, if there are four or more card types among cards in your graveyard, distribute two +1/+1 counters among one or two target creatures. Then if there are six or more card types among cards in your graveyard, double the number of +1/+1 counters on those creatures.| +Omnivorous Flytrap|Duskmourn: House of Horror|192|R|{2}{G}|Creature - Plant|2|4|Delirium -- Whenever this creature enters or attacks, if there are four or more card types among cards in your graveyard, distribute two +1/+1 counters among one or two target creatures. Then if there are six or more card types among cards in your graveyard, double the number of +1/+1 counters on those creatures.| Overgrown Zealot|Duskmourn: House of Horror|193|U|{1}{G}|Creature - Elf Druid|0|4|{T}: Add one mana of any color.${T}: Add two mana of any one color. Spend this mana only to turn permanents face up.| -Overlord of the Hauntwoods|Duskmourn: House of Horror|194|M|{3}{G}{G}|Enchantment Creature - Avatar Horror|6|5|Impending 4--{1}{G}{G}$Whenever Overlord of the Hauntwoods enters or attacks, create a tapped colorless land token named Everywhere that is every basic land type.| -Patchwork Beastie|Duskmourn: House of Horror|195|U|{G}|Artifact Creature - Beast|3|3|Delirium -- Patchwork Beastie can't attack or block unless there are four or more card types among cards in your graveyard.$At the beginning of your upkeep, you may mill a card.| -Rootwise Survivor|Duskmourn: House of Horror|196|U|{3}{G}{G}|Creature - Human Survivor|3|4|Haste$Survival -- At the beginning of your second main phase, if Rootwise Survivor is tapped, put three +1/+1 counters on up to one target land you control. That land becomes a 0/0 Elemental creature in addition to its other types. It gains haste until your next turn.| +Overlord of the Hauntwoods|Duskmourn: House of Horror|194|M|{3}{G}{G}|Enchantment Creature - Avatar Horror|6|5|Impending 4--{1}{G}{G}$Whenever this permanent enters or attacks, create a tapped colorless land token named Everywhere that is every basic land type.| +Patchwork Beastie|Duskmourn: House of Horror|195|U|{G}|Artifact Creature - Beast|3|3|Delirium -- This creature can't attack or block unless there are four or more card types among cards in your graveyard.$At the beginning of your upkeep, you may mill a card.| +Rootwise Survivor|Duskmourn: House of Horror|196|U|{3}{G}{G}|Creature - Human Survivor|3|4|Haste$Survival -- At the beginning of your second main phase, if this creature is tapped, put three +1/+1 counters on up to one target land you control. That land becomes a 0/0 Elemental creature in addition to its other types. It gains haste until your next turn.| Say Its Name|Duskmourn: House of Horror|197|C|{1}{G}|Sorcery|||Mill three cards. Then you may return a creature or land card from your graveyard to your hand.$Exile this card and two other cards named Say Its Name from your graveyard: Search your graveyard, hand, and/or library for a card named Altanak, the Thrice-Called and put it onto the battlefield. If you search your library this way, shuffle. Activate only as a sorcery.| Slavering Branchsnapper|Duskmourn: House of Horror|198|C|{4}{G}{G}|Creature - Lizard|7|6|Trample$Forestcycling {2}| -Spineseeker Centipede|Duskmourn: House of Horror|199|C|{2}{G}|Creature - Insect|2|1|When Spineseeker Centipede enters, search your library for a basic land card, reveal it, put it into your hand, then shuffle.$Delirium -- Spineseeker Centipede gets +1/+2 and has vigilance as long as there are four or more card types among cards in your graveyard.| -Threats Around Every Corner|Duskmourn: House of Horror|200|U|{3}{G}|Enchantment|||When Threats Around Every Corner enters, manifest dread.$Whenever a face-down permanent you control enters, search your library for a basic land card, put it onto the battlefield tapped, then shuffle.| -Twitching Doll|Duskmourn: House of Horror|201|R|{1}{G}|Artifact Creature - Spider Toy|2|2|{T}: Add one mana of any color. Put a nest counter on Twitching Doll.${T}, Sacrifice Twitching Doll: Create a 2/2 green Spider creature token with reach for each counter on Twitching Doll. Activate only as a sorcery.| -Tyvar, the Pummeler|Duskmourn: House of Horror|202|M|{1}{G}{G}|Legendary Creature - Elf Warrior|3|3|Tap another untapped creature you control: Tyvar, the Pummeler gains indestructible until end of turn. Tap it.${3}{G}{G}: Creatures you control get +X/+X until end of turn, where X is the greatest power among creatures you control.| +Spineseeker Centipede|Duskmourn: House of Horror|199|C|{2}{G}|Creature - Insect|2|1|When this creature enters, search your library for a basic land card, reveal it, put it into your hand, then shuffle.$Delirium -- This creature gets +1/+2 and has vigilance as long as there are four or more card types among cards in your graveyard.| +Threats Around Every Corner|Duskmourn: House of Horror|200|U|{3}{G}|Enchantment|||When this enchantment enters, manifest dread.$Whenever a face-down permanent you control enters, search your library for a basic land card, put it onto the battlefield tapped, then shuffle.| +Twitching Doll|Duskmourn: House of Horror|201|R|{1}{G}|Artifact Creature - Spider Toy|2|2|{T}: Add one mana of any color. Put a nest counter on this creature.${T}, Sacrifice this creature: Create a 2/2 green Spider creature token with reach for each counter on this creature. Activate only as a sorcery.| +Tyvar, the Pummeler|Duskmourn: House of Horror|202|M|{1}{G}{G}|Legendary Creature - Elf Warrior|3|3|Tap another untapped creature you control: Tyvar gains indestructible until end of turn. Tap it.${3}{G}{G}: Creatures you control get +X/+X until end of turn, where X is the greatest power among creatures you control.| Under the Skin|Duskmourn: House of Horror|203|U|{2}{G}|Sorcery|||Manifest dread.$You may return a permanent card from your graveyard to your hand.| Valgavoth's Onslaught|Duskmourn: House of Horror|204|R|{X}{X}{G}|Sorcery|||Manifest dread X times, then put X +1/+1 counters on each of those creatures.| -Walk-in Closet // Forgotten Cellar|Duskmourn: House of Horror|205|M|{2}{G}|Enchantment - Room|||(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$You may play lands from your graveyard.$Forgotten Cellar${3}{G}{G}$Enchantment -- Room$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$When you unlock this door, you may cast spells from your graveyard this turn, and if a card would be put into your graveyard from anywhere this turn, exile it instead.| -Wary Watchdog|Duskmourn: House of Horror|206|C|{1}{G}|Creature - Dog|3|1|When Wary Watchdog enters or dies, surveil 1.| -Wickerfolk Thresher|Duskmourn: House of Horror|207|U|{3}{G}|Artifact Creature - Scarecrow|5|4|Delirium -- Whenever Wickerfolk Thresher attacks, if there are four or more card types among cards in your graveyard, look at the top card of your library. If it's a land card, you may put it onto the battlefield. If you don't put the card onto the battlefield, put it into your hand.| -Arabella, Abandoned Doll|Duskmourn: House of Horror|208|U|{R}{W}|Legendary Artifact Creature - Toy|1|3|Whenever Arabella, Abandoned Doll attacks, it deals X damage to each opponent and you gain X life, where X is the number of creatures you control with power 2 or less.| -Baseball Bat|Duskmourn: House of Horror|209|U|{G}{W}|Artifact - Equipment|||When Baseball Bat enters, attach it to target creature you control.$Equipped creature gets +1/+1.$Whenever equipped creature attacks, tap up to one target creature.$Equip {3}| +Walk-In Closet // Forgotten Cellar|Duskmourn: House of Horror|205|M|{2}{G}|Enchantment - Room|||You may play lands from your graveyard.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Forgotten Cellar${3}{G}{G}$Enchantment -- Room$When you unlock this door, you may cast spells from your graveyard this turn, and if a card would be put into your graveyard from anywhere this turn, exile it instead.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| +Wary Watchdog|Duskmourn: House of Horror|206|C|{1}{G}|Creature - Dog|3|1|When this creature enters or dies, surveil 1.| +Wickerfolk Thresher|Duskmourn: House of Horror|207|U|{3}{G}|Artifact Creature - Scarecrow|5|4|Delirium -- Whenever this creature attacks, if there are four or more card types among cards in your graveyard, look at the top card of your library. If it's a land card, you may put it onto the battlefield. If you don't put the card onto the battlefield, put it into your hand.| +Arabella, Abandoned Doll|Duskmourn: House of Horror|208|U|{R}{W}|Legendary Artifact Creature - Toy|1|3|Whenever Arabella attacks, it deals X damage to each opponent and you gain X life, where X is the number of creatures you control with power 2 or less.| +Baseball Bat|Duskmourn: House of Horror|209|U|{G}{W}|Artifact - Equipment|||When this Equipment enters, attach it to target creature you control.$Equipped creature gets +1/+1.$Whenever equipped creature attacks, tap up to one target creature.$Equip {3}| Beastie Beatdown|Duskmourn: House of Horror|210|U|{R}{G}|Sorcery|||Choose target creature you control and target creature an opponent controls.$Delirium -- If there are four or more card types among cards in your graveyard, put two +1/+1 counters on the creature you control.$The creature you control deals damage equal to its power to the creature an opponent controls.| -Broodspinner|Duskmourn: House of Horror|211|U|{B}{G}|Creature - Spider|2|3|Reach$When Broodspinner enters, surveil 2.${4}{B}{G}, {T}, Sacrifice Broodspinner: Create a number of 1/1 black and green Insect creature tokens with flying equal to the number of card types among cards in your graveyard.| -Disturbing Mirth|Duskmourn: House of Horror|212|U|{B}{R}|Enchantment|||When Disturbing Mirth enters, you may sacrifice another enchantment or creature. If you do, draw two cards.$When you sacrifice Disturbing Mirth, manifest dread.| +Broodspinner|Duskmourn: House of Horror|211|U|{B}{G}|Creature - Spider|2|3|Reach$When this creature enters, surveil 2.${4}{B}{G}, {T}, Sacrifice this creature: Create a number of 1/1 black and green Insect creature tokens with flying equal to the number of card types among cards in your graveyard.| +Disturbing Mirth|Duskmourn: House of Horror|212|U|{B}{R}|Enchantment|||When this enchantment enters, you may sacrifice another enchantment or creature. If you do, draw two cards.$When you sacrifice this enchantment, manifest dread.| Drag to the Roots|Duskmourn: House of Horror|213|U|{2}{B}{G}|Instant|||Delirium -- This spell costs {2} less to cast as long as there are four or more card types among cards in your graveyard.$Destroy target nonland permanent.| -Fear of Infinity|Duskmourn: House of Horror|214|U|{1}{U}{B}|Enchantment Creature - Nightmare|2|2|Flying, lifelink$Fear of Infinity can't block.$Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, you may return Fear of Infinity from your graveyard to your hand.| +Fear of Infinity|Duskmourn: House of Horror|214|U|{1}{U}{B}|Enchantment Creature - Nightmare|2|2|Flying, lifelink$This creature can't block.$Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, you may return this card from your graveyard to your hand.| Gremlin Tamer|Duskmourn: House of Horror|215|U|{W}{U}|Creature - Human Scout|2|2|Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, create a 1/1 red Gremlin creature token.| -Growing Dread|Duskmourn: House of Horror|216|U|{G}{U}|Enchantment|||Flash$When Growing Dread enters, manifest dread.$Whenever you turn a permanent face up, put a +1/+1 counter on it.| +Growing Dread|Duskmourn: House of Horror|216|U|{G}{U}|Enchantment|||Flash$When this enchantment enters, manifest dread.$Whenever you turn a permanent face up, put a +1/+1 counter on it.| Inquisitive Glimmer|Duskmourn: House of Horror|217|U|{W}{U}|Enchantment Creature - Fox Glimmer|2|3|Enchantment spells you cast cost {1} less to cast.$Unlock costs you pay cost {1} less.| -Intruding Soulrager|Duskmourn: House of Horror|218|U|{U}{R}|Creature - Spirit|2|2|Vigilance${T}, Sacrifice a Room: Intruding Soulrager deals 2 damage to each opponent. Draw a card.| +Intruding Soulrager|Duskmourn: House of Horror|218|U|{U}{R}|Creature - Spirit|2|2|Vigilance${T}, Sacrifice a Room: This creature deals 2 damage to each opponent. Draw a card.| The Jolly Balloon Man|Duskmourn: House of Horror|219|R|{1}{R}{W}|Legendary Creature - Human Clown|1|4|Haste${1}, {T}: Create a token that's a copy of another target creature you control, except it's a 1/1 red Balloon creature in addition to its other colors and types and it has flying and haste. Sacrifice it at the beginning of the next end step. Activate only as a sorcery.| Kaito, Bane of Nightmares|Duskmourn: House of Horror|220|M|{2}{U}{B}|Legendary Planeswalker - Kaito|4|Ninjutsu {1}{U}{B}$During your turn, as long as Kaito has one or more loyalty counters on him, he's a 3/4 Ninja creature and has hexproof.$+1: You get an emblem with "Ninjas you control get +1/+1."$0: Surveil 2. Then draw a card for each opponent who lost life this turn.$-2: Tap target creature. Put two stun counters on it.| Marina Vendrell|Duskmourn: House of Horror|221|R|{W}{U}{B}{R}{G}|Legendary Creature - Human Warlock|3|5|When Marina Vendrell enters, reveal the top seven cards of your library. Put all enchantment cards from among them into your hand and the rest on the bottom of your library in a random order.${T}: Lock or unlock a door of target Room you control. Activate only as a sorcery.| Midnight Mayhem|Duskmourn: House of Horror|222|U|{2}{R}{W}|Sorcery|||Create three 1/1 red Gremlin creature tokens. Gremlins you control gain menace, lifelink, and haste until end of turn.| -Nashi, Searcher in the Dark|Duskmourn: House of Horror|223|R|{U}{B}|Legendary Creature - Rat Ninja Wizard|2|2|Menace$Whenever Nashi, Searcher in the Dark deals combat damage to a player, you mill that many cards. You may put any number of legendary and/or enchantment cards from among them into your hand. If you put no cards into your hand this way, put a +1/+1 counter on Nashi.| -Niko, Light of Hope|Duskmourn: House of Horror|224|M|{2}{W}{U}|Legendary Creature - Human Wizard|3|4|When Niko, Light of Hope enters, create two Shard tokens.${2}, {T}: Exile target nonlegendary creature you control. Shards you control become copies of it until the beginning of the next end step. Return it to the battlefield under its owner's control at the beginning of the next end step.| +Nashi, Searcher in the Dark|Duskmourn: House of Horror|223|R|{U}{B}|Legendary Creature - Rat Ninja Wizard|2|2|Menace$Whenever Nashi deals combat damage to a player, you mill that many cards. You may put any number of legendary and/or enchantment cards from among them into your hand. If you put no cards into your hand this way, put a +1/+1 counter on Nashi.| +Niko, Light of Hope|Duskmourn: House of Horror|224|M|{2}{W}{U}|Legendary Creature - Human Wizard|3|4|When Niko enters, create two Shard tokens.${2}, {T}: Exile target nonlegendary creature you control. Shards you control become copies of it until the next end step. Return it to the battlefield under its owner's control at the beginning of the next end step.| Oblivious Bookworm|Duskmourn: House of Horror|225|U|{G}{U}|Creature - Human Wizard|2|3|At the beginning of your end step, you may draw a card. If you do, discard a card unless a permanent entered the battlefield face down under your control this turn or you turned a permanent face up this turn.| Peer Past the Veil|Duskmourn: House of Horror|226|R|{2}{R}{G}|Instant|||Discard your hand. Then draw X cards, where X is the number of card types among cards in your graveyard.| -Restricted Office // Lecture Hall|Duskmourn: House of Horror|227|R|{2}{W}{W}|Enchantment - Room|||(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$When you unlock this door, destroy all creatures with power 3 or greater.$Lecture Hall${5}{U}{U}$Enchantment -- Room$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Other permanents you control have hexproof.| -Rip, Spawn Hunter|Duskmourn: House of Horror|228|R|{2}{G}{W}|Legendary Creature - Human Survivor|4|4|Survival -- At the beginning of your second main phase, if Rip, Spawn Hunter is tapped, reveal the top X cards of your library, where X is its power. Put any number of creature and/or Vehicle cards with different powers from among them into your hand. Put the rest on the bottom of your library in a random order.| +Restricted Office // Lecture Hall|Duskmourn: House of Horror|227|R|{2}{W}{W}|Enchantment - Room|||When you unlock this door, destroy all creatures with power 3 or greater.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Lecture Hall${5}{U}{U}$Enchantment -- Room$Other permanents you control have hexproof.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| +Rip, Spawn Hunter|Duskmourn: House of Horror|228|R|{2}{G}{W}|Legendary Creature - Human Survivor|4|4|Survival -- At the beginning of your second main phase, if Rip is tapped, reveal the top X cards of your library, where X is its power. Put any number of creature and/or Vehicle cards with different powers from among them into your hand. Put the rest on the bottom of your library in a random order.| Rite of the Moth|Duskmourn: House of Horror|229|U|{1}{W}{B}{B}|Sorcery|||Return target creature card from your graveyard to the battlefield with a finality counter on it.$Flashback {3}{W}{W}{B}| -Roaring Furnace // Steaming Sauna|Duskmourn: House of Horror|230|R|{1}{R}|Enchantment - Room|||(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$When you unlock this door, this Room deals damage equal to the number of cards in your hand to target creature an opponent controls.$Steaming Sauna${3}{U}{U}$Enchantment -- Room$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$You have no maximum hand size.$At the beginning of your end step, draw a card.| -Sawblade Skinripper|Duskmourn: House of Horror|231|U|{1}{B}{R}|Creature - Human Assassin|3|2|Menace${2}, Sacrifice another creature or enchantment: Put a +1/+1 counter on Sawblade Skinripper.$At the beginning of your end step, if you sacrificed one or more permanents this turn, Sawblade Skinripper deals that much damage to any target.| -Shrewd Storyteller|Duskmourn: House of Horror|232|U|{1}{G}{W}|Creature - Human Survivor|3|3|Survival -- At the beginning of your second main phase, if Shrewd Storyteller is tapped, put a +1/+1 counter on target creature.| -Shroudstomper|Duskmourn: House of Horror|233|U|{3}{W}{W}{B}{B}|Creature - Elemental|5|5|Deathtouch$Whenever Shroudstomper enters or attacks, each opponent loses 2 life. You gain 2 life and draw a card.| +Roaring Furnace // Steaming Sauna|Duskmourn: House of Horror|230|R|{1}{R}|Enchantment - Room|||When you unlock this door, this Room deals damage equal to the number of cards in your hand to target creature an opponent controls.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Steaming Sauna${3}{U}{U}$Enchantment -- Room$You have no maximum hand size.$At the beginning of your end step, draw a card.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| +Sawblade Skinripper|Duskmourn: House of Horror|231|U|{1}{B}{R}|Creature - Human Assassin|3|2|Menace${2}, Sacrifice another creature or enchantment: Put a +1/+1 counter on this creature.$At the beginning of your end step, if you sacrificed one or more permanents this turn, this creature deals that much damage to any target.| +Shrewd Storyteller|Duskmourn: House of Horror|232|U|{1}{G}{W}|Creature - Human Survivor|3|3|Survival -- At the beginning of your second main phase, if this creature is tapped, put a +1/+1 counter on target creature.| +Shroudstomper|Duskmourn: House of Horror|233|U|{3}{W}{W}{B}{B}|Creature - Elemental|5|5|Deathtouch$Whenever this creature enters or attacks, each opponent loses 2 life. You gain 2 life and draw a card.| Skullsnap Nuisance|Duskmourn: House of Horror|234|U|{U}{B}|Creature - Insect Skeleton|1|4|Flying$Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, surveil 1.| -Smoky Lounge // Misty Salon|Duskmourn: House of Horror|235|U|{2}{R}|Enchantment - Room|||(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$At the beginning of your first main phase, add {R}{R}. Spend this mana only to cast Room spells and unlock doors.$Misty Salon${3}{U}$Enchantment -- Room$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$When you unlock this door, create an X/X blue Spirit creature token with flying, where X is the number of unlocked doors among Rooms you control.| +Smoky Lounge // Misty Salon|Duskmourn: House of Horror|235|U|{2}{R}|Enchantment - Room|||At the beginning of your first main phase, add {R}{R}. Spend this mana only to cast Room spells and unlock doors.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Misty Salon${3}{U}$Enchantment -- Room$When you unlock this door, create an X/X blue Spirit creature token with flying, where X is the number of unlocked doors among Rooms you control.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| The Swarmweaver|Duskmourn: House of Horror|236|R|{2}{B}{G}|Legendary Artifact Creature - Scarecrow|2|3|When The Swarmweaver enters, create two 1/1 black and green Insect creature tokens with flying.$Delirium -- As long as there are four or more card types among cards in your graveyard, Insects and Spiders you control get +1/+1 and have deathtouch.| -Undead Sprinter|Duskmourn: House of Horror|237|R|{B}{R}|Creature - Zombie|2|2|Trample, haste$You may cast Undead Sprinter from your graveyard if a non-Zombie creature died this turn. If you do, Undead Sprinter enters with a +1/+1 counter on it.| +Undead Sprinter|Duskmourn: House of Horror|237|R|{B}{R}|Creature - Zombie|2|2|Trample, haste$You may cast this card from your graveyard if a non-Zombie creature died this turn. If you do, this creature enters with a +1/+1 counter on it.| Victor, Valgavoth's Seneschal|Duskmourn: House of Horror|238|R|{1}{W}{B}|Legendary Creature - Human Warlock|3|3|Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, surveil 2 if this is the first time this ability has resolved this turn. If it's the second time, each opponent discards a card. If it's the third time, put a creature card from a graveyard onto the battlefield under your control.| -Wildfire Wickerfolk|Duskmourn: House of Horror|239|U|{R}{G}|Artifact Creature - Scarecrow|3|2|Haste$Delirium -- Wildfire Wickerfolk gets +1/+1 and has trample as long as there are four or more card types among cards in your graveyard.| +Wildfire Wickerfolk|Duskmourn: House of Horror|239|U|{R}{G}|Artifact Creature - Scarecrow|3|2|Haste$Delirium -- This creature gets +1/+1 and has trample as long as there are four or more card types among cards in your graveyard.| Winter, Misanthropic Guide|Duskmourn: House of Horror|240|R|{1}{B}{R}{G}|Legendary Creature - Human Warlock|3|4|Ward {2}$At the beginning of your upkeep, each player draws two cards.$Delirium -- As long as there are four or more card types among cards in your graveyard, each opponent's maximum hand size is equal to seven minus the number of those card types.| Zimone, All-Questioning|Duskmourn: House of Horror|241|R|{1}{G}{U}|Legendary Creature - Human Wizard|1|1|At the beginning of your end step, if a land entered the battlefield under your control this turn and you control a prime number of lands, create Primo, the Indivisible, a legendary 0/0 green and blue Fractal creature token, then put that many +1/+1 counters on it.| -Attack-in-the-Box|Duskmourn: House of Horror|242|U|{3}|Artifact Creature - Toy|2|4|Whenever Attack-in-the-Box attacks, you may have it get +4/+0 until end of turn. If you do, sacrifice it at the beginning of the next end step.| -Bear Trap|Duskmourn: House of Horror|243|C|{1}|Artifact|||Flash${3}, {T}, Sacrifice Bear Trap: It deals 3 damage to target creature.| -Conductive Machete|Duskmourn: House of Horror|244|U|{4}|Artifact - Equipment|||When Conductive Machete enters, manifest dread, then attach Conductive Machete to that creature.$Equipped creature gets +2/+1.$Equip {4}| -Dissection Tools|Duskmourn: House of Horror|245|R|{5}|Artifact - Equipment|||When Dissection Tools enters, manifest dread, then attach Dissection Tools to that creature.$Equipped creature gets +2/+2 and has deathtouch and lifelink.$Equip--Sacrifice a creature.| -Found Footage|Duskmourn: House of Horror|246|C|{1}|Artifact - Clue|||You may look at face-down creatures your opponents control any time.${2}, Sacrifice Found Footage: Surveil 2, then draw a card.| -Friendly Teddy|Duskmourn: House of Horror|247|C|{2}|Artifact Creature - Bear Toy|2|2|When Friendly Teddy dies, each player draws a card.| -Ghost Vacuum|Duskmourn: House of Horror|248|R|{1}|Artifact|||{T}: Exile target card from a graveyard.${6}, {T}, Sacrifice Ghost Vacuum: Put each creature card exiled with Ghost Vacuum onto the battlefield under your control with a flying counter on it. Each of them is a 1/1 Spirit in addition to its other types. Activate only as a sorcery.| -Glimmerlight|Duskmourn: House of Horror|249|C|{2}|Artifact - Equipment|||When Glimmerlight enters, create a 1/1 white Glimmer enchantment creature token.$Equipped creature gets +1/+1.$Equip {1}| -Haunted Screen|Duskmourn: House of Horror|250|U|{3}|Artifact|||{T}: Add {W} or {B}.${T}, Pay 1 life: Add {G}, {U}, or {R}.${7}: Put seven +1/+1 counters on Haunted Screen. It becomes a 0/0 Spirit creature in addition to its other types. Activate only once.| -Keys to the House|Duskmourn: House of Horror|251|U|{1}|Artifact|||{1}, {T}, Sacrifice Keys to the House: Search your library for a basic land card, reveal it, put it into your hand, then shuffle.${3}, {T}, Sacrifice Keys to the House: Lock or unlock a door of target Room you control. Activate only as a sorcery.| +Attack-in-the-Box|Duskmourn: House of Horror|242|U|{3}|Artifact Creature - Toy|2|4|Whenever this creature attacks, you may have it get +4/+0 until end of turn. If you do, sacrifice it at the beginning of the next end step.| +Bear Trap|Duskmourn: House of Horror|243|C|{1}|Artifact|||Flash${3}, {T}, Sacrifice this artifact: It deals 3 damage to target creature.| +Conductive Machete|Duskmourn: House of Horror|244|U|{4}|Artifact - Equipment|||When this Equipment enters, manifest dread, then attach this Equipment to that creature.$Equipped creature gets +2/+1.$Equip {4}| +Dissection Tools|Duskmourn: House of Horror|245|R|{5}|Artifact - Equipment|||When this Equipment enters, manifest dread, then attach this Equipment to that creature.$Equipped creature gets +2/+2 and has deathtouch and lifelink.$Equip--Sacrifice a creature.| +Found Footage|Duskmourn: House of Horror|246|C|{1}|Artifact - Clue|||You may look at face-down creatures your opponents control any time.${2}, Sacrifice this artifact: Surveil 2, then draw a card.| +Friendly Teddy|Duskmourn: House of Horror|247|C|{2}|Artifact Creature - Bear Toy|2|2|When this creature dies, each player draws a card.| +Ghost Vacuum|Duskmourn: House of Horror|248|R|{1}|Artifact|||{T}: Exile target card from a graveyard.${6}, {T}, Sacrifice this artifact: Put each creature card exiled with this artifact onto the battlefield under your control with a flying counter on it. Each of them is a 1/1 Spirit in addition to its other types. Activate only as a sorcery.| +Glimmerlight|Duskmourn: House of Horror|249|C|{2}|Artifact - Equipment|||When this Equipment enters, create a 1/1 white Glimmer enchantment creature token.$Equipped creature gets +1/+1.$Equip {1}| +Haunted Screen|Duskmourn: House of Horror|250|U|{3}|Artifact|||{T}: Add {W} or {B}.${T}, Pay 1 life: Add {G}, {U}, or {R}.${7}: Put seven +1/+1 counters on this artifact. It becomes a 0/0 Spirit creature in addition to its other types. Activate only once.| +Keys to the House|Duskmourn: House of Horror|251|U|{1}|Artifact|||{1}, {T}, Sacrifice this artifact: Search your library for a basic land card, reveal it, put it into your hand, then shuffle.${3}, {T}, Sacrifice this artifact: Lock or unlock a door of target Room you control. Activate only as a sorcery.| Malevolent Chandelier|Duskmourn: House of Horror|252|C|{6}|Artifact Creature - Construct|4|4|Flying${2}: Put target card from a graveyard on the bottom of its owner's library. Activate only as a sorcery.| -Marvin, Murderous Mimic|Duskmourn: House of Horror|253|R|{2}|Legendary Artifact Creature - Toy|2|2|Marvin, Murderous Mimic has all activated abilities of creatures you control that don't have the same name as this creature.| -Saw|Duskmourn: House of Horror|254|U|{2}|Artifact - Equipment|||Equipped creature gets +2/+0.$Whenever equipped creature attacks, you may sacrifice a permanent other than that creature or Saw. If you do, draw a card.$Equip {2}| -Abandoned Campground|Duskmourn: House of Horror|255|C||Land|||Abandoned Campground enters tapped unless a player has 13 or less life.${T}: Add {W} or {U}.| +Marvin, Murderous Mimic|Duskmourn: House of Horror|253|R|{2}|Legendary Artifact Creature - Toy|2|2|Marvin has all activated abilities of creatures you control that don't have the same name as this creature.| +Saw|Duskmourn: House of Horror|254|U|{2}|Artifact - Equipment|||Equipped creature gets +2/+0.$Whenever equipped creature attacks, you may sacrifice a permanent other than that creature or this Equipment. If you do, draw a card.$Equip {2}| +Abandoned Campground|Duskmourn: House of Horror|255|C||Land|||This land enters tapped unless a player has 13 or less life.${T}: Add {W} or {U}.| Blazemire Verge|Duskmourn: House of Horror|256|R||Land|||{T}: Add {B}.${T}: Add {R}. Activate only if you control a Swamp or a Mountain.| -Bleeding Woods|Duskmourn: House of Horror|257|C||Land|||Bleeding Woods enters tapped unless a player has 13 or less life.${T}: Add {R} or {G}.| -Etched Cornfield|Duskmourn: House of Horror|258|C||Land|||Etched Cornfield enters tapped unless a player has 13 or less life.${T}: Add {G} or {W}.| +Bleeding Woods|Duskmourn: House of Horror|257|C||Land|||This land enters tapped unless a player has 13 or less life.${T}: Add {R} or {G}.| +Etched Cornfield|Duskmourn: House of Horror|258|C||Land|||This land enters tapped unless a player has 13 or less life.${T}: Add {G} or {W}.| Floodfarm Verge|Duskmourn: House of Horror|259|R||Land|||{T}: Add {W}.${T}: Add {U}. Activate only if you control a Plains or an Island.| Gloomlake Verge|Duskmourn: House of Horror|260|R||Land|||{T}: Add {U}.${T}: Add {B}. Activate only if you control an Island or a Swamp.| Hushwood Verge|Duskmourn: House of Horror|261|R||Land|||{T}: Add {G}.${T}: Add {W}. Activate only if you control a Forest or a Plains.| -Lakeside Shack|Duskmourn: House of Horror|262|C||Land|||Lakeside Shack enters tapped unless a player has 13 or less life.${T}: Add {G} or {U}.| -Murky Sewer|Duskmourn: House of Horror|263|C||Land|||Murky Sewer enters tapped unless a player has 13 or less life.${T}: Add {U} or {B}.| -Neglected Manor|Duskmourn: House of Horror|264|C||Land|||Neglected Manor enters tapped unless a player has 13 or less life.${T}: Add {W} or {B}.| -Peculiar Lighthouse|Duskmourn: House of Horror|265|C||Land|||Peculiar Lighthouse enters tapped unless a player has 13 or less life.${T}: Add {U} or {R}.| -Raucous Carnival|Duskmourn: House of Horror|266|C||Land|||Raucous Carnival enters tapped unless a player has 13 or less life.${T}: Add {R} or {W}.| -Razortrap Gorge|Duskmourn: House of Horror|267|C||Land|||Razortrap Gorge enters tapped unless a player has 13 or less life.${T}: Add {B} or {R}.| -Strangled Cemetery|Duskmourn: House of Horror|268|C||Land|||Strangled Cemetery enters tapped unless a player has 13 or less life.${T}: Add {B} or {G}.| -Terramorphic Expanse|Duskmourn: House of Horror|269|C||Land|||{T}, Sacrifice Terramorphic Expanse: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle.| +Lakeside Shack|Duskmourn: House of Horror|262|C||Land|||This land enters tapped unless a player has 13 or less life.${T}: Add {G} or {U}.| +Murky Sewer|Duskmourn: House of Horror|263|C||Land|||This land enters tapped unless a player has 13 or less life.${T}: Add {U} or {B}.| +Neglected Manor|Duskmourn: House of Horror|264|C||Land|||This land enters tapped unless a player has 13 or less life.${T}: Add {W} or {B}.| +Peculiar Lighthouse|Duskmourn: House of Horror|265|C||Land|||This land enters tapped unless a player has 13 or less life.${T}: Add {U} or {R}.| +Raucous Carnival|Duskmourn: House of Horror|266|C||Land|||This land enters tapped unless a player has 13 or less life.${T}: Add {R} or {W}.| +Razortrap Gorge|Duskmourn: House of Horror|267|C||Land|||This land enters tapped unless a player has 13 or less life.${T}: Add {B} or {R}.| +Strangled Cemetery|Duskmourn: House of Horror|268|C||Land|||This land enters tapped unless a player has 13 or less life.${T}: Add {B} or {G}.| +Terramorphic Expanse|Duskmourn: House of Horror|269|C||Land|||{T}, Sacrifice this land: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle.| Thornspire Verge|Duskmourn: House of Horror|270|R||Land|||{T}: Add {R}.${T}: Add {G}. Activate only if you control a Mountain or a Forest.| -Valgavoth's Lair|Duskmourn: House of Horror|271|R||Enchantment Land|||Hexproof$Valgavoth's Lair enters tapped. As it enters, choose a color.${T}: Add one mana of the chosen color.| +Valgavoth's Lair|Duskmourn: House of Horror|271|R||Enchantment Land|||Hexproof$This land enters tapped. As it enters, choose a color.${T}: Add one mana of the chosen color.| +Plains|Duskmourn: House of Horror|272|C||Basic Land - Plains|||({T}: Add {W}.)| +Island|Duskmourn: House of Horror|273|C||Basic Land - Island|||({T}: Add {U}.)| +Swamp|Duskmourn: House of Horror|274|C||Basic Land - Swamp|||({T}: Add {B}.)| +Mountain|Duskmourn: House of Horror|275|C||Basic Land - Mountain|||({T}: Add {R}.)| +Forest|Duskmourn: House of Horror|276|C||Basic Land - Forest|||({T}: Add {G}.)| Plains|Duskmourn: House of Horror|277|C||Basic Land - Plains|||({T}: Add {W}.)| +Plains|Duskmourn: House of Horror|278|C||Basic Land - Plains|||({T}: Add {W}.)| Island|Duskmourn: House of Horror|279|C||Basic Land - Island|||({T}: Add {U}.)| +Island|Duskmourn: House of Horror|280|C||Basic Land - Island|||({T}: Add {U}.)| Swamp|Duskmourn: House of Horror|281|C||Basic Land - Swamp|||({T}: Add {B}.)| +Swamp|Duskmourn: House of Horror|282|C||Basic Land - Swamp|||({T}: Add {B}.)| Mountain|Duskmourn: House of Horror|283|C||Basic Land - Mountain|||({T}: Add {R}.)| +Mountain|Duskmourn: House of Horror|284|C||Basic Land - Mountain|||({T}: Add {R}.)| Forest|Duskmourn: House of Horror|285|C||Basic Land - Forest|||({T}: Add {G}.)| +Forest|Duskmourn: House of Horror|286|C||Basic Land - Forest|||({T}: Add {G}.)| +Grand Entryway // Elegant Rotunda|Duskmourn: House of Horror|287|C|{1}{W}|Enchantment - Room|||When you unlock this door, create a 1/1 white Glimmer enchantment creature token.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Elegant Rotunda${2}{W}$Enchantment -- Room$When you unlock this door, put a +1/+1 counter on each of up to two target creatures.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| +Optimistic Scavenger|Duskmourn: House of Horror|288|U|{W}|Creature - Human Scout|1|1|Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, put a +1/+1 counter on target creature.| +Reluctant Role Model|Duskmourn: House of Horror|289|R|{1}{W}|Creature - Human Survivor|2|2|Survival -- At the beginning of your second main phase, if this creature is tapped, put a flying, lifelink, or +1/+1 counter on it.$Whenever this creature or another creature you control dies, if it had counters on it, put those counters on up to one target creature.| +Entity Tracker|Duskmourn: House of Horror|290|R|{2}{U}|Creature - Human Scout|2|3|Flash$Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, draw a card.| +Stay Hidden, Stay Silent|Duskmourn: House of Horror|291|U|{1}{U}|Enchantment - Aura|||Enchant creature$When this Aura enters, tap enchanted creature.$Enchanted creature doesn't untap during its controller's untap step.${4}{U}{U}: Shuffle enchanted creature into its owner's library, then manifest dread. Activate only as a sorcery.| +Come Back Wrong|Duskmourn: House of Horror|292|R|{2}{B}|Sorcery|||Destroy target creature. If a creature card is put into a graveyard this way, return it to the battlefield under your control. Sacrifice it at the beginning of your next end step.| +Meathook Massacre II|Duskmourn: House of Horror|293|M|{X}{X}{B}{B}{B}{B}|Legendary Enchantment|||When Meathook Massacre II enters, each player sacrifices X creatures of their choice.$Whenever a creature you control dies, you may pay 3 life. If you do, return that card under your control with a finality counter on it.$Whenever a creature an opponent controls dies, they may pay 3 life. If they don't, return that card under your control with a finality counter on it.| +Unstoppable Slasher|Duskmourn: House of Horror|294|R|{2}{B}|Creature - Zombie Assassin|2|3|Deathtouch$Whenever this creature deals combat damage to a player, they lose half their life, rounded up.$When this creature dies, if it had no counters on it, return it to the battlefield tapped under its owner's control with two stun counters on it.| +Clockwork Percussionist|Duskmourn: House of Horror|295|C|{R}|Artifact Creature - Monkey Toy|1|1|Haste$When this creature dies, exile the top card of your library. You may play it until the end of your next turn.| +Cursed Recording|Duskmourn: House of Horror|296|R|{2}{R}{R}|Artifact|||Whenever you cast an instant or sorcery spell, put a time counter on this artifact. Then if there are seven or more time counters on it, remove those counters and it deals 20 damage to you.${T}: When you next cast an instant or sorcery spell this turn, copy that spell. You may choose new targets for the copy.| +Norin, Swift Survivalist|Duskmourn: House of Horror|297|U|{R}|Legendary Creature - Human Coward|2|1|Norin can't block.$Whenever a creature you control becomes blocked, you may exile it. You may play that card from exile this turn.| +The Rollercrusher Ride|Duskmourn: House of Horror|298|M|{X}{2}{R}|Legendary Enchantment|||Delirium -- If a source you control would deal noncombat damage to a permanent or player while there are four or more card types among cards in your graveyard, it deals double that damage instead.$When The Rollercrusher Ride enters, it deals X damage to each of up to X target creatures.| +Kona, Rescue Beastie|Duskmourn: House of Horror|299|R|{3}{G}|Legendary Creature - Beast Survivor|4|3|Survival -- At the beginning of your second main phase, if Kona is tapped, you may put a permanent card from your hand onto the battlefield.| +Oblivious Bookworm|Duskmourn: House of Horror|300|U|{G}{U}|Creature - Human Wizard|2|3|At the beginning of your end step, you may draw a card. If you do, discard a card unless a permanent entered the battlefield face down under your control this turn or you turned a permanent face up this turn.| +The Swarmweaver|Duskmourn: House of Horror|301|R|{2}{B}{G}|Legendary Artifact Creature - Scarecrow|2|3|When The Swarmweaver enters, create two 1/1 black and green Insect creature tokens with flying.$Delirium -- As long as there are four or more card types among cards in your graveyard, Insects and Spiders you control get +1/+1 and have deathtouch.| +Ghostly Dancers|Duskmourn: House of Horror|302|R|{3}{W}{W}|Creature - Spirit|2|5|Flying$When this creature enters, return an enchantment card from your graveyard to your hand or unlock a locked door of a Room you control.$Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, create a 3/1 white Spirit creature token with flying.| +Reluctant Role Model|Duskmourn: House of Horror|303|R|{1}{W}|Creature - Human Survivor|2|2|Survival -- At the beginning of your second main phase, if this creature is tapped, put a flying, lifelink, or +1/+1 counter on it.$Whenever this creature or another creature you control dies, if it had counters on it, put those counters on up to one target creature.| +Split Up|Duskmourn: House of Horror|304|R|{1}{W}{W}|Sorcery|||Choose one --$* Destroy all tapped creatures.$* Destroy all untapped creatures.| +Unidentified Hovership|Duskmourn: House of Horror|305|R|{1}{W}{W}|Artifact - Vehicle|2|2|Flying$When this Vehicle enters, exile up to one target creature with toughness 5 or less.$When this Vehicle leaves the battlefield, the exiled card's owner manifests dread.$Crew 1| +Unwanted Remake|Duskmourn: House of Horror|306|U|{W}|Instant|||Destroy target creature. Its controller manifests dread.| +Entity Tracker|Duskmourn: House of Horror|307|R|{2}{U}|Creature - Human Scout|2|3|Flash$Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, draw a card.| +Marina Vendrell's Grimoire|Duskmourn: House of Horror|308|R|{5}{U}|Legendary Artifact|||When Marina Vendrell's Grimoire enters, if you cast it, draw five cards.$You have no maximum hand size and don't lose the game for having 0 or less life.$Whenever you gain life, draw that many cards.$Whenever you lose life, discard that many cards. Then if you have no cards in hand, you lose the game.| +Come Back Wrong|Duskmourn: House of Horror|309|R|{2}{B}|Sorcery|||Destroy target creature. If a creature card is put into a graveyard this way, return it to the battlefield under your control. Sacrifice it at the beginning of your next end step.| +Demonic Counsel|Duskmourn: House of Horror|310|R|{1}{B}|Sorcery|||Search your library for a Demon card, reveal it, put it into your hand, then shuffle.$Delirium -- If there are four or more card types among cards in your graveyard, instead search your library for any card, put it into your hand, then shuffle.| +Meathook Massacre II|Duskmourn: House of Horror|311|M|{X}{X}{B}{B}{B}{B}|Legendary Enchantment|||When Meathook Massacre II enters, each player sacrifices X creatures of their choice.$Whenever a creature you control dies, you may pay 3 life. If you do, return that card under your control with a finality counter on it.$Whenever a creature an opponent controls dies, they may pay 3 life. If they don't, return that card under your control with a finality counter on it.| +Unstoppable Slasher|Duskmourn: House of Horror|312|R|{2}{B}|Creature - Zombie Assassin|2|3|Deathtouch$Whenever this creature deals combat damage to a player, they lose half their life, rounded up.$When this creature dies, if it had no counters on it, return it to the battlefield tapped under its owner's control with two stun counters on it.| +Withering Torment|Duskmourn: House of Horror|313|U|{2}{B}|Instant|||Destroy target creature or enchantment. You lose 2 life.| +Chainsaw|Duskmourn: House of Horror|314|R|{1}{R}|Artifact - Equipment|||When this Equipment enters, it deals 3 damage to up to one target creature.$Whenever one or more creatures die, put a rev counter on this Equipment.$Equipped creature gets +X/+0, where X is the number of rev counters on this Equipment.$Equip {3}| +Cursed Recording|Duskmourn: House of Horror|315|R|{2}{R}{R}|Artifact|||Whenever you cast an instant or sorcery spell, put a time counter on this artifact. Then if there are seven or more time counters on it, remove those counters and it deals 20 damage to you.${T}: When you next cast an instant or sorcery spell this turn, copy that spell. You may choose new targets for the copy.| +Fear of Missing Out|Duskmourn: House of Horror|316|R|{1}{R}|Enchantment Creature - Nightmare|2|3|When this creature enters, discard a card, then draw a card.$Delirium -- Whenever this creature attacks for the first time each turn, if there are four or more card types among cards in your graveyard, untap target creature. After this phase, there is an additional combat phase.| +The Rollercrusher Ride|Duskmourn: House of Horror|317|M|{X}{2}{R}|Legendary Enchantment|||Delirium -- If a source you control would deal noncombat damage to a permanent or player while there are four or more card types among cards in your graveyard, it deals double that damage instead.$When The Rollercrusher Ride enters, it deals X damage to each of up to X target creatures.| +Waltz of Rage|Duskmourn: House of Horror|318|R|{3}{R}{R}|Sorcery|||Target creature you control deals damage equal to its power to each other creature. Until end of turn, whenever a creature you control dies, exile the top card of your library. You may play it until the end of your next turn.| +Balustrade Wurm|Duskmourn: House of Horror|319|R|{3}{G}{G}|Creature - Wurm|5|5|This spell can't be countered.$Trample, haste$Delirium -- {2}{G}{G}: Return this card from your graveyard to the battlefield with a finality counter on it. Activate only if there are four or more card types among cards in your graveyard and only as a sorcery.| +Hedge Shredder|Duskmourn: House of Horror|320|R|{2}{G}{G}|Artifact - Vehicle|5|5|Whenever this Vehicle attacks, you may mill two cards.$Whenever one or more land cards are put into your graveyard from your library, put them onto the battlefield tapped.$Crew 1| +Insidious Fungus|Duskmourn: House of Horror|321|U|{G}|Creature - Fungus|1|2|{2}, Sacrifice this creature: Choose one --$* Destroy target artifact.$* Destroy target enchantment.$* Draw a card. Then you may put a land card from your hand onto the battlefield tapped.| +Omnivorous Flytrap|Duskmourn: House of Horror|322|R|{2}{G}|Creature - Plant|2|4|Delirium -- Whenever this creature enters or attacks, if there are four or more card types among cards in your graveyard, distribute two +1/+1 counters among one or two target creatures. Then if there are six or more card types among cards in your graveyard, double the number of +1/+1 counters on those creatures.| +Under the Skin|Duskmourn: House of Horror|323|U|{2}{G}|Sorcery|||Manifest dread.$You may return a permanent card from your graveyard to your hand.| +Valgavoth's Onslaught|Duskmourn: House of Horror|324|R|{X}{X}{G}|Sorcery|||Manifest dread X times, then put X +1/+1 counters on each of those creatures.| +Peer Past the Veil|Duskmourn: House of Horror|325|R|{2}{R}{G}|Instant|||Discard your hand. Then draw X cards, where X is the number of card types among cards in your graveyard.| +Ghost Vacuum|Duskmourn: House of Horror|326|R|{1}|Artifact|||{T}: Exile target card from a graveyard.${6}, {T}, Sacrifice this artifact: Put each creature card exiled with this artifact onto the battlefield under your control with a flying counter on it. Each of them is a 1/1 Spirit in addition to its other types. Activate only as a sorcery.| +Valgavoth's Lair|Duskmourn: House of Horror|327|R||Enchantment Land|||Hexproof$This land enters tapped. As it enters, choose a color.${T}: Add one mana of the chosen color.| +Kaito, Bane of Nightmares|Duskmourn: House of Horror|328|M|{2}{U}{B}|Legendary Planeswalker - Kaito|4|Ninjutsu {1}{U}{B}$During your turn, as long as Kaito has one or more loyalty counters on him, he's a 3/4 Ninja creature and has hexproof.$+1: You get an emblem with "Ninjas you control get +1/+1."$0: Surveil 2. Then draw a card for each opponent who lost life this turn.$-2: Tap target creature. Put two stun counters on it.| +Blazemire Verge|Duskmourn: House of Horror|329|R||Land|||{T}: Add {B}.${T}: Add {R}. Activate only if you control a Swamp or a Mountain.| +Floodfarm Verge|Duskmourn: House of Horror|330|R||Land|||{T}: Add {W}.${T}: Add {U}. Activate only if you control a Plains or an Island.| +Gloomlake Verge|Duskmourn: House of Horror|331|R||Land|||{T}: Add {U}.${T}: Add {B}. Activate only if you control an Island or a Swamp.| +Hushwood Verge|Duskmourn: House of Horror|332|R||Land|||{T}: Add {G}.${T}: Add {W}. Activate only if you control a Forest or a Plains.| +Thornspire Verge|Duskmourn: House of Horror|333|R||Land|||{T}: Add {R}.${T}: Add {G}. Activate only if you control a Mountain or a Forest.| +Dazzling Theater // Prop Room|Duskmourn: House of Horror|334|R|{3}{W}|Enchantment - Room|||Creature spells you cast have convoke.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Prop Room${2}{W}$Enchantment -- Room$Untap each creature you control during each other player's untap step.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| +Dollmaker's Shop // Porcelain Gallery|Duskmourn: House of Horror|335|M|{1}{W}|Enchantment - Room|||Whenever one or more non-Toy creatures you control attack a player, create a 1/1 white Toy artifact creature token.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Porcelain Gallery${4}{W}{W}$Enchantment -- Room$Creatures you control have base power and toughness each equal to the number of creatures you control.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| +Central Elevator // Promising Stairs|Duskmourn: House of Horror|336|R|{3}{U}|Enchantment - Room|||When you unlock this door, search your library for a Room card that doesn't have the same name as a Room you control, reveal it, put it into your hand, then shuffle.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Promising Stairs${2}{U}$Enchantment -- Room$At the beginning of your upkeep, surveil 1. You win the game if there are eight or more different names among unlocked doors of Rooms you control.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| +Mirror Room // Fractured Realm|Duskmourn: House of Horror|337|M|{2}{U}|Enchantment - Room|||When you unlock this door, create a token that's a copy of target creature you control, except it's a Reflection in addition to its other creature types.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Fractured Realm${5}{U}{U}$Enchantment -- Room$If a triggered ability of a permanent you control triggers, that ability triggers an additional time.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| +Funeral Room // Awakening Hall|Duskmourn: House of Horror|338|M|{2}{B}|Enchantment - Room|||Whenever a creature you control dies, each opponent loses 1 life and you gain 1 life.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Awakening Hall${6}{B}{B}$Enchantment -- Room$When you unlock this door, return all creature cards from your graveyard to the battlefield.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| +Unholy Annex // Ritual Chamber|Duskmourn: House of Horror|339|R|{2}{B}|Enchantment - Room|||At the beginning of your end step, draw a card. If you control a Demon, each opponent loses 2 life and you gain 2 life. Otherwise, you lose 2 life.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Ritual Chamber${3}{B}{B}$Enchantment -- Room$When you unlock this door, create a 6/6 black Demon creature token with flying.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| +Charred Foyer // Warped Space|Duskmourn: House of Horror|340|M|{3}{R}|Enchantment - Room|||At the beginning of your upkeep, exile the top card of your library. You may play it this turn.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Warped Space${4}{R}{R}$Enchantment -- Room$Once each turn, you may pay {0} rather than pay the mana cost for a spell you cast from exile.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| +Walk-In Closet // Forgotten Cellar|Duskmourn: House of Horror|341|M|{2}{G}|Enchantment - Room|||You may play lands from your graveyard.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Forgotten Cellar${3}{G}{G}$Enchantment -- Room$When you unlock this door, you may cast spells from your graveyard this turn, and if a card would be put into your graveyard from anywhere this turn, exile it instead.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| +Restricted Office // Lecture Hall|Duskmourn: House of Horror|342|R|{2}{W}{W}|Enchantment - Room|||When you unlock this door, destroy all creatures with power 3 or greater.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Lecture Hall${5}{U}{U}$Enchantment -- Room$Other permanents you control have hexproof.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| +Roaring Furnace // Steaming Sauna|Duskmourn: House of Horror|343|R|{1}{R}|Enchantment - Room|||When you unlock this door, this Room deals damage equal to the number of cards in your hand to target creature an opponent controls.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)$Steaming Sauna${3}{U}{U}$Enchantment -- Room$You have no maximum hand size.$At the beginning of your end step, draw a card.$(You may cast either half. That door unlocks on the battlefield. As a sorcery, you may pay the mana cost of a locked door to unlock it.)| +Abhorrent Oculus|Duskmourn: House of Horror|344|M|{2}{U}|Creature - Eye|5|5|As an additional cost to cast this spell, exile six cards from your graveyard.$Flying$At the beginning of each opponent's upkeep, manifest dread.| +Silent Hallcreeper|Duskmourn: House of Horror|345|R|{1}{U}|Enchantment Creature - Horror|1|1|This creature can't be blocked.$Whenever this creature deals combat damage to a player, choose one that hasn't been chosen --$* Put two +1/+1 counters on this creature.$* Draw a card.$* This creature becomes a copy of another target creature you control.| +Doomsday Excruciator|Duskmourn: House of Horror|346|R|{B}{B}{B}{B}{B}{B}|Creature - Demon|6|6|Flying$When this creature enters, if it was cast, each player exiles all but the bottom six cards of their library face down.$At the beginning of your upkeep, draw a card.| +Razorkin Needlehead|Duskmourn: House of Horror|347|R|{R}{R}|Creature - Human Assassin|2|2|This creature has first strike during your turn.$Whenever an opponent draws a card, this creature deals 1 damage to them.| +Screaming Nemesis|Duskmourn: House of Horror|348|M|{2}{R}|Creature - Spirit|3|3|Haste$Whenever this creature is dealt damage, it deals that much damage to any other target. If a player is dealt damage this way, they can't gain life for the rest of the game.| +Hauntwoods Shrieker|Duskmourn: House of Horror|349|M|{1}{G}{G}|Creature - Beast Mutant|3|3|Whenever this creature attacks, manifest dread.${1}{G}: Reveal target face-down permanent. If it's a creature card, you may turn it face up.| +Undead Sprinter|Duskmourn: House of Horror|350|R|{B}{R}|Creature - Zombie|2|2|Trample, haste$You may cast this card from your graveyard if a non-Zombie creature died this turn. If you do, this creature enters with a +1/+1 counter on it.| +The Wandering Rescuer|Duskmourn: House of Horror|351|M|{3}{W}{W}|Legendary Creature - Human Samurai Noble|3|4|Flash$Convoke$Double strike$Other tapped creatures you control have hexproof.| +Valgavoth, Terror Eater|Duskmourn: House of Horror|352|M|{6}{B}{B}{B}|Legendary Creature - Elder Demon|9|9|Flying, lifelink$Ward--Sacrifice three nonland permanents.$If a card you didn't control would be put into an opponent's graveyard from anywhere, exile it instead.$During your turn, you may play cards exiled with Valgavoth. If you cast a spell this way, pay life equal to its mana value rather than pay its mana cost.| +Tyvar, the Pummeler|Duskmourn: House of Horror|353|M|{1}{G}{G}|Legendary Creature - Elf Warrior|3|3|Tap another untapped creature you control: Tyvar gains indestructible until end of turn. Tap it.${3}{G}{G}: Creatures you control get +X/+X until end of turn, where X is the greatest power among creatures you control.| +Kaito, Bane of Nightmares|Duskmourn: House of Horror|354|M|{2}{U}{B}|Legendary Planeswalker - Kaito|4|Ninjutsu {1}{U}{B}$During your turn, as long as Kaito has one or more loyalty counters on him, he's a 3/4 Ninja creature and has hexproof.$+1: You get an emblem with "Ninjas you control get +1/+1."$0: Surveil 2. Then draw a card for each opponent who lost life this turn.$-2: Tap target creature. Put two stun counters on it.| +Niko, Light of Hope|Duskmourn: House of Horror|355|M|{2}{W}{U}|Legendary Creature - Human Wizard|3|4|When Niko enters, create two Shard tokens.${2}, {T}: Exile target nonlegendary creature you control. Shards you control become copies of it until the next end step. Return it to the battlefield under its owner's control at the beginning of the next end step.| +Toby, Beastie Befriender|Duskmourn: House of Horror|356|R|{2}{W}|Legendary Creature - Human Wizard|1|1|When Toby enters, create a 4/4 white Beast creature token with "This token can't attack or block alone."$As long as you control four or more creature tokens, creature tokens you control have flying.| +The Mindskinner|Duskmourn: House of Horror|357|R|{U}{U}{U}|Legendary Enchantment Creature - Nightmare|10|1|The Mindskinner can't be blocked.$If a source you control would deal damage to an opponent, prevent that damage and each opponent mills that many cards.| +Kona, Rescue Beastie|Duskmourn: House of Horror|358|R|{3}{G}|Legendary Creature - Beast Survivor|4|3|Survival -- At the beginning of your second main phase, if Kona is tapped, you may put a permanent card from your hand onto the battlefield.| +The Jolly Balloon Man|Duskmourn: House of Horror|359|R|{1}{R}{W}|Legendary Creature - Human Clown|1|4|Haste${1}, {T}: Create a token that's a copy of another target creature you control, except it's a 1/1 red Balloon creature in addition to its other colors and types and it has flying and haste. Sacrifice it at the beginning of the next end step. Activate only as a sorcery.| +Marina Vendrell|Duskmourn: House of Horror|360|R|{W}{U}{B}{R}{G}|Legendary Creature - Human Warlock|3|5|When Marina Vendrell enters, reveal the top seven cards of your library. Put all enchantment cards from among them into your hand and the rest on the bottom of your library in a random order.${T}: Lock or unlock a door of target Room you control. Activate only as a sorcery.| +Nashi, Searcher in the Dark|Duskmourn: House of Horror|361|R|{U}{B}|Legendary Creature - Rat Ninja Wizard|2|2|Menace$Whenever Nashi deals combat damage to a player, you mill that many cards. You may put any number of legendary and/or enchantment cards from among them into your hand. If you put no cards into your hand this way, put a +1/+1 counter on Nashi.| +Rip, Spawn Hunter|Duskmourn: House of Horror|362|R|{2}{G}{W}|Legendary Creature - Human Survivor|4|4|Survival -- At the beginning of your second main phase, if Rip is tapped, reveal the top X cards of your library, where X is its power. Put any number of creature and/or Vehicle cards with different powers from among them into your hand. Put the rest on the bottom of your library in a random order.| +The Swarmweaver|Duskmourn: House of Horror|363|R|{2}{B}{G}|Legendary Artifact Creature - Scarecrow|2|3|When The Swarmweaver enters, create two 1/1 black and green Insect creature tokens with flying.$Delirium -- As long as there are four or more card types among cards in your graveyard, Insects and Spiders you control get +1/+1 and have deathtouch.| +Victor, Valgavoth's Seneschal|Duskmourn: House of Horror|364|R|{1}{W}{B}|Legendary Creature - Human Warlock|3|3|Eerie -- Whenever an enchantment you control enters and whenever you fully unlock a Room, surveil 2 if this is the first time this ability has resolved this turn. If it's the second time, each opponent discards a card. If it's the third time, put a creature card from a graveyard onto the battlefield under your control.| +Winter, Misanthropic Guide|Duskmourn: House of Horror|365|R|{1}{B}{R}{G}|Legendary Creature - Human Warlock|3|4|Ward {2}$At the beginning of your upkeep, each player draws two cards.$Delirium -- As long as there are four or more card types among cards in your graveyard, each opponent's maximum hand size is equal to seven minus the number of those card types.| +Zimone, All-Questioning|Duskmourn: House of Horror|366|R|{1}{G}{U}|Legendary Creature - Human Wizard|1|1|At the beginning of your end step, if a land entered the battlefield under your control this turn and you control a prime number of lands, create Primo, the Indivisible, a legendary 0/0 green and blue Fractal creature token, then put that many +1/+1 counters on it.| +Marvin, Murderous Mimic|Duskmourn: House of Horror|367|R|{2}|Legendary Artifact Creature - Toy|2|2|Marvin has all activated abilities of creatures you control that don't have the same name as this creature.| +Enduring Innocence|Duskmourn: House of Horror|368|R|{1}{W}{W}|Enchantment Creature - Sheep Glimmer|2|1|Lifelink$Whenever one or more other creatures you control with power 2 or less enter, draw a card. This ability triggers only once each turn.$When Enduring Innocence dies, if it was a creature, return it to the battlefield under its owner's control. It's an enchantment.| +Leyline of Hope|Duskmourn: House of Horror|369|R|{2}{W}{W}|Enchantment|||If this card is in your opening hand, you may begin the game with it on the battlefield.$If you would gain life, you gain that much life plus 1 instead.$As long as you have at least 7 life more than your starting life total, creatures you control get +2/+2.| +Overlord of the Mistmoors|Duskmourn: House of Horror|370|M|{5}{W}{W}|Enchantment Creature - Avatar Horror|6|6|Impending 4--{2}{W}{W}$Whenever this permanent enters or attacks, create two 2/1 white Insect creature tokens with flying.| +Enduring Curiosity|Duskmourn: House of Horror|371|R|{2}{U}{U}|Enchantment Creature - Cat Glimmer|4|3|Flash$Whenever a creature you control deals combat damage to a player, draw a card.$When Enduring Curiosity dies, if it was a creature, return it to the battlefield under its owner's control. It's an enchantment.| +Leyline of Transformation|Duskmourn: House of Horror|372|R|{2}{U}{U}|Enchantment|||If this card is in your opening hand, you may begin the game with it on the battlefield.$As this enchantment enters, choose a creature type.$Creatures you control are the chosen type in addition to their other types. The same is true for creature spells you control and creature cards you own that aren't on the battlefield.| +Overlord of the Floodpits|Duskmourn: House of Horror|373|M|{3}{U}{U}|Enchantment Creature - Avatar Horror|5|3|Impending 4--{1}{U}{U}$Flying$Whenever this permanent enters or attacks, draw two cards, then discard a card.| +Enduring Tenacity|Duskmourn: House of Horror|374|R|{2}{B}{B}|Enchantment Creature - Snake Glimmer|4|3|Whenever you gain life, target opponent loses that much life.$When Enduring Tenacity dies, if it was a creature, return it to the battlefield under its owner's control. It's an enchantment.| +Grievous Wound|Duskmourn: House of Horror|375|R|{3}{B}{B}|Enchantment - Aura|||Enchant player$Enchanted player can't gain life.$Whenever enchanted player is dealt damage, they lose half their life, rounded up.| +Leyline of the Void|Duskmourn: House of Horror|376|R|{2}{B}{B}|Enchantment|||If this card is in your opening hand, you may begin the game with it on the battlefield.$If a card would be put into an opponent's graveyard from anywhere, exile it instead.| +Overlord of the Balemurk|Duskmourn: House of Horror|377|M|{3}{B}{B}|Enchantment Creature - Avatar Horror|5|5|Impending 5--{1}{B}$Whenever this permanent enters or attacks, mill four cards, then you may return a non-Avatar creature card or a planeswalker card from your graveyard to your hand.| +Enduring Courage|Duskmourn: House of Horror|378|R|{2}{R}{R}|Enchantment Creature - Dog Glimmer|3|3|Whenever another creature you control enters, it gets +2/+0 and gains haste until end of turn.$When Enduring Courage dies, if it was a creature, return it to the battlefield under its owner's control. It's an enchantment.| +Leyline of Resonance|Duskmourn: House of Horror|379|R|{2}{R}{R}|Enchantment|||If this card is in your opening hand, you may begin the game with it on the battlefield.$Whenever you cast an instant or sorcery spell that targets only a single creature you control, copy that spell. You may choose new targets for the copy.| +Overlord of the Boilerbilges|Duskmourn: House of Horror|380|M|{4}{R}{R}|Enchantment Creature - Avatar Horror|5|5|Impending 4--{2}{R}{R}$Whenever this permanent enters or attacks, it deals 4 damage to any target.| +Enduring Vitality|Duskmourn: House of Horror|381|R|{1}{G}{G}|Enchantment Creature - Elk Glimmer|3|3|Vigilance$Creatures you control have "{T}: Add one mana of any color."$When Enduring Vitality dies, if it was a creature, return it to the battlefield under its owner's control. It's an enchantment.| +Leyline of Mutation|Duskmourn: House of Horror|382|R|{2}{G}{G}|Enchantment|||If this card is in your opening hand, you may begin the game with it on the battlefield.$You may pay {W}{U}{B}{R}{G} rather than pay the mana cost for spells you cast.| +Overlord of the Hauntwoods|Duskmourn: House of Horror|383|M|{3}{G}{G}|Enchantment Creature - Avatar Horror|6|5|Impending 4--{1}{G}{G}$Whenever this permanent enters or attacks, create a tapped colorless land token named Everywhere that is every basic land type.| +Twitching Doll|Duskmourn: House of Horror|384|R|{1}{G}|Artifact Creature - Spider Toy|2|2|{T}: Add one mana of any color. Put a nest counter on this creature.${T}, Sacrifice this creature: Create a 2/2 green Spider creature token with reach for each counter on this creature. Activate only as a sorcery.| +Dissection Tools|Duskmourn: House of Horror|385|R|{5}|Artifact - Equipment|||When this Equipment enters, manifest dread, then attach this Equipment to that creature.$Equipped creature gets +2/+2 and has deathtouch and lifelink.$Equip--Sacrifice a creature.| +Enduring Innocence|Duskmourn: House of Horror|386|M|{1}{W}{W}|Enchantment Creature - Sheep Glimmer|2|1|Lifelink$Whenever one or more other creatures you control with power 2 or less enter, draw a card. This ability triggers only once each turn.$When Enduring Innocence dies, if it was a creature, return it to the battlefield under its owner's control. It's an enchantment.| +Overlord of the Mistmoors|Duskmourn: House of Horror|387|M|{5}{W}{W}|Enchantment Creature - Avatar Horror|6|6|Impending 4--{2}{W}{W}$Whenever this permanent enters or attacks, create two 2/1 white Insect creature tokens with flying.| +Enduring Curiosity|Duskmourn: House of Horror|388|M|{2}{U}{U}|Enchantment Creature - Cat Glimmer|4|3|Flash$Whenever a creature you control deals combat damage to a player, draw a card.$When Enduring Curiosity dies, if it was a creature, return it to the battlefield under its owner's control. It's an enchantment.| +Overlord of the Floodpits|Duskmourn: House of Horror|389|M|{3}{U}{U}|Enchantment Creature - Avatar Horror|5|3|Impending 4--{1}{U}{U}$Flying$Whenever this permanent enters or attacks, draw two cards, then discard a card.| +Enduring Tenacity|Duskmourn: House of Horror|390|M|{2}{B}{B}|Enchantment Creature - Snake Glimmer|4|3|Whenever you gain life, target opponent loses that much life.$When Enduring Tenacity dies, if it was a creature, return it to the battlefield under its owner's control. It's an enchantment.| +Overlord of the Balemurk|Duskmourn: House of Horror|391|M|{3}{B}{B}|Enchantment Creature - Avatar Horror|5|5|Impending 5--{1}{B}$Whenever this permanent enters or attacks, mill four cards, then you may return a non-Avatar creature card or a planeswalker card from your graveyard to your hand.| +Enduring Courage|Duskmourn: House of Horror|392|M|{2}{R}{R}|Enchantment Creature - Dog Glimmer|3|3|Whenever another creature you control enters, it gets +2/+0 and gains haste until end of turn.$When Enduring Courage dies, if it was a creature, return it to the battlefield under its owner's control. It's an enchantment.| +Overlord of the Boilerbilges|Duskmourn: House of Horror|393|M|{4}{R}{R}|Enchantment Creature - Avatar Horror|5|5|Impending 4--{2}{R}{R}$Whenever this permanent enters or attacks, it deals 4 damage to any target.| +Enduring Vitality|Duskmourn: House of Horror|394|M|{1}{G}{G}|Enchantment Creature - Elk Glimmer|3|3|Vigilance$Creatures you control have "{T}: Add one mana of any color."$When Enduring Vitality dies, if it was a creature, return it to the battlefield under its owner's control. It's an enchantment.| +Overlord of the Hauntwoods|Duskmourn: House of Horror|395|M|{3}{G}{G}|Enchantment Creature - Avatar Horror|6|5|Impending 4--{1}{G}{G}$Whenever this permanent enters or attacks, create a tapped colorless land token named Everywhere that is every basic land type.| +Enduring Innocence|Duskmourn: House of Horror|396|M|{1}{W}{W}|Enchantment Creature - Sheep Glimmer|2|1|Lifelink$Whenever one or more other creatures you control with power 2 or less enter, draw a card. This ability triggers only once each turn.$When Enduring Innocence dies, if it was a creature, return it to the battlefield under its owner's control. It's an enchantment.| +Overlord of the Mistmoors|Duskmourn: House of Horror|397|M|{5}{W}{W}|Enchantment Creature - Avatar Horror|6|6|Impending 4--{2}{W}{W}$Whenever this permanent enters or attacks, create two 2/1 white Insect creature tokens with flying.| +Enduring Curiosity|Duskmourn: House of Horror|398|M|{2}{U}{U}|Enchantment Creature - Cat Glimmer|4|3|Flash$Whenever a creature you control deals combat damage to a player, draw a card.$When Enduring Curiosity dies, if it was a creature, return it to the battlefield under its owner's control. It's an enchantment.| +Overlord of the Floodpits|Duskmourn: House of Horror|399|M|{3}{U}{U}|Enchantment Creature - Avatar Horror|5|3|Impending 4--{1}{U}{U}$Flying$Whenever this permanent enters or attacks, draw two cards, then discard a card.| +Enduring Tenacity|Duskmourn: House of Horror|400|M|{2}{B}{B}|Enchantment Creature - Snake Glimmer|4|3|Whenever you gain life, target opponent loses that much life.$When Enduring Tenacity dies, if it was a creature, return it to the battlefield under its owner's control. It's an enchantment.| +Overlord of the Balemurk|Duskmourn: House of Horror|401|M|{3}{B}{B}|Enchantment Creature - Avatar Horror|5|5|Impending 5--{1}{B}$Whenever this permanent enters or attacks, mill four cards, then you may return a non-Avatar creature card or a planeswalker card from your graveyard to your hand.| +Enduring Courage|Duskmourn: House of Horror|402|M|{2}{R}{R}|Enchantment Creature - Dog Glimmer|3|3|Whenever another creature you control enters, it gets +2/+0 and gains haste until end of turn.$When Enduring Courage dies, if it was a creature, return it to the battlefield under its owner's control. It's an enchantment.| +Overlord of the Boilerbilges|Duskmourn: House of Horror|403|M|{4}{R}{R}|Enchantment Creature - Avatar Horror|5|5|Impending 4--{2}{R}{R}$Whenever this permanent enters or attacks, it deals 4 damage to any target.| +Enduring Vitality|Duskmourn: House of Horror|404|M|{1}{G}{G}|Enchantment Creature - Elk Glimmer|3|3|Vigilance$Creatures you control have "{T}: Add one mana of any color."$When Enduring Vitality dies, if it was a creature, return it to the battlefield under its owner's control. It's an enchantment.| +Overlord of the Hauntwoods|Duskmourn: House of Horror|405|M|{3}{G}{G}|Enchantment Creature - Avatar Horror|6|5|Impending 4--{1}{G}{G}$Whenever this permanent enters or attacks, create a tapped colorless land token named Everywhere that is every basic land type.| +The Wandering Rescuer|Duskmourn: House of Horror|406|M|{3}{W}{W}|Legendary Creature - Human Samurai Noble|3|4|Flash$Convoke$Double strike$Other tapped creatures you control have hexproof.| +Valgavoth, Terror Eater|Duskmourn: House of Horror|407|M|{6}{B}{B}{B}|Legendary Creature - Elder Demon|9|9|Flying, lifelink$Ward--Sacrifice three nonland permanents.$If a card you didn't control would be put into an opponent's graveyard from anywhere, exile it instead.$During your turn, you may play cards exiled with Valgavoth. If you cast a spell this way, pay life equal to its mana value rather than pay its mana cost.| +Tyvar, the Pummeler|Duskmourn: House of Horror|408|M|{1}{G}{G}|Legendary Creature - Elf Warrior|3|3|Tap another untapped creature you control: Tyvar gains indestructible until end of turn. Tap it.${3}{G}{G}: Creatures you control get +X/+X until end of turn, where X is the greatest power among creatures you control.| +Kaito, Bane of Nightmares|Duskmourn: House of Horror|409|M|{2}{U}{B}|Legendary Planeswalker - Kaito|4|Ninjutsu {1}{U}{B}$During your turn, as long as Kaito has one or more loyalty counters on him, he's a 3/4 Ninja creature and has hexproof.$+1: You get an emblem with "Ninjas you control get +1/+1."$0: Surveil 2. Then draw a card for each opponent who lost life this turn.$-2: Tap target creature. Put two stun counters on it.| +Niko, Light of Hope|Duskmourn: House of Horror|410|M|{2}{W}{U}|Legendary Creature - Human Wizard|3|4|When Niko enters, create two Shard tokens.${2}, {T}: Exile target nonlegendary creature you control. Shards you control become copies of it until the next end step. Return it to the battlefield under its owner's control at the beginning of the next end step.| +Shardmage's Rescue|Duskmourn: House of Horror|411|U|{W}|Enchantment - Aura|||Flash$Enchant creature you control$As long as this Aura entered this turn, enchanted creature has hexproof.$Enchanted creature gets +1/+1.| +Valgavoth's Faithful|Duskmourn: House of Horror|412|U|{B}|Creature - Human Cleric|1|1|{3}{B}, Sacrifice this creature: Return target creature card from your graveyard to the battlefield. Activate only as a sorcery.| +Pyroclasm|Duskmourn: House of Horror|413|U|{1}{R}|Sorcery|||Pyroclasm deals 2 damage to each creature.| +Drag to the Roots|Duskmourn: House of Horror|414|U|{2}{B}{G}|Instant|||Delirium -- This spell costs {2} less to cast as long as there are four or more card types among cards in your graveyard.$Destroy target nonland permanent.| +Inquisitive Glimmer|Duskmourn: House of Horror|415|U|{W}{U}|Enchantment Creature - Fox Glimmer|2|3|Enchantment spells you cast cost {1} less to cast.$Unlock costs you pay cost {1} less.| +Grievous Wound|Duskmourn: House of Horror|416|R|{3}{B}{B}|Enchantment - Aura|||Enchant player$Enchanted player can't gain life.$Whenever enchanted player is dealt damage, they lose half their life, rounded up.| +Twitching Doll|Duskmourn: House of Horror|417|R|{1}{G}|Artifact Creature - Spider Toy|2|2|{T}: Add one mana of any color. Put a nest counter on this creature.${T}, Sacrifice this creature: Create a 2/2 green Spider creature token with reach for each counter on this creature. Activate only as a sorcery.| Sire of Seven Deaths|Foundations|1|M|{7}|Creature - Eldrazi|7|7|First strike, vigilance$Menace, trample$Reach, lifelink$Ward--Pay 7 life.| Arahbo, the First Fang|Foundations|2|R|{2}{W}|Legendary Creature - Cat Avatar|2|2|Other Cats you control get +1/+1.$Whenever Arahbo or another nontoken Cat you control enters, create a 1/1 white Cat creature token.| Armasaur Guide|Foundations|3|C|{4}{W}|Creature - Dinosaur|4|4|Vigilance$Whenever you attack with three or more creatures, put a +1/+1 counter on target creature you control.| @@ -57179,7 +57321,7 @@ Vineglimmer Snarl|Aetherdrift Commander|183|R||Land|||As Vineglimmer Snarl enter Yavimaya Coast|Aetherdrift Commander|184|R||Land|||{T}: Add {C}.${T}: Add {G} or {U}. Yavimaya Coast deals 1 damage to you.| Celes, Rune Knight|Final Fantasy Commander|1|M|{1}{R}{W}{B}|Legendary Creature - Human Wizard Knight|4|4|When Celes enters, discard any number of cards, then draw that many cards plus one.$Whenever one or more other creatures you control enter, if one or more of them entered from a graveyard or was cast from a graveyard, put a +1/+1 counter on each creature you control.| Cloud, Ex-SOLDIER|Final Fantasy Commander|2|M|{2}{R}{G}{W}|Legendary Creature - Human Soldier Mercenary|4|4|Haste$When Cloud enters, attach up to one target Equipment you control to it.$Whenever Cloud attacks, draw a card for each equipped attacking creature you control. Then if Cloud has power 7 or greater, create two Treasure tokens.| -G'raha Tia, Scion Reborn|Final Fantasy Commander|3|M|{W}{U}{B}|Legendary Creature - Cat Wizard|2|3|Lifelink$Throw Wide the Gates -- Whenever you cast a noncreature spell, you may pay X life, where X is that spell's mana value. If you do, create a 1/1 colorless Hero creature token and put X+1/+1 counters on it. Do this only once each turn.| +G'raha Tia, Scion Reborn|Final Fantasy Commander|3|M|{W}{U}{B}|Legendary Creature - Cat Wizard|2|3|Lifelink$Throw Wide the Gates -- Whenever you cast a noncreature spell, you may pay X life, where X is that spell's mana value. If you do, create a 1/1 colorless Hero creature token and put X +1/+1 counters on it. Do this only once each turn.| Terra, Herald of Hope|Final Fantasy Commander|4|M|{R}{W}{B}|Legendary Creature - Human Wizard Warrior|3|3|Trance -- At the beginning of combat on your turn, mill two cards. Terra gains flying until end of turn.$Whenever Terra deals combat damage to a player, you may pay {2}. When you do, return target creature card with power 3 or less from your graveyard to the battlefield tapped.| Tidus, Yuna's Guardian|Final Fantasy Commander|5|M|{G}{W}{U}|Legendary Creature - Human Warrior|3|3|At the beginning of combat on your turn, you may move a counter from target creature you control onto a second target creature you control.$Cheer -- Whenever one or more creatures you control with counters on them deal combat damage to a player, you may draw a card and proliferate. Do this only once each turn.| Tifa, Martial Artist|Final Fantasy Commander|6|M|{1}{R}{G}{W}|Legendary Creature - Human Monk|4|4|Melee$Whenever one or more creatures you control with power 7 or greater deal combat damage to a player, untap all creatures you control. If it's the first combat phase of your turn, there is an additional combat phase after this phase.| @@ -57189,9 +57331,9 @@ Alisaie Leveilleur|Final Fantasy Commander|9|R|{2}{W}|Legendary Creature - Elf W Auron, Venerated Guardian|Final Fantasy Commander|10|R|{3}{W}|Legendary Creature - Human Spirit Samurai|2|5|Vigilance$Shooting Star -- Whenever Auron attacks, put a +1/+1 counter on it. When you do, exile target creature defending player controls with power less than Auron's power until Auron leaves the battlefield.| Champions from Beyond|Final Fantasy Commander|11|R|{X}{W}{W}|Enchantment|||When this enchantment enters, create X 1/1 colorless Hero creature tokens.$Light Party -- Whenever you attack with four or more creatures, scry 2, then draw a card.$Full Party -- Whenever you attack with eight or more creatures, those creatures get +4/+4 until end of turn.| Chocobo Knights|Final Fantasy Commander|12|R|{3}{W}|Creature - Human Knight|3|3|Whenever you attack, creatures you control with counters on them gain double strike until end of turn.| -Cid, Freeflier Pilot|Final Fantasy Commander|13|R|{1}{W}|Legendary Creature - Human Warrior Pilot|2|2|Equipment and Vehicle spells you cast cost {1} less to cast.$Jump -- During your turn, Cid has flying.${2},{T}: Return target Equipment or Vehicle card from your graveyard to your hand.| +Cid, Freeflier Pilot|Final Fantasy Commander|13|R|{1}{W}|Legendary Creature - Human Warrior Pilot|2|2|Equipment and Vehicle spells you cast cost {1} less to cast.$Jump -- During your turn, Cid has flying.${2}, {T}: Return target Equipment or Vehicle card from your graveyard to your hand.| Cloud's Limit Break|Final Fantasy Commander|14|R|{1}{W}|Instant|||Tiered$* Cross-Slash -- {0} -- Destroy target tapped creature.$* Blade Beam -- {1} -- Destroy any number of target tapped creatures with different controllers.$* Omnislash -- {3}{W} -- Destroy all tapped creatures.| -Coin of Fate|Final Fantasy Commander|15|R|{1}{W}|Artifact|||When this artifact enters, surveil 1.${3}{W},{T}, Exile two creature cards from your graveyard, Sacrifice this artifact: An opponent chooses one of the exiled cards. You put that card on the bottom of your library and return the other to the battlefield tapped. You become the monarch.| +Coin of Fate|Final Fantasy Commander|15|R|{1}{W}|Artifact|||When this artifact enters, surveil 1.${3}{W}, {T}, Exile two creature cards from your graveyard, Sacrifice this artifact: An opponent chooses one of the exiled cards. You put that card on the bottom of your library and return the other to the battlefield tapped. You become the monarch.| Cyan, Vengeful Samurai|Final Fantasy Commander|16|R|{6}{W}|Legendary Creature - Human Samurai|3|3|This spell costs {1} less to cast for each creature card in your graveyard.$Double strike$Whenever one or more creature cards leave your graveyard, put a +1/+1 counter on Cyan.| Dancer's Chakrams|Final Fantasy Commander|17|R|{3}{W}|Artifact - Equipment|||Job select$Equipped creature gets +2/+2, has lifelink and "Other commanders you control get +2/+2 and have lifelink," and is a Performer in addition to its other types.$Krishna -- Equip {3}| Elena, Turk Recruit|Final Fantasy Commander|18|R|{2}{W}|Legendary Creature - Human Assassin|1|4|When Elena enters, return target non-Assassin historic card from your graveyard to your hand.$Whenever you cast a historic spell, put a +1/+1 counter on Elena.| @@ -57210,14 +57352,14 @@ Tataru Taru|Final Fantasy Commander|30|R|{1}{W}|Legendary Creature - Dwarf Advis Thancred Waters|Final Fantasy Commander|31|R|{4}{W}|Legendary Creature - Human Warrior|3|5|Flash$Royal Guard -- When Thancred Waters enters, another target legendary permanent you control gains indestructible for as long as you control Thancred Waters.$Whenever you cast a noncreature spell, Thancred Waters gains indestructible until end of turn.| Ultimate Magic: Holy|Final Fantasy Commander|32|R|{2}{W}|Instant|||Permanents you control gain indestructible until end of turn. If this spell was cast from exile, prevent all damage that would be dealt to you this turn.$Foretell {2}{W}| Alphinaud Leveilleur|Final Fantasy Commander|33|R|{3}{U}|Legendary Creature - Elf Wizard|2|4|Partner with Alisaie Leveilleur$Vigilance$Eukrasia -- Whenever you cast your second spell each turn, draw a card.| -Blitzball Stadium|Final Fantasy Commander|34|R|{X}{U}|Artifact|||When this artifact enters, support X.$Go for the Goal! -- {3},{T}: Until end of turn, target creature gains "Whenever this creature deals combat damage to a player, draw a card for each kind of counter on it" and it can't be blocked this turn.| +Blitzball Stadium|Final Fantasy Commander|34|R|{X}{U}|Artifact|||When this artifact enters, support X.$Go for the Goal! -- {3}, {T}: Until end of turn, target creature gains "Whenever this creature deals combat damage to a player, draw a card for each kind of counter on it" and it can't be blocked this turn.| Blue Mage's Cane|Final Fantasy Commander|35|R|{2}{U}|Artifact - Equipment|||Job select$Equipped creature gets +0/+2, is a Wizard in addition to its other types, and has "Whenever this creature attacks, exile up to one target instant or sorcery card from defending player's graveyard. If you do, copy it. You may cast the copy by paying {3} rather than paying its mana cost."$Spirit of the Whalaqee -- Equip {2}| Hermes, Overseer of Elpis|Final Fantasy Commander|36|R|{3}{U}|Legendary Creature - Elder Wizard|2|4|Whenever you cast a noncreature spell, create a 1/1 blue Bird creature token with flying and vigilance.$Whenever you attack with one or more Birds, scry 2.| Hraesvelgr of the First Brood|Final Fantasy Commander|37|R|{4}{U}|Legendary Creature - Elder Dragon|5|5|Flying, vigilance, ward {2}$Shiva's Aid -- When Hraesvelgr enters and whenever you cast a noncreature spell, target creature gets +1/+0 until end of turn and can't be blocked this turn.| Lulu, Stern Guardian|Final Fantasy Commander|38|R|{2}{U}|Legendary Creature - Human Wizard|2|3|Whenever an opponent attacks you, choose target creature attacking you. Put a stun counter on that creature.${3}{U}: Proliferate.| O'aka, Traveling Merchant|Final Fantasy Commander|39|R|{1}{U}|Legendary Creature - Human Citizen|1|2|{T}, Remove a counter from a nonland permanent you control: Draw a card.| Observed Stasis|Final Fantasy Commander|40|R|{3}{U}|Enchantment - Aura|||Flash$Enchant creature an opponent controls$When this Aura enters, remove enchanted creature from combat. Then draw a card for each tapped creature its controller controls.$Enchanted creature loses all abilities and can't attack or block.| -Rikku, Resourceful Guardian|Final Fantasy Commander|41|R|{2}{U}|Legendary Creature - Human Artificer|2|3|Whenever you put one or more counters on a creature, until end of turn, that creature can't be blocked by creatures your opponents control.$Steal -- {1},{T}: Move a counter from target creature an opponent controls onto target creature you control. Activate only as a sorcery.| +Rikku, Resourceful Guardian|Final Fantasy Commander|41|R|{2}{U}|Legendary Creature - Human Artificer|2|3|Whenever you put one or more counters on a creature, until end of turn, that creature can't be blocked by creatures your opponents control.$Steal -- {1}, {T}: Move a counter from target creature an opponent controls onto target creature you control. Activate only as a sorcery.| Summon: Valefor|Final Fantasy Commander|42|R|{4}{U}|Enchantment Creature - Saga Drake|5|4|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.)$I - Sonic Wings -- Each opponent chooses a creature with the greatest mana value among creatures they control. Return those creatures to their owners' hands.$II, III, IV - Tap up to one target creature and put a stun counter on it.$Flying| Espers to Magicite|Final Fantasy Commander|43|R|{3}{B}|Instant|||Exile each opponent's graveyard. When you do, choose up to one target creature card exiled this way. Create a token that's a copy of that card, except it's an artifact and it loses all other card types.| Eye of Nidhogg|Final Fantasy Commander|44|R|{2}{B}|Legendary Enchantment - Aura|||Enchant creature$Enchanted creature is a black Dragon with base power and toughness 4/2, has flying and deathtouch, and is goaded.$When Eye of Nidhogg is put into a graveyard from the battlefield, return it to its owner's hand.| @@ -57233,9 +57375,9 @@ Avalanche of Sector 7|Final Fantasy Commander|53|R|{2}{R}|Legendary Creature - H Cait Sith, Fortune Teller|Final Fantasy Commander|54|R|{3}{R}|Legendary Artifact Creature - Cat Moogle|3|3|Lucky Slots -- At the beginning of combat on your turn, scry 1, then exile the top card of your library. You may play that card this turn. When you exile a card this way, target creature you control gets +X/+0 until end of turn, where X is that card's mana value.| Gau, Feral Youth|Final Fantasy Commander|55|R|{1}{R}|Legendary Creature - Human Berserker|2|2|Rage -- Whenever Gau attacks, put a +1/+1 counter on it.$At the beginning of each end step, if a card left your graveyard this turn, Gau deals damage equal to its power to each opponent.| Gogo, Mysterious Mime|Final Fantasy Commander|56|R|{3}{R}|Legendary Creature - Wizard|2|2|At the beginning of combat on your turn, you may have Gogo become a copy of another target creature you control until end of turn, except its name is Gogo, Mysterious Mime. If you do, Gogo and that creature each get +2/+0 and gain haste until end of turn and attack this turn if able.| -Sabin, Master Monk|Final Fantasy Commander|57|R|{4}{R}|Legendary Creature Human Noble Monk|4|3|Double strike$Blitz--{2}{R}{R}, Discard a card.$You may cast this card from your graveyard using its blitz ability.| +Sabin, Master Monk|Final Fantasy Commander|57|R|{4}{R}|Legendary Creature - Human Noble Monk|4|3|Double strike$Blitz--{2}{R}{R}, Discard a card.$You may cast this card from your graveyard using its blitz ability.| Snort|Final Fantasy Commander|58|R|{3}{R}|Sorcery|||Each player may discard their hand and draw five cards. Then Snort deals 5 damage to each opponent who discarded their hand this way.$Flashback {5}{R}| -Strago and Relm|Final Fantasy Commander|59|R|{2}{R}|Legendary Creature - Human Wizard|1|3|Sketch and Lore--{2}{R},{T}: Target opponent exiles cards from the top of their library until they exile an instant, sorcery, or creature card. You may cast that card without paying its mana cost. If you cast a creature spell this way, it gains haste and "At the beginning of the end step, sacrifice this creature." Activate only as a sorcery.| +Strago and Relm|Final Fantasy Commander|59|R|{2}{R}|Legendary Creature - Human Wizard|1|3|Sketch and Lore -- {2}{R}, {T}: Target opponent exiles cards from the top of their library until they exile an instant, sorcery, or creature card. You may cast that card without paying its mana cost. If you cast a creature spell this way, it gains haste and "At the beginning of the end step, sacrifice this creature." Activate only as a sorcery.| Summon: Esper Valigarmanda|Final Fantasy Commander|60|R|{3}{R}|Enchantment Creature - Saga Drake|3|3|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.)$I -- Exile an instant or sorcery card from each graveyard.$II, III, IV -- Add {R} for each lore counter on this Saga. You may cast an instant or sorcery card exiled with this Saga, and mana of any type can be spent to cast that spell.$Flying, haste| Summon: Kujata|Final Fantasy Commander|61|R|{5}{R}|Enchantment Creature - Saga Ox|7|5|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Lightning -- This creature deals 3 damage to each of up to two target creatures.$II -- Ice -- Up to three target creatures can't block this turn.$III -- Fire -- Discard a card, then draw two cards. When you discard a card this way, this creature deals damage equal to that card's mana value to each opponent.$Trample, haste| Ultimate Magic: Meteor|Final Fantasy Commander|62|R|{5}{R}|Sorcery|||Ultimate Magic: Meteor deals 7 damage to each creature. If this spell was cast from exile, for each opponent, choose an artifact or land that player controls. Destroy the chosen permanents.$Foretell {5}{R}| @@ -57247,14 +57389,14 @@ Lifestream's Blessing|Final Fantasy Commander|67|R|{4}{G}{G}|Instant|||Draw X ca Maester Seymour|Final Fantasy Commander|68|R|{2}{G}|Legendary Creature - Human Elf Cleric|1|3|At the beginning of combat on your turn, put a number of +1/+1 counters equal to Maester Seymour's power on another target creature you control.${3}{G}{G}: Monstrosity X, where X is the number of counters among creatures you control.| Professor Hojo|Final Fantasy Commander|69|R|{1}{G}|Legendary Creature - Human Scientist|2|2|The first activated ability you activate during your turn that targets a creature you control costs {2} less to activate.$Whenever one or more creatures you control become the target of an activated ability, draw a card. This ability triggers only once each turn.| Sphere Grid|Final Fantasy Commander|70|R|{1}{G}|Enchantment|||Whenever a creature you control deals combat damage to a player, put a +1/+1 counter on that creature.$Unlock Ability -- Creatures you control with +1/+1 counters on them have reach and trample.| -Summon: Magus Sisters|Final Fantasy Commander|71|R|{4}{G}|Enchantment Creature - Saga Faerie|5|5|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I,II,III - Choose one at random --$* Combine Powers! -- Put three +1/+1 counters on target creature.$* Defense! -- Put a shield counter on target creature. You gain 3 life.$* Fight! -- This creature fights up to one target creature an opponent controls.$Haste| +Summon: Magus Sisters|Final Fantasy Commander|71|R|{4}{G}|Enchantment Creature - Saga Faerie|5|5|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I, II, III -- Choose one at random --$* Combine Powers! -- Put three +1/+1 counters on target creature.$* Defense! -- Put a shield counter on target creature. You gain 3 life.$* Fight! -- This creature fights up to one target creature an opponent controls.$Haste| Summoning Materia|Final Fantasy Commander|72|R|{2}{G}|Artifact - Equipment|||You may look at the top card of your library any time.$As long as this Equipment is attached to a creature, you may cast creature spells from the top of your library.$Equipped creature gets +2/+2 and has vigilance and "{T}: Add {G}."$Equip {2}| -Tromell, Seymour's Butler|Final Fantasy Commander|73|R|{2}{G}|Legendary Creature - Elf Advisor|2|3|Each other nontoken creature you control enters with an additional +1/+1 counter on it.${1},{T}: Proliferate X times, where X is the number of nontoken creatures you control that entered this turn.| +Tromell, Seymour's Butler|Final Fantasy Commander|73|R|{2}{G}|Legendary Creature - Elf Advisor|2|3|Each other nontoken creature you control enters with an additional +1/+1 counter on it.${1}, {T}: Proliferate X times, where X is the number of nontoken creatures you control that entered this turn.| Yuna's Decision|Final Fantasy Commander|74|R|{3}{G}|Sorcery|||Choose one --$* Continue the Pilgrimage -- Sacrifice a creature. If you do, draw a card, then you may put a creature card and/or a land card from your hand onto the battlefield.$* Find Another Way -- Return one or two target permanent cards from your graveyard to your hand.| Yuna's Whistle|Final Fantasy Commander|75|R|{1}{G}{G}|Instant|||Reveal cards from the top of your library until you reveal a creature card. Put that card into your hand and the rest on the bottom of your library in a random order. When you reveal a creature card this way, put X+1/+1 counters on target creature you control, where X is the mana value of that card.| Aerith, Last Ancient|Final Fantasy Commander|76|R|{2}{G}{W}|Legendary Creature - Human Cleric Druid|3|5|Lifelink$Raise -- At the beginning of your end step, if you gained life this turn, return target creature card from your graveyard to your hand. If you gained 7 or more life this turn, return that card to the battlefield instead.| Ardbert, Warrior of Darkness|Final Fantasy Commander|77|R|{1}{W}{B}|Legendary Creature - Spirit Warrior|2|2|Whenever you cast a white spell, put a +1/+1 counter on each legendary creature you control. They gain vigilance until end of turn.$Whenever you cast a black spell, put a +1/+1 counter on each legendary creature you control. They gain menace until end of turn.| -Banon, the Returners' Leader|Final Fantasy Commander|78|R|{R}{W}|Legendary Creature Human Cleric Rebel|1|3|Pray -- Once during each of your turns, you may cast a creature spell from among cards in your graveyard that were put there from anywhere other than the battlefield this turn.$Whenever you attack, you may pay {1} and discard a card. If you do, draw a card.| +Banon, the Returners' Leader|Final Fantasy Commander|78|R|{R}{W}|Legendary Creature - Human Cleric Rebel|1|3|Pray -- Once during each of your turns, you may cast a creature spell from among cards in your graveyard that were put there from anywhere other than the battlefield this turn.$Whenever you attack, you may pay {1} and discard a card. If you do, draw a card.| Barret, Avalanche Leader|Final Fantasy Commander|79|R|{2}{R}{G}|Legendary Creature - Human Rebel|4|4|Reach$Avalanche! -- Whenever an Equipment you control enters, create a 2/2 red Rebel creature token.$At the beginning of combat on your turn, attach up to one target Equipment you control to target Rebel you control.| Edgar, Master Machinist|Final Fantasy Commander|80|R|{2}{R}{W}|Legendary Creature - Human Artificer Noble|2|4|Once during each of your turns, you may cast an artifact spell from your graveyard. If you cast a spell this way, that artifact enters tapped.$Tools -- Whenever Edgar attacks, it gets +X/+0 until end of turn, where X is the greatest mana value among artifacts you control.| Emet-Selch of the Third Seat|Final Fantasy Commander|81|R|{2}{U}{B}|Legendary Creature - Elder Wizard|3|4|Spells you cast from your graveyard cost {2} less to cast.$Whenever one or more opponents lose life, you may cast target instant or sorcery card from your graveyard. If that spell would be put into your graveyard, exile it instead. Do this only once each turn.| @@ -57267,7 +57409,7 @@ Krile Baldesion|Final Fantasy Commander|86|R|{W}{U}|Legendary Creature - Dwarf W Locke, Treasure Hunter|Final Fantasy Commander|87|R|{1}{B}{R}|Legendary Creature - Human Rogue|2|3|Locke can't be blocked by creatures with greater power.$Mug -- Whenever Locke attacks, each player mills a card. If a land card was milled this way, create a Treasure token. Until end of turn, you may cast a spell from among those cards.| Lyse Hext|Final Fantasy Commander|88|R|{1}{W}{U}|Legendary Creature - Human Rebel Monk|2|2|Prowess$Noncreature spells you cast cost {1} less to cast.$As long as you've cast two or more noncreature spells this turn, Lyse Hext has double strike.| Mog, Moogle Warrior|Final Fantasy Commander|89|R|{1}{R}{W}|Legendary Creature - Moogle Warrior|1|2|Lifelink$Dance -- At the beginning of your end step, each player may discard a card. Each player who discarded a card this way draws a card. If a creature card was discarded this way, you create a 1/2 white Moogle creature token with lifelink. Then if a noncreature card was discarded this way, put a +1/+1 counter on each Moogle you control.| -Papalymo Totolymo|Final Fantasy Commander|90|R|{W}{B}|Legendary Creature - Dwarf Wizard|1|2|Whenever you cast a noncreature spell, Papalymo Totolymo deals 1 damage to each opponent and you gain 1 life.${4},{T}, Sacrifice Papalymo Totolymo: Each opponent who lost life this turn sacrifices a creature with the greatest power among creatures they control.| +Papalymo Totolymo|Final Fantasy Commander|90|R|{W}{B}|Legendary Creature - Dwarf Wizard|1|2|Whenever you cast a noncreature spell, Papalymo Totolymo deals 1 damage to each opponent and you gain 1 life.${4}, {T}, Sacrifice Papalymo Totolymo: Each opponent who lost life this turn sacrifices a creature with the greatest power among creatures they control.| Red XIII, Proud Warrior|Final Fantasy Commander|91|R|{1}{R}{G}|Legendary Creature - Beast Warrior|3|3|Vigilance, trample$Other modified creatures you control have vigilance and trample.$Cosmo Memory -- When Red XIII enters, return target Aura or Equipment card from your graveyard to your hand.| Sephiroth, Fallen Hero|Final Fantasy Commander|92|R|{3}{R}{W}|Legendary Creature - Human Avatar Soldier|7|5|Jenova Cells -- Whenever Sephiroth attacks, you may put a cell counter on target creature. Until end of turn, each modified creature you control has base power and toughness 7/5.$The Reunion -- {3}, Sacrifice a modified creature: Return this card from your graveyard to the battlefield tapped.| Setzer, Wandering Gambler|Final Fantasy Commander|93|R|{1}{B}{R}|Legendary Creature - Human Rogue Pilot|2|2|When Setzer enters, create The Blackjack, a legendary 3/3 colorless Vehicle artifact token with flying and crew 2.$Whenever a Vehicle you control deals combat damage to a player, flip a coin.$Whenever you win a coin flip, create two tapped Treasure tokens.| @@ -57281,14 +57423,14 @@ Wrecking Ball Arm|Final Fantasy Commander|100|R|{2}|Legendary Artifact - Equipme Champions from Beyond|Final Fantasy Commander|101|R|{X}{W}{W}|Enchantment|||When this enchantment enters, create X 1/1 colorless Hero creature tokens.$Light Party -- Whenever you attack with four or more creatures, scry 2, then draw a card.$Full Party -- Whenever you attack with eight or more creatures, those creatures get +4/+4 until end of turn.| Chocobo Knights|Final Fantasy Commander|102|R|{3}{W}|Creature - Human Knight|3|3|Whenever you attack, creatures you control with counters on them gain double strike until end of turn.| Cloud's Limit Break|Final Fantasy Commander|103|R|{1}{W}|Instant|||Tiered$* Cross-Slash -- {0} -- Destroy target tapped creature.$* Blade Beam -- {1} -- Destroy any number of target tapped creatures with different controllers.$* Omnislash -- {3}{W} -- Destroy all tapped creatures.| -Coin of Fate|Final Fantasy Commander|104|R|{1}{W}|Artifact|||When this artifact enters, surveil 1.${3}{W},{T}, Exile two creature cards from your graveyard, Sacrifice this artifact: An opponent chooses one of the exiled cards. You put that card on the bottom of your library and return the other to the battlefield tapped. You become the monarch.| +Coin of Fate|Final Fantasy Commander|104|R|{1}{W}|Artifact|||When this artifact enters, surveil 1.${3}{W}, {T}, Exile two creature cards from your graveyard, Sacrifice this artifact: An opponent chooses one of the exiled cards. You put that card on the bottom of your library and return the other to the battlefield tapped. You become the monarch.| Dancer's Chakrams|Final Fantasy Commander|105|R|{3}{W}|Artifact - Equipment|||Job select$Equipped creature gets +2/+2, has lifelink and "Other commanders you control get +2/+2 and have lifelink," and is a Performer in addition to its other types.$Krishna -- Equip {3}| Helitrooper|Final Fantasy Commander|106|R|{1}{W}|Creature - Human Soldier|1|2|Flying$Whenever this creature attacks, another target attacking creature gains flying until end of turn.$Equip abilities you activate that target this creature cost {2} less to activate.| Protection Magic|Final Fantasy Commander|107|R|{1}{W}|Instant|||Put a shield counter on each of up to three target creatures.| SOLDIER Military Program|Final Fantasy Commander|108|R|{2}{W}|Enchantment|||At the beginning of combat on your turn, choose one. If you control a commander, you may choose both instead.$* Create a 1/1 white Soldier creature token.$* Put a +1/+1 counter on each of up to two Soldiers you control.| Summoner's Sending|Final Fantasy Commander|109|R|{1}{W}|Enchantment|||At the beginning of your end step, you may exile target creature card from a graveyard. If you do, create a 1/1 white Spirit creature token with flying. Put a +1/+1 counter on it if the exiled card's mana value is 4 or greater.| Ultimate Magic: Holy|Final Fantasy Commander|110|R|{2}{W}|Instant|||Permanents you control gain indestructible until end of turn. If this spell was cast from exile, prevent all damage that would be dealt to you this turn.$Foretell {2}{W}| -Blitzball Stadium|Final Fantasy Commander|111|R|{X}{U}|Artifact|||When this artifact enters, support X.$Go for the Goal! -- {3},{T}: Until end of turn, target creature gains "Whenever this creature deals combat damage to a player, draw a card for each kind of counter on it" and it can't be blocked this turn.| +Blitzball Stadium|Final Fantasy Commander|111|R|{X}{U}|Artifact|||When this artifact enters, support X.$Go for the Goal! -- {3}, {T}: Until end of turn, target creature gains "Whenever this creature deals combat damage to a player, draw a card for each kind of counter on it" and it can't be blocked this turn.| Blue Mage's Cane|Final Fantasy Commander|112|R|{2}{U}|Artifact - Equipment|||Job select$Equipped creature gets +0/+2, is a Wizard in addition to its other types, and has "Whenever this creature attacks, exile up to one target instant or sorcery card from defending player's graveyard. If you do, copy it. You may cast the copy by paying {3} rather than paying its mana cost."$Spirit of the Whalaqee -- Equip {2}| Observed Stasis|Final Fantasy Commander|113|R|{3}{U}|Enchantment - Aura|||Flash$Enchant creature an opponent controls$When this Aura enters, remove enchanted creature from combat. Then draw a card for each tapped creature its controller controls.$Enchanted creature loses all abilities and can't attack or block.| Espers to Magicite|Final Fantasy Commander|114|R|{3}{B}|Instant|||Exile each opponent's graveyard. When you do, choose up to one target creature card exiled this way. Create a token that's a copy of that card, except it's an artifact and it loses all other card types.| @@ -57308,7 +57450,7 @@ Conformer Shuriken|Final Fantasy Commander|127|R|{2}|Legendary Artifact - Equipm Wrecking Ball Arm|Final Fantasy Commander|128|R|{2}|Legendary Artifact - Equipment|||Equipped creature has base power and toughness 7/7 and can't be blocked by creatures with power 2 or less.$Equip legendary creature {3}$Equip {7}| Alisaie Leveilleur|Final Fantasy Commander|129|R|{2}{W}|Legendary Creature - Elf Wizard|3|2|Partner with Alphinaud Leveilleur$First strike$Dualcast -- The second spell you cast each turn costs {2} less to cast.| Auron, Venerated Guardian|Final Fantasy Commander|130|R|{3}{W}|Legendary Creature - Human Spirit Samurai|2|5|Vigilance$Shooting Star -- Whenever Auron attacks, put a +1/+1 counter on it. When you do, exile target creature defending player controls with power less than Auron's power until Auron leaves the battlefield.| -Cid, Freeflier Pilot|Final Fantasy Commander|131|R|{1}{W}|Legendary Creature - Human Warrior Pilot|2|2|Equipment and Vehicle spells you cast cost {1} less to cast.$Jump -- During your turn, Cid has flying.${2},{T}: Return target Equipment or Vehicle card from your graveyard to your hand.| +Cid, Freeflier Pilot|Final Fantasy Commander|131|R|{1}{W}|Legendary Creature - Human Warrior Pilot|2|2|Equipment and Vehicle spells you cast cost {1} less to cast.$Jump -- During your turn, Cid has flying.${2}, {T}: Return target Equipment or Vehicle card from your graveyard to your hand.| Cyan, Vengeful Samurai|Final Fantasy Commander|132|R|{6}{W}|Legendary Creature - Human Samurai|3|3|This spell costs {1} less to cast for each creature card in your graveyard.$Double strike$Whenever one or more creature cards leave your graveyard, put a +1/+1 counter on Cyan.| Elena, Turk Recruit|Final Fantasy Commander|133|R|{2}{W}|Legendary Creature - Human Assassin|1|4|When Elena enters, return target non-Assassin historic card from your graveyard to your hand.$Whenever you cast a historic spell, put a +1/+1 counter on Elena.| Gatta and Luzzu|Final Fantasy Commander|134|R|{2}{W}|Legendary Creature - Human Soldier|1|1|Flash$When Gatta and Luzzu enters, choose target creature you control. If damage would be dealt to that creature this turn, prevent that damage and put that many +1/+1 counters on it.| @@ -57322,7 +57464,7 @@ Hermes, Overseer of Elpis|Final Fantasy Commander|141|R|{3}{U}|Legendary Creatur Hraesvelgr of the First Brood|Final Fantasy Commander|142|R|{4}{U}|Legendary Creature - Elder Dragon|5|5|Flying, vigilance, ward {2}$Shiva's Aid -- When Hraesvelgr enters and whenever you cast a noncreature spell, target creature gets +1/+0 until end of turn and can't be blocked this turn.| Lulu, Stern Guardian|Final Fantasy Commander|143|R|{2}{U}|Legendary Creature - Human Wizard|2|3|Whenever an opponent attacks you, choose target creature attacking you. Put a stun counter on that creature.${3}{U}: Proliferate.| O'aka, Traveling Merchant|Final Fantasy Commander|144|R|{1}{U}|Legendary Creature - Human Citizen|1|2|{T}, Remove a counter from a nonland permanent you control: Draw a card.| -Rikku, Resourceful Guardian|Final Fantasy Commander|145|R|{2}{U}|Legendary Creature - Human Artificer|2|3|Whenever you put one or more counters on a creature, until end of turn, that creature can't be blocked by creatures your opponents control.$Steal -- {1},{T}: Move a counter from target creature an opponent controls onto target creature you control. Activate only as a sorcery.| +Rikku, Resourceful Guardian|Final Fantasy Commander|145|R|{2}{U}|Legendary Creature - Human Artificer|2|3|Whenever you put one or more counters on a creature, until end of turn, that creature can't be blocked by creatures your opponents control.$Steal -- {1}, {T}: Move a counter from target creature an opponent controls onto target creature you control. Activate only as a sorcery.| Fandaniel, Telophoroi Ascian|Final Fantasy Commander|146|R|{4}{B}|Legendary Creature - Elder Wizard|4|5|Whenever you cast an instant or sorcery spell, surveil 1.$At the beginning of your end step, each opponent may sacrifice a nontoken creature of their choice. Each opponent who doesn't loses 2 life for each instant and sorcery card in your graveyard.| Interceptor, Shadow's Hound|Final Fantasy Commander|147|R|{2}{B}{B}|Legendary Creature - Dog|4|3|Menace$Assassins you control have menace.$Whenever you attack with one or more legendary creatures, you may pay {2}{B}. If you do, return this card from your graveyard to the battlefield tapped and attacking.| Shadow, Mysterious Assassin|Final Fantasy Commander|148|R|{2}{B}|Legendary Creature - Human Assassin|3|3|Deathtouch$Throw -- Whenever Shadow deals combat damage to a player, you may sacrifice another nonland permanent. If you do, draw two cards and each opponent loses life equal to the mana value of the sacrificed permanent.| @@ -57331,25 +57473,25 @@ Avalanche of Sector 7|Final Fantasy Commander|150|R|{2}{R}|Legendary Creature - Cait Sith, Fortune Teller|Final Fantasy Commander|151|R|{3}{R}|Legendary Artifact Creature - Cat Moogle|3|3|Lucky Slots -- At the beginning of combat on your turn, scry 1, then exile the top card of your library. You may play that card this turn. When you exile a card this way, target creature you control gets +X/+0 until end of turn, where X is that card's mana value.| Gau, Feral Youth|Final Fantasy Commander|152|R|{1}{R}|Legendary Creature - Human Berserker|2|2|Rage -- Whenever Gau attacks, put a +1/+1 counter on it.$At the beginning of each end step, if a card left your graveyard this turn, Gau deals damage equal to its power to each opponent.| Gogo, Mysterious Mime|Final Fantasy Commander|153|R|{3}{R}|Legendary Creature - Wizard|2|2|At the beginning of combat on your turn, you may have Gogo become a copy of another target creature you control until end of turn, except its name is Gogo, Mysterious Mime. If you do, Gogo and that creature each get +2/+0 and gain haste until end of turn and attack this turn if able.| -Sabin, Master Monk|Final Fantasy Commander|154|R|{4}{R}|Legendary Creature Human Noble Monk|4|3|Double strike$Blitz--{2}{R}{R}, Discard a card.$You may cast this card from your graveyard using its blitz ability.| -Strago and Relm|Final Fantasy Commander|155|R|{2}{R}|Legendary Creature - Human Wizard|1|3|Sketch and Lore--{2}{R},{T}: Target opponent exiles cards from the top of their library until they exile an instant, sorcery, or creature card. You may cast that card without paying its mana cost. If you cast a creature spell this way, it gains haste and "At the beginning of the end step, sacrifice this creature." Activate only as a sorcery.| +Sabin, Master Monk|Final Fantasy Commander|154|R|{4}{R}|Legendary Creature - Human Noble Monk|4|3|Double strike$Blitz--{2}{R}{R}, Discard a card.$You may cast this card from your graveyard using its blitz ability.| +Strago and Relm|Final Fantasy Commander|155|R|{2}{R}|Legendary Creature - Human Wizard|1|3|Sketch and Lore -- {2}{R}, {T}: Target opponent exiles cards from the top of their library until they exile an instant, sorcery, or creature card. You may cast that card without paying its mana cost. If you cast a creature spell this way, it gains haste and "At the beginning of the end step, sacrifice this creature." Activate only as a sorcery.| Umaro, Raging Yeti|Final Fantasy Commander|156|R|{5}{R}|Legendary Creature - Yeti Berserker|6|6|Trample$At the beginning of combat on your turn, choose one at random --$* Other creatures you control get +3/+0 and gain trample until end of turn.$* Discard your hand, then draw four cards.$* Umaro deals 5 damage to any target.| Vincent, Vengeful Atoner|Final Fantasy Commander|157|R|{2}{R}|Legendary Creature - Assassin|3|3|Menace$Whenever one or more creatures you control deal combat damage to a player, put a +1/+1 counter on Vincent.$Chaos -- Whenever Vincent deals combat damage to an opponent, it deals that much damage to each other opponent if Vincent's power is 7 or greater.| Yuffie, Materia Hunter|Final Fantasy Commander|158|R|{2}{R}|Legendary Creature - Human Ninja|3|3|Ninjutsu {1}{R}$When Yuffie enters, gain control of target noncreature artifact for as long as you control Yuffie. Then you may attach an Equipment you control to Yuffie.| Bugenhagen, Wise Elder|Final Fantasy Commander|159|R|{1}{G}|Legendary Creature - Human Shaman|1|3|Reach$At the beginning of your upkeep, if you control a creature with power 7 or greater, draw a card.${T}: Add one mana of any color.| Maester Seymour|Final Fantasy Commander|160|R|{2}{G}|Legendary Creature - Human Elf Cleric|1|3|At the beginning of combat on your turn, put a number of +1/+1 counters equal to Maester Seymour's power on another target creature you control.${3}{G}{G}: Monstrosity X, where X is the number of counters among creatures you control.| Professor Hojo|Final Fantasy Commander|161|R|{1}{G}|Legendary Creature - Human Scientist|2|2|The first activated ability you activate during your turn that targets a creature you control costs {2} less to activate.$Whenever one or more creatures you control become the target of an activated ability, draw a card. This ability triggers only once each turn.| -Tromell, Seymour's Butler|Final Fantasy Commander|162|R|{2}{G}|Legendary Creature - Elf Advisor|2|3|Each other nontoken creature you control enters with an additional +1/+1 counter on it.${1},{T}: Proliferate X times, where X is the number of nontoken creatures you control that entered this turn.| +Tromell, Seymour's Butler|Final Fantasy Commander|162|R|{2}{G}|Legendary Creature - Elf Advisor|2|3|Each other nontoken creature you control enters with an additional +1/+1 counter on it.${1}, {T}: Proliferate X times, where X is the number of nontoken creatures you control that entered this turn.| Aerith, Last Ancient|Final Fantasy Commander|163|R|{2}{G}{W}|Legendary Creature - Human Cleric Druid|3|5|Lifelink$Raise -- At the beginning of your end step, if you gained life this turn, return target creature card from your graveyard to your hand. If you gained 7 or more life this turn, return that card to the battlefield instead.| Ardbert, Warrior of Darkness|Final Fantasy Commander|164|R|{1}{W}{B}|Legendary Creature - Spirit Warrior|2|2|Whenever you cast a white spell, put a +1/+1 counter on each legendary creature you control. They gain vigilance until end of turn.$Whenever you cast a black spell, put a +1/+1 counter on each legendary creature you control. They gain menace until end of turn.| -Banon, the Returners' Leader|Final Fantasy Commander|165|R|{R}{W}|Legendary Creature Human Cleric Rebel|1|3|Pray -- Once during each of your turns, you may cast a creature spell from among cards in your graveyard that were put there from anywhere other than the battlefield this turn.$Whenever you attack, you may pay {1} and discard a card. If you do, draw a card.| +Banon, the Returners' Leader|Final Fantasy Commander|165|R|{R}{W}|Legendary Creature - Human Cleric Rebel|1|3|Pray -- Once during each of your turns, you may cast a creature spell from among cards in your graveyard that were put there from anywhere other than the battlefield this turn.$Whenever you attack, you may pay {1} and discard a card. If you do, draw a card.| Barret, Avalanche Leader|Final Fantasy Commander|166|R|{2}{R}{G}|Legendary Creature - Human Rebel|4|4|Reach$Avalanche! -- Whenever an Equipment you control enters, create a 2/2 red Rebel creature token.$At the beginning of combat on your turn, attach up to one target Equipment you control to target Rebel you control.| Celes, Rune Knight|Final Fantasy Commander|167|M|{1}{R}{W}{B}|Legendary Creature - Human Wizard Knight|4|4|When Celes enters, discard any number of cards, then draw that many cards plus one.$Whenever one or more other creatures you control enter, if one or more of them entered from a graveyard or was cast from a graveyard, put a +1/+1 counter on each creature you control.| Cloud, Ex-SOLDIER|Final Fantasy Commander|168|M|{2}{R}{G}{W}|Legendary Creature - Human Soldier Mercenary|4|4|Haste$When Cloud enters, attach up to one target Equipment you control to it.$Whenever Cloud attacks, draw a card for each equipped attacking creature you control. Then if Cloud has power 7 or greater, create two Treasure tokens.| Edgar, Master Machinist|Final Fantasy Commander|169|R|{2}{R}{W}|Legendary Creature - Human Artificer Noble|2|4|Once during each of your turns, you may cast an artifact spell from your graveyard. If you cast a spell this way, that artifact enters tapped.$Tools -- Whenever Edgar attacks, it gets +X/+0 until end of turn, where X is the greatest mana value among artifacts you control.| Emet-Selch of the Third Seat|Final Fantasy Commander|170|R|{2}{U}{B}|Legendary Creature - Elder Wizard|3|4|Spells you cast from your graveyard cost {2} less to cast.$Whenever one or more opponents lose life, you may cast target instant or sorcery card from your graveyard. If that spell would be put into your graveyard, exile it instead. Do this only once each turn.| Estinien Varlineau|Final Fantasy Commander|171|R|{2}{W}{B}|Legendary Creature - Elf Warrior|3|3|Whenever you cast a noncreature spell, put a +1/+1 counter on Estinien Varlineau. It gains flying until end of turn.$At the beginning of your second main phase, you draw X cards and lose X life, where X is the number of your opponents who were dealt combat damage by Estinien Varlineau or a Dragon this turn.| -G'raha Tia, Scion Reborn|Final Fantasy Commander|172|M|{W}{U}{B}|Legendary Creature - Cat Wizard|2|3|Lifelink$Throw Wide the Gates -- Whenever you cast a noncreature spell, you may pay X life, where X is that spell's mana value. If you do, create a 1/1 colorless Hero creature token and put X+1/+1 counters on it. Do this only once each turn.| +G'raha Tia, Scion Reborn|Final Fantasy Commander|172|M|{W}{U}{B}|Legendary Creature - Cat Wizard|2|3|Lifelink$Throw Wide the Gates -- Whenever you cast a noncreature spell, you may pay X life, where X is that spell's mana value. If you do, create a 1/1 colorless Hero creature token and put X +1/+1 counters on it. Do this only once each turn.| Hildibrand Manderville|Final Fantasy Commander|173|R|{1}{W}|Legendary Creature - Human Detective|2|2|Creature tokens you control get +1/+1.$When Hildibrand Manderville dies, you may cast it from your graveyard as an Adventure until the end of your next turn.| Gentleman's Rise|Final Fantasy Commander|173|R|{2}{B}|Instant - Adventure|2|2|Create a 2/2 black Zombie creature token.| Kefka, Dancing Mad|Final Fantasy Commander|174|R|{5}{B}{R}|Legendary Creature - Human Wizard|6|6|During your turn, Kefka has indestructible.$At the beginning of your end step, exile a card at random from each opponent's graveyard. You may cast any number of spells from among cards exiled this way without paying their mana costs. Then each player who owns a spell you cast this way loses life equal to its mana value.| @@ -57358,7 +57500,7 @@ Krile Baldesion|Final Fantasy Commander|176|R|{W}{U}|Legendary Creature - Dwarf Locke, Treasure Hunter|Final Fantasy Commander|177|R|{1}{B}{R}|Legendary Creature - Human Rogue|2|3|Locke can't be blocked by creatures with greater power.$Mug -- Whenever Locke attacks, each player mills a card. If a land card was milled this way, create a Treasure token. Until end of turn, you may cast a spell from among those cards.| Lyse Hext|Final Fantasy Commander|178|R|{1}{W}{U}|Legendary Creature - Human Rebel Monk|2|2|Prowess$Noncreature spells you cast cost {1} less to cast.$As long as you've cast two or more noncreature spells this turn, Lyse Hext has double strike.| Mog, Moogle Warrior|Final Fantasy Commander|179|R|{1}{R}{W}|Legendary Creature - Moogle Warrior|1|2|Lifelink$Dance -- At the beginning of your end step, each player may discard a card. Each player who discarded a card this way draws a card. If a creature card was discarded this way, you create a 1/2 white Moogle creature token with lifelink. Then if a noncreature card was discarded this way, put a +1/+1 counter on each Moogle you control.| -Papalymo Totolymo|Final Fantasy Commander|180|R|{W}{B}|Legendary Creature - Dwarf Wizard|1|2|Whenever you cast a noncreature spell, Papalymo Totolymo deals 1 damage to each opponent and you gain 1 life.${4},{T}, Sacrifice Papalymo Totolymo: Each opponent who lost life this turn sacrifices a creature with the greatest power among creatures they control.| +Papalymo Totolymo|Final Fantasy Commander|180|R|{W}{B}|Legendary Creature - Dwarf Wizard|1|2|Whenever you cast a noncreature spell, Papalymo Totolymo deals 1 damage to each opponent and you gain 1 life.${4}, {T}, Sacrifice Papalymo Totolymo: Each opponent who lost life this turn sacrifices a creature with the greatest power among creatures they control.| Red XIII, Proud Warrior|Final Fantasy Commander|181|R|{1}{R}{G}|Legendary Creature - Beast Warrior|3|3|Vigilance, trample$Other modified creatures you control have vigilance and trample.$Cosmo Memory -- When Red XIII enters, return target Aura or Equipment card from your graveyard to your hand.| Sephiroth, Fallen Hero|Final Fantasy Commander|182|R|{3}{R}{W}|Legendary Creature - Human Avatar Soldier|7|5|Jenova Cells -- Whenever Sephiroth attacks, you may put a cell counter on target creature. Until end of turn, each modified creature you control has base power and toughness 7/5.$The Reunion -- {3}, Sacrifice a modified creature: Return this card from your graveyard to the battlefield tapped.| Setzer, Wandering Gambler|Final Fantasy Commander|183|R|{1}{B}{R}|Legendary Creature - Human Rogue Pilot|2|2|When Setzer enters, create The Blackjack, a legendary 3/3 colorless Vehicle artifact token with flying and crew 2.$Whenever a Vehicle you control deals combat damage to a player, flip a coin.$Whenever you win a coin flip, create two tapped Treasure tokens.| @@ -57378,10 +57520,10 @@ Summon: Yojimbo|Final Fantasy Commander|196|R|{3}{W}|Enchantment Creature - Saga Summon: Valefor|Final Fantasy Commander|197|R|{4}{U}|Enchantment Creature - Saga Drake|5|4|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.)$I - Sonic Wings -- Each opponent chooses a creature with the greatest mana value among creatures they control. Return those creatures to their owners' hands.$II, III, IV - Tap up to one target creature and put a stun counter on it.$Flying| Summon: Esper Valigarmanda|Final Fantasy Commander|198|R|{3}{R}|Enchantment Creature - Saga Drake|3|3|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.)$I -- Exile an instant or sorcery card from each graveyard.$II, III, IV -- Add {R} for each lore counter on this Saga. You may cast an instant or sorcery card exiled with this Saga, and mana of any type can be spent to cast that spell.$Flying, haste| Summon: Kujata|Final Fantasy Commander|199|R|{5}{R}|Enchantment Creature - Saga Ox|7|5|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Lightning -- This creature deals 3 damage to each of up to two target creatures.$II -- Ice -- Up to three target creatures can't block this turn.$III -- Fire -- Discard a card, then draw two cards. When you discard a card this way, this creature deals damage equal to that card's mana value to each opponent.$Trample, haste| -Summon: Magus Sisters|Final Fantasy Commander|200|R|{4}{G}|Enchantment Creature - Saga Faerie|5|5|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I,II,III - Choose one at random --$* Combine Powers! -- Put three +1/+1 counters on target creature.$* Defense! -- Put a shield counter on target creature. You gain 3 life.$* Fight! -- This creature fights up to one target creature an opponent controls.$Haste| +Summon: Magus Sisters|Final Fantasy Commander|200|R|{4}{G}|Enchantment Creature - Saga Faerie|5|5|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I, II, III -- Choose one at random --$* Combine Powers! -- Put three +1/+1 counters on target creature.$* Defense! -- Put a shield counter on target creature. You gain 3 life.$* Fight! -- This creature fights up to one target creature an opponent controls.$Haste| Celes, Rune Knight|Final Fantasy Commander|201|M|{1}{R}{W}{B}|Legendary Creature - Human Wizard Knight|4|4|When Celes enters, discard any number of cards, then draw that many cards plus one.$Whenever one or more other creatures you control enter, if one or more of them entered from a graveyard or was cast from a graveyard, put a +1/+1 counter on each creature you control.| Cloud, Ex-SOLDIER|Final Fantasy Commander|202|M|{2}{R}{G}{W}|Legendary Creature - Human Soldier Mercenary|4|4|Haste$When Cloud enters, attach up to one target Equipment you control to it.$Whenever Cloud attacks, draw a card for each equipped attacking creature you control. Then if Cloud has power 7 or greater, create two Treasure tokens.| -G'raha Tia, Scion Reborn|Final Fantasy Commander|203|M|{W}{U}{B}|Legendary Creature - Cat Wizard|2|3|Lifelink$Throw Wide the Gates -- Whenever you cast a noncreature spell, you may pay X life, where X is that spell's mana value. If you do, create a 1/1 colorless Hero creature token and put X+1/+1 counters on it. Do this only once each turn.| +G'raha Tia, Scion Reborn|Final Fantasy Commander|203|M|{W}{U}{B}|Legendary Creature - Cat Wizard|2|3|Lifelink$Throw Wide the Gates -- Whenever you cast a noncreature spell, you may pay X life, where X is that spell's mana value. If you do, create a 1/1 colorless Hero creature token and put X +1/+1 counters on it. Do this only once each turn.| Terra, Herald of Hope|Final Fantasy Commander|204|M|{R}{W}{B}|Legendary Creature - Human Wizard Warrior|3|3|Trance -- At the beginning of combat on your turn, mill two cards. Terra gains flying until end of turn.$Whenever Terra deals combat damage to a player, you may pay {2}. When you do, return target creature card with power 3 or less from your graveyard to the battlefield tapped.| Tidus, Yuna's Guardian|Final Fantasy Commander|205|M|{G}{W}{U}|Legendary Creature - Human Warrior|3|3|At the beginning of combat on your turn, you may move a counter from target creature you control onto a second target creature you control.$Cheer -- Whenever one or more creatures you control with counters on them deal combat damage to a player, you may draw a card and proliferate. Do this only once each turn.| Tifa, Martial Artist|Final Fantasy Commander|206|M|{1}{R}{G}{W}|Legendary Creature - Human Monk|4|4|Melee$Whenever one or more creatures you control with power 7 or greater deal combat damage to a player, untap all creatures you control. If it's the first combat phase of your turn, there is an additional combat phase after this phase.| @@ -57389,7 +57531,7 @@ Y'shtola, Night's Blessed|Final Fantasy Commander|207|M|{1}{W}{U}{B}|Legendary C Yuna, Grand Summoner|Final Fantasy Commander|208|M|{1}{G}{W}{U}|Legendary Creature - Human Cleric|1|5|Grand Summon -- {T}: Add one mana of any color. When you next cast a creature spell this turn, that creature enters with two additional +1/+1 counters on it.$Whenever another permanent you control is put into a graveyard from the battlefield, if it had one or more counters on it, you may put that number of +1/+1 counters on target creature.| Celes, Rune Knight|Final Fantasy Commander|209|M|{1}{R}{W}{B}|Legendary Creature - Human Wizard Knight|4|4|When Celes enters, discard any number of cards, then draw that many cards plus one.$Whenever one or more other creatures you control enter, if one or more of them entered from a graveyard or was cast from a graveyard, put a +1/+1 counter on each creature you control.| Cloud, Ex-SOLDIER|Final Fantasy Commander|210|M|{2}{R}{G}{W}|Legendary Creature - Human Soldier Mercenary|4|4|Haste$When Cloud enters, attach up to one target Equipment you control to it.$Whenever Cloud attacks, draw a card for each equipped attacking creature you control. Then if Cloud has power 7 or greater, create two Treasure tokens.| -G'raha Tia, Scion Reborn|Final Fantasy Commander|211|M|{W}{U}{B}|Legendary Creature - Cat Wizard|2|3|Lifelink$Throw Wide the Gates -- Whenever you cast a noncreature spell, you may pay X life, where X is that spell's mana value. If you do, create a 1/1 colorless Hero creature token and put X+1/+1 counters on it. Do this only once each turn.| +G'raha Tia, Scion Reborn|Final Fantasy Commander|211|M|{W}{U}{B}|Legendary Creature - Cat Wizard|2|3|Lifelink$Throw Wide the Gates -- Whenever you cast a noncreature spell, you may pay X life, where X is that spell's mana value. If you do, create a 1/1 colorless Hero creature token and put X +1/+1 counters on it. Do this only once each turn.| Terra, Herald of Hope|Final Fantasy Commander|212|M|{R}{W}{B}|Legendary Creature - Human Wizard Warrior|3|3|Trance -- At the beginning of combat on your turn, mill two cards. Terra gains flying until end of turn.$Whenever Terra deals combat damage to a player, you may pay {2}. When you do, return target creature card with power 3 or less from your graveyard to the battlefield tapped.| Tidus, Yuna's Guardian|Final Fantasy Commander|213|M|{G}{W}{U}|Legendary Creature - Human Warrior|3|3|At the beginning of combat on your turn, you may move a counter from target creature you control onto a second target creature you control.$Cheer -- Whenever one or more creatures you control with counters on them deal combat damage to a player, you may draw a card and proliferate. Do this only once each turn.| Tifa, Martial Artist|Final Fantasy Commander|214|M|{1}{R}{G}{W}|Legendary Creature - Human Monk|4|4|Melee$Whenever one or more creatures you control with power 7 or greater deal combat damage to a player, untap all creatures you control. If it's the first combat phase of your turn, there is an additional combat phase after this phase.| @@ -57400,7 +57542,7 @@ Secret Rendezvous|Final Fantasy Commander|218|U|{1}{W}{W}|Sorcery|||You and targ Secret Rendezvous|Final Fantasy Commander|219|U|{1}{W}{W}|Sorcery|||You and target opponent each draw three cards.| Celes, Rune Knight|Final Fantasy Commander|220|M|{1}{R}{W}{B}|Legendary Creature - Human Wizard Knight|4|4|When Celes enters, discard any number of cards, then draw that many cards plus one.$Whenever one or more other creatures you control enter, if one or more of them entered from a graveyard or was cast from a graveyard, put a +1/+1 counter on each creature you control.| Cloud, Ex-SOLDIER|Final Fantasy Commander|221|M|{2}{R}{G}{W}|Legendary Creature - Human Soldier Mercenary|4|4|Haste$When Cloud enters, attach up to one target Equipment you control to it.$Whenever Cloud attacks, draw a card for each equipped attacking creature you control. Then if Cloud has power 7 or greater, create two Treasure tokens.| -G'raha Tia, Scion Reborn|Final Fantasy Commander|222|M|{W}{U}{B}|Legendary Creature - Cat Wizard|2|3|Lifelink$Throw Wide the Gates -- Whenever you cast a noncreature spell, you may pay X life, where X is that spell's mana value. If you do, create a 1/1 colorless Hero creature token and put X+1/+1 counters on it. Do this only once each turn.| +G'raha Tia, Scion Reborn|Final Fantasy Commander|222|M|{W}{U}{B}|Legendary Creature - Cat Wizard|2|3|Lifelink$Throw Wide the Gates -- Whenever you cast a noncreature spell, you may pay X life, where X is that spell's mana value. If you do, create a 1/1 colorless Hero creature token and put X +1/+1 counters on it. Do this only once each turn.| Terra, Herald of Hope|Final Fantasy Commander|223|M|{R}{W}{B}|Legendary Creature - Human Wizard Warrior|3|3|Trance -- At the beginning of combat on your turn, mill two cards. Terra gains flying until end of turn.$Whenever Terra deals combat damage to a player, you may pay {2}. When you do, return target creature card with power 3 or less from your graveyard to the battlefield tapped.| Tidus, Yuna's Guardian|Final Fantasy Commander|224|M|{G}{W}{U}|Legendary Creature - Human Warrior|3|3|At the beginning of combat on your turn, you may move a counter from target creature you control onto a second target creature you control.$Cheer -- Whenever one or more creatures you control with counters on them deal combat damage to a player, you may draw a card and proliferate. Do this only once each turn.| Tifa, Martial Artist|Final Fantasy Commander|225|M|{1}{R}{G}{W}|Legendary Creature - Human Monk|4|4|Melee$Whenever one or more creatures you control with power 7 or greater deal combat damage to a player, untap all creatures you control. If it's the first combat phase of your turn, there is an additional combat phase after this phase.| @@ -57627,152 +57769,328 @@ Command Tower|Final Fantasy Commander|485|C||Land|||{T}: Add one mana of any col Command Tower|Final Fantasy Commander|486|C||Land|||{T}: Add one mana of any color in your commander's color identity.| Summon: Bahamut|Final Fantasy|1|M|{9}|Enchantment Creature - Saga Dragon|9|9|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.)$I, II -- Destroy up to one target nonland permanent.$III -- Draw two cards.$IV -- Mega Flare -- This creature deals damage equal to the total mana value of other permanents you control to each opponent.$Flying| Ultima, Origin of Oblivion|Final Fantasy|2|R|{5}|Legendary Creature - God|4|4|Flying$Whenever Ultima attacks, put a blight counter on target land. For as long as that land has a blight counter on it, it loses all land types and abilities and has "{T}: Add {C}."$Whenever you tap a land for {C}, add an additional {C}.| +Adelbert Steiner|Final Fantasy|3|U|{1}{W}|Legendary Creature - Human Knight|2|1|Lifelink$Adelbert Steiner gets +1/+1 for each Equipment you control.| Aerith Gainsborough|Final Fantasy|4|R|{2}{W}|Legendary Creature - Human Cleric|2|2|Lifelink$Whenever you gain life, put a +1/+1 counter on Aerith Gainsborough.$When Aerith Gainsborough dies, put X +1/+1 counters on each legendary creature you control, where X is the number of +1/+1 counters on Aerith Gainsborough.| +Aerith Rescue Mission|Final Fantasy|5|C|{3}{W}|Sorcery|||Choose one --$* Take the Elevator -- Create three 1/1 colorless Hero creature tokens.$* Take 59 Flights of Stairs -- Tap up to three target creatures. Put a stun counter on one of them.| +Ambrosia Whiteheart|Final Fantasy|6|U|{1}{W}|Legendary Creature - Bird|2|2|Flash$When Ambrosia Whiteheart enters, you may return another permanent you control to its owner's hand.$Landfall -- Whenever a land you control enters, Ambrosia Whiteheart gets +1/+0 until end of turn.| +Ashe, Princess of Dalmasca|Final Fantasy|7|U|{2}{W}|Legendary Creature - Human Rebel Noble|3|2|Whenever Ashe attacks, look at the top five cards of your library. You may reveal an artifact card from among them and put it into your hand. Put the rest on the bottom of your library in a random order.| Auron's Inspiration|Final Fantasy|8|U|{2}{W}|Instant|||Attacking creatures get +2/+0 until end of turn.$Flashback {2}{W}{W}| +Battle Menu|Final Fantasy|9|U|{1}{W}|Instant|||Choose one --$* Attack -- Create a 2/2 white Knight creature token.$* Ability -- Target creature gets +0/+4 until end of turn.$* Magic -- Destroy target creature with power 4 or greater.$* Item -- You gain 4 life.| Cloud, Midgar Mercenary|Final Fantasy|10|M|{W}{W}|Legendary Creature - Human Soldier Mercenary|2|1|When Cloud enters, search your library for an Equipment card, reveal it, put it into your hand, then shuffle.$As long as Cloud is equipped, if an ability of Cloud or an Equipment attached to it triggers, that ability triggers an additional time.| Cloudbound Moogle|Final Fantasy|11|C|{3}{W}{W}|Creature - Moogle|2|3|Flying$When this creature enters, put a +1/+1 counter on target creature.$Plainscycling {2}| Coeurl|Final Fantasy|12|C|{1}{W}|Creature - Cat Beast|2|2|{1}{W}, {T}: Tap target nonenchantment creature.| +Crystal Fragments|Final Fantasy|13|U|{W}|Artifact - Equipment|||Equipped creature gets +1/+1.${5}{W}{W}: Exile this Equipment, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery.$Equip {1}| +Summon: Alexander|Final Fantasy|13|U||Enchantment Creature - Saga Construct|4|3|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I, II -- Prevent all damage that would be dealt to creatures you control this turn.$III -- Tap all creatures your opponents control.$Flying| The Crystal's Chosen|Final Fantasy|14|U|{5}{W}{W}|Sorcery|||Create four 1/1 colorless Hero creature tokens. Then put a +1/+1 counter on each creature you control.| +Delivery Moogle|Final Fantasy|15|U|{3}{W}|Creature - Moogle|3|2|Flying$When this creature enters, search your library and/or graveyard for an artifact card with mana value 2 or less, reveal it, and put it into your hand. If you search your library this way, shuffle.| +Dion, Bahamut's Dominant|Final Fantasy|16|R|{3}{W}|Legendary Creature - Human Noble Knight|3|3|Dragonfire Dive -- During your turn, Dion and other Knights you control have flying.$When Dion enters, create a 2/2 white Knight creature token.${4}{W}{W}, {T}: Exile Dion, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery.| +Bahamut, Warden of Light|Final Fantasy|16|R||Legendary Enchantment Creature - Saga Dragon|5|5|(As this Saga enters and after your draw step, add a lore counter.)$I, II -- Wings of Light -- Put a +1/+1 counter on each other creature you control. Those creatures gain flying until end of turn.$III -- Gigaflare -- Destroy target permanent. Exile Bahamut, then return it to the battlefield.$Flying| Dragoon's Lance|Final Fantasy|17|U|{1}{W}|Artifact - Equipment|||Job select$Equipped creature gets +1/+0 and is a Knight in addition to its other types.$During your turn, equipped creature has flying.$Gae Bolg -- Equip {4}| +Dwarven Castle Guard|Final Fantasy|18|C|{1}{W}|Creature - Dwarf Soldier|2|1|When this creature dies, create a 1/1 colorless Hero creature token.| Fate of the Sun-Cryst|Final Fantasy|19|C|{4}{W}|Instant|||This spell costs {2} less to cast if it targets a tapped creature.$Destroy target nonland permanent.| +From Father to Son|Final Fantasy|20|R|{1}{W}|Sorcery|||Search your library for a Vehicle card, reveal it, and put it into your hand. If this spell was cast from a graveyard, put that card onto the battlefield instead. Then shuffle.$Flashback {4}{W}{W}{W}| +G'raha Tia|Final Fantasy|21|U|{4}{W}|Legendary Creature - Cat Archer|3|5|Reach$The Allagan Eye -- Whenever one or more other creatures and/or artifacts you control die, draw a card. This ability triggers only once each turn.| +Gaelicat|Final Fantasy|22|C|{2}{W}|Creature - Cat|1|3|Flying, vigilance$As long as you control two or more artifacts, this creature gets +2/+0.| Machinist's Arsenal|Final Fantasy|23|R|{4}{W}|Artifact - Equipment|||Job select$Equipped creature gets +2/+2 for each artifact you control and is an Artificer in addition to its other types.$Machina -- Equip {4}| +Magitek Armor|Final Fantasy|24|U|{3}{W}|Artifact - Vehicle|4|4|When this Vehicle enters, create a 1/1 colorless Hero creature token.$Crew 1| +Magitek Infantry|Final Fantasy|25|C|{W}|Artifact Creature - Robot Soldier|1|1|This creature gets +1/+0 as long as you control another artifact.${2}{W}: Search your library for a card named Magitek Infantry, put it onto the battlefield tapped, then shuffle.| +Minwu, White Mage|Final Fantasy|26|R|{3}{W}{W}|Legendary Creature - Human Cleric|3|3|Vigilance, lifelink$Whenever you gain life, put a +1/+1 counter on each Cleric you control.| Moogles' Valor|Final Fantasy|27|R|{3}{W}{W}|Instant|||For each creature you control, create a 1/2 white Moogle creature token with lifelink. Then creatures you control gain indestructible until end of turn.| Paladin's Arms|Final Fantasy|28|C|{2}{W}|Artifact - Equipment|||Job select$Equipped creature gets +2/+1, has ward {1}, and is a Knight in addition to its other types.$Lightbringer and Hero's Shield -- Equip {4}| -Phoenix Down|Final Fantasy|29|U|{W}|Artifact|||{1}{W}, {T}, Exile this artifact: Choose one--$* Return target creature card with mana value 4 or less from your graveyard to the battlefield tapped.$* Exile target Skeleton, Spirit, or Zombie.| +Phoenix Down|Final Fantasy|29|U|{W}|Artifact|||{1}{W}, {T}, Exile this artifact: Choose one --$* Return target creature card with mana value 4 or less from your graveyard to the battlefield tapped.$* Exile target Skeleton, Spirit, or Zombie.| +Restoration Magic|Final Fantasy|30|U|{W}|Instant|||Tiered$* Cure -- {0} -- Target permanent gains hexproof and indestructible until end of turn.$* Cura -- {1} -- Target permanent gains hexproof and indestructible until end of turn. You gain 3 life.$* Curaga -- {3}{W} -- Permanents you control gain hexproof and indestructible until end of turn. You gain 6 life.| Sidequest: Catch a Fish|Final Fantasy|31|U|{2}{W}|Enchantment|||At the beginning of your upkeep, look at the top card of your library. If it's an artifact or creature card, you may reveal it and put it into your hand. If you put a card into your hand this way, create a Food token and transform this enchantment.| Cooking Campsite|Final Fantasy|31|U||Land|||{T}: Add {W}.${3}, {T}, Sacrifice an artifact: Put a +1/+1 counter on each creature you control. Activate only as a sorcery.| +Slash of Light|Final Fantasy|32|C|{1}{W}|Instant|||Slash of Light deals damage equal to the number of creatures you control plus the number of Equipment you control to target creature.| +Snow Villiers|Final Fantasy|33|U|{2}{W}|Legendary Creature - Human Rebel Monk|*|3|Vigilance$Snow Villiers's power is equal to the number of creatures you control.| Stiltzkin, Moogle Merchant|Final Fantasy|34|R|{W}|Legendary Creature - Moogle|1|2|Lifelink${2}, {T}: Target opponent gains control of another target permanent you control. If they do, you draw a card.| +Summon: Choco/Mog|Final Fantasy|35|C|{2}{W}|Enchantment Creature - Saga Bird Moogle|3|3|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.)$I, II, III, IV -- Stampede! -- Other creatures you control get +1/+0 until end of turn.| Summon: Knights of Round|Final Fantasy|36|M|{6}{W}{W}|Enchantment Creature - Saga Knight|3|3|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after V.)$I, II, III, IV -- Create three 2/2 white Knight creature tokens.$V -- Ultimate End -- Other creatures you control get +2/+2 until end of turn. Put an indestructible counter on each of them.$Indestructible| Summon: Primal Garuda|Final Fantasy|37|U|{3}{W}|Enchantment Creature - Saga Harpy|3|3|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Aerial Blast -- This creature deals 4 damage to target tapped creature an opponent controls.$II, III -- Slipstream -- Another target creature you control gets +1/+0 and gains flying until end of turn.$Flying| +Ultima|Final Fantasy|38|R|{3}{W}{W}|Sorcery|||Destroy all artifacts and creatures. End the turn.| +Venat, Heart of Hydaelyn|Final Fantasy|39|R|{1}{W}{W}|Legendary Creature - Elder Wizard|3|3|Whenever you cast a legendary spell, draw a card. This ability triggers only once each turn.$Hero's Sundering -- {7}, {T}: Exile target nonland permanent. Transform Venat. Activate only as a sorcery.| +Hydaelyn, the Mothercrystal|Final Fantasy|39|R||Legendary Creature - God|4|4|Indestructible$Blessing of Light -- At the beginning of combat on your turn, put a +1/+1 counter on another target creature you control. Until your next turn, it gains indestructible. If that creature is legendary, draw a card.| +Weapons Vendor|Final Fantasy|40|C|{3}{W}|Creature - Human Artificer|2|2|When this creature enters, draw a card.$At the beginning of combat on your turn, if you control an Equipment, you may pay {1}. When you do, attach target Equipment you control to target creature you control.| White Auracite|Final Fantasy|41|C|{2}{W}{W}|Artifact|||When this artifact enters, exile target nonland permanent an opponent controls until this artifact leaves the battlefield.${T}: Add {W}.| White Mage's Staff|Final Fantasy|42|C|{1}{W}|Artifact - Equipment|||Job select$Equipped creature gets +1/+1, has "Whenever this creature attacks, you gain 1 life," and is a Cleric in addition to its other types.$Equip {3}| +The Wind Crystal|Final Fantasy|43|R|{2}{W}{W}|Legendary Artifact|||White spells you cast cost {1} less to cast.$If you would gain life, you gain twice that much life instead.${4}{W}{W}, {T}: Creatures you control gain flying and lifelink until end of turn.| You're Not Alone|Final Fantasy|44|C|{W}|Instant|||Target creature gets +2/+2 until end of turn. If you control three or more creatures, it gets +4/+4 until end of turn instead.| Zack Fair|Final Fantasy|45|U|{W}|Legendary Creature - Human Soldier|0|1|Zack Fair enters with a +1/+1 counter on it.${1}, Sacrifice Zack Fair: Target creature you control gains indestructible until end of turn. Put Zack Fair's counters on that creature and attach an Equipment that was attached to Zack Fair to that creature.| Astrologian's Planisphere|Final Fantasy|46|R|{1}{U}|Artifact - Equipment|||Job select$Equipped creature is a Wizard in addition to its other types and has "Whenever you cast a noncreature spell and whenever you draw your third card each turn, put a +1/+1 counter on this creature."$Diana -- Equip {2}| +Cargo Ship|Final Fantasy|47|U|{1}{U}|Artifact - Vehicle|2|3|Flying, vigilance${T}: Add {C}. Spend this mana only to cast an artifact spell or activate an ability of an artifact source.$Crew 1| +Combat Tutorial|Final Fantasy|48|C|{2}{U}|Sorcery|||Target player draws two cards. Put a +1/+1 counter on up to one target creature you control.| Dragoon's Wyvern|Final Fantasy|49|C|{2}{U}|Creature - Drake|2|1|Flying$When this creature enters, create a 1/1 colorless Hero creature token.| Dreams of Laguna|Final Fantasy|50|C|{1}{U}|Instant|||Surveil 1, then draw a card.$Flashback {3}{U}| +Edgar, King of Figaro|Final Fantasy|51|R|{4}{U}{U}|Legendary Creature - Human Artificer Noble|4|5|When Edgar enters, draw a card for each artifact you control.$Two-Headed Coin -- The first time you flip one or more coins each turn, those coins come up heads and you win those flips.| +Eject|Final Fantasy|52|U|{3}{U}|Instant|||This spell can't be countered.$Return target nonland permanent to its owner's hand.$Draw a card.| +Ether|Final Fantasy|53|U|{3}{U}|Artifact|||{T}, Exile this artifact: Add {U}. When you next cast an instant or sorcery spell this turn, copy that spell. You may choose new targets for the copy.| +Gogo, Master of Mimicry|Final Fantasy|54|M|{2}{U}|Legendary Creature - Wizard|2|4|{X}{X}, {T}: Copy target activated or triggered ability you control X times. You may choose new targets for the copies. This ability can't be copied and X can't be 0.| Ice Flan|Final Fantasy|55|C|{4}{U}{U}|Creature - Elemental Ooze|5|4|When this creature enters, tap target artifact or creature an opponent controls. Put a stun counter on it.$Islandcycling {2}| Ice Magic|Final Fantasy|56|C|{1}{U}|Instant|||Tiered$* Blizzard -- {0} -- Return target creature to its owner's hand.$* Blizzara -- {2} -- Target creature's owner puts it on their choice of the top or bottom of their library.$* Blizzaga -- {5}{U} -- Target creature's owner shuffles it into their library.| +Il Mheg Pixie|Final Fantasy|57|U|{1}{U}|Creature - Faerie|2|1|Flying$Whenever this creature attacks, surveil 1.| Jill, Shiva's Dominant|Final Fantasy|58|R|{2}{U}|Legendary Creature - Human Noble Warrior|2|2|When Jill enters, return up to one other target nonland permanent to its owner's hand.${3}{U}{U}, {T}: Exile Jill, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery.| Shiva, Warden of Ice|Final Fantasy|58|R||Legendary Enchantment Creature - Saga Elemental|4|5|(As this Saga enters and after your draw step, add a lore counter.)$I, II -- Mesmerize -- Target creature can't be blocked this turn.$III -- Cold Snap -- Tap all lands your opponents control. Exile Shiva, then return it to the battlefield.| +Louisoix's Sacrifice|Final Fantasy|59|R|{U}|Instant|||As an additional cost to cast this spell, sacrifice a legendary creature or pay {2}.$Counter target activated ability, triggered ability, or noncreature spell.| +The Lunar Whale|Final Fantasy|60|R|{3}{U}|Legendary Artifact - Vehicle|3|5|Flying$You may look at the top card of your library any time.$As long as The Lunar Whale attacked this turn, you may play the top card of your library.$Crew 1| +Magic Damper|Final Fantasy|61|C|{U}|Instant|||Target creature you control gets +1/+1 and gains hexproof until end of turn. Untap it.| Matoya, Archon Elder|Final Fantasy|62|R|{2}{U}|Legendary Creature - Human Warlock|1|4|Whenever you scry or surveil, draw a card.| +Memories Returning|Final Fantasy|63|R|{2}{U}{U}|Sorcery|||Reveal the top five cards of your library. Put one of them into your hand. Then choose an opponent. They put one on the bottom of your library. Then you put one into your hand. Then they put one on the bottom of your library. Put the other into your hand.$Flashback {7}{U}{U}| The Prima Vista|Final Fantasy|64|U|{4}{U}|Legendary Artifact - Vehicle|5|3|Flying$Whenever you cast a noncreature spell, if at least four mana was spent to cast it, The Prima Vista becomes an artifact creature until end of turn.$Crew 2| +Qiqirn Merchant|Final Fantasy|65|C|{2}{U}|Creature - Beast Citizen|1|4|{1}, {T}: Draw a card, then discard a card.${7}, {T}, Sacrifice this creature: Draw three cards. This ability costs {1} less to activate for each Town you control.| Quistis Trepe|Final Fantasy|66|U|{2}{U}|Legendary Creature - Human Wizard|2|2|Blue Magic -- When Quistis Trepe enters, you may cast target instant or sorcery card from a graveyard, and mana of any type can be spent to cast that spell. If that spell would be put into a graveyard, exile it instead.| Relm's Sketching|Final Fantasy|67|U|{2}{U}{U}|Sorcery|||Create a token that's a copy of target artifact, creature, or land.| Retrieve the Esper|Final Fantasy|68|C|{3}{U}|Sorcery|||Create a 3/3 blue Robot Warrior artifact creature token. Then if this spell was cast from a graveyard, put two +1/+1 counters on that token.$Flashback {5}{U}| +Rook Turret|Final Fantasy|69|C|{3}{U}|Artifact Creature - Construct|3|3|Flying$Whenever another artifact you control enters, you may draw a card. If you do, discard a card.| Sage's Nouliths|Final Fantasy|70|C|{1}{U}|Artifact - Equipment|||Job select$Equipped creature gets +1/+0, has "Whenever this creature attacks, untap target attacking creature," and is a Cleric in addition to its other types.$Hagneia -- Equip {3}| Sahagin|Final Fantasy|71|C|{1}{U}|Creature - Merfolk Warrior|1|3|Whenever you cast a noncreature spell, if at least four mana was spent to cast it, put a +1/+1 counter on this creature and it can't be blocked this turn.| -Summon: Leviathan|Final Fantasy|77|R|{4}{U}{U}|Enchantment Creature - Saga Leviathan|6|6|I -- Return each creature that isn't a Kraken, Leviathan, Merfolk, Octopus, or Serpent to its owner's hand.$II, III -- Until end of turn, whenever a Kraken, Leviathan, Merfolk, Octopus, or Serpent attacks, draw a card.$Ward {2}| +Scorpion Sentinel|Final Fantasy|72|C|{1}{U}|Artifact Creature - Robot Scorpion|1|4|As long as you control seven or more lands, this creature gets +3/+0.| +Sidequest: Card Collection|Final Fantasy|73|U|{3}{U}|Enchantment|||When this enchantment enters, draw three cards, then discard two cards.$At the beginning of your end step, if eight or more cards are in your graveyard, transform this enchantment.| +Magicked Card|Final Fantasy|73|U||Artifact - Vehicle|4|4|Flying$Crew 1| +Sleep Magic|Final Fantasy|74|U|{U}|Enchantment - Aura|||Enchant creature$When this Aura enters, tap enchanted creature.$Enchanted creature doesn't untap during its controller's untap step.$When enchanted creature is dealt damage, sacrifice this Aura.| +Stolen Uniform|Final Fantasy|75|U|{U}|Instant|||Choose target creature you control and target Equipment. Gain control of that Equipment until end of turn. Attach it to the chosen creature. When you lose control of that Equipment this turn, if it's attached to a creature you control, unattach it.| +Stuck in Summoner's Sanctum|Final Fantasy|76|C|{2}{U}|Enchantment - Aura|||Flash$Enchant artifact or creature$When this Aura enters, tap enchanted permanent.$Enchanted permanent doesn't untap during its controller's untap step and its activated abilities can't be activated.| +Summon: Leviathan|Final Fantasy|77|R|{4}{U}{U}|Enchantment Creature - Saga Leviathan|6|6|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Return each creature that isn't a Kraken, Leviathan, Merfolk, Octopus, or Serpent to its owner's hand.$II, III -- Until end of turn, whenever a Kraken, Leviathan, Merfolk, Octopus, or Serpent attacks, draw a card.$Ward {2}| Summon: Shiva|Final Fantasy|78|U|{3}{U}{U}|Enchantment Creature - Saga Elemental|4|5|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I, II -- Heavenly Strike -- Tap target creature an opponent controls. Put a stun counter on it.$III -- Diamond Dust -- Draw a card for each tapped creature your opponents control.| +Swallowed by Leviathan|Final Fantasy|79|U|{2}{U}|Instant|||Choose target spell. Surveil 2, then counter the chosen spell unless its controller pays {1} for each card in your graveyard.| +Syncopate|Final Fantasy|80|C|{X}{U}|Instant|||Counter target spell unless its controller pays {X}. If that spell is countered this way, exile it instead of putting it into its owner's graveyard.| +Thief's Knife|Final Fantasy|81|U|{2}{U}|Artifact - Equipment|||Job select$Equipped creature gets +1/+1, has "Whenever this creature deals combat damage to a player, draw a card," and is a Rogue in addition to its other types.$Equip {4}| +Travel the Overworld|Final Fantasy|82|U|{5}{U}{U}|Sorcery|||Affinity for Towns$Draw four cards.| +Ultros, Obnoxious Octopus|Final Fantasy|83|U|{1}{U}|Legendary Creature - Octopus|2|1|Whenever you cast a noncreature spell, if at least four mana was spent to cast it, tap target creature an opponent controls and put a stun counter on it.$Whenever you cast a noncreature spell, if at least eight mana was spent to cast it, put eight +1/+1 counters on Ultros.| Valkyrie Aerial Unit|Final Fantasy|84|U|{5}{U}{U}|Artifact Creature - Construct|5|4|Affinity for artifacts$Flying$When this creature enters, surveil 2.| +The Water Crystal|Final Fantasy|85|R|{2}{U}{U}|Legendary Artifact|||Blue spells you cast cost {1} less to cast.$If an opponent would mill one or more cards, they mill that many cards plus four instead.${4}{U}{U}, {T}: Each opponent mills cards equal to the number of cards in your hand.| Y'shtola Rhul|Final Fantasy|86|M|{4}{U}{U}|Legendary Creature - Cat Druid|3|5|At the beginning of your end step, exile target creature you control, then return it to the battlefield under its owner's control. Then if it's the first end step of the turn, there is an additional end step after this step.| +Ahriman|Final Fantasy|87|C|{2}{B}|Creature - Eye Horror|2|2|Flying, deathtouch${3}, Sacrifice another creature or artifact: Draw a card.| Al Bhed Salvagers|Final Fantasy|88|U|{2}{B}|Creature - Human Artificer Warrior|2|3|Whenever this creature or another creature or artifact you control dies, target opponent loses 1 life and you gain 1 life.| Ardyn, the Usurper|Final Fantasy|89|R|{5}{B}{B}{B}|Legendary Creature - Elder Human Noble|4|4|Demons you control have menace, lifelink, and haste.$Starscourge -- At the beginning of combat on your turn, exile up to one target creature card from a graveyard. If you exiled a card this way, create a token that's a copy of that card, except it's a 5/5 black Demon.| Black Mage's Rod|Final Fantasy|90|C|{1}{B}|Artifact - Equipment|||Job select$Equipped creature gets +1/+0, has "Whenever you cast a noncreature spell, this creature deals 1 damage to each opponent," and is a Wizard in addition to its other types.$Equip {3}| Cecil, Dark Knight|Final Fantasy|91|R|{B}|Legendary Creature - Human Knight|2|3|Deathtouch$Darkness -- Whenever Cecil deals damage, you lose that much life. Then if your life total is less than or equal to half your starting life total, untap Cecil and transform it.| Cecil, Redeemed Paladin|Final Fantasy|91|R||Legendary Creature - Human Knight|4|4|Lifelink$Protect -- Whenever Cecil attacks, other attacking creatures gain indestructible until end of turn.| Circle of Power|Final Fantasy|92|U|{3}{B}|Sorcery|||You draw two cards and you lose 2 life. Create a 0/1 black Wizard creature token with "Whenever you cast a noncreature spell, this token deals 1 damage to each opponent."$Wizards you control get +1/+0 and gain lifelink until end of turn.| +Cornered by Black Mages|Final Fantasy|93|C|{1}{B}{B}|Sorcery|||Target opponent sacrifices a creature of their choice.$Create a 0/1 black Wizard creature token with "Whenever you cast a noncreature spell, this token deals 1 damage to each opponent."| Dark Confidant|Final Fantasy|94|M|{1}{B}|Creature - Human Wizard|2|1|At the beginning of your upkeep, reveal the top card of your library and put that card into your hand. You lose life equal to its mana value.| +Dark Knight's Greatsword|Final Fantasy|95|U|{2}{B}|Artifact - Equipment|||Job select$Equipped creature gets +3/+0 and is a Knight in addition to its other types.$Chaosbringer -- Equip--Pay 3 life. Activate only once each turn.| The Darkness Crystal|Final Fantasy|96|R|{2}{B}{B}|Legendary Artifact|||Black spells you cast cost {1} less to cast.$If a nontoken creature an opponent controls would die, instead exile it and you gain 2 life.${4}{B}{B}, {T}: Put target creature card exiled with The Darkness Crystal onto the battlefield tapped under your control with two additional +1/+1 counters on it.| +Demon Wall|Final Fantasy|97|U|{1}{B}|Artifact Creature - Demon Wall|3|3|Defender$Menace$As long as this creature has a counter on it, it can attack as though it didn't have defender.${5}{B}: Put two +1/+1 counters on this creature.| Evil Reawakened|Final Fantasy|98|U|{4}{B}|Sorcery|||Return target creature card from your graveyard to the battlefield with two additional +1/+1 counters on it.| Fang, Fearless l'Cie|Final Fantasy|99|U|{2}{B}|Legendary Creature - Human Warrior|2|3|Whenever one or more cards leave your graveyard, you draw a card and you lose 1 life. This ability triggers only once each turn.$(Melds with Vanille, Cheerful l'Cie.)| Ragnarok, Divine Deliverance|Final Fantasy|99b|U||Legendary Creature - Beast Avatar|7|6|Vigilance, menace, trample, reach, haste$When Ragnarok dies, destroy target permanent and return target nonlegendary permanent card from your graveyard to the battlefield.| +Fight On!|Final Fantasy|100|C|{2}{B}|Instant|||Return up to two target creature cards from your graveyard to your hand.| +The Final Days|Final Fantasy|101|U|{2}{B}{B}|Sorcery|||Create two tapped 2/2 black Horror creature tokens. If this spell was cast from a graveyard, instead create X of those tokens, where X is the number of creature cards in your graveyard.$Flashback {4}{B}{B}| +Gaius van Baelsar|Final Fantasy|102|U|{2}{B}{B}|Legendary Creature - Human Soldier|3|2|When Gaius van Baelsar enters, choose one --$* Each player sacrifices a creature token of their choice.$* Each player sacrifices a nontoken creature of their choice.$* Each player sacrifices an enchantment of their choice.| +Hecteyes|Final Fantasy|103|C|{1}{B}|Creature - Ooze Horror|1|1|When this creature enters, each opponent discards a card.| Jecht, Reluctant Guardian|Final Fantasy|104|R|{3}{B}|Legendary Creature - Human Warrior|4|3|Menace$Whenever Jecht deals combat damage to a player, you may exile it, then return it to the battlefield transformed under its owner's control.| Braska's Final Aeon|Final Fantasy|104|R||Legendary Enchantment Creature - Saga Nightmare|7|7|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I, II -- Jecht Beam -- Each opponent discards a card and you draw a card.$III -- Ultimate Jecht Shot -- Each opponent sacrifices two creatures of their choice.$Menace| Kain, Traitorous Dragoon|Final Fantasy|105|R|{2}{B}|Legendary Creature - Human Knight|2|4|Jump -- During your turn, Kain has flying.$Whenever Kain deals combat damage to a player, that player gains control of Kain. If they do, you draw that many cards, create that many tapped Treasure tokens, then lose that much life.| Malboro|Final Fantasy|106|C|{4}{B}{B}|Creature - Plant Horror|4|4|Bad Breath -- When this creature enters, each opponent discards a card, loses 2 life, and exiles the top three cards of their library.$Swampcycling {2}| Namazu Trader|Final Fantasy|107|C|{3}{B}|Creature - Fish Citizen|3|4|When this creature enters, you lose 1 life and create a Treasure token.$Whenever this creature attacks, you may sacrifice another creature or artifact. If you do, surveil 2.| +Ninja's Blades|Final Fantasy|108|R|{2}{B}|Artifact - Equipment|||Job select$Equipped creature gets +1/+1, is a Ninja in addition to its other types, and has "Whenever this creature deals combat damage to a player, draw a card, then discard a card. That player loses life equal to the discarded card's mana value."$Mutsunokami -- Equip {2}| +Overkill|Final Fantasy|109|U|{2}{B}|Instant|||Target creature gets -0/-9999 until end of turn.| Phantom Train|Final Fantasy|110|U|{3}{B}|Artifact - Vehicle|4|4|Trample$Sacrifice another artifact or creature: Put a +1/+1 counter on this Vehicle. It becomes a Spirit artifact creature in addition to its other types until end of turn.| Poison the Waters|Final Fantasy|111|U|{1}{B}|Sorcery|||Choose one --$* All creatures get -1/-1 until end of turn.$* Target player reveals their hand. You choose an artifact or creature card from it. That player discards that card.| +Qutrub Forayer|Final Fantasy|112|C|{2}{B}|Creature - Zombie Horror|3|2|When this creature enters, choose one --$* Destroy target creature that was dealt damage this turn.$* Exile up to two target cards from a single graveyard.| Reno and Rude|Final Fantasy|113|U|{1}{B}|Legendary Creature - Human Assassin|2|1|Menace$Whenever Reno and Rude deals combat damage to a player, exile the top card of that player's library. Then you may sacrifice another creature or artifact. If you do, you may play the exiled card this turn, and mana of any type can be spent to cast it.| +Resentful Revelation|Final Fantasy|114|C|{1}{B}|Sorcery|||Look at the top three cards of your library. Put one of them into your hand and the rest into your graveyard.$Flashback {6}{B}| Sephiroth, Fabled SOLDIER|Final Fantasy|115|M|{2}{B}|Legendary Creature - Human Avatar Soldier|3|3|Whenever Sephiroth enters or attacks, you may sacrifice another creature. If you do, draw a card.$Whenever another creature dies, target opponent loses 1 life and you gain 1 life. If this is the fourth time this ability has resolved this turn, transform Sephiroth.| Sephiroth, One-Winged Angel|Final Fantasy|115|M||Legendary Creature - Angel Nightmare Avatar|5|5|Flying$Super Nova -- As this creature transforms into Sephiroth, One-Winged Angel, you get an emblem with "Whenever a creature dies, target opponent loses 1 life and you gain 1 life."$Whenever Sephiroth attacks, you may sacrifice any number of other creatures. If you do, draw that many cards.| Sephiroth's Intervention|Final Fantasy|116|C|{3}{B}|Instant|||Destroy target creature. You gain 2 life.| Shambling Cie'th|Final Fantasy|117|U|{2}{B}|Creature - Mutant Horror|3|3|This creature enters tapped.$Whenever you cast a noncreature spell, you may pay {B}. If you do, return this card from your graveyard to your hand.| Shinra Reinforcements|Final Fantasy|118|C|{2}{B}|Creature - Human Soldier|2|3|When this creature enters, mill three cards and you gain 3 life.| +Sidequest: Hunt the Mark|Final Fantasy|119|U|{3}{B}{B}|Enchantment|||When this enchantment enters, destroy up to one target creature.$At the beginning of your end step, if a creature died under an opponent's control this turn, create a Treasure token. Then if you control three or more Treasures, transform this enchantment.| +Yiazmat, Ultimate Mark|Final Fantasy|119|U||Legendary Creature - Dragon|5|6|{1}{B}, Sacrifice another creature or artifact: Yiazmat gains indestructible until end of turn. Tap it.| Summon: Anima|Final Fantasy|120|U|{4}{B}{B}|Enchantment Creature - Saga Horror|4|4|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.)$I, II, III -- Pain -- You draw a card and you lose 1 life.$IV -- Oblivion -- Each opponent sacrifices a creature of their choice and loses 3 life.$Menace| Summon: Primal Odin|Final Fantasy|121|R|{4}{B}{B}|Enchantment Creature - Saga Knight|5|3|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Gungnir -- Destroy target creature an opponent controls.$II -- Zantetsuken -- This creature gains "Whenever this creature deals combat damage to a player, that player loses the game."$III -- Hall of Sorrow -- Draw two cards. Each player loses 2 life.| Tonberry|Final Fantasy|122|U|{B}|Creature - Salamander Horror|2|1|This creature enters tapped with a stun counter on it.$Chef's Knife -- During your turn, this creature has first strike and deathtouch.| Undercity Dire Rat|Final Fantasy|123|C|{1}{B}|Creature - Rat|2|2|Rat Tail -- When this creature dies, create a Treasure token.| +Vayne's Treachery|Final Fantasy|124|C|{1}{B}|Instant|||Kicker--Sacrifice an artifact or creature.$Target creature gets -2/-2 until end of turn. If this spell was kicked, that creature gets -6/-6 until end of turn instead.| Vincent Valentine|Final Fantasy|125|R|{2}{B}{B}|Legendary Creature - Assassin|2|2|Whenever a creature an opponent controls dies, put a number of +1/+1 counters on Vincent Valentine equal to that creature's power.$Whenever Vincent Valentine attacks, you may transform it.| Galian Beast|Final Fantasy|125|R||Legendary Creature - Werewolf Beast|3|2|Trample, lifelink$When Galian Beast dies, return it to the battlefield tapped.| +Vincent's Limit Break|Final Fantasy|126|C|{1}{B}|Instant|||Tiered$Until end of turn, target creature you control gains "When this creature dies, return it to the battlefield tapped under its owner's control" and has the chosen base power and toughness.$* Galian Beast -- {0} -- 3/2.$* Death Gigas -- {1} -- 5/2.$* Hellmasker -- {3} -- 7/2| Zenos yae Galvus|Final Fantasy|127|R|{3}{B}{B}|Legendary Creature - Human Noble Warrior|4|4|My First Friend -- When Zenos yae Galvus enters, choose a creature an opponent controls. Until end of turn, creatures other than Zenos yae Galvus and the chosen creature get -2/-2.$When the chosen creature leaves the battlefield, transform Zenos yae Galvus.| Shinryu, Transcendent Rival|Final Fantasy|127|R||Legendary Creature - Dragon|8|8|Flying$As this creature transforms into Shinryu, choose an opponent.$Burning Chains -- When the chosen player loses the game, you win the game.| Zodiark, Umbral God|Final Fantasy|128|R|{B}{B}{B}{B}{B}|Legendary Creature - God|5|5|Indestructible$When Zodiark enters, each player sacrifices half the non-God creatures they control of their choice, rounded down.$Whenever a player sacrifices another creature, put a +1/+1 counter on Zodiark.| Barret Wallace|Final Fantasy|129|U|{3}{R}|Legendary Creature - Human Rebel|4|4|Reach$Whenever Barret Wallace attacks, it deals damage equal to the number of equipped creatures you control to defending player.| +Blazing Bomb|Final Fantasy|130|C|{R}|Creature - Elemental|1|1|Whenever you cast a noncreature spell, if at least four mana was spent to cast it, put a +1/+1 counter on this creature.$Blow Up -- {T}, Sacrifice this creature: It deals damage equal to its power to target creature. Activate only as a sorcery.| +Call the Mountain Chocobo|Final Fantasy|131|C|{3}{R}|Sorcery|||Search your library for a Mountain card, reveal it, put it into your hand, then shuffle. Create a 2/2 green Bird creature token with "Whenever a land you control enters, this token gets +1/+0 until end of turn."$Flashback {5}{R}| +Choco-Comet|Final Fantasy|132|U|{X}{R}{R}|Sorcery|||Choco-Comet deals X damage to any target.$Create a 2/2 green Bird creature token with "Whenever a land you control enters, this token gets +1/+0 until end of turn."| Clive, Ifrit's Dominant|Final Fantasy|133|M|{4}{R}{R}|Legendary Creature - Human Noble Warrior|5|5|When Clive enters, you may discard your hand, then draw cards equal to your devotion to red.${4}{R}{R}, {T}: Exile Clive, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery.| Ifrit, Warden of Inferno|Final Fantasy|133|M||Legendary Enchantment Creature - Saga Demon|9|9|(As this Saga enters and after your draw step, add a lore counter.)$I -- Lunge -- Ifrit fights up to one other target creature.$II, III -- Brimstone -- Add {R}{R}{R}{R}. If Ifrit has three or more lore counters on it, exile it, then return it to the battlefield| +Coral Sword|Final Fantasy|134|U|{R}|Artifact - Equipment|||Flash$When this Equipment enters, attach it to target creature you control. That creature gains first strike until end of turn.$Equipped creature gets +1/+0.$Equip {1}| +The Fire Crystal|Final Fantasy|135|R|{2}{R}{R}|Legendary Artifact|||Red spells you cast cost {1} less to cast.$Creatures you control have haste.${4}{R}{R}, {T}: Create a token that's a copy of target creature you control. Sacrifice it at the beginning of the next end step.| Fire Magic|Final Fantasy|136|U|{R}|Instant|||Tiered$* Fire -- {0} -- Fire Magic deals 1 damage to each creature.$* Fira -- {2} -- Fire Magic deals 2 damage to each creature.$* Firaga -- {5} -- Fire Magic deals 3 damage to each creature.| Firion, Wild Rose Warrior|Final Fantasy|137|R|{2}{R}|Legendary Creature - Human Rebel Warrior|3|3|Equipped creatures you control have haste.$Whenever a nontoken Equipment you control enters, create a token that's a copy of it, except it has "This Equipment's equip abilities cost {2} less to activate." Sacrifice that token at the beginning of the next upkeep.| Freya Crescent|Final Fantasy|138|U|{R}|Legendary Creature - Rat Knight|1|1|Jump -- During your turn, Freya Crescent has flying.${T}: Add {R}. Spend this mana only to cast an Equipment spell or activate an equip ability.| Gilgamesh, Master-at-Arms|Final Fantasy|139|R|{4}{R}{R}|Legendary Creature - Human Samurai|6|6|Whenever Gilgamesh enters or attacks, look at the top six cards of your library. You may put any number of Equipment cards from among them onto the battlefield. Put the rest on the bottom of your library in a random order. When you put one or more Equipment onto the battlefield this way, you may attach one of them to a Samurai you control.| +Haste Magic|Final Fantasy|140|C|{1}{R}|Instant|||Target creature gets +3/+1 and gains haste until end of turn. Exile the top card of your library. You may play it until your next end step.| +Hill Gigas|Final Fantasy|141|C|{4}{R}{R}|Creature - Giant|5|4|Trample, haste$Mountaincycling {2}| Item Shopkeep|Final Fantasy|142|C|{1}{R}|Creature - Human Citizen|2|2|Whenever you attack, target attacking equipped creature gains menace until end of turn.| Laughing Mad|Final Fantasy|143|C|{2}{R}|Instant|||As an additional cost to cast this spell, discard a card.$Draw two cards.$Flashback {3}{R}| +Light of Judgment|Final Fantasy|144|C|{4}{R}|Instant|||Light of Judgment deals 6 damage to target creature. Destroy up to one Equipment attached to that creature.| +Mysidian Elder|Final Fantasy|145|C|{2}{R}|Creature - Human Wizard|1|3|When this creature enters, create a 0/1 black Wizard creature token with "Whenever you cast a noncreature spell, this token deals 1 damage to each opponent."| Nibelheim Aflame|Final Fantasy|146|M|{2}{R}{R}|Sorcery|||Choose target creature you control. It deals damage equal to its power to each other creature. If this spell was cast from a graveyard, discard your hand and draw four cards.$Flashback {5}{R}{R}| +Opera Love Song|Final Fantasy|147|U|{1}{R}|Instant|||Choose one --$* Exile the top two cards of your library. You may play those cards until your next end step.$* One or two target creatures each get +2/+0 until end of turn.| +Prompto Argentum|Final Fantasy|148|U|{1}{R}|Legendary Creature - Human Scout|2|2|Haste$Selfie Shot -- Whenever you cast a noncreature spell, if at least four mana was spent to cast it, create a Treasure token.| +Queen Brahne|Final Fantasy|149|U|{2}{R}|Legendary Creature - Human Noble|2|1|Prowess$Whenever Queen Brahne attacks, create a 0/1 black Wizard creature token with "Whenever you cast a noncreature spell, this token deals 1 damage to each opponent."| +Random Encounter|Final Fantasy|150|U|{4}{R}{R}|Sorcery|||Shuffle your library, then mill four cards. Put each creature card milled this way onto the battlefield. They gain haste. At the beginning of the next end step, return those creatures to their owner's hand.$Flashback {6}{R}{R}| +Raubahn, Bull of Ala Mhigo|Final Fantasy|151|R|{1}{R}|Legendary Creature - Human Warrior|2|2|Ward--Pay life equal to Raubahn's power.$Whenever Raubahn attacks, attach up to one target Equipment you control to target attacking creature.| +Red Mage's Rapier|Final Fantasy|152|C|{1}{R}|Artifact - Equipment|||Job select$Equipped creature has "Whenever you cast a noncreature spell, this creature gets +2/+0 until end of turn" and is a Wizard in addition to its other types.$Equip {3}| +Sabotender|Final Fantasy|153|C|{1}{R}|Creature - Plant|2|1|Reach$Landfall -- Whenever a land you control enters, this creature deals 1 damage to each opponent.| Samurai's Katana|Final Fantasy|154|U|{2}{R}|Artifact - Equipment|||Job select$Equipped creature gets +2/+2, has trample and haste, and is a Samurai in addition to its other types.$Murasame -- Equip {5}| +Sandworm|Final Fantasy|155|U|{4}{R}|Creature - Worm|5|4|Haste$When this creature enters, destroy target land. Its controller may search their library for a basic land card, put it onto the battlefield tapped, then shuffle.| +Seifer Almasy|Final Fantasy|156|R|{3}{R}|Legendary Creature - Human Knight|3|4|Whenever a creature you control attacks alone, it gains double strike until end of turn.$Fire Cross -- Whenever Seifer Almasy deals combat damage to a player, you may cast target instant or sorcery card with mana value 3 or less from your graveyard without paying its mana cost. If that spell would be put into your graveyard, exile it instead.| Self-Destruct|Final Fantasy|157|U|{1}{R}|Instant|||Target creature you control deals X damage to any other target and X damage to itself, where X is its power.| +Sidequest: Play Blitzball|Final Fantasy|158|U|{2}{R}|Enchantment|||At the beginning of combat on your turn, target creature you control gets +2/+0 until end of turn.$At the end of combat on your turn, if a player was dealt 6 or more combat damage this turn, transform this enchantment, then attach it to a creature you control.| +World Champion, Celestial Weapon|Final Fantasy|158|U||Legendary Artifact - Equipment|||Double Overdrive -- Equipped creature gets +2/+0 and has double strike.$Equip {3}| +Sorceress's Schemes|Final Fantasy|159|U|{3}{R}|Sorcery|||Return target instant or sorcery card from your graveyard or exiled card with flashback you own to your hand. Add {R}.$Flashback {4}{R}| +Summon: Brynhildr|Final Fantasy|160|R|{1}{R}|Enchantment Creature - Saga Knight|2|1|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Chain -- Exile the top card of your library. During any turn you put a lore counter on this Saga, you may play that card.$II, III -- Gestalt Mode -- When you next cast a creature spell this turn, it gains haste until end of turn.| Summon: Esper Ramuh|Final Fantasy|161|U|{2}{R}{R}|Enchantment Creature - Saga Wizard|3|3|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Judgment Bolt -- This creature deals damage equal to the number of noncreature, nonland cards in your graveyard to target creature an opponent controls.$II, III -- Wizards you control get +1/+0 until end of turn.| +Summon: G.F. Cerberus|Final Fantasy|162|R|{2}{R}{R}|Enchantment Creature - Saga Dog|3|3|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.)$I -- Surveil 1.$II -- Double -- When you next cast an instant or sorcery spell this turn, copy it. You may choose new targets for the copy.$III -- Triple -- When you next cast an instant or sorcery spell this turn, copy it twice. You may choose new targets for the copies.| +Summon: G.F. Ifrit|Final Fantasy|163|C|{2}{R}|Enchantment Creature - Saga Demon|3|2|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.)$I, II -- You may discard a card. If you do, draw a card.$III, IV -- Add {R}.| Suplex|Final Fantasy|164|C|{1}{R}|Sorcery|||Choose one --$* Suplex deals 3 damage to target creature. If that creature would die this turn, exile it instead.$* Exile target artifact.| +Thunder Magic|Final Fantasy|165|C|{R}|Instant|||Tiered$* Thunder -- {0} -- Thunder Magic deals 2 damage to target creature.$* Thundara -- {3} -- Thunder Magic deals 4 damage to target creature.$* Thundaga -- {5}{R} -- Thunder Magic deals 8 damage to target creature.| Triple Triad|Final Fantasy|166|R|{3}{R}{R}{R}|Enchantment|||At the beginning of your upkeep, each player exiles the top card of their library. Until end of turn, you may play the card you own exiled this way and each other card exiled this way with lesser mana value than it without paying their mana costs.| +Unexpected Request|Final Fantasy|167|U|{2}{R}|Sorcery|||Gain control of target creature until end of turn. Untap that creature. It gains haste until end of turn. You may attach an Equipment you control to that creature. If you do, unattach it at the beginning of the next end step.| +Vaan, Street Thief|Final Fantasy|168|R|{2}{R}|Legendary Creature - Human Scout|2|2|Whenever one or more Scouts, Pirates, and/or Rogues you control deal combat damage to a player, exile the top card of that player's library. You may cast it. If you don't, create a Treasure token.$Whenever you cast a spell you don't own, put a +1/+1 counter on each Scout, Pirate, and Rogue you control.| Warrior's Sword|Final Fantasy|169|C|{3}{R}|Artifact - Equipment|||Job select$Equipped creature gets +3/+2 and is a Warrior in addition to its other types.$Equip {5}| Zell Dincht|Final Fantasy|170|R|{2}{R}|Legendary Creature - Human Monk|0|3|You may play an additional land on each of your turns.$Zell Dincht gets +1/+0 for each land you control.$At the beginning of your end step, return a land you control to its owner's hand.| +Airship Crash|Final Fantasy|171|C|{2}{G}|Instant|||Destroy target artifact, enchantment, or creature with flying.$Cycling {2}| +Ancient Adamantoise|Final Fantasy|172|M|{5}{G}{G}{G}|Creature - Turtle|8|20|Vigilance, ward {3}$Damage isn't removed from this creature during cleanup steps.$All damage that would be dealt to you and other permanents you control is dealt to this creature instead.$When this creature dies, exile it and create ten tapped Treasure tokens.| +Balamb T-Rexaur|Final Fantasy|173|C|{4}{G}{G}|Creature - Dinosaur|6|6|Trample$When this creature enters, you gain 3 life.$Forestcycling {2}| +Bard's Bow|Final Fantasy|174|C|{2}{G}|Artifact - Equipment|||Job select$Equipped creature gets +2/+2, has reach, and is a Bard in addition to its other types.$Perseus's Bow -- Equip {6}| Bartz and Boko|Final Fantasy|175|R|{3}{G}{G}|Legendary Creature - Human Bird|4|3|Affinity for Birds$When Bartz and Boko enters, each other Bird you control deals damage equal to its power to target creature an opponent controls.| +Blitzball Shot|Final Fantasy|176|C|{1}{G}|Instant|||Target creature gets +3/+3 and gains trample until end of turn.| +Cactuar|Final Fantasy|177|U|{G}|Creature - Plant|3|3|Trample$At the beginning of your end step, if this creature didn't enter the battlefield this turn, return it to its owner's hand.| +Chocobo Kick|Final Fantasy|178|C|{1}{G}|Sorcery|||Kicker--Return a land you control to its owner's hand.$Target creature you control deals damage equal to its power to target creature an opponent controls. If this spell was kicked, the creature you control deals twice that much damage instead.| +Chocobo Racetrack|Final Fantasy|179|U|{3}{G}{G}|Artifact|||Landfall -- Whenever a land you control enters, create a 2/2 green Bird creature token with "Whenever a land you control enters, this token gets +1/+0 until end of turn."| +Clash of the Eikons|Final Fantasy|180|U|{G}|Sorcery|||Choose one or more --$* Target creature you control fights target creature an opponent controls.$* Remove a lore counter from target Saga you control.$* Put a lore counter on target Saga you control.| +Coliseum Behemoth|Final Fantasy|181|U|{5}{G}{G}|Creature - Beast|7|7|Trample$When this creature enters, choose one --$* Destroy target artifact or enchantment.$* Draw a card.| Commune with Beavers|Final Fantasy|182|C|{G}|Sorcery|||Look at the top three cards of your library. You may reveal an artifact, creature, or land card from among them and put it into your hand. Put the rest on the bottom of your library in any order.| +Diamond Weapon|Final Fantasy|183|U|{7}{G}{G}|Legendary Artifact Creature - Elemental|8|8|This spell costs {1} less to cast for each permanent card in your graveyard.$Reach$Immune -- Prevent all combat damage that would be dealt to Diamond Weapon.| +The Earth Crystal|Final Fantasy|184|R|{2}{G}{G}|Legendary Artifact|||Green spells you cast cost {1} less to cast.$If one or more +1/+1 counters would be put on a creature you control, twice that many +1/+1 counters are put on that creature instead.${4}{G}{G}, {T}: Distribute two +1/+1 counters among one or two target creatures you control.| Esper Origins|Final Fantasy|185|R|{1}{G}|Sorcery|||Surveil 2. You gain 2 life. If this spell was cast from a graveyard, exile it, then put it onto the battlefield transformed under its owner's control with a finality counter on it.$Flashback {3}{G}| Summon: Esper Maduin|Final Fantasy|185|R||Enchantment Creature - Saga Elemental|4|4|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Reveal the top card of your library. If it's a permanent card, put it into your hand.$II -- Add {G}{G}.$III -- Other creatures you control get +2/+2 and gain trample until end of turn.| Galuf's Final Act|Final Fantasy|186|U|{1}{G}|Instant|||Until end of turn, target creature gets +1/+0 and gains "When this creature dies, put a number of +1/+1 counters equal to its power on up to one target creature."| +Gigantoad|Final Fantasy|187|C|{3}{G}|Creature - Frog|4|4|As long as you control seven or more lands, this creature gets +2/+2.| +Goobbue Gardener|Final Fantasy|188|C|{1}{G}|Creature - Plant Beast|1|3|{T}: Add {G}.| +Gran Pulse Ochu|Final Fantasy|189|C|{G}|Creature - Plant Beast|1|1|Deathtouch${8}: Until end of turn, this creature gets +1/+1 for each permanent card in your graveyard.| +Gysahl Greens|Final Fantasy|190|C|{1}{G}|Sorcery|||Create a 2/2 green Bird creature token with "Whenever a land you control enters, this token gets +1/+0 until end of turn."$Flashback {6}{G}| Jumbo Cactuar|Final Fantasy|191|R|{5}{G}{G}|Creature - Plant|1|7|10,000 Needles -- Whenever this creature attacks, it gets +9999/+0 until end of turn.| +Loporrit Scout|Final Fantasy|192|C|{2}{G}|Creature - Rabbit Scout|3|2|Whenever another creature you control enters, this creature gets +1/+1 until end of turn.| +Prishe's Wanderings|Final Fantasy|193|C|{2}{G}|Instant|||Search your library for a basic land card or Town card, put it onto the battlefield tapped, then shuffle. When you search your library this way, put a +1/+1 counter on target creature you control.| Quina, Qu Gourmet|Final Fantasy|194|U|{2}{G}|Legendary Creature - Qu|2|3|If one or more tokens would be created under your control, those tokens plus a 1/1 green Frog creature token are created instead.${2}, Sacrifice a Frog: Put a +1/+1 counter on Quina.| +Reach the Horizon|Final Fantasy|195|U|{3}{G}|Sorcery|||Search your library for up to two basic land cards and/or Town cards with different names, put them onto the battlefield tapped, then shuffle.| A Realm Reborn|Final Fantasy|196|R|{4}{G}{G}|Enchantment|||Other permanents you control have "{T}: Add one mana of any color."| +Ride the Shoopuf|Final Fantasy|197|U|{1}{G}|Enchantment|||Landfall -- Whenever a land you control enters, put a +1/+1 counter on target creature you control.${5}{G}{G}: This enchantment becomes a 7/7 Beast creature in addition to its other types.| +Rydia's Return|Final Fantasy|198|U|{3}{G}{G}|Sorcery|||Choose one --$* Creatures you control get +3/+3 until end of turn.$* Return up to two target permanent cards from your graveyard to your hand.| Sazh Katzroy|Final Fantasy|199|R|{3}{G}|Legendary Creature - Human Pilot|3|3|When Sazh Katzroy enters, you may search your library for a Bird or basic land card, reveal it, put it into your hand, then shuffle.$Whenever Sazh Katzroy attacks, put a +1/+1 counter on target creature, then double the number of +1/+1 counters on that creature.| Sazh's Chocobo|Final Fantasy|200|U|{G}|Creature - Bird|0|1|Landfall -- Whenever a land you control enters, put a +1/+1 counter on this creature.| +Sidequest: Raise a Chocobo|Final Fantasy|201|U|{1}{G}|Enchantment|||When this enchantment enters, create a 2/2 green Bird creature token with "Whenever a land you control enters, this token gets +1/+0 until end of turn."$At the beginning of your first main phase, if you control four or more Birds, transform this enchantment.| +Black Chocobo|Final Fantasy|201|U||Creature - Bird|2|2|When this permanent transforms into Black Chocobo, search your library for a land card, put it onto the battlefield tapped, then shuffle.$Landfall -- Whenever a land you control enters, Birds you control get +1/+0 until end of turn.| +Summon: Fat Chocobo|Final Fantasy|202|C|{4}{G}|Enchantment Creature - Saga Bird|4|4|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.)$I -- Wark -- Create a 2/2 green Bird creature token with "Whenever a land you control enters, this token gets +1/+0 until end of turn."$II, III, IV -- Kerplunk -- Creatures you control gain trample until end of turn.| Summon: Fenrir|Final Fantasy|203|U|{2}{G}|Enchantment Creature - Saga Wolf|3|2|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Crescent Fang -- Search your library for a basic land card, put it onto the battlefield tapped, then shuffle.$II -- Heavenward Howl -- When you next cast a creature spell this turn, that creature enters with an additional +1/+1 counter on it.$III -- Ecliptic Growl -- Draw a card if you control the creature with the greatest power or tied for the greatest power.| +Summon: Titan|Final Fantasy|204|R|{3}{G}{G}|Enchantment Creature - Saga Giant|7|7|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I - Mill five cards.$II - Return all land cards from your graveyard to the battlefield tapped.$III - Until end of turn, another target creature you control gains trample and gets +X/+X, where X is the number of lands you control.$Reach, trample| Summoner's Grimoire|Final Fantasy|205|R|{3}{G}|Artifact - Equipment|||Job select$Equipped creature is a Shaman in addition to its other types and has "Whenever this creature attacks, you may put a creature card from your hand onto the battlefield. If that card is an enchantment card, it enters tapped and attacking."$Abraxas -- Equip {3}| Tifa Lockhart|Final Fantasy|206|R|{1}{G}|Legendary Creature - Human Monk|1|2|Trample$Landfall -- Whenever a land you control enters, double Tifa Lockhart's power until end of turn.| +Tifa's Limit Break|Final Fantasy|207|U|{G}|Instant|||Tiered$* Somersault -- {0} -- Target creature gets +2/+2 until end of turn.$* Meteor Strikes -- {2} -- Double target creature's power and toughness until end of turn.$* Final Heaven -- {6}{G} -- Triple target creature's power and toughness until end of turn.| +Torgal, A Fine Hound|Final Fantasy|208|U|{1}{G}|Legendary Creature - Wolf|2|2|Whenever you cast your first Human creature spell each turn, that creature enters with an additional +1/+1 counter on it for each Dog and/or Wolf you control.${T}: Add one mana of any color.| Town Greeter|Final Fantasy|209|C|{1}{G}|Creature - Human Citizen|1|1|When this creature enters, mill four cards. You may put a land card from among them into your hand. If you put a Town card into your hand this way, you gain 2 life.| Traveling Chocobo|Final Fantasy|210|M|{2}{G}|Creature - Bird|3|2|You may look at the top card of your library any time.$You may play lands and cast Bird spells from the top of your library.$If a land or Bird you control entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.| Vanille, Cheerful l'Cie|Final Fantasy|211|U|{3}{G}|Legendary Creature - Human Cleric|3|2|When Vanille enters, mill two cards, then return a permanent card from your graveyard to your hand.$At the beginning of your first main phase, if you both own and control Vanille and a creature named Fang, Fearless l'Cie, you may pay {3}{B}{G}. If you do, exile them, then meld them into Ragnarok, Divine Deliverance.| Absolute Virtue|Final Fantasy|212|M|{6}{W}{U}|Legendary Creature - Avatar Warrior|8|8|This spell can't be countered.$Flying$You have protection from each of your opponents.| Balthier and Fran|Final Fantasy|213|R|{1}{R}{G}|Legendary Creature - Human Rabbit|4|3|Reach$Vehicles you control get +1/+1 and have vigilance and reach.$Whenever a Vehicle crewed by Balthier and Fran this turn attacks, if it's the first combat phase of the turn, you may pay {1}{R}{G}. If you do, after this phase, there is an additional combat phase.| +Black Waltz No. 3|Final Fantasy|214|U|{2}{B}{R}|Legendary Creature - Wizard|2|2|Flying, deathtouch$Whenever you cast a noncreature spell, Black Waltz No. 3 deals 2 damage to each opponent.| Choco, Seeker of Paradise|Final Fantasy|215|R|{1}{G}{W}{U}|Legendary Creature - Bird|3|5|Whenever one or more Birds you control attack, look at that many cards from the top of your library. You may put one of them into your hand. Then put any number of land cards from among them onto the battlefield tapped and the rest into your graveyard.$Landfall -- Whenever a land you control enters, Choco gets +1/+0 until end of turn.| Cid, Timeless Artificer|Final Fantasy|216|U|{2}{W}{U}|Legendary Creature - Human Artificer|4|4|Artifact creatures and Heroes you control get +1/+1 for each Artificer you control and each Artificer card in your graveyard.$A deck can have any number of cards named Cid, Timeless Artificer.$Cycling {W}{U}| +Cloud of Darkness|Final Fantasy|217|U|{2}{B}{G}{G}|Legendary Creature - Avatar|3|3|Flying$Particle Beam -- When Cloud of Darkness enters, target creature an opponent controls gets -X/-X until end of turn, where X is the number of permanent cards in your graveyard.| Emet-Selch, Unsundered|Final Fantasy|218|M|{1}{U}{B}|Legendary Creature - Elder Wizard|2|4|Vigilance$Whenever Emet-Selch enters or attacks, draw a card, then discard a card.$At the beginning of your upkeep, if there are fourteen or more cards in your graveyard, you may transform Emet-Selch.| Hades, Sorcerer of Eld|Final Fantasy|218|M||Legendary Creature - Avatar|6|6|Vigilance$Echo of the Lost -- During your turn, you may play cards from your graveyard.$If a card or token would be put into your graveyard from anywhere, exile it instead.| The Emperor of Palamecia|Final Fantasy|219|U|{U}{R}|Legendary Creature - Human Noble Wizard|2|2|{T}: Add {U} or {R}. Spend this mana only to cast a noncreature spell.$Whenever you cast a noncreature spell, if at least four mana was spent to cast it, put a +1/+1 counter on The Emperor of Palamecia. Then if it has three or more +1/+1 counters on it, transform it.| The Lord Master of Hell|Final Fantasy|219|U||Legendary Creature - Demon Noble Wizard|3|3|Starfall -- Whenever The Lord Master of Hell attacks, it deals X damage to each opponent, where X is the number of noncreature, nonland cards in your graveyard.| +Exdeath, Void Warlock|Final Fantasy|220|U|{1}{B}{G}|Legendary Creature - Spirit Warlock|3|3|When Exdeath enters, you gain 3 life.$At the beginning of your end step, if there are six or more permanent cards in your graveyard, transform Exdeath.| +Neo Exdeath, Dimension's End|Final Fantasy|220|U||Legendary Creature - Spirit Avatar|*|3|Trample$Neo Exdeath's power is equal to the number of permanent cards in your graveyard.| Garland, Knight of Cornelia|Final Fantasy|221|U|{B}{R}|Legendary Creature - Human Knight|3|2|Whenever you cast a noncreature spell, surveil 1.${3}{B}{B}{R}{R}: Return this card from your graveyard to the battlefield transformed. Activate only as a sorcery.| Chaos, the Endless|Final Fantasy|221|U||Legendary Creature - Demon|5|5|Flying$When Chaos dies, put it on the bottom of its owner's library.| +Garnet, Princess of Alexandria|Final Fantasy|222|U|{G}{W}|Legendary Creature - Human Noble Cleric|2|2|Lifelink$Whenever Garnet attacks, you may remove a lore counter from each of any number of Sagas you control. Put a +1/+1 counter on Garnet for each lore counter removed this way.| +Giott, King of the Dwarves|Final Fantasy|223|U|{R}{W}|Legendary Creature - Dwarf Noble|1|1|Double strike$Whenever Giott or another Dwarf you control enters and whenever an Equipment you control enters, you may discard a card. If you do, draw a card.| Gladiolus Amicitia|Final Fantasy|224|U|{4}{R}{G}|Legendary Creature - Human Warrior|6|6|When Gladiolus Amicitia enters, search your library for a land card, put it onto the battlefield tapped, then shuffle.$Landfall -- Whenever a land you control enters, another target creature you control gets +2/+2 and gains trample until end of turn.| +Golbez, Crystal Collector|Final Fantasy|225|R|{U}{B}|Legendary Creature - Human Wizard|1|4|Whenever an artifact you control enters, surveil 1.$At the beginning of your end step, if you control four or more artifacts, return target creature card from your graveyard to your hand. Then if you control eight or more artifacts, each opponent loses life equal to that card's power.| +Hope Estheim|Final Fantasy|226|R|{W}{U}|Legendary Creature - Human Wizard|2|2|Lifelink$At the beginning of your end step, each opponent mills X cards, where X is the amount of life you gained this turn.| +Ignis Scientia|Final Fantasy|227|U|{1}{G}{U}|Legendary Creature - Human Advisor|2|2|When Ignis Scientia enters, look at the top six cards of your library. You may put a land card from among them onto the battlefield tapped. Put the rest on the bottom of your library in a random order.$I've Come Up with a New Recipe! -- {1}{G}{U}, {T}: Exile target card from a graveyard. If a creature card was exiled this way, create a Food token.| +Jenova, Ancient Calamity|Final Fantasy|228|R|{2}{B}{G}|Legendary Creature - Alien|1|5|At the beginning of combat on your turn, put a number of +1/+1 counters equal to Jenova's power on up to one other target creature. That creature becomes a Mutant in addition to its other types.$Whenever a Mutant you control dies during your turn, you draw cards equal to its power.| +Joshua, Phoenix's Dominant|Final Fantasy|229|R|{1}{R}{W}|Legendary Creature - Human Noble Wizard|3|4|When Joshua enters, discard up to two cards, then draw that many cards.${3}{R}{W}, {T}: Exile Joshua, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery.| +Phoenix, Warden of Fire|Final Fantasy|229|R||Legendary Enchantment Creature - Saga Phoenix|4|4|(As this Saga enters and after your draw step, add a lore counter.)$I, II -- Rising Flames -- Phoenix deals 2 damage to each opponent.$III -- Flames of Rebirth -- Return any number of target creature cards with total mana value 6 or less from your graveyard to the battlefield. Exile Phoenix, then return it to the battlefield.$Flying, lifelink| +Judge Magister Gabranth|Final Fantasy|230|U|{W}{B}|Legendary Creature - Human Advisor Knight|2|2|Menace$Whenever another creature or artifact you control dies, put a +1/+1 counter on Judge Magister Gabranth.| Kefka, Court Mage|Final Fantasy|231|M|{2}{U}{B}{R}|Legendary Creature - Human Wizard|4|5|Whenever Kefka enters or attacks, each player discards a card. Then you draw a card for each card type among cards discarded this way.${8}: Each opponent sacrifices a permanent of their choice. Transform Kefka. Activate only as a sorcery.| Kefka, Ruler of Ruin|Final Fantasy|231|M||Legendary Creature - Avatar Wizard|5|7|Flying$Whenever an opponent loses life during your turn, you draw that many cards.| Kuja, Genome Sorcerer|Final Fantasy|232|R|{2}{B}{R}|Legendary Creature - Human Mutant Wizard|3|4|At the beginning of your end step, create a tapped 0/1 black Wizard creature token with "Whenever you cast a noncreature spell, this token deals 1 damage to each opponent." Then if you control four or more Wizards, transform Kuja.| Trance Kuja, Fate Defied|Final Fantasy|232|R||Legendary Creature - Avatar Wizard|4|6|Flame Star -- If a Wizard you control would deal damage to a permanent or player, it deals double that damage instead.| Lightning, Army of One|Final Fantasy|233|M|{1}{R}{W}|Legendary Creature - Human Soldier|3|2|First strike, trample, lifelink$Stagger -- Whenever Lightning deals combat damage to a player, 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.| +Locke Cole|Final Fantasy|234|U|{1}{U}{B}|Legendary Creature - Human Rogue|2|3|Deathtouch, lifelink$Whenever Locke Cole deals combat damage to a player, draw a card, then discard a card.| Noctis, Prince of Lucis|Final Fantasy|235|R|{1}{W}{U}{B}|Legendary Creature - Human Noble|4|3|Lifelink$You may cast artifact spells from your graveyard by paying 3 life in addition to paying their other costs. If you cast a spell this way, that artifact enters with a finality counter on it.| +Omega, Heartless Evolution|Final Fantasy|236|U|{5}{G}{U}|Legendary Artifact Creature - Robot|8|8|Wave Cannon -- When Omega enters, for each opponent, tap up to one target nonland permanent that opponent controls. Put X stun counters on each of those permanents and you gain X life, where X is the number of nonbasic lands you control.| Rinoa Heartilly|Final Fantasy|237|U|{3}{G}{W}|Legendary Creature - Human Rebel Warlock|4|4|When Rinoa Heartilly enters, create Angelo, a legendary 1/1 green and white Dog creature token.$Angelo Cannon -- Whenever Rinoa Heartilly attacks, another target creature you control gets +1/+1 until end of turn for each creature you control.| +Rufus Shinra|Final Fantasy|238|U|{1}{W}{B}|Legendary Creature - Human Noble|2|4|Whenever Rufus Shinra attacks, if you don't control a creature named Darkstar, create Darkstar, a legendary 2/2 white and black Dog creature token.| +Rydia, Summoner of Mist|Final Fantasy|239|U|{R}{G}|Legendary Creature - Human Shaman|1|2|Landfall -- Whenever a land you control enters, you may discard a card. If you do, draw a card.$Summon -- {X}, {T}: Return target Saga card with mana value X from your graveyard to the battlefield with a finality counter on it. It gains haste until end of turn. Activate only as a sorcery.| Serah Farron|Final Fantasy|240|R|{1}{G}{W}|Legendary Creature - Human Citizen|2|2|The first legendary creature spell you cast each turn costs {2} less to cast.$At the beginning of combat on your turn, if you control two or more other legendary creatures, you may transform Serah Farron.| Crystallized Serah|Final Fantasy|240|R||Legendary Artifact|||The first legendary creature spell you cast each turn costs {2} less to cast.$Legendary creatures you control get +2/+2.| Shantotto, Tactician Magician|Final Fantasy|241|U|{1}{U}{R}|Legendary Creature - Dwarf Wizard|0|4|Whenever you cast a noncreature spell, Shantotto gets +X/+0 until end of turn, where X is the amount of mana spent to cast that spell. If X is 4 or more, draw a card.| Sin, Spira's Punishment|Final Fantasy|242|R|{4}{B}{G}{U}|Legendary Creature - Leviathan Avatar|7|7|Flying$Whenever Sin enters or attacks, exile a permanent card from your graveyard at random, then create a tapped token that's a copy of that card. If the exiled card is a land card, repeat this process.| Squall, SeeD Mercenary|Final Fantasy|243|R|{2}{W}{B}|Legendary Creature - Human Knight Mercenary|3|4|Rough Divide -- Whenever a creature you control attacks alone, it gains double strike until end of turn.$Whenever Squall deals combat damage to a player, return target permanent card with mana value 3 or less from your graveyard to the battlefield.| +Tellah, Great Sage|Final Fantasy|244|R|{3}{U}{R}|Legendary Creature - Human Wizard|3|3|Whenever you cast a noncreature spell, create a 1/1 colorless Hero creature token. If four or more mana was spent to cast that spell, draw two cards. If eight or more mana was spent to cast that spell, sacrifice Tellah and it deals that much damage to each opponent.| Terra, Magical Adept|Final Fantasy|245|M|{1}{R}{G}|Legendary Creature - Human Wizard Warrior|4|2|When Terra enters, mill five cards. Put up to one enchantment card milled this way into your hand.$Trance -- {4}{R}{G}, {T}: Exile Terra, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery.| Esper Terra|Final Fantasy|245|M||Legendary Enchantment Creature - Saga Wizard|6|6|(As this Saga enters and after your draw step, add a lore counter.)$I, II, III -- Create a token that's a copy of target nonlegendary enchantment you control. It gains haste. If it's a Saga, put up to three lore counters on it. Sacrifice it at the beginning of your next end step.$IV -- Add {W}{W}, {U}{U}, {B}{B}, {R}{R}, and {G}{G}. Exile Esper Terra, then return it to the battlefield.$Flying| +Tidus, Blitzball Star|Final Fantasy|246|U|{1}{W}{U}|Legendary Creature - Human Warrior|2|1|Whenever an artifact you control enters, put a +1/+1 counter on Tidus.$Whenever Tidus attacks, tap target creature an opponent controls.| Ultimecia, Time Sorceress|Final Fantasy|247|U|{3}{U}{B}|Legendary Creature - Human Warlock|4|5|Whenever Ultimecia enters or attacks, surveil 2.$At the beginning of your end step, you may pay {4}{U}{U}{B}{B} and exile eight cards from your graveyard. If you do, transform Ultimecia.| Ultimecia, Omnipotent|Final Fantasy|247|U||Legendary Creature - Nightmare Warlock|7|7|Menace$Time Compression -- When this creature transforms into Ultimecia, Omnipotent, take an extra turn after this one.| Vivi Ornitier|Final Fantasy|248|M|{1}{U}{R}|Legendary Creature - Wizard|0|3|{0}: Add X mana in any combination of {U} and/or {R}, where X is Vivi Ornitier's power. Activate only during your turn and only once each turn.$Whenever you cast a noncreature spell, put a +1/+1 counter on Vivi Ornitier and it deals 1 damage to each opponent.| +The Wandering Minstrel|Final Fantasy|249|R|{G}{U}|Legendary Creature - Human Bard|1|3|Lands you control enter untapped.$The Minstrel's Ballad -- At the beginning of combat on your turn, if you control five or more Towns, create a 2/2 Elemental creature token that's all colors.${3}{W}{U}{B}{R}{G}: Other creatures you control get +X/+X until end of turn, where X is the number of Towns you control.| Yuna, Hope of Spira|Final Fantasy|250|M|{3}{G}{W}|Legendary Creature - Human Cleric|3|5|During your turn, Yuna and enchantment creatures you control have trample, lifelink, and ward {2}.$At the beginning of your end step, return up to one target enchantment card from your graveyard to the battlefield with a finality counter on it.| -Zidane, Tantalus Thief|Final Fantasy|251|U|{3}{R}{W}|Legendary Creature - Human Mutant Scout|3|3|When Zidane enters, gain control of target creature an opponent controls until end of turn. Untap that creature. It gains lifelink and haste until end of turn.$Whenever an opponent gains control of a permanent you control, create a Treasure token.| +Zidane, Tantalus Thief|Final Fantasy|251|U|{3}{R}{W}|Legendary Creature - Human Mutant Scout|3|3|When Zidane enters, gain control of target creature an opponent controls until end of turn. Untap it. It gains lifelink and haste until end of turn.$Whenever an opponent gains control of a permanent from you, create a Treasure token.| +Adventurer's Airship|Final Fantasy|252|C|{3}|Artifact - Vehicle|3|2|Flying$When this Vehicle attacks, draw a card then discard a card.$Crew 2| +Aettir and Priwen|Final Fantasy|253|M|{6}|Legendary Artifact - Equipment|||Equipped creature has base power and toughness X/X, where X is your life total.$Equip {5}| +Blitzball|Final Fantasy|254|C|{3}|Artifact|||{T}: Add one mana of any color.$GOOOOAAAALLL! -- {T}, Sacrifice this artifact: Draw two cards. Activate only if an opponent was dealt combat damage by a legendary creature this turn.| Buster Sword|Final Fantasy|255|M|{3}|Artifact - Equipment|||Equipped creature gets +3/+2.$Whenever equipped creature deals combat damage to a player, draw a card, then you may cast a spell from your hand with mana value less than or equal to that damage without paying its mana cost.$Equip {2}| +Elixir|Final Fantasy|256|U|{1}|Artifact|||This artifact enters tapped.${5}, {T}, Exile this artifact: Shuffle all nonland cards from your graveyard into your library. You gain life equal to the number of cards shuffled into your library this way.| +Excalibur II|Final Fantasy|257|R|{1}|Legendary Artifact - Equipment|||Whenever you gain life, put a charge counter on Excalibur II.$Equipped creature gets +1/+1 for each charge counter on Excalibur II.$Equip {3}| +Genji Glove|Final Fantasy|258|R|{5}|Artifact - Equipment|||Equipped creature has double strike.$Whenever equipped creature attacks, if it's the first combat phase of the turn, untap it. After this phase, there is an additional combat phase.$Equip {3}| Instant Ramen|Final Fantasy|259|C|{2}|Artifact - Food|||Flash$When this artifact enters, draw a card.${2}, {T}, Sacrifice this artifact: You gain 3 life.| +Iron Giant|Final Fantasy|260|C|{7}|Artifact Creature - Demon|6|6|Vigilance, reach, trample| +Lion Heart|Final Fantasy|261|U|{4}|Artifact - Equipment|||When this Equipment enters, it deals 2 damage to any target.$Equipped creature gets +2/+1.$Equip {2}| +Lunatic Pandora|Final Fantasy|262|C|{1}|Legendary Artifact|||{2}, {T}: Surveil 1.${6}, {T}, Sacrifice Lunatic Pandora: Destroy target nonland permanent.| +Magic Pot|Final Fantasy|263|C|{3}|Artifact Creature - Goblin Construct|1|4|When this creature dies, create a Treasure token.${2}, {T}: Exile target card from a graveyard.| +The Masamune|Final Fantasy|264|R|{3}|Legendary Artifact - Equipment|||As long as equipped creature is attacking, it has first strike and must be blocked if able.$Equipped creature has "If a creature dying causes a triggered ability of this creature or an emblem you own to trigger, that ability triggers an additional time."$Equip {2}| +Monk's Fist|Final Fantasy|265|C|{2}|Artifact - Equipment|||Job select$Equipped creature gets +1/+0 and is a Monk in addition to its other types.$Equip {2}| PuPu UFO|Final Fantasy|266|U|{2}|Artifact Creature - Construct Alien|0|4|Flying${T}: You may put a land card from your hand onto the battlefield.${3}: Until end of turn, this creature's base power becomes equal to the number of Towns you control.| +The Regalia|Final Fantasy|267|R|{4}|Legendary Artifact - Vehicle|4|4|Haste$Whenever The Regalia attacks, reveal cards from the top of your library until you reveal a land card. Put that card onto the battlefield tapped and the rest on the bottom of your library in a random order.$Crew 1| +Relentless X-ATM092|Final Fantasy|268|U|{6}|Artifact Creature - Robot Spider|6|5|This creature can't be blocked except by three or more creatures.${8}: Return this card from your graveyard to the battlefield tapped with a finality counter on it.| +Ring of the Lucii|Final Fantasy|269|U|{4}|Legendary Artifact|||{T}: Add {C}{C}.${2}, {T}, Pay 1 life: Tap target nonland permanent.| +World Map|Final Fantasy|270|C|{1}|Artifact|||{1}, {T}, Sacrifice this artifact: Search your library for a basic land card, reveal it, put it into your hand, then shuffle.${3}, {T}, Sacrifice this artifact: Search your library for a land card, reveal it, put it into your hand, then shuffle.| Adventurer's Inn|Final Fantasy|271|C||Land - Town|||When this land enters, you gain 2 life.${T}: Add {C}.| +Balamb Garden, SeeD Academy|Final Fantasy|272|R||Land - Town|||This land enters tapped.$Add {G} or {U}.${5}{G}{U}, {T}: Transform this land. This ability costs {1} less to activate for each other Town you control.| +Balamb Garden, Airborne|Final Fantasy|272|R||Legendary Artifact - Vehicle|5|4|Flying$Whenever Balamb Garden attacks, draw a card.$Crew 1| +Baron, Airship Kingdom|Final Fantasy|273|C||Land - Town|||This land enters tapped.${T}: Add {U} or {R}.| Capital City|Final Fantasy|274|U||Land - Town|||{T}: Add {C}.${1}, {T}: Add one mana of any color.$Cycling {2}| +Clive's Hideaway|Final Fantasy|275|R||Land - Town|||Hideaway 4${T}: Add {C}.${2}, {T}: You may play the exiled card without paying its mana cost if you control four or more legendary creatures.| +Crossroads Village|Final Fantasy|276|C||Land - Town|||This land enters tapped. As it enters, choose a color.${T}: Add one mana of the chosen color.| +Eden, Seat of the Sanctum|Final Fantasy|277|U||Land - Town|||{T}: Add {C}.${5}, {T}: Mill two cards. Then you may sacrifice this land. When you do, return another target permanent card from your graveyard to your hand.| +Gohn, Town of Ruin|Final Fantasy|278|C||Land - Town|||This land enters tapped.${T}: Add {B} or {G}.| +The Gold Saucer|Final Fantasy|279|U||Land - Town|||{T}: Add {C}.${2}, {T}: Flip a coin. If you win the flip, create a Treasure token.${3}, {T}, Sacrifice two artifacts: Draw a card.| +Gongaga, Reactor Town|Final Fantasy|280|C||Land - Town|||This land enters tapped.${T}: Add {R} or {G}.| +Guadosalam, Farplane Gateway|Final Fantasy|281|C||Land - Town|||This land enters tapped.${T}: Add {G} or {U}.| +Insomnia, Crown City|Final Fantasy|282|C||Land - Town|||This land enters tapped.${T}: Add {W} or {B}.| Ishgard, the Holy See|Final Fantasy|283|R|{3}{W}{W}|Land - Town|||This land enters tapped.${T}: Add {W}.| Faith & Grief|Final Fantasy|283|R||Sorcery - Adventure|||Return up to two target artifact and/or enchantment cards from your graveyard to your hand.| Jidoor, Aristocratic Capital|Final Fantasy|284|R|{4}{U}{U}|Land - Town|||This land enters tapped.${T}: Add {U}.| Overture|Final Fantasy|284|R||Sorcery - Adventure|||Target opponent mills half their library, rounded down.| +Lindblum, Industrial Regency|Final Fantasy|285|R|{2}{R}|Land - Town|||This land enters tapped.${T}: Add {R}.| +Mage Siege|Final Fantasy|285|R||Instant - Adventure|||Create a 0/1 black Wizard creature token with "Whenever you cast a noncreature spell, this token deals 1 damage to each opponent."| +Midgar, City of Mako|Final Fantasy|286|R|{2}{B}|Land - Town|||This land enters tapped.${T}: Add {B}.| +Reactor Raid|Final Fantasy|286|R||Sorcery - Adventure|||You may sacrifice an artifact or creature. If you do, draw two cards.| Rabanastre, Royal City|Final Fantasy|287|C||Land - Town|||This land enters tapped.${T}: Add {R} or {W}.| +Sharlayan, Nation of Scholars|Final Fantasy|288|C||Land - Town|||This land enters tapped.${T}: Add {W} or {U}.| Starting Town|Final Fantasy|289|R||Land - Town|||This land enters tapped unless it's your first, second, or third turn of the game.${T}: Add {C}.${T}, Pay 1 life: Add one mana of any color.| +Treno, Dark City|Final Fantasy|290|C||Land - Town|||This land enters tapped.${T}: Add {U} or {B}.| +Vector, Imperial Capital|Final Fantasy|291|C||Land - Town|||This land enters tapped.${T}: Add {B} or {R}.| +Windurst, Federation Center|Final Fantasy|292|C||Land - Town|||This land enters tapped.${T}: Add {G} or {W}.| Zanarkand, Ancient Metropolis|Final Fantasy|293|R|{4}{G}{G}|Land - Town|||This land enters tapped.${T}: Add {G}.| Lasting Fayth|Final Fantasy|293|R||Sorcery - Adventure|||Create a 1/1 colorless Hero creature token. Put a +1/+1 counter on it for each land you control.| Plains|Final Fantasy|294|C||Basic Land - Plains|||({T}: Add {W}.)| @@ -57795,6 +58113,10 @@ Ishgard, the Holy See|Final Fantasy|310|R|{3}{W}{W}|Land - Town|||This land ente Faith & Grief|Final Fantasy|310|R||Sorcery - Adventure|||Return up to two target artifact and/or enchantment cards from your graveyard to your hand.| Jidoor, Aristocratic Capital|Final Fantasy|311|R|{4}{U}{U}|Land - Town|||This land enters tapped.${T}: Add {U}.| Overture|Final Fantasy|311|R||Sorcery - Adventure|||Target opponent mills half their library, rounded down.| +Lindblum, Industrial Regency|Final Fantasy|312|R|{2}{R}|Land - Town|||This land enters tapped.${T}: Add {R}.| +Mage Siege|Final Fantasy|312|R||Instant - Adventure|||Create a 0/1 black Wizard creature token with "Whenever you cast a noncreature spell, this token deals 1 damage to each opponent."| +Midgar, City of Mako|Final Fantasy|313|R|{2}{B}|Land - Town|||This land enters tapped.${T}: Add {B}.| +Reactor Raid|Final Fantasy|313|R||Sorcery - Adventure|||You may sacrifice an artifact or creature. If you do, draw two cards.| Zanarkand, Ancient Metropolis|Final Fantasy|314|R|{4}{G}{G}|Land - Town|||This land enters tapped.${T}: Add {G}.| Lasting Fayth|Final Fantasy|314|R||Sorcery - Adventure|||Create a 1/1 colorless Hero creature token. Put a +1/+1 counter on it for each land you control.| Ardyn, the Usurper|Final Fantasy|315|R|{5}{B}{B}{B}|Legendary Creature - Elder Human Noble|4|4|Demons you control have menace, lifelink, and haste.$Starscourge -- At the beginning of combat on your turn, exile up to one target creature card from a graveyard. If you exiled a card this way, create a token that's a copy of that card, except it's a 5/5 black Demon.| @@ -57811,33 +58133,65 @@ Kefka, Ruler of Ruin|Final Fantasy|322|M||Legendary Creature - Avatar Wizard|5|7 Terra, Magical Adept|Final Fantasy|323|M|{1}{R}{G}|Legendary Creature - Human Wizard Warrior|4|2|When Terra enters, mill five cards. Put up to one enchantment card milled this way into your hand.$Trance -- {4}{R}{G}, {T}: Exile Terra, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery.| Esper Terra|Final Fantasy|323|M||Legendary Enchantment Creature - Saga Wizard|6|6|(As this Saga enters and after your draw step, add a lore counter.)$I, II, III -- Create a token that's a copy of target nonlegendary enchantment you control. It gains haste. If it's a Saga, put up to three lore counters on it. Sacrifice it at the beginning of your next end step.$IV -- Add {W}{W}, {U}{U}, {B}{B}, {R}{R}, and {G}{G}. Exile Esper Terra, then return it to the battlefield.$Flying| Ultima, Origin of Oblivion|Final Fantasy|324|R|{5}|Legendary Creature - God|4|4|Flying$Whenever Ultima attacks, put a blight counter on target land. For as long as that land has a blight counter on it, it loses all land types and abilities and has "{T}: Add {C}."$Whenever you tap a land for {C}, add an additional {C}.| +Ambrosia Whiteheart|Final Fantasy|325|U|{1}{W}|Legendary Creature - Bird|2|2|Flash$When Ambrosia Whiteheart enters, you may return another permanent you control to its owner's hand.$Landfall -- Whenever a land you control enters, Ambrosia Whiteheart gets +1/+0 until end of turn.| Moogles' Valor|Final Fantasy|326|R|{3}{W}{W}|Instant|||For each creature you control, create a 1/2 white Moogle creature token with lifelink. Then creatures you control gain indestructible until end of turn.| Stiltzkin, Moogle Merchant|Final Fantasy|327|R|{W}|Legendary Creature - Moogle|1|2|Lifelink${2}, {T}: Target opponent gains control of another target permanent you control. If they do, you draw a card.| +Ultima|Final Fantasy|328|R|{3}{W}{W}|Sorcery|||Destroy all artifacts and creatures. End the turn.| +Venat, Heart of Hydaelyn|Final Fantasy|329|R|{1}{W}{W}|Legendary Creature - Elder Wizard|3|3|Whenever you cast a legendary spell, draw a card. This ability triggers only once each turn.$Hero's Sundering -- {7}, {T}: Exile target nonland permanent. Transform Venat. Activate only as a sorcery.| +Hydaelyn, the Mothercrystal|Final Fantasy|329|R||Legendary Creature - God|4|4|Indestructible$Blessing of Light -- At the beginning of combat on your turn, put a +1/+1 counter on another target creature you control. Until your next turn, it gains indestructible. If that creature is legendary, draw a card.| +The Wind Crystal|Final Fantasy|330|R|{2}{W}{W}|Legendary Artifact|||White spells you cast cost {1} less to cast.$If you would gain life, you gain twice that much life instead.${4}{W}{W}, {T}: Creatures you control gain flying and lifelink until end of turn.| +Memories Returning|Final Fantasy|331|R|{2}{U}{U}|Sorcery|||Reveal the top five cards of your library. Put one of them into your hand. Then choose an opponent. They put one on the bottom of your library. Then you put one into your hand. Then they put one on the bottom of your library. Put the other into your hand.$Flashback {7}{U}{U}| +Stolen Uniform|Final Fantasy|332|U|{U}|Instant|||Choose target creature you control and target Equipment. Gain control of that Equipment until end of turn. Attach it to the chosen creature. When you lose control of that Equipment this turn, if it's attached to a creature you control, unattach it.| +The Water Crystal|Final Fantasy|333|R|{2}{U}{U}|Legendary Artifact|||Blue spells you cast cost {1} less to cast.$If an opponent would mill one or more cards, they mill that many cards plus four instead.${4}{U}{U}, {T}: Each opponent mills cards equal to the number of cards in your hand.| Dark Confidant|Final Fantasy|334|M|{1}{B}|Creature - Human Wizard|2|1|At the beginning of your upkeep, reveal the top card of your library and put that card into your hand. You lose life equal to its mana value.| The Darkness Crystal|Final Fantasy|335|R|{2}{B}{B}|Legendary Artifact|||Black spells you cast cost {1} less to cast.$If a nontoken creature an opponent controls would die, instead exile it and you gain 2 life.${4}{B}{B}, {T}: Put target creature card exiled with The Darkness Crystal onto the battlefield tapped under your control with two additional +1/+1 counters on it.| Zodiark, Umbral God|Final Fantasy|336|R|{B}{B}{B}{B}{B}|Legendary Creature - God|5|5|Indestructible$When Zodiark enters, each player sacrifices half the non-God creatures they control of their choice, rounded down.$Whenever a player sacrifices another creature, put a +1/+1 counter on Zodiark.| +The Fire Crystal|Final Fantasy|337|R|{2}{R}{R}|Legendary Artifact|||Red spells you cast cost {1} less to cast.$Creatures you control have haste.${4}{R}{R}, {T}: Create a token that's a copy of target creature you control. Sacrifice it at the beginning of the next end step.| Gilgamesh, Master-at-Arms|Final Fantasy|338|R|{4}{R}{R}|Legendary Creature - Human Samurai|6|6|Whenever Gilgamesh enters or attacks, look at the top six cards of your library. You may put any number of Equipment cards from among them onto the battlefield. Put the rest on the bottom of your library in a random order. When you put one or more Equipment onto the battlefield this way, you may attach one of them to a Samurai you control.| Nibelheim Aflame|Final Fantasy|339|M|{2}{R}{R}|Sorcery|||Choose target creature you control. It deals damage equal to its power to each other creature. If this spell was cast from a graveyard, discard your hand and draw four cards.$Flashback {5}{R}{R}| Triple Triad|Final Fantasy|340|R|{3}{R}{R}{R}|Enchantment|||At the beginning of your upkeep, each player exiles the top card of their library. Until end of turn, you may play the card you own exiled this way and each other card exiled this way with lesser mana value than it without paying their mana costs.| +Clash of the Eikons|Final Fantasy|341|U|{G}|Sorcery|||Choose one or more --$* Target creature you control fights target creature an opponent controls.$* Remove a lore counter from target Saga you control.$* Put a lore counter on target Saga you control.| +The Earth Crystal|Final Fantasy|342|R|{2}{G}{G}|Legendary Artifact|||Green spells you cast cost {1} less to cast.$If one or more +1/+1 counters would be put on a creature you control, twice that many +1/+1 counters are put on that creature instead.${4}{G}{G}, {T}: Distribute two +1/+1 counters among one or two target creatures you control.| Jumbo Cactuar|Final Fantasy|343|R|{5}{G}{G}|Creature - Plant|1|7|10,000 Needles -- Whenever this creature attacks, it gets +9999/+0 until end of turn.| A Realm Reborn|Final Fantasy|344|R|{4}{G}{G}|Enchantment|||Other permanents you control have "{T}: Add one mana of any color."| +Torgal, A Fine Hound|Final Fantasy|345|U|{1}{G}|Legendary Creature - Wolf|2|2|Whenever you cast your first Human creature spell each turn, that creature enters with an additional +1/+1 counter on it for each Dog and/or Wolf you control.${T}: Add one mana of any color.| +Jenova, Ancient Calamity|Final Fantasy|346|R|{2}{B}{G}|Legendary Creature - Alien|1|5|At the beginning of combat on your turn, put a number of +1/+1 counters equal to Jenova's power on up to one other target creature. That creature becomes a Mutant in addition to its other types.$Whenever a Mutant you control dies during your turn, you draw cards equal to its power.| +Omega, Heartless Evolution|Final Fantasy|347|U|{5}{G}{U}|Legendary Artifact Creature - Robot|8|8|Wave Cannon -- When Omega enters, for each opponent, tap up to one target nonland permanent that opponent controls. Put X stun counters on each of those permanents and you gain X life, where X is the number of nonbasic lands you control.| Sin, Spira's Punishment|Final Fantasy|348|R|{4}{B}{G}{U}|Legendary Creature - Leviathan Avatar|7|7|Flying$Whenever Sin enters or attacks, exile a permanent card from your graveyard at random, then create a tapped token that's a copy of that card. If the exiled card is a land card, repeat this process.| +Tellah, Great Sage|Final Fantasy|349|R|{3}{U}{R}|Legendary Creature - Human Wizard|3|3|Whenever you cast a noncreature spell, create a 1/1 colorless Hero creature token. If four or more mana was spent to cast that spell, draw two cards. If eight or more mana was spent to cast that spell, sacrifice Tellah and it deals that much damage to each opponent.| +Aettir and Priwen|Final Fantasy|350|M|{6}|Legendary Artifact - Equipment|||Equipped creature has base power and toughness X/X, where X is your life total.$Equip {5}| Buster Sword|Final Fantasy|351|M|{3}|Artifact - Equipment|||Equipped creature gets +3/+2.$Whenever equipped creature deals combat damage to a player, draw a card, then you may cast a spell from your hand with mana value less than or equal to that damage without paying its mana cost.$Equip {2}| +Excalibur II|Final Fantasy|352|R|{1}|Legendary Artifact - Equipment|||Whenever you gain life, put a charge counter on Excalibur II.$Equipped creature gets +1/+1 for each charge counter on Excalibur II.$Equip {3}| +The Masamune|Final Fantasy|353|R|{3}|Legendary Artifact - Equipment|||As long as equipped creature is attacking, it has first strike and must be blocked if able.$Equipped creature has "If a creature dying causes a triggered ability of this creature or an emblem you own to trigger, that ability triggers an additional time."$Equip {2}| +Balamb Garden, SeeD Academy|Final Fantasy|354|R||Land - Town|||This land enters tapped.$Add {G} or {U}.${5}{G}{U}, {T}: Transform this land. This ability costs {1} less to activate for each other Town you control.| +Balamb Garden, Airborne|Final Fantasy|354|R||Legendary Artifact - Vehicle|5|4|Flying$Whenever Balamb Garden attacks, draw a card.$Crew 1| +Eden, Seat of the Sanctum|Final Fantasy|355|U||Land - Town|||{T}: Add {C}.${5}, {T}: Mill two cards. Then you may sacrifice this land. When you do, return another target permanent card from your graveyard to your hand.| Summon: Bahamut|Final Fantasy|356|M|{9}|Enchantment Creature - Saga Dragon|9|9|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.)$I, II -- Destroy up to one target nonland permanent.$III -- Draw two cards.$IV -- Mega Flare -- This creature deals damage equal to the total mana value of other permanents you control to each opponent.$Flying| +Crystal Fragments|Final Fantasy|357|U|{W}|Artifact - Equipment|||Equipped creature gets +1/+1.${5}{W}{W}: Exile this Equipment, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery.$Equip {1}| +Summon: Alexander|Final Fantasy|357|U||Enchantment Creature - Saga Construct|4|3|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I, II -- Prevent all damage that would be dealt to creatures you control this turn.$III -- Tap all creatures your opponents control.$Flying| +Summon: Choco/Mog|Final Fantasy|358|C|{2}{W}|Enchantment Creature - Saga Bird Moogle|3|3|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.)$I, II, III, IV -- Stampede! -- Other creatures you control get +1/+0 until end of turn.| Summon: Knights of Round|Final Fantasy|359|M|{6}{W}{W}|Enchantment Creature - Saga Knight|3|3|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after V.)$I, II, III, IV -- Create three 2/2 white Knight creature tokens.$V -- Ultimate End -- Other creatures you control get +2/+2 until end of turn. Put an indestructible counter on each of them.$Indestructible| Summon: Primal Garuda|Final Fantasy|360|U|{3}{W}|Enchantment Creature - Saga Harpy|3|3|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Aerial Blast -- This creature deals 4 damage to target tapped creature an opponent controls.$II, III -- Slipstream -- Another target creature you control gets +1/+0 and gains flying until end of turn.$Flying| -Summon: Leviathan|Final Fantasy|361|R|{4}{U}{U}|Enchantment Creature - Saga Leviathan|6|6|I -- Return each creature that isn't a Kraken, Leviathan, Merfolk, Octopus, or Serpent to its owner's hand.$II, III -- Until end of turn, whenever a Kraken, Leviathan, Merfolk, Octopus, or Serpent attacks, draw a card.$Ward {2}| +Summon: Leviathan|Final Fantasy|361|R|{4}{U}{U}|Enchantment Creature - Saga Leviathan|6|6|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Return each creature that isn't a Kraken, Leviathan, Merfolk, Octopus, or Serpent to its owner's hand.$II, III -- Until end of turn, whenever a Kraken, Leviathan, Merfolk, Octopus, or Serpent attacks, draw a card.$Ward {2}| Summon: Shiva|Final Fantasy|362|U|{3}{U}{U}|Enchantment Creature - Saga Elemental|4|5|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I, II -- Heavenly Strike -- Tap target creature an opponent controls. Put a stun counter on it.$III -- Diamond Dust -- Draw a card for each tapped creature your opponents control.| Jecht, Reluctant Guardian|Final Fantasy|363|R|{3}{B}|Legendary Creature - Human Warrior|4|3|Menace$Whenever Jecht deals combat damage to a player, you may exile it, then return it to the battlefield transformed under its owner's control.| Braska's Final Aeon|Final Fantasy|363|R||Legendary Enchantment Creature - Saga Nightmare|7|7|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I, II -- Jecht Beam -- Each opponent discards a card and you draw a card.$III -- Ultimate Jecht Shot -- Each opponent sacrifices two creatures of their choice.$Menace| Summon: Anima|Final Fantasy|364|U|{4}{B}{B}|Enchantment Creature - Saga Horror|4|4|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.)$I, II, III -- Pain -- You draw a card and you lose 1 life.$IV -- Oblivion -- Each opponent sacrifices a creature of their choice and loses 3 life.$Menace| Summon: Primal Odin|Final Fantasy|365|R|{4}{B}{B}|Enchantment Creature - Saga Knight|5|3|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Gungnir -- Destroy target creature an opponent controls.$II -- Zantetsuken -- This creature gains "Whenever this creature deals combat damage to a player, that player loses the game."$III -- Hall of Sorrow -- Draw two cards. Each player loses 2 life.| +Summon: Brynhildr|Final Fantasy|366|R|{1}{R}|Enchantment Creature - Saga Knight|2|1|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Chain -- Exile the top card of your library. During any turn you put a lore counter on this Saga, you may play that card.$II, III -- Gestalt Mode -- When you next cast a creature spell this turn, it gains haste until end of turn.| Summon: Esper Ramuh|Final Fantasy|367|U|{2}{R}{R}|Enchantment Creature - Saga Wizard|3|3|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Judgment Bolt -- This creature deals damage equal to the number of noncreature, nonland cards in your graveyard to target creature an opponent controls.$II, III -- Wizards you control get +1/+0 until end of turn.| +Summon: G.F. Cerberus|Final Fantasy|368|R|{2}{R}{R}|Enchantment Creature - Saga Dog|3|3|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.)$I -- Surveil 1.$II -- Double -- When you next cast an instant or sorcery spell this turn, copy it. You may choose new targets for the copy.$III -- Triple -- When you next cast an instant or sorcery spell this turn, copy it twice. You may choose new targets for the copies.| +Summon: G.F. Ifrit|Final Fantasy|369|C|{2}{R}|Enchantment Creature - Saga Demon|3|2|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.)$I, II -- You may discard a card. If you do, draw a card.$III, IV -- Add {R}.| Esper Origins|Final Fantasy|370|R|{1}{G}|Sorcery|||Surveil 2. You gain 2 life. If this spell was cast from a graveyard, exile it, then put it onto the battlefield transformed under its owner's control with a finality counter on it.$Flashback {3}{G}| Summon: Esper Maduin|Final Fantasy|370|R||Enchantment Creature - Saga Elemental|4|4|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Reveal the top card of your library. If it's a permanent card, put it into your hand.$II -- Add {G}{G}.$III -- Other creatures you control get +2/+2 and gain trample until end of turn.| +Summon: Fat Chocobo|Final Fantasy|371|C|{4}{G}|Enchantment Creature - Saga Bird|4|4|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.)$I -- Wark -- Create a 2/2 green Bird creature token with "Whenever a land you control enters, this token gets +1/+0 until end of turn."$II, III, IV -- Kerplunk -- Creatures you control gain trample until end of turn.| Summon: Fenrir|Final Fantasy|372|U|{2}{G}|Enchantment Creature - Saga Wolf|3|2|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Crescent Fang -- Search your library for a basic land card, put it onto the battlefield tapped, then shuffle.$II -- Heavenward Howl -- When you next cast a creature spell this turn, that creature enters with an additional +1/+1 counter on it.$III -- Ecliptic Growl -- Draw a card if you control the creature with the greatest power or tied for the greatest power.| +Summon: Titan|Final Fantasy|373|R|{3}{G}{G}|Enchantment Creature - Saga Giant|7|7|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I - Mill five cards.$II - Return all land cards from your graveyard to the battlefield tapped.$III - Until end of turn, another target creature you control gains trample and gets +X/+X, where X is the number of lands you control.$Reach, trample| Aerith Gainsborough|Final Fantasy|374|R|{2}{W}|Legendary Creature - Human Cleric|2|2|Lifelink$Whenever you gain life, put a +1/+1 counter on Aerith Gainsborough.$When Aerith Gainsborough dies, put X +1/+1 counters on each legendary creature you control, where X is the number of +1/+1 counters on Aerith Gainsborough.| Cloud, Midgar Mercenary|Final Fantasy|375|M|{W}{W}|Legendary Creature - Human Soldier Mercenary|2|1|When Cloud enters, search your library for an Equipment card, reveal it, put it into your hand, then shuffle.$As long as Cloud is equipped, if an ability of Cloud or an Equipment attached to it triggers, that ability triggers an additional time.| +Dion, Bahamut's Dominant|Final Fantasy|376|R|{3}{W}|Legendary Creature - Human Noble Knight|3|3|Dragonfire Dive -- During your turn, Dion and other Knights you control have flying.$When Dion enters, create a 2/2 white Knight creature token.${4}{W}{W}, {T}: Exile Dion, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery.| +Bahamut, Warden of Light|Final Fantasy|376|R||Legendary Enchantment Creature - Saga Dragon|5|5|(As this Saga enters and after your draw step, add a lore counter.)$I, II -- Wings of Light -- Put a +1/+1 counter on each other creature you control. Those creatures gain flying until end of turn.$III -- Gigaflare -- Destroy target permanent. Exile Bahamut, then return it to the battlefield.$Flying| +Gogo, Master of Mimicry|Final Fantasy|377|M|{2}{U}|Legendary Creature - Wizard|2|4|{X}{X}, {T}: Copy target activated or triggered ability you control X times. You may choose new targets for the copies. This ability can't be copied and X can't be 0.| Jill, Shiva's Dominant|Final Fantasy|378|R|{2}{U}|Legendary Creature - Human Noble Warrior|2|2|When Jill enters, return up to one other target nonland permanent to its owner's hand.${3}{U}{U}, {T}: Exile Jill, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery.| Shiva, Warden of Ice|Final Fantasy|378|R||Legendary Enchantment Creature - Saga Elemental|4|5|(As this Saga enters and after your draw step, add a lore counter.)$I, II -- Mesmerize -- Target creature can't be blocked this turn.$III -- Cold Snap -- Tap all lands your opponents control. Exile Shiva, then return it to the battlefield.| Ardyn, the Usurper|Final Fantasy|379|R|{5}{B}{B}{B}|Legendary Creature - Elder Human Noble|4|4|Demons you control have menace, lifelink, and haste.$Starscourge -- At the beginning of combat on your turn, exile up to one target creature card from a graveyard. If you exiled a card this way, create a token that's a copy of that card, except it's a 5/5 black Demon.| @@ -57854,11 +58208,19 @@ Shinryu, Transcendent Rival|Final Fantasy|384|R||Legendary Creature - Dragon|8|8 Clive, Ifrit's Dominant|Final Fantasy|385|M|{4}{R}{R}|Legendary Creature - Human Noble Warrior|5|5|When Clive enters, you may discard your hand, then draw cards equal to your devotion to red.${4}{R}{R}, {T}: Exile Clive, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery.| Ifrit, Warden of Inferno|Final Fantasy|385|M||Legendary Enchantment Creature - Saga Demon|9|9|(As this Saga enters and after your draw step, add a lore counter.)$I -- Lunge -- Ifrit fights up to one other target creature.$II, III -- Brimstone -- Add {R}{R}{R}{R}. If Ifrit has three or more lore counters on it, exile it, then return it to the battlefield| Firion, Wild Rose Warrior|Final Fantasy|386|R|{2}{R}|Legendary Creature - Human Rebel Warrior|3|3|Equipped creatures you control have haste.$Whenever a nontoken Equipment you control enters, create a token that's a copy of it, except it has "This Equipment's equip abilities cost {2} less to activate." Sacrifice that token at the beginning of the next upkeep.| +Prompto Argentum|Final Fantasy|387|U|{1}{R}|Legendary Creature - Human Scout|2|2|Haste$Selfie Shot -- Whenever you cast a noncreature spell, if at least four mana was spent to cast it, create a Treasure token.| +Raubahn, Bull of Ala Mhigo|Final Fantasy|388|R|{1}{R}|Legendary Creature - Human Warrior|2|2|Ward--Pay life equal to Raubahn's power.$Whenever Raubahn attacks, attach up to one target Equipment you control to target attacking creature.| +Seifer Almasy|Final Fantasy|389|R|{3}{R}|Legendary Creature - Human Knight|3|4|Whenever a creature you control attacks alone, it gains double strike until end of turn.$Fire Cross -- Whenever Seifer Almasy deals combat damage to a player, you may cast target instant or sorcery card with mana value 3 or less from your graveyard without paying its mana cost. If that spell would be put into your graveyard, exile it instead.| +Vaan, Street Thief|Final Fantasy|390|R|{2}{R}|Legendary Creature - Human Scout|2|2|Whenever one or more Scouts, Pirates, and/or Rogues you control deal combat damage to a player, exile the top card of that player's library. You may cast it. If you don't, create a Treasure token.$Whenever you cast a spell you don't own, put a +1/+1 counter on each Scout, Pirate, and Rogue you control.| Tifa Lockhart|Final Fantasy|391|R|{1}{G}|Legendary Creature - Human Monk|1|2|Trample$Landfall -- Whenever a land you control enters, double Tifa Lockhart's power until end of turn.| Vanille, Cheerful l'Cie|Final Fantasy|392|U|{3}{G}|Legendary Creature - Human Cleric|3|2|When Vanille enters, mill two cards, then return a permanent card from your graveyard to your hand.$At the beginning of your first main phase, if you both own and control Vanille and a creature named Fang, Fearless l'Cie, you may pay {3}{B}{G}. If you do, exile them, then meld them into Ragnarok, Divine Deliverance.| Balthier and Fran|Final Fantasy|393|R|{1}{R}{G}|Legendary Creature - Human Rabbit|4|3|Reach$Vehicles you control get +1/+1 and have vigilance and reach.$Whenever a Vehicle crewed by Balthier and Fran this turn attacks, if it's the first combat phase of the turn, you may pay {1}{R}{G}. If you do, after this phase, there is an additional combat phase.| Emet-Selch, Unsundered|Final Fantasy|394|M|{1}{U}{B}|Legendary Creature - Elder Wizard|2|4|Vigilance$Whenever Emet-Selch enters or attacks, draw a card, then discard a card.$At the beginning of your upkeep, if there are fourteen or more cards in your graveyard, you may transform Emet-Selch.| Hades, Sorcerer of Eld|Final Fantasy|394|M||Legendary Creature - Avatar|6|6|Vigilance$Echo of the Lost -- During your turn, you may play cards from your graveyard.$If a card or token would be put into your graveyard from anywhere, exile it instead.| +Golbez, Crystal Collector|Final Fantasy|395|R|{U}{B}|Legendary Creature - Human Wizard|1|4|Whenever an artifact you control enters, surveil 1.$At the beginning of your end step, if you control four or more artifacts, return target creature card from your graveyard to your hand. Then if you control eight or more artifacts, each opponent loses life equal to that card's power.| +Hope Estheim|Final Fantasy|396|R|{W}{U}|Legendary Creature - Human Wizard|2|2|Lifelink$At the beginning of your end step, each opponent mills X cards, where X is the amount of life you gained this turn.| +Joshua, Phoenix's Dominant|Final Fantasy|397|R|{1}{R}{W}|Legendary Creature - Human Noble Wizard|3|4|When Joshua enters, discard up to two cards, then draw that many cards.${3}{R}{W}, {T}: Exile Joshua, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery.| +Phoenix, Warden of Fire|Final Fantasy|397|R||Legendary Enchantment Creature - Saga Phoenix|4|4|(As this Saga enters and after your draw step, add a lore counter.)$I, II -- Rising Flames -- Phoenix deals 2 damage to each opponent.$III -- Flames of Rebirth -- Return any number of target creature cards with total mana value 6 or less from your graveyard to the battlefield. Exile Phoenix, then return it to the battlefield.$Flying, lifelink| Kefka, Court Mage|Final Fantasy|398|M|{2}{U}{B}{R}|Legendary Creature - Human Wizard|4|5|Whenever Kefka enters or attacks, each player discards a card. Then you draw a card for each card type among cards discarded this way.${8}: Each opponent sacrifices a permanent of their choice. Transform Kefka. Activate only as a sorcery.| Kefka, Ruler of Ruin|Final Fantasy|398|M||Legendary Creature - Avatar Wizard|5|7|Flying$Whenever an opponent loses life during your turn, you draw that many cards.| Kuja, Genome Sorcerer|Final Fantasy|399|R|{2}{B}{R}|Legendary Creature - Human Mutant Wizard|3|4|At the beginning of your end step, create a tapped 0/1 black Wizard creature token with "Whenever you cast a noncreature spell, this token deals 1 damage to each opponent." Then if you control four or more Wizards, transform Kuja.| @@ -57866,8 +58228,9 @@ Trance Kuja, Fate Defied|Final Fantasy|399|R||Legendary Creature - Avatar Wizard Lightning, Army of One|Final Fantasy|400|M|{1}{R}{W}|Legendary Creature - Human Soldier|3|2|First strike, trample, lifelink$Stagger -- Whenever Lightning deals combat damage to a player, 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.| Noctis, Prince of Lucis|Final Fantasy|401|R|{1}{W}{U}{B}|Legendary Creature - Human Noble|4|3|Lifelink$You may cast artifact spells from your graveyard by paying 3 life in addition to paying their other costs. If you cast a spell this way, that artifact enters with a finality counter on it.| Squall, SeeD Mercenary|Final Fantasy|402|R|{2}{W}{B}|Legendary Creature - Human Knight Mercenary|3|4|Rough Divide -- Whenever a creature you control attacks alone, it gains double strike until end of turn.$Whenever Squall deals combat damage to a player, return target permanent card with mana value 3 or less from your graveyard to the battlefield.| +The Wandering Minstrel|Final Fantasy|403|R|{G}{U}|Legendary Creature - Human Bard|1|3|Lands you control enter untapped.$The Minstrel's Ballad -- At the beginning of combat on your turn, if you control five or more Towns, create a 2/2 Elemental creature token that's all colors.${3}{W}{U}{B}{R}{G}: Other creatures you control get +X/+X until end of turn, where X is the number of Towns you control.| Yuna, Hope of Spira|Final Fantasy|404|M|{3}{G}{W}|Legendary Creature - Human Cleric|3|5|During your turn, Yuna and enchantment creatures you control have trample, lifelink, and ward {2}.$At the beginning of your end step, return up to one target enchantment card from your graveyard to the battlefield with a finality counter on it.| -Zidane, Tantalus Thief|Final Fantasy|405|U|{3}{R}{W}|Legendary Creature - Human Mutant Scout|3|3|When Zidane enters, gain control of target creature an opponent controls until end of turn. Untap that creature. It gains lifelink and haste until end of turn.$Whenever an opponent gains control of a permanent you control, create a Treasure token.| +Zidane, Tantalus Thief|Final Fantasy|405|U|{3}{R}{W}|Legendary Creature - Human Mutant Scout|3|3|When Zidane enters, gain control of target creature an opponent controls until end of turn. Untap it. It gains lifelink and haste until end of turn.$Whenever an opponent gains control of a permanent from you, create a Treasure token.| Traveling Chocobo|Final Fantasy|406|M|{2}{G}|Creature - Bird|3|2|You may look at the top card of your library any time.$You may play lands and cast Bird spells from the top of your library.$If a land or Bird you control entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.| Cid, Timeless Artificer|Final Fantasy|407|U|{2}{W}{U}|Legendary Creature - Human Artificer|4|4|Artifact creatures and Heroes you control get +1/+1 for each Artificer you control and each Artificer card in your graveyard.$A deck can have any number of cards named Cid, Timeless Artificer.$Cycling {W}{U}| Cid, Timeless Artificer|Final Fantasy|408|U|{2}{W}{U}|Legendary Creature - Human Artificer|4|4|Artifact creatures and Heroes you control get +1/+1 for each Artificer you control and each Artificer card in your graveyard.$A deck can have any number of cards named Cid, Timeless Artificer.$Cycling {W}{U}| @@ -57884,80 +58247,129 @@ Cid, Timeless Artificer|Final Fantasy|418|U|{2}{W}{U}|Legendary Creature - Human Cid, Timeless Artificer|Final Fantasy|419|U|{2}{W}{U}|Legendary Creature - Human Artificer|4|4|Artifact creatures and Heroes you control get +1/+1 for each Artificer you control and each Artificer card in your graveyard.$A deck can have any number of cards named Cid, Timeless Artificer.$Cycling {W}{U}| Cid, Timeless Artificer|Final Fantasy|420|U|{2}{W}{U}|Legendary Creature - Human Artificer|4|4|Artifact creatures and Heroes you control get +1/+1 for each Artificer you control and each Artificer card in your graveyard.$A deck can have any number of cards named Cid, Timeless Artificer.$Cycling {W}{U}| Ultima, Origin of Oblivion|Final Fantasy|421|R|{5}|Legendary Creature - God|4|4|Flying$Whenever Ultima attacks, put a blight counter on target land. For as long as that land has a blight counter on it, it loses all land types and abilities and has "{T}: Add {C}."$Whenever you tap a land for {C}, add an additional {C}.| +Adelbert Steiner|Final Fantasy|422|U|{1}{W}|Legendary Creature - Human Knight|2|1|Lifelink$Adelbert Steiner gets +1/+1 for each Equipment you control.| Aerith Gainsborough|Final Fantasy|423|R|{2}{W}|Legendary Creature - Human Cleric|2|2|Lifelink$Whenever you gain life, put a +1/+1 counter on Aerith Gainsborough.$When Aerith Gainsborough dies, put X +1/+1 counters on each legendary creature you control, where X is the number of +1/+1 counters on Aerith Gainsborough.| +Ambrosia Whiteheart|Final Fantasy|424|U|{1}{W}|Legendary Creature - Bird|2|2|Flash$When Ambrosia Whiteheart enters, you may return another permanent you control to its owner's hand.$Landfall -- Whenever a land you control enters, Ambrosia Whiteheart gets +1/+0 until end of turn.| +Ashe, Princess of Dalmasca|Final Fantasy|425|U|{2}{W}|Legendary Creature - Human Rebel Noble|3|2|Whenever Ashe attacks, look at the top five cards of your library. You may reveal an artifact card from among them and put it into your hand. Put the rest on the bottom of your library in a random order.| Beatrix, Loyal General|Final Fantasy|426|R|{4}{W}{W}|Legendary Creature - Human Soldier|4|4|Vigilance$At the beginning of combat on your turn, you may attach any number of Equipment you control to target creature you control.| Cloud, Midgar Mercenary|Final Fantasy|427|M|{W}{W}|Legendary Creature - Human Soldier Mercenary|2|1|When Cloud enters, search your library for an Equipment card, reveal it, put it into your hand, then shuffle.$As long as Cloud is equipped, if an ability of Cloud or an Equipment attached to it triggers, that ability triggers an additional time.| +Dion, Bahamut's Dominant|Final Fantasy|428|R|{3}{W}|Legendary Creature - Human Noble Knight|3|3|Dragonfire Dive -- During your turn, Dion and other Knights you control have flying.$When Dion enters, create a 2/2 white Knight creature token.${4}{W}{W}, {T}: Exile Dion, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery.| +Bahamut, Warden of Light|Final Fantasy|428|R||Legendary Enchantment Creature - Saga Dragon|5|5|(As this Saga enters and after your draw step, add a lore counter.)$I, II -- Wings of Light -- Put a +1/+1 counter on each other creature you control. Those creatures gain flying until end of turn.$III -- Gigaflare -- Destroy target permanent. Exile Bahamut, then return it to the battlefield.$Flying| +G'raha Tia|Final Fantasy|429|U|{4}{W}|Legendary Creature - Cat Archer|3|5|Reach$The Allagan Eye -- Whenever one or more other creatures and/or artifacts you control die, draw a card. This ability triggers only once each turn.| +Minwu, White Mage|Final Fantasy|430|R|{3}{W}{W}|Legendary Creature - Human Cleric|3|3|Vigilance, lifelink$Whenever you gain life, put a +1/+1 counter on each Cleric you control.| Rosa, Resolute White Mage|Final Fantasy|431|R|{3}{W}|Legendary Creature - Human Noble Cleric|2|3|Reach$At the beginning of combat on your turn, put a +1/+1 counter on target creature you control. It gains lifelink until end of turn.| +Snow Villiers|Final Fantasy|432|U|{2}{W}|Legendary Creature - Human Rebel Monk|*|3|Vigilance$Snow Villiers's power is equal to the number of creatures you control.| Stiltzkin, Moogle Merchant|Final Fantasy|433|R|{W}|Legendary Creature - Moogle|1|2|Lifelink${2}, {T}: Target opponent gains control of another target permanent you control. If they do, you draw a card.| +Venat, Heart of Hydaelyn|Final Fantasy|434|R|{1}{W}{W}|Legendary Creature - Elder Wizard|3|3|Whenever you cast a legendary spell, draw a card. This ability triggers only once each turn.$Hero's Sundering -- {7}, {T}: Exile target nonland permanent. Transform Venat. Activate only as a sorcery.| +Hydaelyn, the Mothercrystal|Final Fantasy|434|R||Legendary Creature - God|4|4|Indestructible$Blessing of Light -- At the beginning of combat on your turn, put a +1/+1 counter on another target creature you control. Until your next turn, it gains indestructible. If that creature is legendary, draw a card.| +Zack Fair|Final Fantasy|435|U|{W}|Legendary Creature - Human Soldier|0|1|Zack Fair enters with a +1/+1 counter on it.${1}, Sacrifice Zack Fair: Target creature you control gains indestructible until end of turn. Put Zack Fair's counters on that creature and attach an Equipment that was attached to Zack Fair to that creature.| +Edgar, King of Figaro|Final Fantasy|436|R|{4}{U}{U}|Legendary Creature - Human Artificer Noble|4|5|When Edgar enters, draw a card for each artifact you control.$Two-Headed Coin -- The first time you flip one or more coins each turn, those coins come up heads and you win those flips.| +Gogo, Master of Mimicry|Final Fantasy|437|M|{2}{U}|Legendary Creature - Wizard|2|4|{X}{X}, {T}: Copy target activated or triggered ability you control X times. You may choose new targets for the copies. This ability can't be copied and X can't be 0.| Jill, Shiva's Dominant|Final Fantasy|438|R|{2}{U}|Legendary Creature - Human Noble Warrior|2|2|When Jill enters, return up to one other target nonland permanent to its owner's hand.${3}{U}{U}, {T}: Exile Jill, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery.| Shiva, Warden of Ice|Final Fantasy|438|R||Legendary Enchantment Creature - Saga Elemental|4|5|(As this Saga enters and after your draw step, add a lore counter.)$I, II -- Mesmerize -- Target creature can't be blocked this turn.$III -- Cold Snap -- Tap all lands your opponents control. Exile Shiva, then return it to the battlefield.| Matoya, Archon Elder|Final Fantasy|439|R|{2}{U}|Legendary Creature - Human Warlock|1|4|Whenever you scry or surveil, draw a card.| Quistis Trepe|Final Fantasy|440|U|{2}{U}|Legendary Creature - Human Wizard|2|2|Blue Magic -- When Quistis Trepe enters, you may cast target instant or sorcery card from a graveyard, and mana of any type can be spent to cast that spell. If that spell would be put into a graveyard, exile it instead.| Ultimecia, Temporal Threat|Final Fantasy|441|R|{4}{U}{U}|Legendary Creature - Human Warlock|4|4|When Ultimecia enters, tap all creatures your opponents control.$Whenever a creature you control deals combat damage to a player, draw a card.| +Ultros, Obnoxious Octopus|Final Fantasy|442|U|{1}{U}|Legendary Creature - Octopus|2|1|Whenever you cast a noncreature spell, if at least four mana was spent to cast it, tap target creature an opponent controls and put a stun counter on it.$Whenever you cast a noncreature spell, if at least eight mana was spent to cast it, put eight +1/+1 counters on Ultros.| Y'shtola Rhul|Final Fantasy|443|M|{4}{U}{U}|Legendary Creature - Cat Druid|3|5|At the beginning of your end step, exile target creature you control, then return it to the battlefield under its owner's control. Then if it's the first end step of the turn, there is an additional end step after this step.| Ardyn, the Usurper|Final Fantasy|444|R|{5}{B}{B}{B}|Legendary Creature - Elder Human Noble|4|4|Demons you control have menace, lifelink, and haste.$Starscourge -- At the beginning of combat on your turn, exile up to one target creature card from a graveyard. If you exiled a card this way, create a token that's a copy of that card, except it's a 5/5 black Demon.| Cecil, Dark Knight|Final Fantasy|445|R|{B}|Legendary Creature - Human Knight|2|3|Deathtouch$Darkness -- Whenever Cecil deals damage, you lose that much life. Then if your life total is less than or equal to half your starting life total, untap Cecil and transform it.| Cecil, Redeemed Paladin|Final Fantasy|445|R||Legendary Creature - Human Knight|4|4|Lifelink$Protect -- Whenever Cecil attacks, other attacking creatures gain indestructible until end of turn.| Fang, Fearless l'Cie|Final Fantasy|446|U|{2}{B}|Legendary Creature - Human Warrior|2|3|Whenever one or more cards leave your graveyard, you draw a card and you lose 1 life. This ability triggers only once each turn.$(Melds with Vanille, Cheerful l'Cie.)| Ragnarok, Divine Deliverance|Final Fantasy|446b|U||Legendary Creature - Beast Avatar|7|6|Vigilance, menace, trample, reach, haste$When Ragnarok dies, destroy target permanent and return target nonlegendary permanent card from your graveyard to the battlefield.| +Gaius van Baelsar|Final Fantasy|447|U|{2}{B}{B}|Legendary Creature - Human Soldier|3|2|When Gaius van Baelsar enters, choose one --$* Each player sacrifices a creature token of their choice.$* Each player sacrifices a nontoken creature of their choice.$* Each player sacrifices an enchantment of their choice.| Jecht, Reluctant Guardian|Final Fantasy|448|R|{3}{B}|Legendary Creature - Human Warrior|4|3|Menace$Whenever Jecht deals combat damage to a player, you may exile it, then return it to the battlefield transformed under its owner's control.| Braska's Final Aeon|Final Fantasy|448|R||Legendary Enchantment Creature - Saga Nightmare|7|7|(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I, II -- Jecht Beam -- Each opponent discards a card and you draw a card.$III -- Ultimate Jecht Shot -- Each opponent sacrifices two creatures of their choice.$Menace| Kain, Traitorous Dragoon|Final Fantasy|449|R|{2}{B}|Legendary Creature - Human Knight|2|4|Jump -- During your turn, Kain has flying.$Whenever Kain deals combat damage to a player, that player gains control of Kain. If they do, you draw that many cards, create that many tapped Treasure tokens, then lose that much life.| +Reno and Rude|Final Fantasy|450|U|{1}{B}|Legendary Creature - Human Assassin|2|1|Menace$Whenever Reno and Rude deals combat damage to a player, exile the top card of that player's library. Then you may sacrifice another creature or artifact. If you do, you may play the exiled card this turn, and mana of any type can be spent to cast it.| Sephiroth, Fabled SOLDIER|Final Fantasy|451|M|{2}{B}|Legendary Creature - Human Avatar Soldier|3|3|Whenever Sephiroth enters or attacks, you may sacrifice another creature. If you do, draw a card.$Whenever another creature dies, target opponent loses 1 life and you gain 1 life. If this is the fourth time this ability has resolved this turn, transform Sephiroth.| Sephiroth, One-Winged Angel|Final Fantasy|451|M||Legendary Creature - Angel Nightmare Avatar|5|5|Flying$Super Nova -- As this creature transforms into Sephiroth, One-Winged Angel, you get an emblem with "Whenever a creature dies, target opponent loses 1 life and you gain 1 life."$Whenever Sephiroth attacks, you may sacrifice any number of other creatures. If you do, draw that many cards.| Seymour Flux|Final Fantasy|452|R|{4}{B}|Legendary Creature - Spirit Avatar|5|5|At the beginning of your upkeep, you may pay 1 life. If you do, draw a card and put a +1/+1 counter on Seymour Flux.| +Sidequest: Hunt the Mark|Final Fantasy|453|U|{3}{B}{B}|Enchantment|||When this enchantment enters, destroy up to one target creature.$At the beginning of your end step, if a creature died under an opponent's control this turn, create a Treasure token. Then if you control three or more Treasures, transform this enchantment.| +Yiazmat, Ultimate Mark|Final Fantasy|453|U||Legendary Creature - Dragon|5|6|{1}{B}, Sacrifice another creature or artifact: Yiazmat gains indestructible until end of turn. Tap it.| Vincent Valentine|Final Fantasy|454|R|{2}{B}{B}|Legendary Creature - Assassin|2|2|Whenever a creature an opponent controls dies, put a number of +1/+1 counters on Vincent Valentine equal to that creature's power.$Whenever Vincent Valentine attacks, you may transform it.| Galian Beast|Final Fantasy|454|R||Legendary Creature - Werewolf Beast|3|2|Trample, lifelink$When Galian Beast dies, return it to the battlefield tapped.| Zenos yae Galvus|Final Fantasy|455|R|{3}{B}{B}|Legendary Creature - Human Noble Warrior|4|4|My First Friend -- When Zenos yae Galvus enters, choose a creature an opponent controls. Until end of turn, creatures other than Zenos yae Galvus and the chosen creature get -2/-2.$When the chosen creature leaves the battlefield, transform Zenos yae Galvus.| Shinryu, Transcendent Rival|Final Fantasy|455|R||Legendary Creature - Dragon|8|8|Flying$As this creature transforms into Shinryu, choose an opponent.$Burning Chains -- When the chosen player loses the game, you win the game.| Zodiark, Umbral God|Final Fantasy|456|R|{B}{B}{B}{B}{B}|Legendary Creature - God|5|5|Indestructible$When Zodiark enters, each player sacrifices half the non-God creatures they control of their choice, rounded down.$Whenever a player sacrifices another creature, put a +1/+1 counter on Zodiark.| +Barret Wallace|Final Fantasy|457|U|{3}{R}|Legendary Creature - Human Rebel|4|4|Reach$Whenever Barret Wallace attacks, it deals damage equal to the number of equipped creatures you control to defending player.| Clive, Ifrit's Dominant|Final Fantasy|458|M|{4}{R}{R}|Legendary Creature - Human Noble Warrior|5|5|When Clive enters, you may discard your hand, then draw cards equal to your devotion to red.${4}{R}{R}, {T}: Exile Clive, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery.| Ifrit, Warden of Inferno|Final Fantasy|458|M||Legendary Enchantment Creature - Saga Demon|9|9|(As this Saga enters and after your draw step, add a lore counter.)$I -- Lunge -- Ifrit fights up to one other target creature.$II, III -- Brimstone -- Add {R}{R}{R}{R}. If Ifrit has three or more lore counters on it, exile it, then return it to the battlefield| Firion, Wild Rose Warrior|Final Fantasy|459|R|{2}{R}|Legendary Creature - Human Rebel Warrior|3|3|Equipped creatures you control have haste.$Whenever a nontoken Equipment you control enters, create a token that's a copy of it, except it has "This Equipment's equip abilities cost {2} less to activate." Sacrifice that token at the beginning of the next upkeep.| Freya Crescent|Final Fantasy|460|U|{R}|Legendary Creature - Rat Knight|1|1|Jump -- During your turn, Freya Crescent has flying.${T}: Add {R}. Spend this mana only to cast an Equipment spell or activate an equip ability.| Gilgamesh, Master-at-Arms|Final Fantasy|461|R|{4}{R}{R}|Legendary Creature - Human Samurai|6|6|Whenever Gilgamesh enters or attacks, look at the top six cards of your library. You may put any number of Equipment cards from among them onto the battlefield. Put the rest on the bottom of your library in a random order. When you put one or more Equipment onto the battlefield this way, you may attach one of them to a Samurai you control.| Lightning, Security Sergeant|Final Fantasy|462|R|{2}{R}|Legendary Creature - Human Soldier|2|3|Menace$Whenever Lightning deals combat damage to a player, exile the top card of your library. You may play that card for as long as you control Lightning.| +Prompto Argentum|Final Fantasy|463|U|{1}{R}|Legendary Creature - Human Scout|2|2|Haste$Selfie Shot -- Whenever you cast a noncreature spell, if at least four mana was spent to cast it, create a Treasure token.| +Queen Brahne|Final Fantasy|464|U|{2}{R}|Legendary Creature - Human Noble|2|1|Prowess$Whenever Queen Brahne attacks, create a 0/1 black Wizard creature token with "Whenever you cast a noncreature spell, this token deals 1 damage to each opponent."| +Raubahn, Bull of Ala Mhigo|Final Fantasy|465|R|{1}{R}|Legendary Creature - Human Warrior|2|2|Ward--Pay life equal to Raubahn's power.$Whenever Raubahn attacks, attach up to one target Equipment you control to target attacking creature.| +Seifer Almasy|Final Fantasy|466|R|{3}{R}|Legendary Creature - Human Knight|3|4|Whenever a creature you control attacks alone, it gains double strike until end of turn.$Fire Cross -- Whenever Seifer Almasy deals combat damage to a player, you may cast target instant or sorcery card with mana value 3 or less from your graveyard without paying its mana cost. If that spell would be put into your graveyard, exile it instead.| +Vaan, Street Thief|Final Fantasy|467|R|{2}{R}|Legendary Creature - Human Scout|2|2|Whenever one or more Scouts, Pirates, and/or Rogues you control deal combat damage to a player, exile the top card of that player's library. You may cast it. If you don't, create a Treasure token.$Whenever you cast a spell you don't own, put a +1/+1 counter on each Scout, Pirate, and Rogue you control.| Zell Dincht|Final Fantasy|468|R|{2}{R}|Legendary Creature - Human Monk|0|3|You may play an additional land on each of your turns.$Zell Dincht gets +1/+0 for each land you control.$At the beginning of your end step, return a land you control to its owner's hand.| Bartz and Boko|Final Fantasy|469|R|{3}{G}{G}|Legendary Creature - Human Bird|4|3|Affinity for Birds$When Bartz and Boko enters, each other Bird you control deals damage equal to its power to target creature an opponent controls.| +Diamond Weapon|Final Fantasy|470|U|{7}{G}{G}|Legendary Artifact Creature - Elemental|8|8|This spell costs {1} less to cast for each permanent card in your graveyard.$Reach$Immune -- Prevent all combat damage that would be dealt to Diamond Weapon.| Quina, Qu Gourmet|Final Fantasy|471|U|{2}{G}|Legendary Creature - Qu|2|3|If one or more tokens would be created under your control, those tokens plus a 1/1 green Frog creature token are created instead.${2}, Sacrifice a Frog: Put a +1/+1 counter on Quina.| Sazh Katzroy|Final Fantasy|472|R|{3}{G}|Legendary Creature - Human Pilot|3|3|When Sazh Katzroy enters, you may search your library for a Bird or basic land card, reveal it, put it into your hand, then shuffle.$Whenever Sazh Katzroy attacks, put a +1/+1 counter on target creature, then double the number of +1/+1 counters on that creature.| Tifa Lockhart|Final Fantasy|473|R|{1}{G}|Legendary Creature - Human Monk|1|2|Trample$Landfall -- Whenever a land you control enters, double Tifa Lockhart's power until end of turn.| +Torgal, A Fine Hound|Final Fantasy|474|U|{1}{G}|Legendary Creature - Wolf|2|2|Whenever you cast your first Human creature spell each turn, that creature enters with an additional +1/+1 counter on it for each Dog and/or Wolf you control.${T}: Add one mana of any color.| Vanille, Cheerful l'Cie|Final Fantasy|475|U|{3}{G}|Legendary Creature - Human Cleric|3|2|When Vanille enters, mill two cards, then return a permanent card from your graveyard to your hand.$At the beginning of your first main phase, if you both own and control Vanille and a creature named Fang, Fearless l'Cie, you may pay {3}{B}{G}. If you do, exile them, then meld them into Ragnarok, Divine Deliverance.| Absolute Virtue|Final Fantasy|476|M|{6}{W}{U}|Legendary Creature - Avatar Warrior|8|8|This spell can't be countered.$Flying$You have protection from each of your opponents.| Balthier and Fran|Final Fantasy|477|R|{1}{R}{G}|Legendary Creature - Human Rabbit|4|3|Reach$Vehicles you control get +1/+1 and have vigilance and reach.$Whenever a Vehicle crewed by Balthier and Fran this turn attacks, if it's the first combat phase of the turn, you may pay {1}{R}{G}. If you do, after this phase, there is an additional combat phase.| +Black Waltz No. 3|Final Fantasy|478|U|{2}{B}{R}|Legendary Creature - Wizard|2|2|Flying, deathtouch$Whenever you cast a noncreature spell, Black Waltz No. 3 deals 2 damage to each opponent.| Choco, Seeker of Paradise|Final Fantasy|479|R|{1}{G}{W}{U}|Legendary Creature - Bird|3|5|Whenever one or more Birds you control attack, look at that many cards from the top of your library. You may put one of them into your hand. Then put any number of land cards from among them onto the battlefield tapped and the rest into your graveyard.$Landfall -- Whenever a land you control enters, Choco gets +1/+0 until end of turn.| Cid, Timeless Artificer|Final Fantasy|480|U|{2}{W}{U}|Legendary Creature - Human Artificer|4|4|Artifact creatures and Heroes you control get +1/+1 for each Artificer you control and each Artificer card in your graveyard.$A deck can have any number of cards named Cid, Timeless Artificer.$Cycling {W}{U}| +Cloud of Darkness|Final Fantasy|481|U|{2}{B}{G}{G}|Legendary Creature - Avatar|3|3|Flying$Particle Beam -- When Cloud of Darkness enters, target creature an opponent controls gets -X/-X until end of turn, where X is the number of permanent cards in your graveyard.| Cloud, Planet's Champion|Final Fantasy|482|M|{3}{R}{W}|Legendary Creature - Human Soldier Mercenary|4|4|During your turn, as long as Cloud is equipped, it has double strike and indestructible.$Equip abilities you activate that target Cloud cost {2} less to activate.| Emet-Selch, Unsundered|Final Fantasy|483|M|{1}{U}{B}|Legendary Creature - Elder Wizard|2|4|Vigilance$Whenever Emet-Selch enters or attacks, draw a card, then discard a card.$At the beginning of your upkeep, if there are fourteen or more cards in your graveyard, you may transform Emet-Selch.| Hades, Sorcerer of Eld|Final Fantasy|483|M||Legendary Creature - Avatar|6|6|Vigilance$Echo of the Lost -- During your turn, you may play cards from your graveyard.$If a card or token would be put into your graveyard from anywhere, exile it instead.| The Emperor of Palamecia|Final Fantasy|484|U|{U}{R}|Legendary Creature - Human Noble Wizard|2|2|{T}: Add {U} or {R}. Spend this mana only to cast a noncreature spell.$Whenever you cast a noncreature spell, if at least four mana was spent to cast it, put a +1/+1 counter on The Emperor of Palamecia. Then if it has three or more +1/+1 counters on it, transform it.| The Lord Master of Hell|Final Fantasy|484|U||Legendary Creature - Demon Noble Wizard|3|3|Starfall -- Whenever The Lord Master of Hell attacks, it deals X damage to each opponent, where X is the number of noncreature, nonland cards in your graveyard.| +Exdeath, Void Warlock|Final Fantasy|485|U|{1}{B}{G}|Legendary Creature - Spirit Warlock|3|3|When Exdeath enters, you gain 3 life.$At the beginning of your end step, if there are six or more permanent cards in your graveyard, transform Exdeath.| +Neo Exdeath, Dimension's End|Final Fantasy|485|U||Legendary Creature - Spirit Avatar|*|3|Trample$Neo Exdeath's power is equal to the number of permanent cards in your graveyard.| Garland, Knight of Cornelia|Final Fantasy|486|U|{B}{R}|Legendary Creature - Human Knight|3|2|Whenever you cast a noncreature spell, surveil 1.${3}{B}{B}{R}{R}: Return this card from your graveyard to the battlefield transformed. Activate only as a sorcery.| Chaos, the Endless|Final Fantasy|486|U||Legendary Creature - Demon|5|5|Flying$When Chaos dies, put it on the bottom of its owner's library.| +Garnet, Princess of Alexandria|Final Fantasy|487|U|{G}{W}|Legendary Creature - Human Noble Cleric|2|2|Lifelink$Whenever Garnet attacks, you may remove a lore counter from each of any number of Sagas you control. Put a +1/+1 counter on Garnet for each lore counter removed this way.| +Giott, King of the Dwarves|Final Fantasy|488|U|{R}{W}|Legendary Creature - Dwarf Noble|1|1|Double strike$Whenever Giott or another Dwarf you control enters and whenever an Equipment you control enters, you may discard a card. If you do, draw a card.| Gladiolus Amicitia|Final Fantasy|489|U|{4}{R}{G}|Legendary Creature - Human Warrior|6|6|When Gladiolus Amicitia enters, search your library for a land card, put it onto the battlefield tapped, then shuffle.$Landfall -- Whenever a land you control enters, another target creature you control gets +2/+2 and gains trample until end of turn.| +Golbez, Crystal Collector|Final Fantasy|490|R|{U}{B}|Legendary Creature - Human Wizard|1|4|Whenever an artifact you control enters, surveil 1.$At the beginning of your end step, if you control four or more artifacts, return target creature card from your graveyard to your hand. Then if you control eight or more artifacts, each opponent loses life equal to that card's power.| +Hope Estheim|Final Fantasy|491|R|{W}{U}|Legendary Creature - Human Wizard|2|2|Lifelink$At the beginning of your end step, each opponent mills X cards, where X is the amount of life you gained this turn.| +Ignis Scientia|Final Fantasy|492|U|{1}{G}{U}|Legendary Creature - Human Advisor|2|2|When Ignis Scientia enters, look at the top six cards of your library. You may put a land card from among them onto the battlefield tapped. Put the rest on the bottom of your library in a random order.$I've Come Up with a New Recipe! -- {1}{G}{U}, {T}: Exile target card from a graveyard. If a creature card was exiled this way, create a Food token.| +Jenova, Ancient Calamity|Final Fantasy|493|R|{2}{B}{G}|Legendary Creature - Alien|1|5|At the beginning of combat on your turn, put a number of +1/+1 counters equal to Jenova's power on up to one other target creature. That creature becomes a Mutant in addition to its other types.$Whenever a Mutant you control dies during your turn, you draw cards equal to its power.| +Joshua, Phoenix's Dominant|Final Fantasy|494|R|{1}{R}{W}|Legendary Creature - Human Noble Wizard|3|4|When Joshua enters, discard up to two cards, then draw that many cards.${3}{R}{W}, {T}: Exile Joshua, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery.| +Phoenix, Warden of Fire|Final Fantasy|494|R||Legendary Enchantment Creature - Saga Phoenix|4|4|(As this Saga enters and after your draw step, add a lore counter.)$I, II -- Rising Flames -- Phoenix deals 2 damage to each opponent.$III -- Flames of Rebirth -- Return any number of target creature cards with total mana value 6 or less from your graveyard to the battlefield. Exile Phoenix, then return it to the battlefield.$Flying, lifelink| +Judge Magister Gabranth|Final Fantasy|495|U|{W}{B}|Legendary Creature - Human Advisor Knight|2|2|Menace$Whenever another creature or artifact you control dies, put a +1/+1 counter on Judge Magister Gabranth.| Kefka, Court Mage|Final Fantasy|496|M|{2}{U}{B}{R}|Legendary Creature - Human Wizard|4|5|Whenever Kefka enters or attacks, each player discards a card. Then you draw a card for each card type among cards discarded this way.${8}: Each opponent sacrifices a permanent of their choice. Transform Kefka. Activate only as a sorcery.| Kefka, Ruler of Ruin|Final Fantasy|496|M||Legendary Creature - Avatar Wizard|5|7|Flying$Whenever an opponent loses life during your turn, you draw that many cards.| Kuja, Genome Sorcerer|Final Fantasy|497|R|{2}{B}{R}|Legendary Creature - Human Mutant Wizard|3|4|At the beginning of your end step, create a tapped 0/1 black Wizard creature token with "Whenever you cast a noncreature spell, this token deals 1 damage to each opponent." Then if you control four or more Wizards, transform Kuja.| Trance Kuja, Fate Defied|Final Fantasy|497|R||Legendary Creature - Avatar Wizard|4|6|Flame Star -- If a Wizard you control would deal damage to a permanent or player, it deals double that damage instead.| Lightning, Army of One|Final Fantasy|498|M|{1}{R}{W}|Legendary Creature - Human Soldier|3|2|First strike, trample, lifelink$Stagger -- Whenever Lightning deals combat damage to a player, 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.| +Locke Cole|Final Fantasy|499|U|{1}{U}{B}|Legendary Creature - Human Rogue|2|3|Deathtouch, lifelink$Whenever Locke Cole deals combat damage to a player, draw a card, then discard a card.| Noctis, Prince of Lucis|Final Fantasy|500|R|{1}{W}{U}{B}|Legendary Creature - Human Noble|4|3|Lifelink$You may cast artifact spells from your graveyard by paying 3 life in addition to paying their other costs. If you cast a spell this way, that artifact enters with a finality counter on it.| +Omega, Heartless Evolution|Final Fantasy|501|U|{5}{G}{U}|Legendary Artifact Creature - Robot|8|8|Wave Cannon -- When Omega enters, for each opponent, tap up to one target nonland permanent that opponent controls. Put X stun counters on each of those permanents and you gain X life, where X is the number of nonbasic lands you control.| Rinoa Heartilly|Final Fantasy|502|U|{3}{G}{W}|Legendary Creature - Human Rebel Warlock|4|4|When Rinoa Heartilly enters, create Angelo, a legendary 1/1 green and white Dog creature token.$Angelo Cannon -- Whenever Rinoa Heartilly attacks, another target creature you control gets +1/+1 until end of turn for each creature you control.| +Rufus Shinra|Final Fantasy|503|U|{1}{W}{B}|Legendary Creature - Human Noble|2|4|Whenever Rufus Shinra attacks, if you don't control a creature named Darkstar, create Darkstar, a legendary 2/2 white and black Dog creature token.| +Rydia, Summoner of Mist|Final Fantasy|504|U|{R}{G}|Legendary Creature - Human Shaman|1|2|Landfall -- Whenever a land you control enters, you may discard a card. If you do, draw a card.$Summon -- {X}, {T}: Return target Saga card with mana value X from your graveyard to the battlefield with a finality counter on it. It gains haste until end of turn. Activate only as a sorcery.| Sephiroth, Planet's Heir|Final Fantasy|505|M|{4}{U}{B}|Legendary Creature - Human Avatar Soldier|4|4|Vigilance$When Sephiroth enters, creatures your opponents control get -2/-2 until end of turn.$Whenever a creature an opponent controls dies, put a +1/+1 counter on Sephiroth.| Serah Farron|Final Fantasy|506|R|{1}{G}{W}|Legendary Creature - Human Citizen|2|2|The first legendary creature spell you cast each turn costs {2} less to cast.$At the beginning of combat on your turn, if you control two or more other legendary creatures, you may transform Serah Farron.| Crystallized Serah|Final Fantasy|506|R||Legendary Artifact|||The first legendary creature spell you cast each turn costs {2} less to cast.$Legendary creatures you control get +2/+2.| Shantotto, Tactician Magician|Final Fantasy|507|U|{1}{U}{R}|Legendary Creature - Dwarf Wizard|0|4|Whenever you cast a noncreature spell, Shantotto gets +X/+0 until end of turn, where X is the amount of mana spent to cast that spell. If X is 4 or more, draw a card.| Sin, Spira's Punishment|Final Fantasy|508|R|{4}{B}{G}{U}|Legendary Creature - Leviathan Avatar|7|7|Flying$Whenever Sin enters or attacks, exile a permanent card from your graveyard at random, then create a tapped token that's a copy of that card. If the exiled card is a land card, repeat this process.| Squall, SeeD Mercenary|Final Fantasy|509|R|{2}{W}{B}|Legendary Creature - Human Knight Mercenary|3|4|Rough Divide -- Whenever a creature you control attacks alone, it gains double strike until end of turn.$Whenever Squall deals combat damage to a player, return target permanent card with mana value 3 or less from your graveyard to the battlefield.| +Tellah, Great Sage|Final Fantasy|510|R|{3}{U}{R}|Legendary Creature - Human Wizard|3|3|Whenever you cast a noncreature spell, create a 1/1 colorless Hero creature token. If four or more mana was spent to cast that spell, draw two cards. If eight or more mana was spent to cast that spell, sacrifice Tellah and it deals that much damage to each opponent.| Terra, Magical Adept|Final Fantasy|511|M|{1}{R}{G}|Legendary Creature - Human Wizard Warrior|4|2|When Terra enters, mill five cards. Put up to one enchantment card milled this way into your hand.$Trance -- {4}{R}{G}, {T}: Exile Terra, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery.| Esper Terra|Final Fantasy|511|M||Legendary Enchantment Creature - Saga Wizard|6|6|(As this Saga enters and after your draw step, add a lore counter.)$I, II, III -- Create a token that's a copy of target nonlegendary enchantment you control. It gains haste. If it's a Saga, put up to three lore counters on it. Sacrifice it at the beginning of your next end step.$IV -- Add {W}{W}, {U}{U}, {B}{B}, {R}{R}, and {G}{G}. Exile Esper Terra, then return it to the battlefield.$Flying| +Tidus, Blitzball Star|Final Fantasy|512|U|{1}{W}{U}|Legendary Creature - Human Warrior|2|1|Whenever an artifact you control enters, put a +1/+1 counter on Tidus.$Whenever Tidus attacks, tap target creature an opponent controls.| Ultimecia, Time Sorceress|Final Fantasy|513|U|{3}{U}{B}|Legendary Creature - Human Warlock|4|5|Whenever Ultimecia enters or attacks, surveil 2.$At the beginning of your end step, you may pay {4}{U}{U}{B}{B} and exile eight cards from your graveyard. If you do, transform Ultimecia.| Ultimecia, Omnipotent|Final Fantasy|513|U||Legendary Creature - Nightmare Warlock|7|7|Menace$Time Compression -- When this creature transforms into Ultimecia, Omnipotent, take an extra turn after this one.| Vivi Ornitier|Final Fantasy|514|M|{1}{U}{R}|Legendary Creature - Wizard|0|3|{0}: Add X mana in any combination of {U} and/or {R}, where X is Vivi Ornitier's power. Activate only during your turn and only once each turn.$Whenever you cast a noncreature spell, put a +1/+1 counter on Vivi Ornitier and it deals 1 damage to each opponent.| +The Wandering Minstrel|Final Fantasy|515|R|{G}{U}|Legendary Creature - Human Bard|1|3|Lands you control enter untapped.$The Minstrel's Ballad -- At the beginning of combat on your turn, if you control five or more Towns, create a 2/2 Elemental creature token that's all colors.${3}{W}{U}{B}{R}{G}: Other creatures you control get +X/+X until end of turn, where X is the number of Towns you control.| Xande, Dark Mage|Final Fantasy|516|R|{2}{U}{B}|Legendary Creature - Human Wizard|3|3|Menace$Xande gets +1/+1 for each noncreature, nonland card in your graveyard.| Yuna, Hope of Spira|Final Fantasy|517|M|{3}{G}{W}|Legendary Creature - Human Cleric|3|5|During your turn, Yuna and enchantment creatures you control have trample, lifelink, and ward {2}.$At the beginning of your end step, return up to one target enchantment card from your graveyard to the battlefield with a finality counter on it.| -Zidane, Tantalus Thief|Final Fantasy|518|U|{3}{R}{W}|Legendary Creature - Human Mutant Scout|3|3|When Zidane enters, gain control of target creature an opponent controls until end of turn. Untap that creature. It gains lifelink and haste until end of turn.$Whenever an opponent gains control of a permanent you control, create a Treasure token.| +Zidane, Tantalus Thief|Final Fantasy|518|U|{3}{R}{W}|Legendary Creature - Human Mutant Scout|3|3|When Zidane enters, gain control of target creature an opponent controls until end of turn. Untap it. It gains lifelink and haste until end of turn.$Whenever an opponent gains control of a permanent from you, create a Treasure token.| Aerith Gainsborough|Final Fantasy|519|R|{2}{W}|Legendary Creature - Human Cleric|2|2|Lifelink$Whenever you gain life, put a +1/+1 counter on Aerith Gainsborough.$When Aerith Gainsborough dies, put X +1/+1 counters on each legendary creature you control, where X is the number of +1/+1 counters on Aerith Gainsborough.| Cloud, Midgar Mercenary|Final Fantasy|520|M|{W}{W}|Legendary Creature - Human Soldier Mercenary|2|1|When Cloud enters, search your library for an Equipment card, reveal it, put it into your hand, then shuffle.$As long as Cloud is equipped, if an ability of Cloud or an Equipment attached to it triggers, that ability triggers an additional time.| +Dion, Bahamut's Dominant|Final Fantasy|521|R|{3}{W}|Legendary Creature - Human Noble Knight|3|3|Dragonfire Dive -- During your turn, Dion and other Knights you control have flying.$When Dion enters, create a 2/2 white Knight creature token.${4}{W}{W}, {T}: Exile Dion, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery.| +Bahamut, Warden of Light|Final Fantasy|521|R||Legendary Enchantment Creature - Saga Dragon|5|5|(As this Saga enters and after your draw step, add a lore counter.)$I, II -- Wings of Light -- Put a +1/+1 counter on each other creature you control. Those creatures gain flying until end of turn.$III -- Gigaflare -- Destroy target permanent. Exile Bahamut, then return it to the battlefield.$Flying| +Gogo, Master of Mimicry|Final Fantasy|522|M|{2}{U}|Legendary Creature - Wizard|2|4|{X}{X}, {T}: Copy target activated or triggered ability you control X times. You may choose new targets for the copies. This ability can't be copied and X can't be 0.| Jill, Shiva's Dominant|Final Fantasy|523|R|{2}{U}|Legendary Creature - Human Noble Warrior|2|2|When Jill enters, return up to one other target nonland permanent to its owner's hand.${3}{U}{U}, {T}: Exile Jill, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery.| Shiva, Warden of Ice|Final Fantasy|523|R||Legendary Enchantment Creature - Saga Elemental|4|5|(As this Saga enters and after your draw step, add a lore counter.)$I, II -- Mesmerize -- Target creature can't be blocked this turn.$III -- Cold Snap -- Tap all lands your opponents control. Exile Shiva, then return it to the battlefield.| Ardyn, the Usurper|Final Fantasy|524|R|{5}{B}{B}{B}|Legendary Creature - Elder Human Noble|4|4|Demons you control have menace, lifelink, and haste.$Starscourge -- At the beginning of combat on your turn, exile up to one target creature card from a graveyard. If you exiled a card this way, create a token that's a copy of that card, except it's a 5/5 black Demon.| @@ -57974,11 +58386,19 @@ Shinryu, Transcendent Rival|Final Fantasy|529|R||Legendary Creature - Dragon|8|8 Clive, Ifrit's Dominant|Final Fantasy|530|M|{4}{R}{R}|Legendary Creature - Human Noble Warrior|5|5|When Clive enters, you may discard your hand, then draw cards equal to your devotion to red.${4}{R}{R}, {T}: Exile Clive, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery.| Ifrit, Warden of Inferno|Final Fantasy|530|M||Legendary Enchantment Creature - Saga Demon|9|9|(As this Saga enters and after your draw step, add a lore counter.)$I -- Lunge -- Ifrit fights up to one other target creature.$II, III -- Brimstone -- Add {R}{R}{R}{R}. If Ifrit has three or more lore counters on it, exile it, then return it to the battlefield| Firion, Wild Rose Warrior|Final Fantasy|531|R|{2}{R}|Legendary Creature - Human Rebel Warrior|3|3|Equipped creatures you control have haste.$Whenever a nontoken Equipment you control enters, create a token that's a copy of it, except it has "This Equipment's equip abilities cost {2} less to activate." Sacrifice that token at the beginning of the next upkeep.| +Prompto Argentum|Final Fantasy|532|U|{1}{R}|Legendary Creature - Human Scout|2|2|Haste$Selfie Shot -- Whenever you cast a noncreature spell, if at least four mana was spent to cast it, create a Treasure token.| +Raubahn, Bull of Ala Mhigo|Final Fantasy|533|R|{1}{R}|Legendary Creature - Human Warrior|2|2|Ward--Pay life equal to Raubahn's power.$Whenever Raubahn attacks, attach up to one target Equipment you control to target attacking creature.| +Seifer Almasy|Final Fantasy|534|R|{3}{R}|Legendary Creature - Human Knight|3|4|Whenever a creature you control attacks alone, it gains double strike until end of turn.$Fire Cross -- Whenever Seifer Almasy deals combat damage to a player, you may cast target instant or sorcery card with mana value 3 or less from your graveyard without paying its mana cost. If that spell would be put into your graveyard, exile it instead.| +Vaan, Street Thief|Final Fantasy|535|R|{2}{R}|Legendary Creature - Human Scout|2|2|Whenever one or more Scouts, Pirates, and/or Rogues you control deal combat damage to a player, exile the top card of that player's library. You may cast it. If you don't, create a Treasure token.$Whenever you cast a spell you don't own, put a +1/+1 counter on each Scout, Pirate, and Rogue you control.| Tifa Lockhart|Final Fantasy|536|R|{1}{G}|Legendary Creature - Human Monk|1|2|Trample$Landfall -- Whenever a land you control enters, double Tifa Lockhart's power until end of turn.| Vanille, Cheerful l'Cie|Final Fantasy|537|U|{3}{G}|Legendary Creature - Human Cleric|3|2|When Vanille enters, mill two cards, then return a permanent card from your graveyard to your hand.$At the beginning of your first main phase, if you both own and control Vanille and a creature named Fang, Fearless l'Cie, you may pay {3}{B}{G}. If you do, exile them, then meld them into Ragnarok, Divine Deliverance.| Balthier and Fran|Final Fantasy|538|R|{1}{R}{G}|Legendary Creature - Human Rabbit|4|3|Reach$Vehicles you control get +1/+1 and have vigilance and reach.$Whenever a Vehicle crewed by Balthier and Fran this turn attacks, if it's the first combat phase of the turn, you may pay {1}{R}{G}. If you do, after this phase, there is an additional combat phase.| Emet-Selch, Unsundered|Final Fantasy|539|M|{1}{U}{B}|Legendary Creature - Elder Wizard|2|4|Vigilance$Whenever Emet-Selch enters or attacks, draw a card, then discard a card.$At the beginning of your upkeep, if there are fourteen or more cards in your graveyard, you may transform Emet-Selch.| Hades, Sorcerer of Eld|Final Fantasy|539|M||Legendary Creature - Avatar|6|6|Vigilance$Echo of the Lost -- During your turn, you may play cards from your graveyard.$If a card or token would be put into your graveyard from anywhere, exile it instead.| +Golbez, Crystal Collector|Final Fantasy|540|R|{U}{B}|Legendary Creature - Human Wizard|1|4|Whenever an artifact you control enters, surveil 1.$At the beginning of your end step, if you control four or more artifacts, return target creature card from your graveyard to your hand. Then if you control eight or more artifacts, each opponent loses life equal to that card's power.| +Hope Estheim|Final Fantasy|541|R|{W}{U}|Legendary Creature - Human Wizard|2|2|Lifelink$At the beginning of your end step, each opponent mills X cards, where X is the amount of life you gained this turn.| +Joshua, Phoenix's Dominant|Final Fantasy|542|R|{1}{R}{W}|Legendary Creature - Human Noble Wizard|3|4|When Joshua enters, discard up to two cards, then draw that many cards.${3}{R}{W}, {T}: Exile Joshua, then return it to the battlefield transformed under its owner's control. Activate only as a sorcery.| +Phoenix, Warden of Fire|Final Fantasy|542|R||Legendary Enchantment Creature - Saga Phoenix|4|4|(As this Saga enters and after your draw step, add a lore counter.)$I, II -- Rising Flames -- Phoenix deals 2 damage to each opponent.$III -- Flames of Rebirth -- Return any number of target creature cards with total mana value 6 or less from your graveyard to the battlefield. Exile Phoenix, then return it to the battlefield.$Flying, lifelink| Kefka, Court Mage|Final Fantasy|543|M|{2}{U}{B}{R}|Legendary Creature - Human Wizard|4|5|Whenever Kefka enters or attacks, each player discards a card. Then you draw a card for each card type among cards discarded this way.${8}: Each opponent sacrifices a permanent of their choice. Transform Kefka. Activate only as a sorcery.| Kefka, Ruler of Ruin|Final Fantasy|543|M||Legendary Creature - Avatar Wizard|5|7|Flying$Whenever an opponent loses life during your turn, you draw that many cards.| Kuja, Genome Sorcerer|Final Fantasy|544|R|{2}{B}{R}|Legendary Creature - Human Mutant Wizard|3|4|At the beginning of your end step, create a tapped 0/1 black Wizard creature token with "Whenever you cast a noncreature spell, this token deals 1 damage to each opponent." Then if you control four or more Wizards, transform Kuja.| @@ -57986,8 +58406,9 @@ Trance Kuja, Fate Defied|Final Fantasy|544|R||Legendary Creature - Avatar Wizard Lightning, Army of One|Final Fantasy|545|M|{1}{R}{W}|Legendary Creature - Human Soldier|3|2|First strike, trample, lifelink$Stagger -- Whenever Lightning deals combat damage to a player, 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.| Noctis, Prince of Lucis|Final Fantasy|546|R|{1}{W}{U}{B}|Legendary Creature - Human Noble|4|3|Lifelink$You may cast artifact spells from your graveyard by paying 3 life in addition to paying their other costs. If you cast a spell this way, that artifact enters with a finality counter on it.| Squall, SeeD Mercenary|Final Fantasy|547|R|{2}{W}{B}|Legendary Creature - Human Knight Mercenary|3|4|Rough Divide -- Whenever a creature you control attacks alone, it gains double strike until end of turn.$Whenever Squall deals combat damage to a player, return target permanent card with mana value 3 or less from your graveyard to the battlefield.| +The Wandering Minstrel|Final Fantasy|548|R|{G}{U}|Legendary Creature - Human Bard|1|3|Lands you control enter untapped.$The Minstrel's Ballad -- At the beginning of combat on your turn, if you control five or more Towns, create a 2/2 Elemental creature token that's all colors.${3}{W}{U}{B}{R}{G}: Other creatures you control get +X/+X until end of turn, where X is the number of Towns you control.| Yuna, Hope of Spira|Final Fantasy|549|M|{3}{G}{W}|Legendary Creature - Human Cleric|3|5|During your turn, Yuna and enchantment creatures you control have trample, lifelink, and ward {2}.$At the beginning of your end step, return up to one target enchantment card from your graveyard to the battlefield with a finality counter on it.| -Zidane, Tantalus Thief|Final Fantasy|550|U|{3}{R}{W}|Legendary Creature - Human Mutant Scout|3|3|When Zidane enters, gain control of target creature an opponent controls until end of turn. Untap that creature. It gains lifelink and haste until end of turn.$Whenever an opponent gains control of a permanent you control, create a Treasure token.| +Zidane, Tantalus Thief|Final Fantasy|550|U|{3}{R}{W}|Legendary Creature - Human Mutant Scout|3|3|When Zidane enters, gain control of target creature an opponent controls until end of turn. Untap it. It gains lifelink and haste until end of turn.$Whenever an opponent gains control of a permanent from you, create a Treasure token.| Traveling Chocobo|Final Fantasy|551|M|{2}{G}|Creature - Bird|3|2|You may look at the top card of your library any time.$You may play lands and cast Bird spells from the top of your library.$If a land or Bird you control entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.| Traveling Chocobo|Final Fantasy|551a|M|{2}{G}|Creature - Bird|3|2|You may look at the top card of your library any time.$You may play lands and cast Bird spells from the top of your library.$If a land or Bird you control entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.| Traveling Chocobo|Final Fantasy|551b|M|{2}{G}|Creature - Bird|3|2|You may look at the top card of your library any time.$You may play lands and cast Bird spells from the top of your library.$If a land or Bird you control entering the battlefield causes a triggered ability of a permanent you control to trigger, that ability triggers an additional time.| @@ -58012,7 +58433,7 @@ Swamp|Final Fantasy|574|C||Basic Land - Swamp|||({T}: Add {B}.)| Mountain|Final Fantasy|575|C||Basic Land - Mountain|||({T}: Add {R}.)| Forest|Final Fantasy|576|C||Basic Land - Forest|||({T}: Add {G}.)| Y'shtola Rhul|Final Fantasy|577|M|{4}{U}{U}|Legendary Creature - Cat Druid|3|5|At the beginning of your end step, exile target creature you control, then return it to the battlefield under its owner's control. Then if it's the first end step of the turn, there is an additional end step after this step.| -Phoenix Down|Final Fantasy|578|U|{W}|Artifact|||{1}{W}, {T}, Exile this artifact: Choose one--$* Return target creature card with mana value 4 or less from your graveyard to the battlefield tapped.$* Exile target Skeleton, Spirit, or Zombie.| +Phoenix Down|Final Fantasy|578|U|{W}|Artifact|||{1}{W}, {T}, Exile this artifact: Choose one --$* Return target creature card with mana value 4 or less from your graveyard to the battlefield tapped.$* Exile target Skeleton, Spirit, or Zombie.| White Auracite|Final Fantasy|579|C|{2}{W}{W}|Artifact|||When this artifact enters, exile target nonland permanent an opponent controls until this artifact leaves the battlefield.${T}: Add {W}.| Zack Fair|Final Fantasy|580|U|{W}|Legendary Creature - Human Soldier|0|1|Zack Fair enters with a +1/+1 counter on it.${1}, Sacrifice Zack Fair: Target creature you control gains indestructible until end of turn. Put Zack Fair's counters on that creature and attach an Equipment that was attached to Zack Fair to that creature.| Astrologian's Planisphere|Final Fantasy|581|R|{1}{U}|Artifact - Equipment|||Job select$Equipped creature is a Wizard in addition to its other types and has "Whenever you cast a noncreature spell and whenever you draw your third card each turn, put a +1/+1 counter on this creature."$Diana -- Equip {2}|