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..aa163daaa4a --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/components/BracketLegalityLabel.java @@ -0,0 +1,343 @@ +package mage.client.components; + +import mage.MageObject; +import mage.cards.Card; +import mage.cards.decks.Deck; +import mage.client.util.GUISizeHelper; + +import java.awt.*; +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 + * - [ ] infinite combos + * - [x] mass land destruction + * - [x] extra turns + * - [x] tutors + * + * @author JayDi85 + */ +public class BracketLegalityLabel extends LegalityLabel { + + private static final String GROUP_GAME_CHANGES = "Game Changers"; + private static final String GROUP_INFINITE_COMBOS = "Infinite Combos (unsupported)"; + 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 final BracketLevel level; + + 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<>(); + + public enum BracketLevel { + BRACKET_1("Bracket 1"), + BRACKET_2_3("Bracket 2-3"), + BRACKET_4_5("Bracket 4-5"); + + private final String name; + + BracketLevel(String name) { + this.name = name; + } + + @Override + public String toString() { + return this.name; + } + } + + public BracketLegalityLabel(BracketLevel level) { + super(level.toString(), null); + this.level = level; + setPreferredSize(DIM_PREFERRED); + } + + @Override + public List selectCards() { + return new ArrayList<>(this.badCards); + } + + private void validateBracketLevel() { + this.badCards.clear(); + switch (this.level) { + case BRACKET_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. + this.badCards.addAll(this.foundGameChangers); + this.badCards.addAll(this.foundInfiniteCombos); + this.badCards.addAll(this.foundMassLandDestruction); + this.badCards.addAll(this.foundExtraTurn); + if (this.foundTutors.size() > 3) { + this.badCards.addAll(this.foundTutors); + } + break; + case BRACKET_2_3: + // 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. + if (this.foundGameChangers.size() > 3) { + this.badCards.addAll(this.foundGameChangers); + } + this.badCards.addAll(this.foundInfiniteCombos); + this.badCards.addAll(this.foundMassLandDestruction); + if (this.foundExtraTurn.size() > 3) { + this.badCards.addAll(this.foundExtraTurn); + } + // this.badCards.addAll(this.foundTutors); // allow any amount + break; + case BRACKET_4_5: + // allow any cards + break; + default: + throw new IllegalArgumentException("Unsupported level: " + this.level); + } + } + + @Override + public void validateDeck(Deck deck) { + collectAll(deck); + validateBracketLevel(); + + int infoFontSize = 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("

Deck is GOOD for " + this.level + "

"); + } else { + showInfo.add("

Deck is BAD for " + this.level + "

"); + showInfo.add("

(click here to select all bad cards)

"); + } + + Map> groups = new LinkedHashMap<>(); + groups.put(GROUP_GAME_CHANGES, this.foundGameChangers); + groups.put(GROUP_INFINITE_COMBOS, this.foundInfiniteCombos); + groups.put(GROUP_MASS_LAND_DESTRUCTION, this.foundMassLandDestruction); + groups.put(GROUP_EXTRA_TURN, this.foundExtraTurn); + groups.put(GROUP_TUTORS, this.foundTutors); + groups.forEach((group, cards) -> { + showInfo.add("
"); + showInfo.add("
"); + showInfo.add("" + group + ": " + cards.size() + ""); + if (!cards.isEmpty()) { + showInfo.add("
    "); + cards.forEach(s -> showInfo.add(String.format("
  • %s
  • ", s))); + showInfo.add("
"); + } + }); + + String showText = "" + String.join("\n", showInfo) + ""; + showState(showColor, showText, false); + } + + 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) { + // TODO: implement + this.foundInfiniteCombos.clear(); + } + + 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..fd96815df30 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/components/EdhPowerLevelLegalityLabel.java @@ -0,0 +1,76 @@ +package mage.client.components; + +import mage.cards.decks.Deck; +import mage.client.util.GUISizeHelper; +import mage.client.util.gui.GuiDisplayUtil; +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_X3); + } + + @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..95d2a13c7bf 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 { @@ -25,8 +26,10 @@ public class LegalityLabel extends JLabel { 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_X2 = new Dimension(DIM_PREFERRED.width * 2 + 5, 25); + protected static final Dimension DIM_PREFERRED_X3 = new Dimension(DIM_PREFERRED.width * 3 + 5 + 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; @@ -92,19 +95,6 @@ public class LegalityLabel extends JLabel { 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 +136,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 +189,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..29f7fce598f 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,14 @@ 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(BracketLegalityLabel.BracketLevel.BRACKET_1)); + this.add(new BracketLegalityLabel(BracketLegalityLabel.BracketLevel.BRACKET_2_3)); + this.add(new BracketLegalityLabel(BracketLegalityLabel.BracketLevel.BRACKET_4_5)); + addHidePanelButton(); revalidate(); @@ -147,5 +157,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/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..aa9b7405784 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"; @@ -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/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.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/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..d11b3c5950b 100644 --- a/Mage.Server/src/main/java/mage/server/TableController.java +++ b/Mage.Server/src/main/java/mage/server/TableController.java @@ -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 ") 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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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..09b72a49a57 100644 --- a/Mage.Sets/src/mage/cards/b/BalduvianAtrocity.java +++ b/Mage.Sets/src/mage/cards/b/BalduvianAtrocity.java @@ -24,6 +24,7 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; /** * @@ -98,7 +99,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/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/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/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/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/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/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/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/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/BlazingBomb.java b/Mage.Sets/src/mage/cards/b/BlazingBomb.java new file mode 100644 index 00000000000..fbd0d801467 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BlazingBomb.java @@ -0,0 +1,57 @@ +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, "it"), 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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/CloudExSOLDIER.java b/Mage.Sets/src/mage/cards/c/CloudExSOLDIER.java index 722d1801066..e2f15389d07 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); } 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/CodsworthHandyHelper.java b/Mage.Sets/src/mage/cards/c/CodsworthHandyHelper.java index 6ad04dd7aba..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); @@ -124,45 +124,3 @@ class CodsworthHandyHelperManaCondition implements Condition { 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/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/CollectiveEffort.java b/Mage.Sets/src/mage/cards/c/CollectiveEffort.java index a56194bcda7..d5e86128201 100644 --- a/Mage.Sets/src/mage/cards/c/CollectiveEffort.java +++ b/Mage.Sets/src/mage/cards/c/CollectiveEffort.java @@ -18,7 +18,6 @@ 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; @@ -38,7 +37,6 @@ 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 { @@ -66,7 +64,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/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..9bc468d89f6 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")); + 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/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/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/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/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/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/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/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/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/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/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/d/DaghatarTheAdamant.java b/Mage.Sets/src/mage/cards/d/DaghatarTheAdamant.java index 2d2d495ac2b..9c679a78189 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")); + 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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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..fec2310512c 100644 --- a/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java +++ b/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java @@ -107,7 +107,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/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/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/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/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/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/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/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/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/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/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..e160bd5e9f7 100644 --- a/Mage.Sets/src/mage/cards/g/GossipsTalent.java +++ b/Mage.Sets/src/mage/cards/g/GossipsTalent.java @@ -1,10 +1,7 @@ package mage.cards.g; import mage.abilities.Ability; -import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; -import mage.abilities.common.OneOrMoreCombatDamagePlayerTriggeredAbility; -import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; -import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.*; import mage.abilities.effects.common.ExileThenReturnTargetEffect; import mage.abilities.effects.common.combat.CantBeBlockedTargetEffect; import mage.abilities.effects.common.continuous.GainClassAbilitySourceEffect; @@ -59,11 +56,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_PERMANENT_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/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/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/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/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/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/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/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..85331db5be1 100644 --- a/Mage.Sets/src/mage/cards/h/HammerOfRuin.java +++ b/Mage.Sets/src/mage/cards/h/HammerOfRuin.java @@ -14,8 +14,7 @@ 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; @@ -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}"); 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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/KaitoBaneOfNightmares.java b/Mage.Sets/src/mage/cards/k/KaitoBaneOfNightmares.java index f947283d7f0..f7d515aa699 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 { @@ -62,7 +64,7 @@ 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()); + 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/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/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/KefkaRulerOfRuin.java b/Mage.Sets/src/mage/cards/k/KefkaRulerOfRuin.java index 6846844ea27..aa642b63b2b 100644 --- a/Mage.Sets/src/mage/cards/k/KefkaRulerOfRuin.java +++ b/Mage.Sets/src/mage/cards/k/KefkaRulerOfRuin.java @@ -3,7 +3,6 @@ package mage.cards.k; import mage.MageInt; import mage.abilities.common.LoseLifeTriggeredAbility; import mage.abilities.condition.common.MyTurnCondition; -import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.dynamicvalue.common.SavedLifeLossValue; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.FlyingAbility; @@ -38,10 +37,10 @@ public final class KefkaRulerOfRuin extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever an opponent loses life during your turn, you draw that many cards. - this.addAbility(new ConditionalTriggeredAbility(new LoseLifeTriggeredAbility( - new DrawCardSourceControllerEffect(SavedLifeLossValue.MANY), + this.addAbility(new LoseLifeTriggeredAbility( + new DrawCardSourceControllerEffect(SavedLifeLossValue.MANY, true), TargetController.OPPONENT, false, false - ), MyTurnCondition.instance, "Whenever an opponent loses life during your turn, you draw that many cards.")); + ).withTriggerCondition(MyTurnCondition.instance)); } private KefkaRulerOfRuin(final KefkaRulerOfRuin card) { 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/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/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/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/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/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/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/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/LeechBonder.java b/Mage.Sets/src/mage/cards/l/LeechBonder.java index 07759436689..0bc52070d2b 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")); + 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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/MoiraAndTeshar.java b/Mage.Sets/src/mage/cards/m/MoiraAndTeshar.java index 94122d6d173..2abe1bb1625 100644 --- a/Mage.Sets/src/mage/cards/m/MoiraAndTeshar.java +++ b/Mage.Sets/src/mage/cards/m/MoiraAndTeshar.java @@ -24,6 +24,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; @@ -91,7 +92,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/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/MonksFist.java b/Mage.Sets/src/mage/cards/m/MonksFist.java new file mode 100644 index 00000000000..2df1dd242f2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MonksFist.java @@ -0,0 +1,48 @@ +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")); + + // 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/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/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/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/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..cc3cbe926ac 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"); + + 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")); + 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..d9fc5f5756d 100644 --- a/Mage.Sets/src/mage/cards/n/NetheresePuzzleWard.java +++ b/Mage.Sets/src/mage/cards/n/NetheresePuzzleWard.java @@ -2,6 +2,7 @@ package mage.cards.n; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.keyword.ScryEffect; import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -67,7 +68,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; } } 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/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/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/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/NoctisPrinceOfLucis.java b/Mage.Sets/src/mage/cards/n/NoctisPrinceOfLucis.java index 3499f2b11df..14ad8788e14 100644 --- a/Mage.Sets/src/mage/cards/n/NoctisPrinceOfLucis.java +++ b/Mage.Sets/src/mage/cards/n/NoctisPrinceOfLucis.java @@ -43,7 +43,7 @@ public final class NoctisPrinceOfLucis extends CardImpl { 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())); + this.addAbility(new SimpleStaticAbility(new NoctisPrinceOfLucisEffect()), new NoctisPrinceOfLucisWatcher()); } private NoctisPrinceOfLucis(final NoctisPrinceOfLucis card) { 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/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/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..ee8dc6ccc24 --- /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.ForEachOpponentTargetsAdjuster; +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).concatBy("and")); + ability.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_PERMANENT_NON_LAND)); + this.addAbility(ability.withFlavorWord("Wave Cannon").setTargetAdjuster(new ForEachOpponentTargetsAdjuster())); + } + + 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/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/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/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/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/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/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/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/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/PheliaExuberantShepherd.java b/Mage.Sets/src/mage/cards/p/PheliaExuberantShepherd.java index 70c0fa47b49..63f6fdba3df 100644 --- a/Mage.Sets/src/mage/cards/p/PheliaExuberantShepherd.java +++ b/Mage.Sets/src/mage/cards/p/PheliaExuberantShepherd.java @@ -106,7 +106,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/PhoenixWardenOfFire.java b/Mage.Sets/src/mage/cards/p/PhoenixWardenOfFire.java index 8c126c5fc65..f95a9a46b15 100644 --- a/Mage.Sets/src/mage/cards/p/PhoenixWardenOfFire.java +++ b/Mage.Sets/src/mage/cards/p/PhoenixWardenOfFire.java @@ -5,7 +5,7 @@ import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SagaAbility; import mage.abilities.effects.common.DamagePlayersEffect; -import mage.abilities.effects.common.ExileAndReturnSourceEffect; +import mage.abilities.effects.common.ExileSourceAndReturnFaceUpEffect; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.LifelinkAbility; @@ -51,7 +51,7 @@ public final class PhoenixWardenOfFire extends CardImpl { // 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 ExileAndReturnSourceEffect(PutCards.BATTLEFIELD)); + ability.addEffect(new ExileSourceAndReturnFaceUpEffect()); ability.addTarget(new PhoenixWardenOfFireTarget()); ability.withFlavorWord("Flames of Rebirth"); }); 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/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/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/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/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/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/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/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/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/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/QuestingBeast.java b/Mage.Sets/src/mage/cards/q/QuestingBeast.java index 4be886eed59..5e8592460fa 100644 --- a/Mage.Sets/src/mage/cards/q/QuestingBeast.java +++ b/Mage.Sets/src/mage/cards/q/QuestingBeast.java @@ -14,12 +14,13 @@ 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.TargetPermanent; import mage.target.targetadjustment.DamagedPlayerControlsTargetAdjuster; 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,7 +56,7 @@ 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.addTarget(new TargetPermanent(filter)); ability.setTargetAdjuster(new DamagedPlayerControlsTargetAdjuster()); 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/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/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/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/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/RaubahnBullOfAlaMhigo.java b/Mage.Sets/src/mage/cards/r/RaubahnBullOfAlaMhigo.java new file mode 100644 index 00000000000..4635e005ccc --- /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, "Pay 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/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/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/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/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/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/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/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/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/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/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/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/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..ac7d4fb79c7 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")); + 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/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/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/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/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/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/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/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/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/SeasonedWarrenguard.java b/Mage.Sets/src/mage/cards/s/SeasonedWarrenguard.java index ade01547b90..5b12cac1eb3 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) + ).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/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/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/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/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/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/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/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..4404cf0a4c9 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; @@ -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/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/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/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 index 75b783ff205..394e1f3b6e4 100644 --- a/Mage.Sets/src/mage/cards/s/SidequestPlayBlitzball.java +++ b/Mage.Sets/src/mage/cards/s/SidequestPlayBlitzball.java @@ -14,14 +14,19 @@ 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; /** @@ -90,7 +95,21 @@ class SidequestPlayBlitzballEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - return true; + 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); } } 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/SimicGuildmage.java b/Mage.Sets/src/mage/cards/s/SimicGuildmage.java index 861474e5e91..4a2ab0f780a 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")); + 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 + .ofNullable(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 + .ofNullable(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/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/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/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/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/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/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/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/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..323e3b4dcf0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StolenUniform.java @@ -0,0 +1,184 @@ +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. When you lose control of that Equipment this turn, " + + "if it's attached to a creature you control, unattach it"; + 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/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/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/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..174228f4533 --- /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); + } + + 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/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..14f09cb5fd2 --- /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, 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..4258df81a63 --- /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 IV.) + 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/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/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/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/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/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/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/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/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/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/TheGoldSaucer.java b/Mage.Sets/src/mage/cards/t/TheGoldSaucer.java new file mode 100644 index 00000000000..70b50f64d43 --- /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.custom.CreatureToken; + +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 CreatureToken())), new GenericManaCost(2) + ); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + + // {3}, {T}, Sacrifice two artifacts: Draw a card. + ability = new SimpleActivatedAbility(new DrawCardSourceControllerEffect(2), 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/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/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/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/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/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/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/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/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/TolarianContempt.java b/Mage.Sets/src/mage/cards/t/TolarianContempt.java index f3263351279..6a86e043dd4 100644 --- a/Mage.Sets/src/mage/cards/t/TolarianContempt.java +++ b/Mage.Sets/src/mage/cards/t/TolarianContempt.java @@ -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/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/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/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..ae340291b87 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TravelTheOverworld.java @@ -0,0 +1,44 @@ +package mage.cards.t; + +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.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.constants.Zone; +import mage.filter.common.FilterControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TravelTheOverworld extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.TOWN, "Towns"); + private static final Hint hint = new ValueHint("Towns you control", new PermanentsOnBattlefieldCount(filter)); + + public TravelTheOverworld(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{U}{U}"); + + // Affinity for Towns + this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filter)).setRuleAtTheTop(true).addHint(hint)); + + // 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/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/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/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/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..cb29537cf3a 100644 --- a/Mage.Sets/src/mage/cards/t/TurfWar.java +++ b/Mage.Sets/src/mage/cards/t/TurfWar.java @@ -1,7 +1,5 @@ package mage.cards.t; -import java.util.UUID; - import mage.MageObject; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; @@ -10,7 +8,10 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; 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; @@ -23,12 +24,13 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.players.PlayerList; import mage.target.Target; -import mage.target.common.TargetLandPermanent; +import mage.target.TargetPermanent; import mage.target.targetadjustment.TargetAdjuster; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author weirddan455 */ public final class TurfWar extends CardImpl { @@ -105,7 +107,7 @@ enum TurfWarAdjuster implements TargetAdjuster { } FilterLandPermanent filter = new FilterLandPermanent("land controlled by " + player.getName()); filter.add(new ControllerIdPredicate(playerId)); - ability.addTarget(new TargetLandPermanent(filter)); + ability.addTarget(new TargetPermanent(filter)); } } } @@ -191,7 +193,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/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/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/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/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/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/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/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/w/WakkaDevotedGuardian.java b/Mage.Sets/src/mage/cards/w/WakkaDevotedGuardian.java new file mode 100644 index 00000000000..35c0389ef5f --- /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), 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/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/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/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/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/WorldMap.java b/Mage.Sets/src/mage/cards/w/WorldMap.java new file mode 100644 index 00000000000..1bc2d325e0c --- /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.SearchLibraryPutInPlayEffect; +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 SearchLibraryPutInPlayEffect( + 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 SearchLibraryPutInPlayEffect( + 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/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/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/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..fc358bd8549 --- /dev/null +++ b/Mage.Sets/src/mage/cards/y/YiazmatUltimateMark.java @@ -0,0 +1,64 @@ +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.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()), 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/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/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/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/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/DoctorWho.java b/Mage.Sets/src/mage/sets/DoctorWho.java index f70b1c0a707..e81979805f7 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)); diff --git a/Mage.Sets/src/mage/sets/FinalFantasy.java b/Mage.Sets/src/mage/sets/FinalFantasy.java index e544679c260..2e7f091b075 100644 --- a/Mage.Sets/src/mage/sets/FinalFantasy.java +++ b/Mage.Sets/src/mage/sets/FinalFantasy.java @@ -20,49 +20,79 @@ 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", 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("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)); @@ -75,6 +105,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)); @@ -93,11 +126,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)); @@ -106,33 +144,58 @@ 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("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)); @@ -145,6 +208,9 @@ public final class FinalFantasy extends ExpansionSet { 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)); @@ -154,16 +220,39 @@ 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)); + 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("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)); @@ -171,7 +260,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)); @@ -182,6 +276,9 @@ 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)); @@ -189,7 +286,11 @@ public final class FinalFantasy extends ExpansionSet { 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)); @@ -212,6 +313,7 @@ public final class FinalFantasy extends ExpansionSet { 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)); @@ -221,63 +323,116 @@ public final class FinalFantasy extends ExpansionSet { 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("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("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("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, 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("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)); @@ -299,6 +454,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)); @@ -309,10 +465,17 @@ public final class FinalFantasy extends ExpansionSet { 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: 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("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)); @@ -321,14 +484,27 @@ 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: 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)); @@ -339,31 +515,46 @@ 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)); + 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 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)); @@ -376,6 +567,7 @@ public final class FinalFantasy extends ExpansionSet { 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)); @@ -384,9 +576,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)); @@ -396,12 +591,17 @@ 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("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("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)); @@ -411,20 +611,27 @@ public final class FinalFantasy extends ExpansionSet { 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", 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)); diff --git a/Mage.Sets/src/mage/sets/FinalFantasyCommander.java b/Mage.Sets/src/mage/sets/FinalFantasyCommander.java index bc64161d818..b13ea9739ed 100644 --- a/Mage.Sets/src/mage/sets/FinalFantasyCommander.java +++ b/Mage.Sets/src/mage/sets/FinalFantasyCommander.java @@ -112,8 +112,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)); @@ -130,6 +130,8 @@ public final class FinalFantasyCommander extends ExpansionSet { 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)); @@ -214,10 +216,14 @@ public final class FinalFantasyCommander extends ExpansionSet { 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)); @@ -232,6 +238,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)); @@ -240,6 +248,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)); @@ -250,12 +260,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)); @@ -288,6 +302,8 @@ public final class FinalFantasyCommander extends ExpansionSet { 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)); @@ -362,6 +378,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)); @@ -379,6 +400,8 @@ 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)); @@ -390,6 +413,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)); @@ -405,5 +430,6 @@ 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)); } } 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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index e79cc0bc719..f07ab9cceb0 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 @@ -3240,8 +3240,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 +3794,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.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java index e8563d8e838..a64121303de 100644 --- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java +++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java @@ -132,6 +132,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 +308,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 +1047,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 +1063,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 +1126,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 +1723,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 +2172,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 +2182,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 +2203,7 @@ public class VerifyCardDataTest { } } } + boolean needTargetedAbility = targetedKeywords.stream().anyMatch(refLowerText::contains); boolean foundTargetedAbility = card.getAbilities() .stream() @@ -2876,6 +2883,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()) { diff --git a/Mage/src/main/java/mage/abilities/Ability.java b/Mage/src/main/java/mage/abilities/Ability.java index 512da5e120a..93e286a4f56 100644 --- a/Mage/src/main/java/mage/abilities/Ability.java +++ b/Mage/src/main/java/mage/abilities/Ability.java @@ -534,6 +534,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..84d5ce45c48 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); @@ -1733,6 +1735,17 @@ 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 instanceof GenericTargetAdjuster && this.getTargets().isEmpty()) { diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbility.java b/Mage/src/main/java/mage/abilities/TriggeredAbility.java index 24266aeaaad..db5e60317f1 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbility.java @@ -66,10 +66,35 @@ 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); + /** + * 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); + + Condition getTriggerCondition(); + + boolean checkTriggerCondition(Game game); + boolean isOptional(); TriggeredAbility setOptional(); diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java b/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java index 0daef751810..3a7b24f68b2 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); @@ -228,6 +230,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)) { 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/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/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/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/delayed/AddCounterNextSpellDelayedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/delayed/AddCounterNextSpellDelayedTriggeredAbility.java new file mode 100644 index 00000000000..acf91114f15 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/delayed/AddCounterNextSpellDelayedTriggeredAbility.java @@ -0,0 +1,103 @@ +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; + +/** + * @author TheElk801 + */ +public class AddCounterNextSpellDelayedTriggeredAbility extends DelayedTriggeredAbility { + + private final FilterSpell filter; + + public AddCounterNextSpellDelayedTriggeredAbility() { + this(StaticFilters.FILTER_SPELL_A_CREATURE); + } + + public AddCounterNextSpellDelayedTriggeredAbility(FilterSpell filter) { + super(new AddCounterNextSpellEffect(), Duration.EndOfTurn, true, false); + this.filter = filter; + this.setTriggerPhrase("When you next cast " + " 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 { + + AddCounterNextSpellEffect() { + super(Duration.EndOfStep, Outcome.BoostCreature); + staticText = "that creature enters the battlefield with an additional +1/+1 counter on it"; + } + + private AddCounterNextSpellEffect(AddCounterNextSpellEffect 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) { + return false; + } + creature.addCounters( + CounterType.P1P1.createInstance(), 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/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/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/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/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/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/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/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/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/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/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/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/counter/MoveCounterTargetsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/counter/MoveCounterTargetsEffect.java new file mode 100644 index 00000000000..bda45285c45 --- /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 target "); + 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/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..f1c7ffae874 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(CardUtil.numberToText(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/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/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/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/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/StaticFilters.java b/Mage/src/main/java/mage/filter/StaticFilters.java index 31d2277e82c..ab982e0c53b 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 @@ -643,6 +640,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 { @@ -1202,6 +1213,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 +1236,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 { 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/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/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/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/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/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/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/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/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..b79e974e14a 100644 --- a/Mage/src/main/java/mage/game/stack/StackAbility.java +++ b/Mage/src/main/java/mage/game/stack/StackAbility.java @@ -718,6 +718,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/players/Player.java b/Mage/src/main/java/mage/players/Player.java index a0bc38d90ff..136e841bcb4 100644 --- a/Mage/src/main/java/mage/players/Player.java +++ b/Mage/src/main/java/mage/players/Player.java @@ -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); diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 02f2f2db436..0b693a17178 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -1034,8 +1034,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 @@ -3054,44 +3054,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, winnable).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/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/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/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index 5c32c6b6d3a..6018834b1b4 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -1315,6 +1315,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 * 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/Utils/gen-card.pl b/Utils/gen-card.pl index 62abda856e2..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; } diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index 77bfcd4e4b0..4daa42addff 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -57179,7 +57179,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 +57189,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 +57210,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 +57233,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 +57247,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 +57267,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 +57281,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 +57308,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 +57322,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 +57331,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 +57358,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 +57378,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 +57389,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 +57400,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.| @@ -57629,89 +57629,142 @@ Summon: Bahamut|Final Fantasy|1|M|{9}|Enchantment Creature - Saga Dragon|9|9|(As 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.| +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.| +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.| +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.| 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}| @@ -57721,32 +57774,69 @@ Firion, Wild Rose Warrior|Final Fantasy|137|R|{2}{R}|Legendary Creature - Human 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.| 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.| 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}| @@ -57757,29 +57847,42 @@ Traveling Chocobo|Final Fantasy|210|M|{2}{G}|Creature - Bird|3|2|You may look at 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.| 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.| 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.| @@ -57788,19 +57891,51 @@ Ultimecia, Omnipotent|Final Fantasy|247|U||Legendary Creature - Nightmare Warloc 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.| 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 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}| 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}.)| @@ -57825,6 +57960,8 @@ Jidoor, Aristocratic Capital|Final Fantasy|311|R|{4}{U}{U}|Land - Town|||This la 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.| @@ -57844,7 +57981,10 @@ Ultima, Origin of Oblivion|Final Fantasy|324|R|{5}|Legendary Creature - God|4|4| 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.| 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.| @@ -57853,29 +57993,46 @@ The Fire Crystal|Final Fantasy|337|R|{2}{R}{R}|Legendary Artifact|||Red spells y 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: 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.| 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.| @@ -57892,11 +58049,18 @@ 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.| +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.| @@ -57925,15 +58089,24 @@ Ultima, Origin of Oblivion|Final Fantasy|421|R|{5}|Legendary Creature - God|4|4| 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.| +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.| @@ -57948,6 +58121,8 @@ Reno and Rude|Final Fantasy|450|U|{1}{B}|Legendary Creature - Human Assassin|2|1 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.| @@ -57959,9 +58134,14 @@ Firion, Wild Rose Warrior|Final Fantasy|459|R|{2}{R}|Legendary Creature - Human 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.| @@ -57969,8 +58149,10 @@ Torgal, A Fine Hound|Final Fantasy|474|U|{1}{G}|Legendary Creature - Wolf|2|2|Wh 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.| @@ -57978,22 +58160,32 @@ The Emperor of Palamecia|Final Fantasy|484|U|{U}{R}|Legendary Creature - Human N 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.| 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.| +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.| 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.| @@ -58005,6 +58197,9 @@ Yuna, Hope of Spira|Final Fantasy|517|M|{3}{G}{W}|Legendary Creature - Human Cle 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.| @@ -58021,11 +58216,18 @@ 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.| +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.|