mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 02:30:08 -08:00
Merge branch 'master' into feature/implement-spdr-piloted-by-peni
This commit is contained in:
commit
04bc7b8b2a
1185 changed files with 32207 additions and 8890 deletions
|
|
@ -18,10 +18,8 @@ import javax.swing.*;
|
|||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
|
|
@ -258,7 +256,7 @@ public class CardArea extends JPanel implements CardEventProducer {
|
|||
this.reloaded = false;
|
||||
}
|
||||
|
||||
public void selectCards(List<UUID> selected) {
|
||||
public void selectCards(Set<UUID> selected) {
|
||||
for (Component component : cardArea.getComponents()) {
|
||||
if (component instanceof MageCard) {
|
||||
MageCard mageCard = (MageCard) component;
|
||||
|
|
@ -269,7 +267,7 @@ public class CardArea extends JPanel implements CardEventProducer {
|
|||
}
|
||||
}
|
||||
|
||||
public void markCards(List<UUID> marked) {
|
||||
public void markCards(Set<UUID> marked) {
|
||||
for (Component component : cardArea.getComponents()) {
|
||||
if (component instanceof MageCard) {
|
||||
MageCard mageCard = (MageCard) component;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package mage.client.deckeditor;
|
||||
|
||||
import mage.cards.decks.importer.MtgaImporter;
|
||||
import mage.client.MageFrame;
|
||||
import mage.client.dialog.MageDialog;
|
||||
import mage.util.DeckUtil;
|
||||
|
|
@ -20,11 +21,18 @@ import java.util.Optional;
|
|||
public class DeckImportClipboardDialog extends MageDialog {
|
||||
|
||||
private static final String FORMAT_TEXT =
|
||||
"// Example:\n" +
|
||||
"//1 Library of Congress\n" +
|
||||
"//1 Cryptic Gateway\n" +
|
||||
"//1 Azami, Lady of Scrolls\n" +
|
||||
"// NB: This is slow as, and will lock your screen :)\n" +
|
||||
"// MTGO format example:\n" +
|
||||
"// 1 Library of Congress\n" +
|
||||
"// 3 Cryptic Gateway\n" +
|
||||
"//\n" +
|
||||
"// MTGA, moxfield, archidekt format example:\n" +
|
||||
"// Deck\n" +
|
||||
"// 4 Accumulated Knowledge (A25) 40\n" +
|
||||
"// 2 Adarkar Wastes (EOC) 147\n" +
|
||||
"// Commander\n" +
|
||||
"// 1 Grizzly Bears\n" +
|
||||
"//\n" +
|
||||
"// Importing deck can take some time to finish\n" +
|
||||
"\n" +
|
||||
"// Your current clipboard:\n" +
|
||||
"\n";
|
||||
|
|
@ -68,17 +76,19 @@ public class DeckImportClipboardDialog extends MageDialog {
|
|||
}
|
||||
|
||||
private void onOK() {
|
||||
String decklist = editData.getText();
|
||||
decklist = decklist.replace(FORMAT_TEXT, "");
|
||||
String importData = editData.getText();
|
||||
importData = importData.replace(FORMAT_TEXT, "").trim();
|
||||
|
||||
// find possible data format
|
||||
String tempDeckPath;
|
||||
// This dialog also accepts a paste in .mtga format
|
||||
if (decklist.startsWith("Deck\n")) { // An .mtga list always starts with the first line being "Deck". This kind of paste is processed as .mtga
|
||||
tempDeckPath = DeckUtil.writeTextToTempFile("cbimportdeck", ".mtga", decklist);
|
||||
if (MtgaImporter.isMTGA(importData)) {
|
||||
// MTGA or Moxfield
|
||||
tempDeckPath = DeckUtil.writeTextToTempFile("cbimportdeck", ".mtga", importData);
|
||||
} else {
|
||||
// If the paste is not .mtga format, it's processed as plaintext
|
||||
tempDeckPath = DeckUtil.writeTextToTempFile(decklist);
|
||||
// text
|
||||
tempDeckPath = DeckUtil.writeTextToTempFile(importData);
|
||||
}
|
||||
|
||||
if (this.callback != null) {
|
||||
callback.onImportDone(tempDeckPath);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,11 +100,11 @@
|
|||
cardArea.loadCards(showCards, bigCard, gameId);
|
||||
if (options != null) {
|
||||
if (options.containsKey("chosenTargets")) {
|
||||
java.util.List<UUID> chosenCards = (java.util.List<UUID>) options.get("chosenTargets");
|
||||
java.util.Set<UUID> chosenCards = (java.util.Set<UUID>) options.get("chosenTargets");
|
||||
cardArea.selectCards(chosenCards);
|
||||
}
|
||||
if (options.containsKey("possibleTargets")) {
|
||||
java.util.List<UUID> choosableCards = (java.util.List<UUID>) options.get("possibleTargets");
|
||||
java.util.Set<UUID> choosableCards = (java.util.Set<UUID>) options.get("possibleTargets");
|
||||
cardArea.markCards(choosableCards);
|
||||
}
|
||||
if (options.containsKey("queryType") && options.get("queryType") == QueryType.PICK_ABILITY) {
|
||||
|
|
|
|||
|
|
@ -216,17 +216,9 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
});
|
||||
}
|
||||
|
||||
public List<UUID> getPossibleTargets() {
|
||||
if (options != null && options.containsKey("possibleTargets")) {
|
||||
return (List<UUID>) options.get("possibleTargets");
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public Set<UUID> getChosenTargets() {
|
||||
if (options != null && options.containsKey("chosenTargets")) {
|
||||
return new HashSet<>((List<UUID>) options.get("chosenTargets"));
|
||||
return (Set<UUID>) options.get("chosenTargets");
|
||||
} else {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,7 @@ import java.io.BufferedReader;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.net.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
|
|
@ -93,7 +90,9 @@ public class XmageURLConnection {
|
|||
initDefaultProxy();
|
||||
|
||||
try {
|
||||
URL url = new URL(this.url);
|
||||
// convert utf8 url to ascii format (e.g. url encode)
|
||||
URI uri = new URI(this.url);
|
||||
URL url = new URL(uri.toASCIIString());
|
||||
|
||||
// proxy settings
|
||||
if (this.proxy != null) {
|
||||
|
|
@ -107,7 +106,7 @@ public class XmageURLConnection {
|
|||
this.connection.setReadTimeout(CONNECTION_READING_TIMEOUT_MS);
|
||||
|
||||
initDefaultHeaders();
|
||||
} catch (IOException e) {
|
||||
} catch (IOException | URISyntaxException e) {
|
||||
this.connection = null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ public class GathererSets implements Iterable<DownloadJob> {
|
|||
"JMP", "2XM", "ZNR", "KLR", "CMR", "KHC", "KHM", "TSR", "STX", "STA",
|
||||
"C21", "MH2", "AFR", "AFC", "J21", "MID", "MIC", "VOW", "VOC", "YMID",
|
||||
"NEC", "YNEO", "NEO", "SNC", "NCC", "CLB", "2X2", "DMU", "DMC", "40K", "GN3",
|
||||
"UNF", "BRO", "BRC", "BOT", "J22", "DMR", "ONE", "ONC",
|
||||
"UNF", "BRO", "BRC", "BOT", "J22", "DMR", "ONE", "ONC", "SCH",
|
||||
"MOM", "MOC", "MUL", "MAT", "LTR", "CMM", "WOE", "WHO", "RVR", "WOT",
|
||||
"WOC", "SPG", "LCI", "LCC", "REX", "PIP", "MKM", "MKC", "CLU", "OTJ",
|
||||
"OTC", "OTP", "BIG", "MH3", "M3C", "ACR", "BLB", "BLC", "DSK", "DSC",
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ public class ScryfallImageSupportCards {
|
|||
add("2ED"); // Unlimited Edition
|
||||
//add("CEI"); // Intl. Collectors’ Edition
|
||||
//add("CED"); // Collectors’ Edition
|
||||
add("RIN"); // Rinascimento
|
||||
add("REN"); // Renaissance
|
||||
add("ARN"); // Arabian Nights
|
||||
add("ATQ"); // Antiquities
|
||||
//add("FBB"); // Foreign Black Border
|
||||
|
|
@ -465,6 +467,7 @@ public class ScryfallImageSupportCards {
|
|||
// add("MD1"); // Modern Event Deck
|
||||
// add("DD3"); // Duel Decks Anthology
|
||||
// add("PZ1"); // Legendary Cube
|
||||
add("PLG20"); // Love Your LGS 2020
|
||||
add("IKO"); // Ikoria: Lair of Behemoths
|
||||
add("C20"); // Commander 2020
|
||||
add("M21"); // Core Set 2021
|
||||
|
|
@ -514,10 +517,13 @@ public class ScryfallImageSupportCards {
|
|||
add("NCC"); // New Capenna Commander
|
||||
add("SLX"); // Universes Within
|
||||
add("CLB"); // Commander Legends: Battle for Baldur's Gate
|
||||
add("PLG22"); // Love Your LGS 2022
|
||||
add("2X2"); // Double Masters 2022
|
||||
add("SCH"); // Store Championships
|
||||
add("DMU"); // Dominaria United
|
||||
add("DMC"); // Dominaria United Commander
|
||||
add("YDMU"); // Alchemy: Dominaria
|
||||
add("PRCQ"); // Regional Championship Qualifiers 2022
|
||||
add("40K"); // Warhammer 40,000 Commander
|
||||
add("UNF"); // Unfinity
|
||||
add("GN3"); // Game Night: Free-for-All
|
||||
|
|
@ -545,9 +551,11 @@ public class ScryfallImageSupportCards {
|
|||
add("30A"); // 30th Anniversary Edition
|
||||
add("P30A"); // 30th Anniversary Play Promos
|
||||
add("P30M"); // 30th Anniversary Misc Promos
|
||||
add("P30H"); // 30th Anniversary History Promos
|
||||
add("PEWK"); // Eternal Weekend
|
||||
add("LTR"); // The Lord of the Rings: Tales of Middle-Earth
|
||||
add("LTC"); // Tales of Middle-Earth Commander
|
||||
add("PF23"); // MagicFest 2023
|
||||
add("CMM"); // Commander Masters
|
||||
add("WHO"); // Doctor Who
|
||||
add("WOE"); // Wilds of Eldraine
|
||||
|
|
@ -558,10 +566,13 @@ public class ScryfallImageSupportCards {
|
|||
add("REX"); // Jurassic World Collection
|
||||
add("SPG"); // Special Guests
|
||||
add("PW24"); // Wizards Play Network 2024
|
||||
add("PF24"); // MagicFest 2024
|
||||
add("RVR"); // Ravnica Remastered
|
||||
add("PL24"); // Year of the Dragon 2024
|
||||
add("PIP"); // Fallout
|
||||
add("MKM"); // Murders at Karlov Manor
|
||||
add("MKC"); // Murders at Karlov Manor Commander
|
||||
add("PSS4"); // MKM Standard Showdown
|
||||
add("CLU"); // Ravnica: Clue Edition
|
||||
add("OTJ"); // Outlaws of Thunder Junction
|
||||
add("OTC"); // Outlaws of Thunder Junction Commander
|
||||
|
|
@ -569,9 +580,12 @@ public class ScryfallImageSupportCards {
|
|||
add("BIG"); // The Big Score
|
||||
add("MH3"); // Modern Horizons 3
|
||||
add("M3C"); // Modern Horizons 3 Commander
|
||||
add("H2R"); // Modern Horizons 2 Timeshifts
|
||||
add("ACR"); // Assassin's Creed
|
||||
add("BLB"); // Bloomburrow
|
||||
add("BLC"); // Bloomburrow Commander
|
||||
add("PLG24"); // Love Your LGS 2024
|
||||
add("PCBB"); // Cowboy Bebop
|
||||
add("MB2"); // Mystery Booster 2
|
||||
add("DSK"); // Duskmourn: House of Horror
|
||||
add("DSC"); // Duskmourn: House of Horror Commander
|
||||
|
|
@ -579,21 +593,26 @@ public class ScryfallImageSupportCards {
|
|||
add("J25"); // Foundations Jumpstart
|
||||
add("PIO"); // Pioneer Masters
|
||||
add("PW25"); // Wizards Play Network 2025
|
||||
add("PSPL"); // Spotlight Series
|
||||
add("INR"); // Innistrad Remastered
|
||||
add("PF25"); // MagicFest 2025
|
||||
add("PL25"); // Year of the Snake 2025
|
||||
add("DFT"); // Aetherdrift
|
||||
add("DRC"); // Aetherdrift Commander
|
||||
add("PLG25"); // Love Your LGS 2025
|
||||
add("TDM"); // Tarkir: Dragonstorm
|
||||
add("TDC"); // Tarkir: Dragonstorm Commander
|
||||
add("FIN"); // Final Fantasy
|
||||
add("FIC"); // Final Fantasy Commander
|
||||
add("FCA"); // Final Fantasy: Through the Ages
|
||||
add("PSS5"); // FIN Standard Showdown
|
||||
add("EOE"); // Edge of Eternities
|
||||
add("EOC"); // Edge of Eternities Commander
|
||||
add("EOS"); // Edge of Eternities: Stellar Sights
|
||||
add("SPM"); // Marvel's Spider-Man
|
||||
add("SPE"); // Marvel's Spider-Man Eternal
|
||||
add("TLA"); // Avatar: The Last Airbender
|
||||
add("TLE"); // Avatar: The Last Airbender Eternal
|
||||
|
||||
// Custom sets using Scryfall images - must provide a direct link for each card in directDownloadLinks
|
||||
add("CALC"); // Custom Alchemized versions of existing cards
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
package org.mage.plugins.card.dl.sources;
|
||||
|
||||
import mage.cards.repository.TokenRepository;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import mage.cards.repository.TokenRepository;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
|
|
@ -837,6 +837,8 @@ public class ScryfallImageSupportTokens {
|
|||
put("SLD/Hydra", "https://api.scryfall.com/cards/sld/1334?format=image");
|
||||
put("SLD/Icingdeath, Frost Tongue", "https://api.scryfall.com/cards/sld/1018?format=image");
|
||||
put("SLD/Marit Lage", "https://api.scryfall.com/cards/sld/1681?format=image");
|
||||
put("SLD/Mechtitan/1", "https://api.scryfall.com/cards/sld/1969?format=image");
|
||||
put("SLD/Mechtitan/2", "https://api.scryfall.com/cards/sld/1969/en?format=image&face=back");
|
||||
put("SLD/Mechtitan", "https://api.scryfall.com/cards/sld/1969?format=image");
|
||||
put("SLD/Myr", "https://api.scryfall.com/cards/sld/2101?format=image");
|
||||
put("SLD/Saproling", "https://api.scryfall.com/cards/sld/1139?format=image");
|
||||
|
|
@ -2787,6 +2789,7 @@ public class ScryfallImageSupportTokens {
|
|||
|
||||
// EOE
|
||||
put("EOE/Drone", "https://api.scryfall.com/cards/teoe/3?format=image");
|
||||
put("EOE/Emblem Tezzeret", "https://api.scryfall.com/cards/teoe/11?format=image");
|
||||
put("EOE/Human Soldier", "https://api.scryfall.com/cards/teoe/2?format=image");
|
||||
put("EOE/Lander/1", "https://api.scryfall.com/cards/teoe/4?format=image");
|
||||
put("EOE/Lander/2", "https://api.scryfall.com/cards/teoe/5?format=image");
|
||||
|
|
@ -2928,6 +2931,12 @@ public class ScryfallImageSupportTokens {
|
|||
// PL23
|
||||
put("PL23/Food", "https://api.scryfall.com/cards/pl23/2?format=image");
|
||||
|
||||
// PL24
|
||||
put("PL24/Dragon", "https://api.scryfall.com/cards/pl24/3?format=image");
|
||||
|
||||
// PL25
|
||||
put("PL25/Snake", "https://api.scryfall.com/cards/pl25/2?format=image");
|
||||
|
||||
// generate supported sets
|
||||
supportedSets.clear();
|
||||
for (String cardName : this.keySet()) {
|
||||
|
|
|
|||
|
|
@ -43,6 +43,12 @@ public class DownloaderTest {
|
|||
Assert.assertTrue("must have text data (redirect to login page)", s.contains("Sign in to GitHub"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_DownloadText_ScryfallUtf8() {
|
||||
String s = XmageURLConnection.downloadText("https://api.scryfall.com/cards/sld/379★/en");
|
||||
Assert.assertTrue("must have text data (utf8 url must work)", s.contains("Zndrsplt, Eye of Wisdom"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_DownloadFile_ByHttp() throws IOException {
|
||||
// use any public image here
|
||||
|
|
|
|||
|
|
@ -785,7 +785,7 @@ public final class SystemUtil {
|
|||
* @return added cards list
|
||||
*/
|
||||
private static Set<Card> addNewCardsToGame(Game game, String cardName, String setCode, int amount, Player owner) {
|
||||
CardInfo cardInfo = CardLookup.instance.lookupCardInfo(cardName, setCode).orElse(null);
|
||||
CardInfo cardInfo = CardLookup.instance.lookupCardInfo(cardName, setCode, null);
|
||||
if (cardInfo == null || amount <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import mage.game.Game;
|
|||
import mage.players.Player;
|
||||
import mage.target.Target;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.TargetPlayer;
|
||||
import mage.target.common.TargetPermanentOrPlayer;
|
||||
|
||||
/**
|
||||
|
|
@ -76,6 +77,10 @@ abstract class BaseTestableDialog implements TestableDialog {
|
|||
return createAnyTarget(min, max, false);
|
||||
}
|
||||
|
||||
static Target createPlayerTarget(int min, int max, boolean notTarget) {
|
||||
return new TargetPlayer(min, max, notTarget);
|
||||
}
|
||||
|
||||
private static Target createAnyTarget(int min, int max, boolean notTarget) {
|
||||
return new TargetPermanentOrPlayer(min, max).withNotTarget(notTarget);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,6 +126,19 @@ class ChooseTargetTestableDialog extends BaseTestableDialog {
|
|||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 1-3", createImpossibleTarget(1, 3)).aiMustChoose(false, 0));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 2-3", createImpossibleTarget(2, 3)).aiMustChoose(false, 0));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible max", createImpossibleTarget(0, Integer.MAX_VALUE)).aiMustChoose(false, 0));
|
||||
//
|
||||
// additional tests for 2 possible options limitation
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 0", createPlayerTarget(0, 0, notTarget)).aiMustChoose(false, 0));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 0-1", createPlayerTarget(0, 1, notTarget)).aiMustChoose(true, 1));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 0-2", createPlayerTarget(0, 2, notTarget)).aiMustChoose(true, 1));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 0-5", createPlayerTarget(0, 5, notTarget)).aiMustChoose(true, 1));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 1", createPlayerTarget(1, 1, notTarget)).aiMustChoose(true, 1));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 1-2", createPlayerTarget(1, 2, notTarget)).aiMustChoose(true, 1));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 1-5", createPlayerTarget(1, 5, notTarget)).aiMustChoose(true, 1));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 2", createPlayerTarget(2, 2, notTarget)).aiMustChoose(true, 2));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 2-5", createPlayerTarget(2, 5, notTarget)).aiMustChoose(true, 2));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 3", createPlayerTarget(3, 3, notTarget)).aiMustChoose(false, 0));
|
||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 3-5", createPlayerTarget(3, 5, notTarget)).aiMustChoose(false, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,7 @@ import mage.constants.Outcome;
|
|||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
|
|
@ -120,10 +117,8 @@ public class TestableDialogsRunner {
|
|||
choice = prepareSelectDialogChoice(needGroup);
|
||||
player.choose(Outcome.Benefit, choice, game);
|
||||
if (choice.getChoiceKey() != null) {
|
||||
int needIndex = Integer.parseInt(choice.getChoiceKey());
|
||||
if (needIndex < this.dialogs.size()) {
|
||||
needDialog = this.dialogs.get(needIndex);
|
||||
}
|
||||
int needRegNumber = Integer.parseInt(choice.getChoiceKey());
|
||||
needDialog = this.dialogs.getOrDefault(needRegNumber, null);
|
||||
}
|
||||
}
|
||||
if (needDialog == null) {
|
||||
|
|
@ -144,15 +139,20 @@ public class TestableDialogsRunner {
|
|||
Choice choice = new ChoiceImpl(false);
|
||||
choice.setMessage("Choose dialogs group to run");
|
||||
|
||||
// use min reg number for groups
|
||||
Map<String, Integer> groupNumber = new HashMap<>();
|
||||
this.dialogs.values().forEach(dialog -> {
|
||||
groupNumber.put(dialog.getGroup(), Math.min(groupNumber.getOrDefault(dialog.getGroup(), Integer.MAX_VALUE), dialog.getRegNumber()));
|
||||
});
|
||||
|
||||
// main groups
|
||||
int recNumber = 0;
|
||||
for (int i = 0; i < groups.size(); i++) {
|
||||
recNumber++;
|
||||
String group = groups.get(i);
|
||||
Integer groupMinNumber = groupNumber.getOrDefault(group, 0);
|
||||
choice.withItem(
|
||||
String.valueOf(i),
|
||||
String.format("%02d. %s", recNumber, group),
|
||||
recNumber,
|
||||
String.format("%02d. %s", groupMinNumber, group),
|
||||
groupMinNumber,
|
||||
ChoiceHintType.TEXT,
|
||||
String.join("<br>", group)
|
||||
);
|
||||
|
|
@ -186,18 +186,15 @@ public class TestableDialogsRunner {
|
|||
private Choice prepareSelectDialogChoice(String needGroup) {
|
||||
Choice choice = new ChoiceImpl(false);
|
||||
choice.setMessage("Choose game dialog to run from " + needGroup);
|
||||
int recNumber = 0;
|
||||
for (int i = 0; i < this.dialogs.size(); i++) {
|
||||
TestableDialog dialog = this.dialogs.get(i);
|
||||
for (TestableDialog dialog : this.dialogs.values()) {
|
||||
if (!dialog.getGroup().equals(needGroup)) {
|
||||
continue;
|
||||
}
|
||||
recNumber++;
|
||||
String info = String.format("%s - %s - %s", dialog.getGroup(), dialog.getName(), dialog.getDescription());
|
||||
choice.withItem(
|
||||
String.valueOf(i),
|
||||
String.format("%02d. %s", recNumber, info),
|
||||
recNumber,
|
||||
String.valueOf(dialog.getRegNumber()),
|
||||
String.format("%02d. %s", dialog.getRegNumber(), info),
|
||||
dialog.getRegNumber(),
|
||||
ChoiceHintType.TEXT,
|
||||
String.join("<br>", info)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -13,17 +13,20 @@ public class DuelCommander extends Commander {
|
|||
banned.add("Balance");
|
||||
banned.add("Bazaar of Baghdad");
|
||||
banned.add("Black Lotus");
|
||||
banned.add("Blood Moon");
|
||||
banned.add("Capture of Jingzhou");
|
||||
banned.add("Cavern of Souls");
|
||||
banned.add("Channel");
|
||||
banned.add("Chrome Mox");
|
||||
banned.add("Comet, Stellar Pup");
|
||||
banned.add("Dark Ritual");
|
||||
banned.add("Deadly Rollick");
|
||||
banned.add("Deflecting Swat");
|
||||
banned.add("Dig Through Time");
|
||||
banned.add("Emrakul, the Aeons Torn");
|
||||
banned.add("Entomb");
|
||||
banned.add("Fastbond");
|
||||
banned.add("Force of Will");
|
||||
banned.add("Field of the Dead");
|
||||
banned.add("Fierce Guardianship");
|
||||
banned.add("Flawless Maneuver");
|
||||
|
|
@ -82,6 +85,7 @@ public class DuelCommander extends Commander {
|
|||
banned.add("Tolarian Academy");
|
||||
banned.add("Trazyn The Infinite");
|
||||
banned.add("Treasure Cruise");
|
||||
banned.add("Underworld Breach");
|
||||
banned.add("Uro, Titan of Nature's Wrath");
|
||||
banned.add("Vampiric Tutor");
|
||||
banned.add("Wasteland");
|
||||
|
|
|
|||
|
|
@ -219,6 +219,7 @@ public class ComputerPlayer6 extends ComputerPlayer {
|
|||
}
|
||||
// Condition to stop deeper simulation
|
||||
if (SimulationNode2.nodeCount > MAX_SIMULATED_NODES_PER_ERROR) {
|
||||
// how-to fix: make sure you are disabled debug mode by COMPUTER_DISABLE_TIMEOUT_IN_GAME_SIMULATIONS = false
|
||||
throw new IllegalStateException("AI ERROR: too much nodes (possible actions)");
|
||||
}
|
||||
if (depth <= 0
|
||||
|
|
@ -398,20 +399,23 @@ public class ComputerPlayer6 extends ComputerPlayer {
|
|||
}
|
||||
|
||||
protected void resolve(SimulationNode2 node, int depth, Game game) {
|
||||
StackObject stackObject = game.getStack().getFirst();
|
||||
StackObject stackObject = game.getStack().getFirstOrNull();
|
||||
if (stackObject == null) {
|
||||
throw new IllegalStateException("Catch empty stack on resolve (something wrong with sim code)");
|
||||
}
|
||||
if (stackObject instanceof StackAbility) {
|
||||
// AI hint for search effects (calc all possible cards for best score)
|
||||
SearchEffect effect = getSearchEffect((StackAbility) stackObject);
|
||||
if (effect != null
|
||||
&& stackObject.getControllerId().equals(playerId)) {
|
||||
Target target = effect.getTarget();
|
||||
if (!target.isChoiceCompleted(getId(), (StackAbility) stackObject, game)) {
|
||||
if (!target.isChoiceCompleted(getId(), (StackAbility) stackObject, game, null)) {
|
||||
for (UUID targetId : target.possibleTargets(stackObject.getControllerId(), stackObject.getStackAbility(), game)) {
|
||||
Game sim = game.createSimulationForAI();
|
||||
StackAbility newAbility = (StackAbility) stackObject.copy();
|
||||
SearchEffect newEffect = getSearchEffect(newAbility);
|
||||
newEffect.getTarget().addTarget(targetId, newAbility, sim);
|
||||
sim.getStack().push(newAbility);
|
||||
sim.getStack().push(sim, newAbility);
|
||||
SimulationNode2 newNode = new SimulationNode2(node, sim, depth, stackObject.getControllerId());
|
||||
node.children.add(newNode);
|
||||
newNode.getTargets().add(targetId);
|
||||
|
|
@ -498,7 +502,7 @@ public class ComputerPlayer6 extends ComputerPlayer {
|
|||
}
|
||||
logger.warn("Possible freeze chain:");
|
||||
if (root != null && chain.isEmpty()) {
|
||||
logger.warn(" - unknown use case"); // maybe can't finish any calc, maybe related to target options, I don't know
|
||||
logger.warn(" - unknown use case (too many possible targets?)"); // maybe can't finish any calc, maybe related to target options
|
||||
}
|
||||
chain.forEach(s -> {
|
||||
logger.warn(" - " + s);
|
||||
|
|
@ -639,7 +643,7 @@ public class ComputerPlayer6 extends ComputerPlayer {
|
|||
return "unknown";
|
||||
})
|
||||
.collect(Collectors.joining(", "));
|
||||
logger.info(String.format("Sim Prio [%d] -> with choices (TODO): [%d]<diff %s> (%s)",
|
||||
logger.info(String.format("Sim Prio [%d] -> with possible choices: [%d]<diff %s> (%s)",
|
||||
depth,
|
||||
currentNode.getDepth(),
|
||||
printDiffScore(currentScore - prevScore),
|
||||
|
|
@ -648,14 +652,18 @@ public class ComputerPlayer6 extends ComputerPlayer {
|
|||
} else if (!currentNode.getChoices().isEmpty()) {
|
||||
// ON CHOICES
|
||||
String choicesInfo = String.join(", ", currentNode.getChoices());
|
||||
logger.info(String.format("Sim Prio [%d] -> with choices (TODO): [%d]<diff %s> (%s)",
|
||||
logger.info(String.format("Sim Prio [%d] -> with possible choices (must not see that code): [%d]<diff %s> (%s)",
|
||||
depth,
|
||||
currentNode.getDepth(),
|
||||
printDiffScore(currentScore - prevScore),
|
||||
choicesInfo)
|
||||
);
|
||||
} else {
|
||||
throw new IllegalStateException("AI CALC ERROR: unknown calculation result (no abilities, no targets, no choices)");
|
||||
logger.info(String.format("Sim Prio [%d] -> with do nothing: [%d]<diff %s>",
|
||||
depth,
|
||||
currentNode.getDepth(),
|
||||
printDiffScore(currentScore - prevScore))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -886,10 +894,10 @@ public class ComputerPlayer6 extends ComputerPlayer {
|
|||
}
|
||||
|
||||
UUID abilityControllerId = target.getAffectedAbilityControllerId(getId());
|
||||
if (!target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
if (!target.isChoiceCompleted(abilityControllerId, source, game, cards)) {
|
||||
for (UUID targetId : targets) {
|
||||
target.addTarget(targetId, source, game);
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game, cards)) {
|
||||
targets.clear();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -906,10 +914,10 @@ public class ComputerPlayer6 extends ComputerPlayer {
|
|||
}
|
||||
|
||||
UUID abilityControllerId = target.getAffectedAbilityControllerId(getId());
|
||||
if (!target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
if (!target.isChoiceCompleted(abilityControllerId, source, game, cards)) {
|
||||
for (UUID targetId : targets) {
|
||||
target.add(targetId, game);
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game, cards)) {
|
||||
targets.clear();
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ public final class SimulatedPlayer2 extends ComputerPlayer {
|
|||
return list;
|
||||
}
|
||||
|
||||
protected void simulateOptions(Game game) {
|
||||
private void simulateOptions(Game game) {
|
||||
List<ActivatedAbility> playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer);
|
||||
for (ActivatedAbility ability : playables) {
|
||||
if (ability.isManaAbility()) {
|
||||
|
|
@ -176,6 +176,10 @@ public final class SimulatedPlayer2 extends ComputerPlayer {
|
|||
return options;
|
||||
}
|
||||
|
||||
// remove invalid targets
|
||||
// TODO: is it useless cause it already filtered before?
|
||||
options.removeIf(option -> !option.getTargets().isChosen(game));
|
||||
|
||||
if (AI_SIMULATE_ALL_BAD_AND_GOOD_TARGETS) {
|
||||
return options;
|
||||
}
|
||||
|
|
@ -314,14 +318,17 @@ public final class SimulatedPlayer2 extends ComputerPlayer {
|
|||
Ability ability = source.copy();
|
||||
List<Ability> options = getPlayableOptions(ability, game);
|
||||
if (options.isEmpty()) {
|
||||
// no options - activate as is
|
||||
logger.debug("simulating -- triggered ability:" + ability);
|
||||
game.getStack().push(new StackAbility(ability, playerId));
|
||||
game.getStack().push(game, new StackAbility(ability, playerId));
|
||||
if (ability.activate(game, false) && ability.isUsesStack()) {
|
||||
game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability, ability.getControllerId()));
|
||||
}
|
||||
game.applyEffects();
|
||||
game.getPlayers().resetPassed();
|
||||
} else {
|
||||
// many options - activate and add to sims tree
|
||||
// TODO: AI run all sims, but do not use best option for triggers yet
|
||||
SimulationNode2 parent = (SimulationNode2) game.getCustomData();
|
||||
int depth = parent.getDepth() - 1;
|
||||
if (depth == 0) {
|
||||
|
|
@ -337,16 +344,16 @@ public final class SimulatedPlayer2 extends ComputerPlayer {
|
|||
|
||||
protected void addAbilityNode(SimulationNode2 parent, Ability ability, int depth, Game game) {
|
||||
Game sim = game.createSimulationForAI();
|
||||
sim.getStack().push(new StackAbility(ability, playerId));
|
||||
sim.getStack().push(sim, new StackAbility(ability, playerId));
|
||||
if (ability.activate(sim, false) && ability.isUsesStack()) {
|
||||
game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability, ability.getControllerId()));
|
||||
sim.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability, ability.getControllerId()));
|
||||
}
|
||||
sim.applyEffects();
|
||||
SimulationNode2 newNode = new SimulationNode2(parent, sim, depth, playerId);
|
||||
logger.debug("simulating -- node #:" + SimulationNode2.getCount() + " triggered ability option");
|
||||
for (Target target : ability.getTargets()) {
|
||||
for (UUID targetId : target.getTargets()) {
|
||||
newNode.getTargets().add(targetId); // save for info only (real targets in newNode.ability already)
|
||||
newNode.getTargets().add(targetId); // save for info only (real targets in newNode.game.stack already)
|
||||
}
|
||||
}
|
||||
parent.children.add(newNode);
|
||||
|
|
|
|||
|
|
@ -142,17 +142,27 @@ public class ComputerPlayer extends PlayerImpl {
|
|||
UUID abilityControllerId = target.getAffectedAbilityControllerId(getId());
|
||||
|
||||
// nothing to choose, e.g. X=0
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game, fromCards)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// default logic for any targets
|
||||
PossibleTargetsSelector possibleTargetsSelector = new PossibleTargetsSelector(outcome, target, abilityControllerId, source, game);
|
||||
possibleTargetsSelector.findNewTargets(fromCards);
|
||||
|
||||
// nothing to choose, e.g. no valid targets
|
||||
if (!possibleTargetsSelector.hasAnyTargets()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// can't choose
|
||||
if (!possibleTargetsSelector.hasMinNumberOfTargets()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// good targets -- choose as much as possible
|
||||
for (MageItem item : possibleTargetsSelector.getGoodTargets()) {
|
||||
target.add(item.getId(), game);
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game, fromCards)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -226,7 +236,7 @@ public class ComputerPlayer extends PlayerImpl {
|
|||
UUID abilityControllerId = target.getAffectedAbilityControllerId(getId());
|
||||
|
||||
// nothing to choose, e.g. X=0
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -238,6 +248,11 @@ public class ComputerPlayer extends PlayerImpl {
|
|||
return false;
|
||||
}
|
||||
|
||||
// can't choose
|
||||
if (!possibleTargetsSelector.hasMinNumberOfTargets()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// KILL PRIORITY
|
||||
if (outcome == Outcome.Damage) {
|
||||
// opponent first
|
||||
|
|
@ -251,7 +266,7 @@ public class ComputerPlayer extends PlayerImpl {
|
|||
int leftLife = PossibleTargetsComparator.getLifeForDamage(item, game);
|
||||
if (leftLife > 0 && leftLife <= target.getAmountRemaining()) {
|
||||
target.addTarget(item.getId(), leftLife, source, game);
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -268,7 +283,7 @@ public class ComputerPlayer extends PlayerImpl {
|
|||
int leftLife = PossibleTargetsComparator.getLifeForDamage(item, game);
|
||||
if (leftLife > 0 && leftLife <= target.getAmountRemaining()) {
|
||||
target.addTarget(item.getId(), leftLife, source, game);
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -283,7 +298,7 @@ public class ComputerPlayer extends PlayerImpl {
|
|||
continue;
|
||||
}
|
||||
target.addTarget(item.getId(), target.getAmountRemaining(), source, game);
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -303,7 +318,7 @@ public class ComputerPlayer extends PlayerImpl {
|
|||
int leftLife = PossibleTargetsComparator.getLifeForDamage(item, game);
|
||||
if (leftLife > 1) {
|
||||
target.addTarget(item.getId(), Math.min(leftLife - 1, target.getAmountRemaining()), source, game);
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -322,7 +337,7 @@ public class ComputerPlayer extends PlayerImpl {
|
|||
return !target.getTargets().isEmpty();
|
||||
}
|
||||
target.addTarget(item.getId(), target.getAmountRemaining(), source, game);
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -339,7 +354,7 @@ public class ComputerPlayer extends PlayerImpl {
|
|||
continue;
|
||||
}
|
||||
target.addTarget(item.getId(), target.getAmountRemaining(), source, game);
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -355,7 +370,7 @@ public class ComputerPlayer extends PlayerImpl {
|
|||
return !target.getTargets().isEmpty();
|
||||
}
|
||||
target.addTarget(item.getId(), target.getAmountRemaining(), source, game);
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ public class PossibleTargetsSelector {
|
|||
// collect new valid targets
|
||||
List<MageItem> found = target.possibleTargets(abilityControllerId, source, game, fromTargetsList).stream()
|
||||
.filter(id -> !target.contains(id))
|
||||
.filter(id -> target.canTarget(abilityControllerId, id, source, game))
|
||||
.map(id -> {
|
||||
Player player = game.getPlayer(id);
|
||||
if (player != null) {
|
||||
|
|
@ -137,6 +136,10 @@ public class PossibleTargetsSelector {
|
|||
}
|
||||
}
|
||||
|
||||
public List<MageItem> getAny() {
|
||||
return this.any;
|
||||
}
|
||||
|
||||
public static boolean isMyItem(UUID abilityControllerId, MageItem item) {
|
||||
if (item instanceof Player) {
|
||||
return item.getId().equals(abilityControllerId);
|
||||
|
|
@ -181,7 +184,12 @@ public class PossibleTargetsSelector {
|
|||
return false;
|
||||
}
|
||||
|
||||
boolean hasAnyTargets() {
|
||||
public boolean hasAnyTargets() {
|
||||
return !this.any.isEmpty();
|
||||
}
|
||||
|
||||
public boolean hasMinNumberOfTargets() {
|
||||
return this.target.getMinNumberOfTargets() == 0
|
||||
|| this.any.size() >= this.target.getMinNumberOfTargets();
|
||||
}
|
||||
}
|
||||
|
|
@ -121,7 +121,7 @@ public final class SimulatedPlayerMCTS extends MCTSPlayer {
|
|||
}
|
||||
}
|
||||
if (ability.isUsesStack()) {
|
||||
game.getStack().push(new StackAbility(ability, playerId));
|
||||
game.getStack().push(game, new StackAbility(ability, playerId));
|
||||
if (ability.activate(game, false)) {
|
||||
game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability, ability.getControllerId()));
|
||||
actionCount++;
|
||||
|
|
@ -187,8 +187,8 @@ public final class SimulatedPlayerMCTS extends MCTSPlayer {
|
|||
abort = true;
|
||||
}
|
||||
|
||||
protected boolean chooseRandom(Target target, Game game) {
|
||||
Set<UUID> possibleTargets = target.possibleTargets(playerId, game);
|
||||
private boolean chooseRandom(Target target, Ability source, Game game) {
|
||||
Set<UUID> possibleTargets = target.possibleTargets(playerId, source, game);
|
||||
if (possibleTargets.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -233,7 +233,7 @@ public final class SimulatedPlayerMCTS extends MCTSPlayer {
|
|||
@Override
|
||||
public boolean choose(Outcome outcome, Target target, Ability source, Game game) {
|
||||
if (this.isHuman()) {
|
||||
return chooseRandom(target, game);
|
||||
return chooseRandom(target, source, game);
|
||||
}
|
||||
return super.choose(outcome, target, source, game);
|
||||
}
|
||||
|
|
@ -241,7 +241,7 @@ public final class SimulatedPlayerMCTS extends MCTSPlayer {
|
|||
@Override
|
||||
public boolean choose(Outcome outcome, Target target, Ability source, Game game, Map<String, Serializable> options) {
|
||||
if (this.isHuman()) {
|
||||
return chooseRandom(target, game);
|
||||
return chooseRandom(target, source, game);
|
||||
}
|
||||
return super.choose(outcome, target, source, game, options);
|
||||
}
|
||||
|
|
@ -252,7 +252,7 @@ public final class SimulatedPlayerMCTS extends MCTSPlayer {
|
|||
if (cards.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
Set<UUID> possibleTargets = target.possibleTargets(playerId, cards, source, game);
|
||||
Set<UUID> possibleTargets = target.possibleTargets(playerId, source, game, cards);
|
||||
if (possibleTargets.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package mage.player.human;
|
|||
import mage.MageIdentifier;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.*;
|
||||
import mage.abilities.costs.VariableCost;
|
||||
import mage.abilities.costs.common.SacrificeSourceCost;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.mana.ManaCost;
|
||||
|
|
@ -639,8 +638,8 @@ public class HumanPlayer extends PlayerImpl {
|
|||
// Check check if the spell being paid for cares about the color of mana being paid
|
||||
// See: https://github.com/magefree/mage/issues/9070
|
||||
boolean caresAboutManaColor = false;
|
||||
if (!game.getStack().isEmpty() && game.getStack().getFirst() instanceof Spell) {
|
||||
Spell spellBeingCast = (Spell) game.getStack().getFirst();
|
||||
if (game.getStack().getFirstOrNull() instanceof Spell) {
|
||||
Spell spellBeingCast = (Spell) game.getStack().getFirstOrNull();
|
||||
if (!spellBeingCast.isResolving() && spellBeingCast.getControllerId().equals(this.getId())) {
|
||||
CardImpl card = (CardImpl) game.getCard(spellBeingCast.getSourceId());
|
||||
caresAboutManaColor = card.caresAboutManaColor(game);
|
||||
|
|
@ -697,7 +696,7 @@ public class HumanPlayer extends PlayerImpl {
|
|||
}
|
||||
|
||||
// stop on completed, e.g. X=0
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -729,7 +728,7 @@ public class HumanPlayer extends PlayerImpl {
|
|||
// continue to next target (example: auto-choose must fill min/max = 2 from 2 possible cards)
|
||||
} else {
|
||||
// manual choose
|
||||
options.put("chosenTargets", (Serializable) target.getTargets());
|
||||
options.put("chosenTargets", new HashSet<>(target.getTargets()));
|
||||
|
||||
prepareForResponse(game);
|
||||
if (!isExecutingMacro()) {
|
||||
|
|
@ -747,9 +746,9 @@ public class HumanPlayer extends PlayerImpl {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (possibleTargets.contains(responseId) && target.canTarget(getId(), responseId, source, game)) {
|
||||
if (possibleTargets.contains(responseId)) {
|
||||
target.add(responseId, game);
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -794,7 +793,7 @@ public class HumanPlayer extends PlayerImpl {
|
|||
|
||||
// manual choice
|
||||
if (responseId == null) {
|
||||
options.put("chosenTargets", (Serializable) target.getTargets());
|
||||
options.put("chosenTargets", new HashSet<>(target.getTargets()));
|
||||
|
||||
prepareForResponse(game);
|
||||
if (!isExecutingMacro()) {
|
||||
|
|
@ -814,11 +813,9 @@ public class HumanPlayer extends PlayerImpl {
|
|||
|
||||
// add new target
|
||||
if (possibleTargets.contains(responseId)) {
|
||||
if (target.canTarget(abilityControllerId, responseId, source, game)) {
|
||||
target.addTarget(responseId, source, game);
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
return true;
|
||||
}
|
||||
target.addTarget(responseId, source, game);
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -864,13 +861,7 @@ public class HumanPlayer extends PlayerImpl {
|
|||
UUID abilityControllerId = target.getAffectedAbilityControllerId(this.getId());
|
||||
|
||||
while (canRespond()) {
|
||||
|
||||
List<UUID> possibleTargets = new ArrayList<>();
|
||||
for (UUID cardId : cards) {
|
||||
if (target.canTarget(abilityControllerId, cardId, source, cards, game)) {
|
||||
possibleTargets.add(cardId);
|
||||
}
|
||||
}
|
||||
Set<UUID> possibleTargets = target.possibleTargets(abilityControllerId, source, game, cards);
|
||||
|
||||
boolean required = target.isRequired(source != null ? source.getSourceId() : null, game);
|
||||
int count = cards.count(target.getFilter(), abilityControllerId, source, game);
|
||||
|
|
@ -880,7 +871,7 @@ public class HumanPlayer extends PlayerImpl {
|
|||
}
|
||||
|
||||
// if nothing to choose then show dialog (user must see non selectable items and click on any of them)
|
||||
// TODO: need research - is it used?
|
||||
// TODO: need research - is it used (test player and AI player don't see empty dialogs)?
|
||||
if (required && possibleTargets.isEmpty()) {
|
||||
required = false;
|
||||
}
|
||||
|
|
@ -894,7 +885,7 @@ public class HumanPlayer extends PlayerImpl {
|
|||
} else {
|
||||
// manual choose
|
||||
Map<String, Serializable> options = getOptions(target, null);
|
||||
options.put("chosenTargets", (Serializable) target.getTargets());
|
||||
options.put("chosenTargets", new HashSet<>(target.getTargets()));
|
||||
if (!possibleTargets.isEmpty()) {
|
||||
options.put("possibleTargets", (Serializable) possibleTargets);
|
||||
}
|
||||
|
|
@ -916,9 +907,9 @@ public class HumanPlayer extends PlayerImpl {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (possibleTargets.contains(responseId) && target.canTarget(getId(), responseId, source, cards, game)) {
|
||||
if (possibleTargets.contains(responseId)) {
|
||||
target.add(responseId, game);
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game, cards)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -962,12 +953,8 @@ public class HumanPlayer extends PlayerImpl {
|
|||
required = false;
|
||||
}
|
||||
|
||||
List<UUID> possibleTargets = new ArrayList<>();
|
||||
for (UUID cardId : cards) {
|
||||
if (target.canTarget(abilityControllerId, cardId, source, cards, game)) {
|
||||
possibleTargets.add(cardId);
|
||||
}
|
||||
}
|
||||
Set<UUID> possibleTargets = target.possibleTargets(abilityControllerId, source, game, cards);
|
||||
|
||||
// if nothing to choose then show dialog (user must see non-selectable items and click on any of them)
|
||||
if (possibleTargets.isEmpty()) {
|
||||
required = false;
|
||||
|
|
@ -977,7 +964,7 @@ public class HumanPlayer extends PlayerImpl {
|
|||
|
||||
if (responseId == null) {
|
||||
Map<String, Serializable> options = getOptions(target, null);
|
||||
options.put("chosenTargets", (Serializable) target.getTargets());
|
||||
options.put("chosenTargets", new HashSet<>(target.getTargets()));
|
||||
|
||||
if (!possibleTargets.isEmpty()) {
|
||||
options.put("possibleTargets", (Serializable) possibleTargets);
|
||||
|
|
@ -995,9 +982,9 @@ public class HumanPlayer extends PlayerImpl {
|
|||
if (responseId != null) {
|
||||
if (target.contains(responseId)) { // if already included remove it
|
||||
target.remove(responseId);
|
||||
} else if (target.canTarget(abilityControllerId, responseId, source, cards, game)) {
|
||||
} else if (possibleTargets.contains(responseId)) {
|
||||
target.addTarget(responseId, source, game);
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||
if (target.isChoiceCompleted(abilityControllerId, source, game, cards)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1054,9 +1041,9 @@ public class HumanPlayer extends PlayerImpl {
|
|||
// 1. Select targets
|
||||
// TODO: rework to use existing chooseTarget instead custom select?
|
||||
while (canRespond()) {
|
||||
Set<UUID> possibleTargetIds = target.possibleTargets(abilityControllerId, source, game);
|
||||
Set<UUID> possibleTargets = target.possibleTargets(abilityControllerId, source, game);
|
||||
boolean required = target.isRequired(source.getSourceId(), game);
|
||||
if (possibleTargetIds.isEmpty()
|
||||
if (possibleTargets.isEmpty()
|
||||
|| target.getSize() >= target.getMinNumberOfTargets()) {
|
||||
required = false;
|
||||
}
|
||||
|
|
@ -1065,12 +1052,6 @@ public class HumanPlayer extends PlayerImpl {
|
|||
|
||||
// responseId is null if a choice couldn't be automatically made
|
||||
if (responseId == null) {
|
||||
List<UUID> possibleTargets = new ArrayList<>();
|
||||
for (UUID targetId : possibleTargetIds) {
|
||||
if (target.canTarget(abilityControllerId, targetId, source, game)) {
|
||||
possibleTargets.add(targetId);
|
||||
}
|
||||
}
|
||||
// if nothing to choose then show dialog (user must see non selectable items and click on any of them)
|
||||
if (required && possibleTargets.isEmpty()) {
|
||||
required = false;
|
||||
|
|
@ -1078,7 +1059,7 @@ public class HumanPlayer extends PlayerImpl {
|
|||
|
||||
// selected
|
||||
Map<String, Serializable> options = getOptions(target, null);
|
||||
options.put("chosenTargets", (Serializable) target.getTargets());
|
||||
options.put("chosenTargets", new HashSet<>(target.getTargets()));
|
||||
if (!possibleTargets.isEmpty()) {
|
||||
options.put("possibleTargets", (Serializable) possibleTargets);
|
||||
}
|
||||
|
|
@ -1087,7 +1068,7 @@ public class HumanPlayer extends PlayerImpl {
|
|||
if (!isExecutingMacro()) {
|
||||
String multiType = multiAmountType == MultiAmountType.DAMAGE ? " to divide %d damage" : " to distribute %d counters";
|
||||
String message = target.getMessage(game) + String.format(multiType, amountTotal);
|
||||
game.fireSelectTargetEvent(playerId, new MessageToClient(message, getRelatedObjectName(source, game)), possibleTargetIds, required, options);
|
||||
game.fireSelectTargetEvent(playerId, new MessageToClient(message, getRelatedObjectName(source, game)), possibleTargets, required, options);
|
||||
}
|
||||
waitForResponse(game);
|
||||
|
||||
|
|
@ -1098,9 +1079,7 @@ public class HumanPlayer extends PlayerImpl {
|
|||
if (target.contains(responseId)) {
|
||||
// unselect
|
||||
target.remove(responseId);
|
||||
} else if (possibleTargetIds.contains(responseId)
|
||||
&& target.canTarget(abilityControllerId, responseId, source, game)
|
||||
&& target.getSize() < amountTotal) {
|
||||
} else if (possibleTargets.contains(responseId) && target.getSize() < amountTotal) {
|
||||
// select
|
||||
target.addTarget(responseId, source, game);
|
||||
}
|
||||
|
|
@ -2094,32 +2073,33 @@ public class HumanPlayer extends PlayerImpl {
|
|||
if (!canCallFeedback(game)) {
|
||||
return;
|
||||
}
|
||||
TargetAttackingCreature target = new TargetAttackingCreature();
|
||||
|
||||
// TODO: add canRespond cycle?
|
||||
// no need in cycle, cause parent selectBlockers used it already
|
||||
if (!canRespond()) {
|
||||
return;
|
||||
}
|
||||
|
||||
UUID responseId = null;
|
||||
|
||||
prepareForResponse(game);
|
||||
if (!isExecutingMacro()) {
|
||||
// possible attackers to block
|
||||
Set<UUID> attackers = target.possibleTargets(playerId, null, game);
|
||||
TargetAttackingCreature target = new TargetAttackingCreature();
|
||||
Permanent blocker = game.getPermanent(blockerId);
|
||||
Set<UUID> possibleTargets = new HashSet<>();
|
||||
for (UUID attackerId : attackers) {
|
||||
Set<UUID> allAttackers = target.possibleTargets(playerId, null, game);
|
||||
Set<UUID> possibleAttackersToBlock = new HashSet<>();
|
||||
for (UUID attackerId : allAttackers) {
|
||||
CombatGroup group = game.getCombat().findGroup(attackerId);
|
||||
if (group != null && blocker != null && group.canBlock(blocker, game)) {
|
||||
possibleTargets.add(attackerId);
|
||||
possibleAttackersToBlock.add(attackerId);
|
||||
}
|
||||
}
|
||||
if (possibleTargets.size() == 1) {
|
||||
responseId = possibleTargets.stream().iterator().next();
|
||||
if (possibleAttackersToBlock.size() == 1) {
|
||||
// auto-choice
|
||||
responseId = possibleAttackersToBlock.stream().iterator().next();
|
||||
} else {
|
||||
prepareForResponse(game);
|
||||
game.fireSelectTargetEvent(playerId, new MessageToClient("Select attacker to block", getRelatedObjectName(blockerId, game)),
|
||||
possibleTargets, false, getOptions(target, null));
|
||||
possibleAttackersToBlock, false, getOptions(target, null));
|
||||
waitForResponse(game);
|
||||
}
|
||||
}
|
||||
|
|
@ -2174,7 +2154,7 @@ public class HumanPlayer extends PlayerImpl {
|
|||
break;
|
||||
}
|
||||
|
||||
return xValue;
|
||||
return xValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
54
Mage.Sets/src/mage/cards/a/AangAirNomad.java
Normal file
54
Mage.Sets/src/mage/cards/a/AangAirNomad.java
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
|
||||
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.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.filter.StaticFilters;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AangAirNomad extends CardImpl {
|
||||
|
||||
public AangAirNomad(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.AVATAR);
|
||||
this.subtype.add(SubType.ALLY);
|
||||
this.power = new MageInt(5);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// Vigilance
|
||||
this.addAbility(VigilanceAbility.getInstance());
|
||||
|
||||
// Other creatures you control have vigilance.
|
||||
this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect(
|
||||
VigilanceAbility.getInstance(), Duration.WhileOnBattlefield,
|
||||
StaticFilters.FILTER_PERMANENT_CREATURES, true
|
||||
)));
|
||||
}
|
||||
|
||||
private AangAirNomad(final AangAirNomad card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AangAirNomad copy() {
|
||||
return new AangAirNomad(this);
|
||||
}
|
||||
}
|
||||
67
Mage.Sets/src/mage/cards/a/AangAirbendingMaster.java
Normal file
67
Mage.Sets/src/mage/cards/a/AangAirbendingMaster.java
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.common.OneOrMoreLeaveWithoutDyingTriggeredAbility;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.dynamicvalue.common.CountersControllerCount;
|
||||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
import mage.abilities.effects.common.counter.AddCountersPlayersEffect;
|
||||
import mage.abilities.effects.keyword.AirbendTargetEffect;
|
||||
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
|
||||
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.counters.CounterType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.permanent.token.AllyToken;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AangAirbendingMaster extends CardImpl {
|
||||
|
||||
private static final DynamicValue xValue = new CountersControllerCount(CounterType.EXPERIENCE);
|
||||
|
||||
public AangAirbendingMaster(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.AVATAR);
|
||||
this.subtype.add(SubType.ALLY);
|
||||
this.power = new MageInt(4);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
// When Aang enters, airbend another target creature.
|
||||
Ability ability = new EntersBattlefieldTriggeredAbility(new AirbendTargetEffect());
|
||||
ability.addTarget(new TargetPermanent(StaticFilters.FILTER_ANOTHER_TARGET_CREATURE));
|
||||
this.addAbility(ability);
|
||||
|
||||
// Whenever one or more creatures you control leave the battlefield without dying, you get an experience counter.
|
||||
this.addAbility(new OneOrMoreLeaveWithoutDyingTriggeredAbility(
|
||||
new AddCountersPlayersEffect(CounterType.EXPERIENCE.createInstance(), TargetController.YOU),
|
||||
StaticFilters.FILTER_CONTROLLED_CREATURES
|
||||
));
|
||||
|
||||
// At the beginning of your upkeep, create a 1/1 white Ally creature token for each experience counter you have.
|
||||
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new CreateTokenEffect(new AllyToken(), xValue)
|
||||
.setText("create a 1/1 white Ally creature token for each experience counter you have")));
|
||||
}
|
||||
|
||||
private AangAirbendingMaster(final AangAirbendingMaster card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AangAirbendingMaster copy() {
|
||||
return new AangAirbendingMaster(this);
|
||||
}
|
||||
}
|
||||
112
Mage.Sets/src/mage/cards/a/AangMasterOfElements.java
Normal file
112
Mage.Sets/src/mage/cards/a/AangMasterOfElements.java
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.CostImpl;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.dynamicvalue.common.StaticValue;
|
||||
import mage.abilities.effects.common.DamagePlayersEffect;
|
||||
import mage.abilities.effects.common.DoIfCostPaid;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.effects.common.GainLifeEffect;
|
||||
import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect;
|
||||
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.constants.TargetController;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AangMasterOfElements extends CardImpl {
|
||||
|
||||
private static final FilterCard filter = new FilterCard("spells");
|
||||
|
||||
public AangMasterOfElements(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.AVATAR);
|
||||
this.subtype.add(SubType.ALLY);
|
||||
this.power = new MageInt(6);
|
||||
this.toughness = new MageInt(6);
|
||||
this.nightCard = true;
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// Spells you cast cost {W}{U}{B}{R}{G} less to cast.
|
||||
this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(
|
||||
filter, new ManaCostsImpl<>("{W}{U}{B}{R}{G}"), StaticValue.get(1), true
|
||||
)));
|
||||
|
||||
// At the beginning of each upkeep, you may transform Aang, Master of Elements. If you do, you gain 4 life, draw four cards, put four +1/+1 counters on him, and he deals 4 damage to each opponent.
|
||||
this.addAbility(new BeginningOfUpkeepTriggeredAbility(
|
||||
TargetController.ANY,
|
||||
new DoIfCostPaid(new GainLifeEffect(4), new AangMasterOfElementsCost())
|
||||
.addEffect(new DrawCardSourceControllerEffect(4).concatBy(","))
|
||||
.addEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance(4))
|
||||
.setText(", put four +1/+1 counters on him"))
|
||||
.addEffect(new DamagePlayersEffect(4, TargetController.OPPONENT)
|
||||
.setText(", and he deals 4 damage to each opponent")),
|
||||
false
|
||||
));
|
||||
}
|
||||
|
||||
private AangMasterOfElements(final AangMasterOfElements card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AangMasterOfElements copy() {
|
||||
return new AangMasterOfElements(this);
|
||||
}
|
||||
}
|
||||
|
||||
class AangMasterOfElementsCost extends CostImpl {
|
||||
|
||||
AangMasterOfElementsCost() {
|
||||
super();
|
||||
text = "transform {this}";
|
||||
}
|
||||
|
||||
private AangMasterOfElementsCost(final AangMasterOfElementsCost cost) {
|
||||
super(cost);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AangMasterOfElementsCost copy() {
|
||||
return new AangMasterOfElementsCost(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
||||
return Optional
|
||||
.ofNullable(source.getSourcePermanentIfItStillExists(game))
|
||||
.filter(Card::isTransformable)
|
||||
.isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) {
|
||||
paid = Optional
|
||||
.ofNullable(source.getSourcePermanentIfItStillExists(game))
|
||||
.filter(permanent -> permanent.transform(source, game))
|
||||
.isPresent();
|
||||
return paid;
|
||||
}
|
||||
}
|
||||
70
Mage.Sets/src/mage/cards/a/AangTheLastAirbender.java
Normal file
70
Mage.Sets/src/mage/cards/a/AangTheLastAirbender.java
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.common.SpellCastControllerTriggeredAbility;
|
||||
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
|
||||
import mage.abilities.effects.keyword.AirbendTargetEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.abilities.keyword.LifelinkAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.FilterSpell;
|
||||
import mage.filter.common.FilterNonlandPermanent;
|
||||
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AangTheLastAirbender extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterNonlandPermanent("other target nonland permanent");
|
||||
private static final FilterSpell filter2 = new FilterSpell("a Lesson spell");
|
||||
|
||||
static {
|
||||
filter.add(AnotherPredicate.instance);
|
||||
filter2.add(SubType.LESSON.getPredicate());
|
||||
}
|
||||
|
||||
public AangTheLastAirbender(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.AVATAR);
|
||||
this.subtype.add(SubType.ALLY);
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(2);
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// When Aang enters, airbend up to one other target nonland permanent.
|
||||
Ability ability = new EntersBattlefieldTriggeredAbility(new AirbendTargetEffect());
|
||||
ability.addTarget(new TargetPermanent(0, 1, filter));
|
||||
this.addAbility(ability);
|
||||
|
||||
// Whenever you cast a Lesson spell, Aang gains lifelink until end of turn.
|
||||
this.addAbility(new SpellCastControllerTriggeredAbility(
|
||||
new GainAbilitySourceEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn), filter2, false
|
||||
));
|
||||
}
|
||||
|
||||
private AangTheLastAirbender(final AangTheLastAirbender card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AangTheLastAirbender copy() {
|
||||
return new AangTheLastAirbender(this);
|
||||
}
|
||||
}
|
||||
45
Mage.Sets/src/mage/cards/a/AangsDefense.java
Normal file
45
Mage.Sets/src/mage/cards/a/AangsDefense.java
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostTargetEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.predicate.permanent.BlockingPredicate;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AangsDefense extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterControlledCreaturePermanent("blocking creature you control");
|
||||
|
||||
static {
|
||||
filter.add(BlockingPredicate.instance);
|
||||
}
|
||||
|
||||
public AangsDefense(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}");
|
||||
|
||||
// Target blocking creature you control gets +2/+2 until end of turn.
|
||||
this.getSpellAbility().addEffect(new BoostTargetEffect(2, 2));
|
||||
this.getSpellAbility().addTarget(new TargetPermanent(filter));
|
||||
|
||||
// Draw a card.
|
||||
this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("<br>"));
|
||||
}
|
||||
|
||||
private AangsDefense(final AangsDefense card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AangsDefense copy() {
|
||||
return new AangsDefense(this);
|
||||
}
|
||||
}
|
||||
58
Mage.Sets/src/mage/cards/a/AangsIceberg.java
Normal file
58
Mage.Sets/src/mage/cards/a/AangsIceberg.java
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.common.SacrificeSourceCost;
|
||||
import mage.abilities.costs.common.WaterbendCost;
|
||||
import mage.abilities.effects.common.DoIfCostPaid;
|
||||
import mage.abilities.effects.common.ExileUntilSourceLeavesEffect;
|
||||
import mage.abilities.effects.keyword.ScryEffect;
|
||||
import mage.abilities.keyword.FlashAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterNonlandPermanent;
|
||||
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AangsIceberg extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterNonlandPermanent("other target nonland permanent");
|
||||
|
||||
static {
|
||||
filter.add(AnotherPredicate.instance);
|
||||
}
|
||||
|
||||
public AangsIceberg(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}");
|
||||
|
||||
// Flash
|
||||
this.addAbility(FlashAbility.getInstance());
|
||||
|
||||
// When this enchantment enters, exile up to one other target nonland permanent until this enchantment leaves the battlefield.
|
||||
Ability ability = new EntersBattlefieldTriggeredAbility(new ExileUntilSourceLeavesEffect());
|
||||
ability.addTarget(new TargetPermanent(0, 1, filter));
|
||||
this.addAbility(ability);
|
||||
|
||||
// Waterbend {3}: Sacrifice this enchantment. If you do, scry 2.
|
||||
this.addAbility(new SimpleActivatedAbility(new DoIfCostPaid(
|
||||
new ScryEffect(2), new SacrificeSourceCost(), null, false
|
||||
), new WaterbendCost(3)));
|
||||
}
|
||||
|
||||
private AangsIceberg(final AangsIceberg card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AangsIceberg copy() {
|
||||
return new AangsIceberg(this);
|
||||
}
|
||||
}
|
||||
64
Mage.Sets/src/mage/cards/a/AangsJourney.java
Normal file
64
Mage.Sets/src/mage/cards/a/AangsJourney.java
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.abilities.condition.common.KickedCondition;
|
||||
import mage.abilities.decorator.ConditionalOneShotEffect;
|
||||
import mage.abilities.effects.common.GainLifeEffect;
|
||||
import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect;
|
||||
import mage.abilities.keyword.KickerAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.predicate.Predicate;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.target.common.TargetCardAndOrCardInLibrary;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AangsJourney extends CardImpl {
|
||||
|
||||
private static final Predicate<Card> predicate = Predicates.and(
|
||||
SuperType.BASIC.getPredicate(),
|
||||
CardType.LAND.getPredicate()
|
||||
);
|
||||
|
||||
public AangsJourney(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}");
|
||||
|
||||
this.subtype.add(SubType.LESSON);
|
||||
|
||||
// Kicker {2}
|
||||
this.addAbility(new KickerAbility("{2}"));
|
||||
|
||||
// Search your library for a basic land card. If this spell was kicked, instead search your library for a basic land card and a Shrine card. Reveal those cards, put them into your hand, then shuffle.
|
||||
this.getSpellAbility().addEffect(new ConditionalOneShotEffect(
|
||||
new SearchLibraryPutInHandEffect(new TargetCardAndOrCardInLibrary(
|
||||
predicate, SubType.SHRINE.getPredicate(),
|
||||
"a basic land card and/or a Shrine card"
|
||||
), true),
|
||||
new SearchLibraryPutInHandEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true),
|
||||
KickedCondition.ONCE, "search your library for a basic land card. If this spell was kicked, " +
|
||||
"instead search your library for a basic land card and a Shrine card. " +
|
||||
"Reveal those cards, put them into your hand, then shuffle"
|
||||
));
|
||||
|
||||
// You gain 2 life.
|
||||
this.getSpellAbility().addEffect(new GainLifeEffect(2).concatBy("<br>"));
|
||||
}
|
||||
|
||||
private AangsJourney(final AangsJourney card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AangsJourney copy() {
|
||||
return new AangsJourney(this);
|
||||
}
|
||||
}
|
||||
37
Mage.Sets/src/mage/cards/a/AardvarkSloth.java
Normal file
37
Mage.Sets/src/mage/cards/a/AardvarkSloth.java
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.keyword.LifelinkAbility;
|
||||
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 AardvarkSloth extends CardImpl {
|
||||
|
||||
public AardvarkSloth(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}");
|
||||
|
||||
this.subtype.add(SubType.SLOTH);
|
||||
this.subtype.add(SubType.BEAST);
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(3);
|
||||
|
||||
// Lifelink
|
||||
this.addAbility(LifelinkAbility.getInstance());
|
||||
}
|
||||
|
||||
private AardvarkSloth(final AardvarkSloth card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AardvarkSloth copy() {
|
||||
return new AardvarkSloth(this);
|
||||
}
|
||||
}
|
||||
35
Mage.Sets/src/mage/cards/a/AbandonAttachments.java
Normal file
35
Mage.Sets/src/mage/cards/a/AbandonAttachments.java
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.abilities.costs.common.DiscardCardCost;
|
||||
import mage.abilities.effects.common.DoIfCostPaid;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
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 AbandonAttachments extends CardImpl {
|
||||
|
||||
public AbandonAttachments(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U/R}");
|
||||
|
||||
this.subtype.add(SubType.LESSON);
|
||||
|
||||
// You may discard a card. If you do, draw two cards.
|
||||
this.getSpellAbility().addEffect(new DoIfCostPaid(new DrawCardSourceControllerEffect(2), new DiscardCardCost()));
|
||||
}
|
||||
|
||||
private AbandonAttachments(final AbandonAttachments card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbandonAttachments copy() {
|
||||
return new AbandonAttachments(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -23,6 +23,8 @@ import java.util.UUID;
|
|||
*/
|
||||
public final class AccursedWitch extends CardImpl {
|
||||
|
||||
private static final FilterCard filter = new FilterCard("spells");
|
||||
|
||||
public AccursedWitch(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}");
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
|
|
@ -34,7 +36,7 @@ public final class AccursedWitch extends CardImpl {
|
|||
|
||||
// Spells your opponents cast that target Accursed Witch cost {1} less to cast.
|
||||
this.addAbility(new SimpleStaticAbility(
|
||||
new SpellsCostModificationThatTargetSourceEffect(-1, new FilterCard("Spells"), TargetController.OPPONENT))
|
||||
new SpellsCostModificationThatTargetSourceEffect(-1, filter, TargetController.OPPONENT))
|
||||
);
|
||||
|
||||
// When Accursed Witch dies, return it to the battlefield transformed under your control attached to target opponent.
|
||||
|
|
|
|||
|
|
@ -13,10 +13,8 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterBasicLandCard;
|
||||
import mage.filter.common.FilterLandPermanent;
|
||||
import mage.filter.predicate.permanent.DefendingPlayerControlsSourceAttackingPredicate;
|
||||
import mage.game.Controllable;
|
||||
|
|
@ -32,8 +30,6 @@ import java.util.stream.Collectors;
|
|||
*/
|
||||
public final class AerialSurveyor extends CardImpl {
|
||||
|
||||
private static final FilterCard filter = new FilterBasicLandCard(SubType.PLAINS);
|
||||
|
||||
public AerialSurveyor(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{W}");
|
||||
|
||||
|
|
@ -45,7 +41,7 @@ public final class AerialSurveyor extends CardImpl {
|
|||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// Whenever Aerial Surveyor attacks, if defending player controls more lands than you, search your library for a basic Plains card, put it onto the battlefield tapped, then shuffle.
|
||||
this.addAbility(new AttacksTriggeredAbility(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter), true))
|
||||
this.addAbility(new AttacksTriggeredAbility(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_PLAINS), true))
|
||||
.withInterveningIf(AerialSurveyorCondition.instance)
|
||||
.addHint(LandsYouControlHint.instance)
|
||||
.addHint(AerialSurveyorHint.instance));
|
||||
|
|
|
|||
66
Mage.Sets/src/mage/cards/a/AgentVenom.java
Normal file
66
Mage.Sets/src/mage/cards/a/AgentVenom.java
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.DiesCreatureTriggeredAbility;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.effects.common.LoseLifeSourceControllerEffect;
|
||||
import mage.abilities.keyword.FlashAbility;
|
||||
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.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||
import mage.filter.predicate.permanent.TokenPredicate;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AgentVenom extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterControlledCreaturePermanent("another nontoken creature you control");
|
||||
|
||||
static {
|
||||
filter.add(AnotherPredicate.instance);
|
||||
filter.add(TokenPredicate.FALSE);
|
||||
}
|
||||
|
||||
public AgentVenom(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.SYMBIOTE);
|
||||
this.subtype.add(SubType.SOLDIER);
|
||||
this.subtype.add(SubType.HERO);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(3);
|
||||
|
||||
// Flash
|
||||
this.addAbility(FlashAbility.getInstance());
|
||||
|
||||
// Menace
|
||||
this.addAbility(new MenaceAbility());
|
||||
|
||||
// Whenever another nontoken creature you control dies, you draw a card and lose 1 life.
|
||||
Ability ability = new DiesCreatureTriggeredAbility(
|
||||
new DrawCardSourceControllerEffect(1, true), false, filter
|
||||
);
|
||||
ability.addEffect(new LoseLifeSourceControllerEffect(1).setText("and lose 1 life"));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
private AgentVenom(final AgentVenom card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AgentVenom copy() {
|
||||
return new AgentVenom(this);
|
||||
}
|
||||
}
|
||||
39
Mage.Sets/src/mage/cards/a/AirbendingLesson.java
Normal file
39
Mage.Sets/src/mage/cards/a/AirbendingLesson.java
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.effects.keyword.AirbendTargetEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.target.common.TargetNonlandPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AirbendingLesson extends CardImpl {
|
||||
|
||||
public AirbendingLesson(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}");
|
||||
|
||||
this.subtype.add(SubType.LESSON);
|
||||
|
||||
// Airbend target nonland permanent.
|
||||
this.getSpellAbility().addEffect(new AirbendTargetEffect());
|
||||
this.getSpellAbility().addTarget(new TargetNonlandPermanent());
|
||||
|
||||
// Draw a card.
|
||||
this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("<br>"));
|
||||
}
|
||||
|
||||
private AirbendingLesson(final AirbendingLesson card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AirbendingLesson copy() {
|
||||
return new AirbendingLesson(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -77,7 +77,7 @@ class AjanisChosenEffect extends OneShotEffect {
|
|||
Permanent oldCreature = game.getPermanent(enchantment.getAttachedTo());
|
||||
if (oldCreature != null) {
|
||||
boolean canAttach = enchantment.getSpellAbility() == null
|
||||
|| (!enchantment.getSpellAbility().getTargets().isEmpty() && enchantment.getSpellAbility().getTargets().get(0).canTarget(tokenPermanent.getId(), game));
|
||||
|| (!enchantment.getSpellAbility().getTargets().isEmpty() && enchantment.getSpellAbility().getTargets().get(0).canTarget(tokenPermanent.getId(), source, game));
|
||||
if (canAttach && controller.chooseUse(Outcome.Neutral, "Attach " + enchantment.getName() + " to the token ?", source, game)) {
|
||||
if (oldCreature.removeAttachment(enchantment.getId(), source, game)) {
|
||||
tokenPermanent.addAttachment(enchantment.getId(), source, game);
|
||||
|
|
|
|||
101
Mage.Sets/src/mage/cards/a/AlienSymbiosis.java
Normal file
101
Mage.Sets/src/mage/cards/a/AlienSymbiosis.java
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageIdentifier;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.Costs;
|
||||
import mage.abilities.costs.CostsImpl;
|
||||
import mage.abilities.costs.common.DiscardCardCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.abilities.effects.common.AttachEffect;
|
||||
import mage.abilities.effects.common.continuous.AddCardSubtypeAttachedEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostEnchantedEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect;
|
||||
import mage.abilities.keyword.EnchantAbility;
|
||||
import mage.abilities.keyword.MenaceAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jmlundeen
|
||||
*/
|
||||
public final class AlienSymbiosis extends CardImpl {
|
||||
|
||||
public AlienSymbiosis(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}");
|
||||
|
||||
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));
|
||||
|
||||
// Enchanted creature gets +1/+1, has menace, and is a Symbiote in addition to its other types.
|
||||
Ability ability = new SimpleStaticAbility(new BoostEnchantedEffect(1, 1));
|
||||
ability.addEffect(new GainAbilityAttachedEffect(new MenaceAbility(), null).concatBy(","));
|
||||
ability.addEffect(new AddCardSubtypeAttachedEffect(SubType.SYMBIOTE, AttachmentType.AURA).concatBy(",").setText("and is a Symbiote in addition to its other types"));
|
||||
this.addAbility(ability);
|
||||
|
||||
// You may cast this card from your graveyard by discarding a card in addition to paying its other costs.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.GRAVEYARD, new AlienSymbiosisGraveyardEffect()));
|
||||
}
|
||||
|
||||
private AlienSymbiosis(final AlienSymbiosis card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlienSymbiosis copy() {
|
||||
return new AlienSymbiosis(this);
|
||||
}
|
||||
}
|
||||
class AlienSymbiosisGraveyardEffect extends AsThoughEffectImpl {
|
||||
|
||||
AlienSymbiosisGraveyardEffect() {
|
||||
super(AsThoughEffectType.CAST_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfGame, Outcome.PutCreatureInPlay);
|
||||
this.staticText = "You may cast this card from your graveyard by discarding a card in addition to paying its other costs.";
|
||||
}
|
||||
|
||||
private AlienSymbiosisGraveyardEffect(final AlienSymbiosisGraveyardEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlienSymbiosisGraveyardEffect copy() {
|
||||
return new AlienSymbiosisGraveyardEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
if (!objectId.equals(source.getSourceId()) || !source.isControlledBy(affectedControllerId)
|
||||
|| game.getState().getZone(source.getSourceId()) != Zone.GRAVEYARD) {
|
||||
return false;
|
||||
}
|
||||
Player controller = game.getPlayer(affectedControllerId);
|
||||
if (controller != null) {
|
||||
Costs<Cost> costs = new CostsImpl<>();
|
||||
costs.add(new DiscardCardCost());
|
||||
controller.setCastSourceIdWithAlternateMana(objectId, new ManaCostsImpl<>("{1}{B}"), costs,
|
||||
MageIdentifier.AlienSymbiosisAlternateCast);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,8 @@ import mage.filter.common.FilterCreaturePlayerOrPlaneswalker;
|
|||
import mage.filter.common.FilterPermanentOrPlayer;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetPermanentOrPlayer;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
@ -68,6 +70,16 @@ class AllWillBeOneTriggeredAbility extends TriggeredAbilityImpl {
|
|||
if (!isControlledBy(event.getPlayerId())) {
|
||||
return false;
|
||||
}
|
||||
Player player = game.getPlayer(event.getTargetId());
|
||||
if (player == null) {
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());
|
||||
if (permanent == null) {
|
||||
permanent = game.getPermanentEntering(event.getTargetId());
|
||||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
getEffects().setValue("damage", event.getAmount());
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
43
Mage.Sets/src/mage/cards/a/AlliedTeamwork.java
Normal file
43
Mage.Sets/src/mage/cards/a/AlliedTeamwork.java
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
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.SubType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.game.permanent.token.AllyToken;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AlliedTeamwork extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterCreaturePermanent(SubType.ALLY, "Allies");
|
||||
|
||||
public AlliedTeamwork(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}");
|
||||
|
||||
// When this enchantment enters, create a 1/1 white Ally creature token.
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new AllyToken())));
|
||||
|
||||
// Allies you control get +1/+1.
|
||||
this.addAbility(new SimpleStaticAbility(new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filter)));
|
||||
}
|
||||
|
||||
private AlliedTeamwork(final AlliedTeamwork card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlliedTeamwork copy() {
|
||||
return new AlliedTeamwork(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,6 @@ import mage.constants.SubType;
|
|||
import mage.constants.SuperType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterBySubtypeCard;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
|
||||
|
|
@ -24,7 +23,7 @@ import java.util.UUID;
|
|||
*/
|
||||
public final class AlpineGuide extends CardImpl {
|
||||
|
||||
private static final FilterCard filter = new FilterBySubtypeCard(SubType.MOUNTAIN);
|
||||
private static final FilterCard filter = new FilterCard(SubType.MOUNTAIN);
|
||||
private static final FilterPermanent filter2 = new FilterControlledPermanent(SubType.MOUNTAIN, "Mountain");
|
||||
|
||||
public AlpineGuide(UUID ownerId, CardSetInfo setInfo) {
|
||||
|
|
|
|||
|
|
@ -57,6 +57,12 @@ class AlpineMoonEffect extends ContinuousEffectImpl {
|
|||
this.staticText = "lands your opponents control with the chosen name "
|
||||
+ "lose all land types and abilities, "
|
||||
+ "and they gain \"{T}: Add one mana of any color.\"";
|
||||
addDependedToType(DependencyType.BecomeMountain);
|
||||
addDependedToType(DependencyType.BecomeForest);
|
||||
addDependedToType(DependencyType.BecomeIsland);
|
||||
addDependedToType(DependencyType.BecomeSwamp);
|
||||
addDependedToType(DependencyType.BecomePlains);
|
||||
addDependedToType(DependencyType.BecomeNonbasicLand);
|
||||
}
|
||||
|
||||
private AlpineMoonEffect(final AlpineMoonEffect effect) {
|
||||
|
|
@ -84,9 +90,6 @@ class AlpineMoonEffect extends ContinuousEffectImpl {
|
|||
for (Permanent land : game.getBattlefield().getActivePermanents(filter2, source.getControllerId(), game)) {
|
||||
switch (layer) {
|
||||
case TypeChangingEffects_4:
|
||||
// 305.7 Note that this doesn't remove any abilities that were granted to the land by other effects
|
||||
// So the ability removing has to be done before Layer 6
|
||||
land.removeAllAbilities(source.getSourceId(), game);
|
||||
land.removeAllSubTypes(game, SubTypeSet.NonBasicLandType);
|
||||
break;
|
||||
case AbilityAddingRemovingEffects_6:
|
||||
|
|
|
|||
47
Mage.Sets/src/mage/cards/a/AmazingAcrobatics.java
Normal file
47
Mage.Sets/src/mage/cards/a/AmazingAcrobatics.java
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.common.CounterTargetEffect;
|
||||
import mage.abilities.effects.common.TapTargetEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.TargetSpell;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jmlundeen
|
||||
*/
|
||||
public final class AmazingAcrobatics extends CardImpl {
|
||||
|
||||
public AmazingAcrobatics(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}{U}");
|
||||
|
||||
|
||||
// Choose one or both --
|
||||
this.getSpellAbility().getModes().setMinModes(1);
|
||||
this.getSpellAbility().getModes().setMaxModes(2);
|
||||
|
||||
// * Counter target spell.
|
||||
this.getSpellAbility().addEffect(new CounterTargetEffect());
|
||||
this.getSpellAbility().addTarget(new TargetSpell());
|
||||
|
||||
// * Tap one or two target creatures.
|
||||
Mode mode = new Mode(new TapTargetEffect());
|
||||
mode.addTarget(new TargetPermanent(1, 2, StaticFilters.FILTER_PERMANENT_CREATURES));
|
||||
this.getSpellAbility().getModes().addMode(mode);
|
||||
}
|
||||
|
||||
private AmazingAcrobatics(final AmazingAcrobatics card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AmazingAcrobatics copy() {
|
||||
return new AmazingAcrobatics(this);
|
||||
}
|
||||
}
|
||||
46
Mage.Sets/src/mage/cards/a/AmazingAlliance.java
Normal file
46
Mage.Sets/src/mage/cards/a/AmazingAlliance.java
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.abilities.common.AttacksWithCreaturesTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.dynamicvalue.common.EffectKeyValue;
|
||||
import mage.abilities.effects.common.GainLifeEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostControlledEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.filter.StaticFilters;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AmazingAlliance extends CardImpl {
|
||||
|
||||
private static final DynamicValue xValue = new EffectKeyValue(
|
||||
AttacksWithCreaturesTriggeredAbility.VALUEKEY_NUMBER_ATTACKERS, "that much"
|
||||
);
|
||||
|
||||
public AmazingAlliance(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}{W}");
|
||||
|
||||
// Creatures you control get +1/+1.
|
||||
this.addAbility(new SimpleStaticAbility(new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield)));
|
||||
|
||||
// Whenever you attack with one or more legendary creatures, you gain that much life.
|
||||
this.addAbility(new AttacksWithCreaturesTriggeredAbility(
|
||||
new GainLifeEffect(xValue), 1, StaticFilters.FILTER_CREATURES_LEGENDARY
|
||||
));
|
||||
}
|
||||
|
||||
private AmazingAlliance(final AmazingAlliance card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AmazingAlliance copy() {
|
||||
return new AmazingAlliance(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -14,8 +14,7 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.AbilityWord;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
@ -25,13 +24,6 @@ import java.util.UUID;
|
|||
*/
|
||||
public final class AmbitiousFarmhand extends CardImpl {
|
||||
|
||||
private static final FilterCard filter = new FilterCard("basic Plains card");
|
||||
|
||||
static {
|
||||
filter.add(SuperType.BASIC.getPredicate());
|
||||
filter.add(SubType.PLAINS.getPredicate());
|
||||
}
|
||||
|
||||
public AmbitiousFarmhand(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
|
||||
|
||||
|
|
@ -43,7 +35,7 @@ public final class AmbitiousFarmhand extends CardImpl {
|
|||
|
||||
// When Ambitious Farmhand enters the battlefield, you may search your library for a basic Plains card, reveal it, put it into your hand, then shuffle.
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(
|
||||
new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true), true
|
||||
new SearchLibraryPutInHandEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_PLAINS), true), true
|
||||
));
|
||||
|
||||
// Coven—{1}{W}{W}: Transform Ambitious Farmhand. Activate only if you control three or more creatures with different powers.
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.*;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.game.permanent.token.custom.CreatureToken;
|
||||
import mage.target.common.TargetControlledPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
@ -42,7 +41,11 @@ public final class AmbushCommander extends CardImpl {
|
|||
ContinuousEffect effect = new BecomesCreatureAllEffect(
|
||||
new CreatureToken(1, 1, "1/1 green Elf creatures").withColor("G").withSubType(SubType.ELF),
|
||||
"lands", filter2, Duration.WhileOnBattlefield, true);
|
||||
effect.getDependencyTypes().add(DependencyType.BecomeForest);
|
||||
effect.addDependedToType(DependencyType.BecomeForest);
|
||||
effect.addDependedToType(DependencyType.BecomeIsland);
|
||||
effect.addDependedToType(DependencyType.BecomeMountain);
|
||||
effect.addDependedToType(DependencyType.BecomePlains);
|
||||
effect.addDependedToType(DependencyType.BecomeSwamp);
|
||||
this.addAbility(new SimpleStaticAbility(effect));
|
||||
|
||||
// {1}{G}, Sacrifice an Elf: Target creature gets +3/+3 until end of turn.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.ObjectColor;
|
||||
|
|
@ -16,6 +15,8 @@ import mage.filter.predicate.mageobject.ColorPredicate;
|
|||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fireshoes
|
||||
|
|
@ -29,7 +30,7 @@ public final class AnHavvaConstable extends CardImpl {
|
|||
this.toughness = new MageInt(1);
|
||||
|
||||
// An-Havva Constable's toughness is equal to 1 plus the number of green creatures on the battlefield.
|
||||
this.addAbility(new SimpleStaticAbility(new AnHavvaConstableEffect()));
|
||||
this.addAbility(new SimpleStaticAbility(Zone.ALL, new AnHavvaConstableEffect()));
|
||||
}
|
||||
|
||||
private AnHavvaConstable(final AnHavvaConstable card) {
|
||||
|
|
@ -72,7 +73,7 @@ class AnHavvaConstableEffect extends ContinuousEffectImpl {
|
|||
|
||||
FilterCreaturePermanent filter = new FilterCreaturePermanent("green creatures");
|
||||
filter.add(new ColorPredicate(ObjectColor.GREEN));
|
||||
int numberOfGreenCreatures = game.getBattlefield().count(filter, source.getSourceId(), source, game);
|
||||
int numberOfGreenCreatures = game.getBattlefield().count(filter, source.getControllerId(), source, game);
|
||||
|
||||
mageObject.getToughness().setModifiedBaseValue(1 + numberOfGreenCreatures);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.PutIntoGraveFromAnywhereSourceAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.AttachEffect;
|
||||
import mage.abilities.effects.common.ExileSourceEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect;
|
||||
import mage.abilities.keyword.DisturbAbility;
|
||||
import mage.abilities.keyword.EnchantAbility;
|
||||
import mage.abilities.keyword.LifelinkAbility;
|
||||
import mage.cards.CardImpl;
|
||||
|
|
@ -32,8 +30,7 @@ public final class AncestorsEmbrace extends CardImpl {
|
|||
TargetPermanent auraTarget = new TargetCreaturePermanent();
|
||||
this.getSpellAbility().addTarget(auraTarget);
|
||||
this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature));
|
||||
Ability ability = new EnchantAbility(auraTarget);
|
||||
this.addAbility(ability);
|
||||
this.addAbility(new EnchantAbility(auraTarget));
|
||||
|
||||
// Enchanted creature has lifelink.
|
||||
this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect(
|
||||
|
|
@ -41,7 +38,7 @@ public final class AncestorsEmbrace extends CardImpl {
|
|||
)));
|
||||
|
||||
// If Ancestor's Embrace would be put into a graveyard from anywhere, exile it instead.
|
||||
this.addAbility(new PutIntoGraveFromAnywhereSourceAbility(new ExileSourceEffect().setText("exile it instead")));
|
||||
this.addAbility(DisturbAbility.makeBackAbility());
|
||||
}
|
||||
|
||||
private AncestorsEmbrace(final AncestorsEmbrace card) {
|
||||
|
|
|
|||
|
|
@ -108,8 +108,8 @@ class AncientBrassDragonTarget extends TargetCardInGraveyard {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
||||
return super.canTarget(controllerId, id, source, game)
|
||||
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||
return super.canTarget(playerId, id, source, game)
|
||||
&& CardUtil.checkCanTargetTotalValueLimit(
|
||||
this.getTargets(), id, MageObject::getManaValue, xValue, game);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ class AngelOfDestinyLoseEffect extends OneShotEffect {
|
|||
return false;
|
||||
}
|
||||
Set<UUID> playerSet = watcher.getPlayers(new MageObjectReference(
|
||||
source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game
|
||||
source.getSourceId(), source.getStackMomentSourceZCC(), game
|
||||
));
|
||||
if (playerSet == null) {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -1,23 +1,16 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.CantPayLifeOrSacrificeAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.common.SacrificeTargetCost;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.abilities.effects.common.continuous.BoostControlledEffect;
|
||||
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.filter.Filter;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -39,9 +32,7 @@ public final class AngelOfJubilation extends CardImpl {
|
|||
this.addAbility(new SimpleStaticAbility(new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, StaticFilters.FILTER_PERMANENT_CREATURES_NON_BLACK, true)));
|
||||
|
||||
// Players can't pay life or sacrifice creatures to cast spells or activate abilities.
|
||||
Ability ability = new SimpleStaticAbility(new AngelOfJubilationEffect());
|
||||
ability.addEffect(new AngelOfJubilationSacrificeFilterEffect());
|
||||
this.addAbility(ability);
|
||||
this.addAbility(new CantPayLifeOrSacrificeAbility(StaticFilters.FILTER_PERMANENT_CREATURES));
|
||||
}
|
||||
|
||||
private AngelOfJubilation(final AngelOfJubilation card) {
|
||||
|
|
@ -53,66 +44,3 @@ public final class AngelOfJubilation extends CardImpl {
|
|||
return new AngelOfJubilation(this);
|
||||
}
|
||||
}
|
||||
|
||||
class AngelOfJubilationEffect extends ContinuousEffectImpl {
|
||||
|
||||
AngelOfJubilationEffect() {
|
||||
super(Duration.WhileOnBattlefield, Layer.PlayerEffects, SubLayer.NA, Outcome.Detriment);
|
||||
staticText = "Players can't pay life or sacrifice creatures to cast spells";
|
||||
}
|
||||
|
||||
private AngelOfJubilationEffect(final AngelOfJubilationEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AngelOfJubilationEffect copy() {
|
||||
return new AngelOfJubilationEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
player.setPayLifeCostLevel(Player.PayLifeCostLevel.nonSpellnonActivatedAbilities);
|
||||
player.setCanPaySacrificeCostFilter(new FilterCreaturePermanent());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class AngelOfJubilationSacrificeFilterEffect extends CostModificationEffectImpl {
|
||||
|
||||
AngelOfJubilationSacrificeFilterEffect() {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Detriment, CostModificationType.SET_COST);
|
||||
staticText = "or activate abilities";
|
||||
}
|
||||
|
||||
protected AngelOfJubilationSacrificeFilterEffect(AngelOfJubilationSacrificeFilterEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source, Ability abilityToModify) {
|
||||
for (Cost cost : abilityToModify.getCosts()) {
|
||||
if (cost instanceof SacrificeTargetCost) {
|
||||
SacrificeTargetCost sacrificeCost = (SacrificeTargetCost) cost;
|
||||
Filter filter = sacrificeCost.getTargets().get(0).getFilter();
|
||||
filter.add(Predicates.not(CardType.CREATURE.getPredicate()));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(Ability abilityToModify, Ability source, Game game) {
|
||||
return (abilityToModify.isActivatedAbility() || abilityToModify.getAbilityType() == AbilityType.SPELL)
|
||||
&& game.getState().getPlayersInRange(source.getControllerId(), game).contains(abilityToModify.getControllerId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AngelOfJubilationSacrificeFilterEffect copy() {
|
||||
return new AngelOfJubilationSacrificeFilterEffect(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
89
Mage.Sets/src/mage/cards/a/AntiVenomHorrifyingHealer.java
Normal file
89
Mage.Sets/src/mage/cards/a/AntiVenomHorrifyingHealer.java
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.condition.common.CastFromEverywhereSourceCondition;
|
||||
import mage.abilities.effects.PreventionEffectImpl;
|
||||
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.common.TargetCardInYourGraveyard;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AntiVenomHorrifyingHealer extends CardImpl {
|
||||
|
||||
public AntiVenomHorrifyingHealer(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{W}{W}{W}{W}");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.SYMBIOTE);
|
||||
this.subtype.add(SubType.HERO);
|
||||
this.power = new MageInt(5);
|
||||
this.toughness = new MageInt(5);
|
||||
|
||||
// When Anti-Venom enters, if he was cast, return target creature card from your graveyard to the battlefield.
|
||||
Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect())
|
||||
.withInterveningIf(CastFromEverywhereSourceCondition.instance);
|
||||
ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD));
|
||||
this.addAbility(ability);
|
||||
|
||||
// If damage would be dealt to Anti-Venom, prevent that damage and put that many +1/+1 counters on him.
|
||||
this.addAbility(new SimpleStaticAbility(new AntiVenomHorrifyingHealerEffect()));
|
||||
}
|
||||
|
||||
private AntiVenomHorrifyingHealer(final AntiVenomHorrifyingHealer card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AntiVenomHorrifyingHealer copy() {
|
||||
return new AntiVenomHorrifyingHealer(this);
|
||||
}
|
||||
}
|
||||
|
||||
class AntiVenomHorrifyingHealerEffect extends PreventionEffectImpl {
|
||||
|
||||
AntiVenomHorrifyingHealerEffect() {
|
||||
super(Duration.WhileOnBattlefield, Integer.MAX_VALUE, false, false);
|
||||
staticText = "if damage would be dealt to {this}, prevent that damage and put that many +1/+1 counters on him";
|
||||
}
|
||||
|
||||
private AntiVenomHorrifyingHealerEffect(final AntiVenomHorrifyingHealerEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AntiVenomHorrifyingHealerEffect copy() {
|
||||
return new AntiVenomHorrifyingHealerEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
return super.applies(event, source, game)
|
||||
&& event.getTargetId().equals(source.getSourceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||
if (permanent != null) {
|
||||
permanent.addCounters(CounterType.P1P1.createInstance(event.getAmount()), source.getControllerId(), source, game);
|
||||
}
|
||||
return super.replaceEvent(event, source, game);
|
||||
}
|
||||
}
|
||||
|
|
@ -84,7 +84,7 @@ class AnuridScavengerCost extends CostImpl {
|
|||
|
||||
@Override
|
||||
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
||||
return this.getTargets().canChoose(controllerId, source, game);
|
||||
return canChooseOrAlreadyChosen(ability, source, controllerId, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -134,8 +134,8 @@ class AoTheDawnSkyTarget extends TargetCardInLibrary {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
||||
return super.canTarget(controllerId, id, source, game)
|
||||
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||
return super.canTarget(playerId, id, source, game)
|
||||
&& CardUtil.checkCanTargetTotalValueLimit(
|
||||
this.getTargets(), id, MageObject::getManaValue, 4, game);
|
||||
}
|
||||
|
|
|
|||
62
Mage.Sets/src/mage/cards/a/AppaAangsCompanion.java
Normal file
62
Mage.Sets/src/mage/cards/a/AppaAangsCompanion.java
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.AttacksTriggeredAbility;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
|
||||
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.FilterPermanent;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.AbilityPredicate;
|
||||
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||
import mage.filter.predicate.permanent.AttackingPredicate;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AppaAangsCompanion extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterControlledCreaturePermanent("another target attacking creature without flying");
|
||||
|
||||
static {
|
||||
filter.add(AnotherPredicate.instance);
|
||||
filter.add(AttackingPredicate.instance);
|
||||
filter.add(Predicates.not(new AbilityPredicate(FlyingAbility.class)));
|
||||
}
|
||||
|
||||
public AppaAangsCompanion(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.BISON);
|
||||
this.subtype.add(SubType.ALLY);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// Whenever Appa attacks, another target attacking creature without flying gains flying until until end of turn.
|
||||
Ability ability = new AttacksTriggeredAbility(new GainAbilityTargetEffect(FlyingAbility.getInstance()));
|
||||
ability.addTarget(new TargetPermanent(filter));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
private AppaAangsCompanion(final AppaAangsCompanion card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppaAangsCompanion copy() {
|
||||
return new AppaAangsCompanion(this);
|
||||
}
|
||||
}
|
||||
70
Mage.Sets/src/mage/cards/a/AppaSteadfastGuardian.java
Normal file
70
Mage.Sets/src/mage/cards/a/AppaSteadfastGuardian.java
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.common.SpellCastControllerTriggeredAbility;
|
||||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
import mage.abilities.effects.keyword.AirbendTargetEffect;
|
||||
import mage.abilities.keyword.FlashAbility;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterNonlandPermanent;
|
||||
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||
import mage.game.permanent.token.AllyToken;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AppaSteadfastGuardian extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterNonlandPermanent("other target nonland permanents you control");
|
||||
|
||||
static {
|
||||
filter.add(AnotherPredicate.instance);
|
||||
filter.add(TargetController.YOU.getControllerPredicate());
|
||||
}
|
||||
|
||||
public AppaSteadfastGuardian(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.BISON);
|
||||
this.subtype.add(SubType.ALLY);
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
// Flash
|
||||
this.addAbility(FlashAbility.getInstance());
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// When Appa enters, airbend any number of other target nonland permanents you control.
|
||||
Ability ability = new EntersBattlefieldTriggeredAbility(new AirbendTargetEffect());
|
||||
ability.addTarget(new TargetPermanent(0, Integer.MAX_VALUE, filter));
|
||||
this.addAbility(ability);
|
||||
|
||||
// Whenever you cast a spell from exile, create a 1/1 white Ally creature token.
|
||||
this.addAbility(new SpellCastControllerTriggeredAbility(
|
||||
Zone.BATTLEFIELD, new CreateTokenEffect(new AllyToken()), StaticFilters.FILTER_SPELL_A,
|
||||
false, SetTargetPointer.NONE, Zone.EXILED
|
||||
));
|
||||
}
|
||||
|
||||
private AppaSteadfastGuardian(final AppaSteadfastGuardian card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppaSteadfastGuardian copy() {
|
||||
return new AppaSteadfastGuardian(this);
|
||||
}
|
||||
}
|
||||
70
Mage.Sets/src/mage/cards/a/ArachnePsionicWeaver.java
Normal file
70
Mage.Sets/src/mage/cards/a/ArachnePsionicWeaver.java
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.AsEntersBattlefieldAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.ChooseCardTypeEffect;
|
||||
import mage.abilities.effects.common.LookAtTargetPlayerHandEffect;
|
||||
import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect;
|
||||
import mage.abilities.keyword.WebSlingingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.mageobject.ChosenCardTypePredicate;
|
||||
import mage.target.common.TargetOpponent;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jmlundeen
|
||||
*/
|
||||
public final class ArachnePsionicWeaver extends CardImpl {
|
||||
|
||||
private static final FilterCard filter = new FilterCard("spells of the chosen type");
|
||||
|
||||
static {
|
||||
filter.add(ChosenCardTypePredicate.TRUE);
|
||||
}
|
||||
|
||||
public ArachnePsionicWeaver(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.SPIDER);
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.HERO);
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(3);
|
||||
|
||||
// Web-slinging {W}
|
||||
this.addAbility(new WebSlingingAbility(this, "{W}"));
|
||||
|
||||
// As Arachne enters, look at target opponent's hand, then choose a noncreature card type.
|
||||
List<CardType> types = Arrays.stream(CardType.values()).filter(cardType -> cardType != CardType.CREATURE)
|
||||
.collect(Collectors.toList());
|
||||
Ability ability = new AsEntersBattlefieldAbility(new LookAtTargetPlayerHandEffect());
|
||||
ability.addEffect(new ChooseCardTypeEffect(Outcome.Benefit, types)
|
||||
.setText("choose a noncreature card type")
|
||||
.concatBy(", then"));
|
||||
ability.addTarget(new TargetOpponent());
|
||||
this.addAbility(ability);
|
||||
|
||||
// Spells of the chosen type cost {1} more to cast.
|
||||
this.addAbility(new SimpleStaticAbility(new SpellsCostIncreasingAllEffect(1, filter, TargetController.ANY)));
|
||||
}
|
||||
|
||||
private ArachnePsionicWeaver(final ArachnePsionicWeaver card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArachnePsionicWeaver copy() {
|
||||
return new ArachnePsionicWeaver(this);
|
||||
}
|
||||
}
|
||||
63
Mage.Sets/src/mage/cards/a/AranaHeartOfTheSpider.java
Normal file
63
Mage.Sets/src/mage/cards/a/AranaHeartOfTheSpider.java
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.AttacksWithCreaturesTriggeredAbility;
|
||||
import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility;
|
||||
import mage.abilities.effects.common.ExileTopXMayPlayUntilEffect;
|
||||
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.predicate.permanent.ModifiedPredicate;
|
||||
import mage.target.common.TargetAttackingCreature;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AranaHeartOfTheSpider extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterControlledCreaturePermanent("modified creature you control");
|
||||
|
||||
static {
|
||||
filter.add(ModifiedPredicate.instance);
|
||||
}
|
||||
|
||||
public AranaHeartOfTheSpider(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{W}");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.SPIDER);
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.HERO);
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(3);
|
||||
|
||||
// Whenever you attack, put a +1/+1 counter on target attacking creature.
|
||||
Ability ability = new AttacksWithCreaturesTriggeredAbility(
|
||||
new AddCountersTargetEffect(CounterType.P1P1.createInstance()), 1
|
||||
);
|
||||
ability.addTarget(new TargetAttackingCreature());
|
||||
this.addAbility(ability);
|
||||
|
||||
// Whenever a modified creature you control deals combat damage to a player, exile the top card of your library. You may play that card this turn.
|
||||
this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility(
|
||||
new ExileTopXMayPlayUntilEffect(1, Duration.EndOfTurn),
|
||||
filter, false, SetTargetPointer.NONE, true
|
||||
));
|
||||
}
|
||||
|
||||
private AranaHeartOfTheSpider(final AranaHeartOfTheSpider card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AranaHeartOfTheSpider copy() {
|
||||
return new AranaHeartOfTheSpider(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -9,10 +9,7 @@ import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect;
|
|||
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.FilterCard;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterLandPermanent;
|
||||
|
|
@ -26,13 +23,10 @@ import java.util.UUID;
|
|||
*/
|
||||
public final class ArchaeomancersMap extends CardImpl {
|
||||
|
||||
private static final FilterCard filter = new FilterCard("basic Plains cards");
|
||||
private static final FilterPermanent filter2 = new FilterLandPermanent("a land an opponent controls");
|
||||
private static final FilterPermanent filter = new FilterLandPermanent("a land an opponent controls");
|
||||
|
||||
static {
|
||||
filter.add(SubType.PLAINS.getPredicate());
|
||||
filter.add(SuperType.BASIC.getPredicate());
|
||||
filter2.add(TargetController.OPPONENT.getControllerPredicate());
|
||||
filter.add(TargetController.OPPONENT.getControllerPredicate());
|
||||
}
|
||||
|
||||
public ArchaeomancersMap(UUID ownerId, CardSetInfo setInfo) {
|
||||
|
|
@ -40,12 +34,12 @@ public final class ArchaeomancersMap extends CardImpl {
|
|||
|
||||
// When Archaeomancer's Map enters the battlefield, search your library for up to two basic Plains cards, reveal them, put them into your hand, then shuffle.
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(
|
||||
new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 2, filter), true)
|
||||
new SearchLibraryPutInHandEffect(new TargetCardInLibrary(0, 2, StaticFilters.FILTER_CARD_BASIC_PLAINS), true)
|
||||
));
|
||||
|
||||
// Whenever a land enters the battlefield under an opponent's control, if that player controls more lands than you, you may put a land card from your hand onto the battlefield.
|
||||
this.addAbility(new EntersBattlefieldAllTriggeredAbility(
|
||||
new PutCardFromHandOntoBattlefieldEffect(StaticFilters.FILTER_CARD_LAND_A), filter2
|
||||
new PutCardFromHandOntoBattlefieldEffect(StaticFilters.FILTER_CARD_LAND_A), filter
|
||||
).withInterveningIf(ArchaeomancersMapCondition.instance));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,18 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.costs.common.SacrificeTargetCost;
|
||||
import mage.abilities.effects.common.DamageControllerEffect;
|
||||
import mage.abilities.effects.common.DoIfCostPaid;
|
||||
import mage.abilities.effects.common.TapSourceEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.abilities.keyword.TrampleAbility;
|
||||
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetSacrifice;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -24,11 +21,7 @@ import java.util.UUID;
|
|||
*/
|
||||
public final class ArchdemonOfGreed extends CardImpl {
|
||||
|
||||
private static final FilterControlledPermanent filter = new FilterControlledPermanent("Human");
|
||||
|
||||
static {
|
||||
filter.add(SubType.HUMAN.getPredicate());
|
||||
}
|
||||
private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.HUMAN, "Human");
|
||||
|
||||
public ArchdemonOfGreed(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "");
|
||||
|
|
@ -44,7 +37,10 @@ public final class ArchdemonOfGreed extends CardImpl {
|
|||
this.addAbility(TrampleAbility.getInstance());
|
||||
|
||||
// At the beginning of your upkeep, sacrifice a Human. If you can't, tap Archdemon of Greed and it deals 9 damage to you.
|
||||
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new ArchdemonOfGreedEffect()));
|
||||
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new DoIfCostPaid(
|
||||
null, new TapSourceEffect(), new SacrificeTargetCost(filter), false
|
||||
).addOtherwiseEffect(new DamageControllerEffect(9))
|
||||
.setText("sacrifice a Human. If you can't, tap {this} and it deals 9 damage to you")));
|
||||
}
|
||||
|
||||
private ArchdemonOfGreed(final ArchdemonOfGreed card) {
|
||||
|
|
@ -55,48 +51,4 @@ public final class ArchdemonOfGreed extends CardImpl {
|
|||
public ArchdemonOfGreed copy() {
|
||||
return new ArchdemonOfGreed(this);
|
||||
}
|
||||
|
||||
static class ArchdemonOfGreedEffect extends OneShotEffect {
|
||||
|
||||
public ArchdemonOfGreedEffect() {
|
||||
super(Outcome.Damage);
|
||||
this.staticText = "sacrifice a Human. If you can't, tap {this} and it deals 9 damage to you.";
|
||||
}
|
||||
|
||||
private ArchdemonOfGreedEffect(final ArchdemonOfGreedEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArchdemonOfGreedEffect copy() {
|
||||
return new ArchdemonOfGreedEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = game.getPermanent(source.getSourceId());
|
||||
|
||||
if (permanent != null) {
|
||||
// create cost for sacrificing a human
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player != null) {
|
||||
TargetSacrifice target = new TargetSacrifice(filter);
|
||||
// if they can pay the cost, then they must pay
|
||||
if (target.canChoose(player.getId(), source, game)) {
|
||||
player.choose(Outcome.Sacrifice, target, source, game);
|
||||
Permanent humanSacrifice = game.getPermanent(target.getFirstTarget());
|
||||
if (humanSacrifice != null) {
|
||||
// sacrifice the chosen card
|
||||
return humanSacrifice.sacrifice(source, game);
|
||||
}
|
||||
} else {
|
||||
permanent.tap(source, game);
|
||||
player.damage(9, source.getSourceId(), source, game);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,8 @@ package mage.cards.a;
|
|||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.AttacksTriggeredAbility;
|
||||
import mage.abilities.common.PutIntoGraveFromAnywhereSourceAbility;
|
||||
import mage.abilities.effects.common.DrawDiscardControllerEffect;
|
||||
import mage.abilities.effects.common.ExileSourceEffect;
|
||||
import mage.abilities.keyword.DisturbAbility;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
|
|
@ -35,7 +34,7 @@ public final class ArchiveHaunt extends CardImpl {
|
|||
this.addAbility(new AttacksTriggeredAbility(new DrawDiscardControllerEffect(1, 1)));
|
||||
|
||||
// If Archive Haunt would be put into a graveyard from anywhere, exile it instead.
|
||||
this.addAbility(new PutIntoGraveFromAnywhereSourceAbility(new ExileSourceEffect().setText("exile it instead")));
|
||||
this.addAbility(DisturbAbility.makeBackAbility());
|
||||
}
|
||||
|
||||
private ArchiveHaunt(final ArchiveHaunt card) {
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ class ArdentDustspeakerCost extends CostImpl {
|
|||
|
||||
@Override
|
||||
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
||||
return this.getTargets().canChoose(controllerId, source, game);
|
||||
return canChooseOrAlreadyChosen(ability, source, controllerId, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.LoyaltyAbility;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.common.DamageTargetEffect;
|
||||
import mage.abilities.effects.common.GetEmblemEffect;
|
||||
import mage.abilities.effects.common.TransformSourceEffect;
|
||||
|
|
@ -21,8 +19,9 @@ import mage.filter.StaticFilters;
|
|||
import mage.game.command.emblems.ArlinnEmbracedByTheMoonEmblem;
|
||||
import mage.target.common.TargetAnyTarget;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fireshoes
|
||||
*/
|
||||
public final class ArlinnEmbracedByTheMoon extends CardImpl {
|
||||
|
|
@ -37,12 +36,14 @@ public final class ArlinnEmbracedByTheMoon extends CardImpl {
|
|||
this.nightCard = true;
|
||||
|
||||
// +1: Creatures you control get +1/+1 and gain trample until end of turn.
|
||||
Effect effect = new BoostControlledEffect(1, 1, Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURE);
|
||||
effect.setText("Creatures you control get +1/+1");
|
||||
LoyaltyAbility ability = new LoyaltyAbility(effect, 1);
|
||||
effect = new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURE);
|
||||
effect.setText("and gain trample until end of turn");
|
||||
ability.addEffect(effect);
|
||||
Ability ability = new LoyaltyAbility(new BoostControlledEffect(
|
||||
1, 1, Duration.EndOfTurn,
|
||||
StaticFilters.FILTER_PERMANENT_CREATURE
|
||||
).setText("Creatures you control get +1/+1"), 1);
|
||||
ability.addEffect(new GainAbilityControlledEffect(
|
||||
TrampleAbility.getInstance(), Duration.EndOfTurn,
|
||||
StaticFilters.FILTER_PERMANENT_CREATURE
|
||||
).setText("and gain trample until end of turn"));
|
||||
this.addAbility(ability);
|
||||
|
||||
// -1: Arlinn, Embraced by the Moon deals 3 damage to any target. Transform Arlinn, Embraced by the Moon.
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.LoyaltyAbility;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
import mage.abilities.effects.common.TransformSourceEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostTargetEffect;
|
||||
|
|
@ -20,14 +18,15 @@ import mage.constants.SuperType;
|
|||
import mage.game.permanent.token.WolfToken;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fireshoes
|
||||
*/
|
||||
public final class ArlinnKord extends CardImpl {
|
||||
|
||||
public ArlinnKord(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.PLANESWALKER},"{2}{R}{G}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{R}{G}");
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.ARLINN);
|
||||
|
||||
|
|
@ -36,15 +35,15 @@ public final class ArlinnKord extends CardImpl {
|
|||
this.setStartingLoyalty(3);
|
||||
|
||||
// +1: Until end of turn, up to one target creature gets +2/+2 and gains vigilance and haste.
|
||||
Effect effect = new BoostTargetEffect(2, 2, Duration.EndOfTurn);
|
||||
effect.setText("Until end of turn, up to one target creature gets +2/+2");
|
||||
LoyaltyAbility ability = new LoyaltyAbility(effect, 1);
|
||||
effect = new GainAbilityTargetEffect(VigilanceAbility.getInstance(), Duration.EndOfTurn);
|
||||
effect.setText("and gains vigilance");
|
||||
ability.addEffect(effect);
|
||||
effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn);
|
||||
effect.setText("and haste");
|
||||
ability.addEffect(effect);
|
||||
Ability ability = new LoyaltyAbility(new BoostTargetEffect(
|
||||
2, 2, Duration.EndOfTurn
|
||||
).setText("until end of turn, up to one target creature gets +2/+2"), 1);
|
||||
ability.addEffect(new GainAbilityTargetEffect(
|
||||
VigilanceAbility.getInstance(), Duration.EndOfTurn
|
||||
).setText("and gains vigilance"));
|
||||
ability.addEffect(new GainAbilityTargetEffect(
|
||||
HasteAbility.getInstance(), Duration.EndOfTurn
|
||||
).setText("and haste"));
|
||||
ability.addTarget(new TargetCreaturePermanent(0, 1));
|
||||
this.addAbility(ability);
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.AttachmentType;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.common.FilterBySubtypeCard;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.game.Game;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
|
|
@ -30,6 +30,8 @@ import java.util.UUID;
|
|||
*/
|
||||
public final class ArmMountedAnchor extends CardImpl {
|
||||
|
||||
private static final FilterCard filter = new FilterCard(SubType.PIRATE);
|
||||
|
||||
public ArmMountedAnchor(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
|
||||
this.subtype.add(SubType.EQUIPMENT);
|
||||
|
|
@ -42,11 +44,12 @@ public final class ArmMountedAnchor extends CardImpl {
|
|||
this.addAbility(firstAbility);
|
||||
|
||||
// Whenever equipped creature deals combat damage to a player, draw two cards. Then discard two cards unless you discard a Pirate card.
|
||||
Ability drawAbility = new DealsDamageToAPlayerAttachedTriggeredAbility(new DrawCardSourceControllerEffect(2), "equipped creature", false);
|
||||
DiscardCardCost cost = new DiscardCardCost(new FilterBySubtypeCard(SubType.PIRATE));
|
||||
cost.setText("Discard a Pirate card instead of discarding two cards");
|
||||
Ability drawAbility = new DealsDamageToAPlayerAttachedTriggeredAbility(
|
||||
new DrawCardSourceControllerEffect(2), "equipped creature", false
|
||||
);
|
||||
drawAbility.addEffect(new DoIfCostPaid(
|
||||
null, new DiscardControllerEffect(2), cost
|
||||
null, new DiscardControllerEffect(2),
|
||||
new DiscardCardCost(filter).setText("Discard a Pirate card instead of discarding two cards")
|
||||
).setText("Then discard two cards unless you discard a Pirate card"));
|
||||
this.addAbility(drawAbility);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.AttachEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect;
|
||||
|
|
@ -9,11 +8,16 @@ import mage.abilities.keyword.EnchantAbility;
|
|||
import mage.abilities.keyword.ProtectionAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.constants.AttachmentType;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.common.FilterArtifactCard;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author MarcoMarin
|
||||
|
|
@ -33,7 +37,7 @@ public final class ArtifactWard extends CardImpl {
|
|||
// Enchanted creature can't be blocked by artifact creatures.
|
||||
// Prevent all damage that would be dealt to enchanted creature by artifact sources.
|
||||
// Enchanted creature can't be the target of abilities from artifact sources.
|
||||
this.addAbility(new SimpleStaticAbility(
|
||||
this.addAbility(new SimpleStaticAbility( // TODO: Implement as separate abilities, this isn't quite the same as "Enchanted creature gains protection from artifacts"
|
||||
new GainAbilityAttachedEffect(new ProtectionAbility(new FilterArtifactCard("artifacts")), AttachmentType.AURA)));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ class AshayaSoulOfTheWildEffect extends ContinuousEffectImpl {
|
|||
staticText = "Nontoken creatures you control are Forest lands in addition to their other types";
|
||||
this.dependendToTypes.add(DependencyType.BecomeCreature);
|
||||
this.dependencyTypes.add(DependencyType.BecomeForest);
|
||||
this.dependencyTypes.add(DependencyType.BecomeNonbasicLand);
|
||||
}
|
||||
|
||||
private AshayaSoulOfTheWildEffect(final AshayaSoulOfTheWildEffect effect) {
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ class AshioksErasureExileEffect extends OneShotEffect {
|
|||
|| spell == null) {
|
||||
return false;
|
||||
}
|
||||
UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
|
||||
UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC());
|
||||
return controller.moveCardsToExile(spell, source, game, true, exileId, sourceObject.getIdName());
|
||||
}
|
||||
}
|
||||
|
|
@ -126,7 +126,7 @@ class AshioksErasureReplacementEffect extends ContinuousRuleModifyingEffectImpl
|
|||
|| card == null) {
|
||||
return false;
|
||||
}
|
||||
UUID exileZone = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
|
||||
UUID exileZone = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC());
|
||||
|
||||
ExileZone exile = game.getExile().getExileZone(exileZone);
|
||||
if (exile == null) {
|
||||
|
|
|
|||
|
|
@ -1,44 +1,40 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.mana.GenericManaCost;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.common.continuous.BoostEquippedEffect;
|
||||
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.target.common.TargetControlledCreaturePermanent;
|
||||
import mage.constants.AttachmentType;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fireshoes
|
||||
*/
|
||||
public final class AshmouthBlade extends CardImpl {
|
||||
|
||||
public AshmouthBlade(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "");
|
||||
this.subtype.add(SubType.EQUIPMENT);
|
||||
|
||||
// this card is the second face of double-faced card
|
||||
this.nightCard = true;
|
||||
|
||||
// Equipped creature gets +3/+3
|
||||
// Equipped creature gets +3/+3 and has first strike.
|
||||
Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(3, 3));
|
||||
ability.addEffect(new GainAbilityAttachedEffect(
|
||||
FirstStrikeAbility.getInstance(), AttachmentType.EQUIPMENT
|
||||
).setText("and has first strike"));
|
||||
this.addAbility(ability);
|
||||
|
||||
// and has first strike.
|
||||
Effect effect = new GainAbilityAttachedEffect(FirstStrikeAbility.getInstance(), AttachmentType.EQUIPMENT);
|
||||
effect.setText("and has first strike");
|
||||
ability.addEffect(effect);
|
||||
|
||||
// Equip {3}
|
||||
this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(3), new TargetControlledCreaturePermanent(), false));
|
||||
this.addAbility(new EquipAbility(3, false));
|
||||
}
|
||||
|
||||
private AshmouthBlade(final AshmouthBlade card) {
|
||||
|
|
|
|||
|
|
@ -68,10 +68,7 @@ class AstralDriftTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (game.getState().getStack().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
StackObject item = game.getState().getStack().getFirst();
|
||||
StackObject item = game.getState().getStack().getFirstOrNull();
|
||||
if (!(item instanceof StackAbility
|
||||
&& item.getStackAbility() instanceof CyclingAbility)) {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ class AuratouchedMageEffect extends OneShotEffect {
|
|||
Card aura = game.getCard(target.getFirstTarget());
|
||||
Permanent auratouchedMage = source.getSourcePermanentIfItStillExists(game);
|
||||
if (aura != null && auratouchedMage != null
|
||||
&& game.getState().getZoneChangeCounter(source.getSourceId()) == source.getSourceObjectZoneChangeCounter()) {
|
||||
&& game.getState().getZoneChangeCounter(source.getSourceId()) == source.getStackMomentSourceZCC()) {
|
||||
game.getState().setValue("attachTo:" + aura.getId(), auratouchedMage);
|
||||
if (controller.moveCards(aura, Zone.BATTLEFIELD, source, game)) {
|
||||
auratouchedMage.addAttachment(aura.getId(), source, game);
|
||||
|
|
|
|||
|
|
@ -1,22 +1,21 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.dynamicvalue.common.CardTypesInGraveyardCount;
|
||||
import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.condition.common.DeliriumCondition;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.dynamicvalue.common.CardTypesInGraveyardCount;
|
||||
import mage.abilities.effects.common.MillCardsControllerEffect;
|
||||
import mage.abilities.effects.common.TransformSourceEffect;
|
||||
import mage.abilities.keyword.TransformAbility;
|
||||
import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.AbilityWord;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.TargetController;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
|
@ -31,10 +30,9 @@ public final class AutumnalGloom extends CardImpl {
|
|||
|
||||
// <i>Delirium</i> — At the beginning of your end step, if there are four or more card types among cards in your graveyard, transform Autumnal Gloom.
|
||||
this.addAbility(new TransformAbility());
|
||||
Ability ability = new BeginningOfEndStepTriggeredAbility(TargetController.YOU, new TransformSourceEffect(), false, DeliriumCondition.instance);
|
||||
ability.setAbilityWord(AbilityWord.DELIRIUM);
|
||||
ability.addHint(CardTypesInGraveyardCount.YOU.getHint());
|
||||
this.addAbility(ability);
|
||||
this.addAbility(new BeginningOfEndStepTriggeredAbility(
|
||||
TargetController.YOU, new TransformSourceEffect(), false, DeliriumCondition.instance
|
||||
).setAbilityWord(AbilityWord.DELIRIUM).addHint(CardTypesInGraveyardCount.YOU.getHint()));
|
||||
}
|
||||
|
||||
private AutumnalGloom(final AutumnalGloom card) {
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ package mage.cards.a;
|
|||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.triggers.BeginningOfCombatTriggeredAbility;
|
||||
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
|
||||
import mage.abilities.keyword.DayboundAbility;
|
||||
import mage.abilities.keyword.HexproofAbility;
|
||||
import mage.abilities.triggers.BeginningOfCombatTriggeredAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
|
|
@ -35,9 +35,7 @@ public final class AvabruckCaretaker extends CardImpl {
|
|||
|
||||
// At the beginning of combat on your turn, put two +1/+1 counters on another target creature you control.
|
||||
Ability ability = new BeginningOfCombatTriggeredAbility(
|
||||
new AddCountersTargetEffect(
|
||||
CounterType.P1P1.createInstance(2)
|
||||
)
|
||||
new AddCountersTargetEffect(CounterType.P1P1.createInstance(2))
|
||||
);
|
||||
ability.addTarget(new TargetPermanent(StaticFilters.FILTER_ANOTHER_TARGET_CREATURE_YOU_CONTROL));
|
||||
this.addAbility(ability);
|
||||
|
|
|
|||
156
Mage.Sets/src/mage/cards/a/AvatarAang.java
Normal file
156
Mage.Sets/src/mage/cards/a/AvatarAang.java
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.decorator.ConditionalOneShotEffect;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.effects.common.TransformSourceEffect;
|
||||
import mage.abilities.keyword.FirebendingAbility;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
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 AvatarAang extends CardImpl {
|
||||
|
||||
public AvatarAang(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{G}{W}{U}");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.AVATAR);
|
||||
this.subtype.add(SubType.ALLY);
|
||||
this.power = new MageInt(4);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
this.secondSideCardClazz = mage.cards.a.AangMasterOfElements.class;
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// Firebending 2
|
||||
this.addAbility(new FirebendingAbility(2));
|
||||
|
||||
// Whenever you waterbend, earthbend, firebend, or airbend, draw a card. Then if you've done all four this turn, transform Avatar Aang.
|
||||
this.addAbility(new AvatarAangTriggeredAbility());
|
||||
}
|
||||
|
||||
private AvatarAang(final AvatarAang card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AvatarAang copy() {
|
||||
return new AvatarAang(this);
|
||||
}
|
||||
}
|
||||
|
||||
class AvatarAangTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
private enum AvatarAangCondition implements Condition {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return AvatarAangWatcher.checkPlayer(game, source);
|
||||
}
|
||||
}
|
||||
|
||||
AvatarAangTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1));
|
||||
this.addEffect(new ConditionalOneShotEffect(
|
||||
new TransformSourceEffect(), AvatarAangCondition.instance,
|
||||
"Then if you've done all four this turn, transform {this}"
|
||||
));
|
||||
this.setTriggerPhrase("Whenever you waterbend, earthbend, firebend, or airbend, ");
|
||||
this.addWatcher(new AvatarAangWatcher());
|
||||
}
|
||||
|
||||
private AvatarAangTriggeredAbility(final AvatarAangTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AvatarAangTriggeredAbility copy() {
|
||||
return new AvatarAangTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
switch (event.getType()) {
|
||||
case EARTHBENDED:
|
||||
case AIRBENDED:
|
||||
case FIREBENDED:
|
||||
case WATERBENDED:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
return isControlledBy(event.getPlayerId());
|
||||
}
|
||||
}
|
||||
|
||||
class AvatarAangWatcher extends Watcher {
|
||||
|
||||
private final Set<UUID> earthSet = new HashSet<>();
|
||||
private final Set<UUID> airSet = new HashSet<>();
|
||||
private final Set<UUID> fireSet = new HashSet<>();
|
||||
private final Set<UUID> waterSet = new HashSet<>();
|
||||
|
||||
AvatarAangWatcher() {
|
||||
super(WatcherScope.GAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void watch(GameEvent event, Game game) {
|
||||
switch (event.getType()) {
|
||||
case EARTHBENDED:
|
||||
earthSet.add(event.getPlayerId());
|
||||
return;
|
||||
case AIRBENDED:
|
||||
airSet.add(event.getPlayerId());
|
||||
return;
|
||||
case FIREBENDED:
|
||||
fireSet.add(event.getPlayerId());
|
||||
return;
|
||||
case WATERBENDED:
|
||||
waterSet.add(event.getPlayerId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
earthSet.clear();
|
||||
airSet.clear();
|
||||
fireSet.clear();
|
||||
earthSet.clear();
|
||||
}
|
||||
|
||||
private boolean checkPlayer(UUID playerId) {
|
||||
return earthSet.contains(playerId)
|
||||
&& airSet.contains(playerId)
|
||||
&& fireSet.contains(playerId)
|
||||
&& earthSet.contains(playerId);
|
||||
}
|
||||
|
||||
static boolean checkPlayer(Game game, Ability source) {
|
||||
return game.getState().getWatcher(AvatarAangWatcher.class).checkPlayer(source.getControllerId());
|
||||
}
|
||||
}
|
||||
51
Mage.Sets/src/mage/cards/a/AvatarEnthusiasts.java
Normal file
51
Mage.Sets/src/mage/cards/a/AvatarEnthusiasts.java
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.EntersBattlefieldAllTriggeredAbility;
|
||||
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.FilterPermanent;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AvatarEnthusiasts extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterControlledPermanent(SubType.ALLY, "another Ally you control");
|
||||
|
||||
static {
|
||||
filter.add(AnotherPredicate.instance);
|
||||
}
|
||||
|
||||
public AvatarEnthusiasts(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}");
|
||||
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.PEASANT);
|
||||
this.subtype.add(SubType.ALLY);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(2);
|
||||
|
||||
// Whenever another Ally you control enters, put a +1/+1 counter on this creature.
|
||||
this.addAbility(new EntersBattlefieldAllTriggeredAbility(
|
||||
new AddCountersSourceEffect(CounterType.P1P1.createInstance()), filter
|
||||
));
|
||||
}
|
||||
|
||||
private AvatarEnthusiasts(final AvatarEnthusiasts card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AvatarEnthusiasts copy() {
|
||||
return new AvatarEnthusiasts(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,15 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.mana.GenericManaCost;
|
||||
import mage.abilities.decorator.ConditionalOneShotEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.effects.common.GainLifeEffect;
|
||||
import mage.abilities.effects.common.TransformSourceEffect;
|
||||
import mage.abilities.effects.common.UntapSourceEffect;
|
||||
import mage.abilities.keyword.TransformAbility;
|
||||
|
|
@ -19,11 +22,10 @@ import mage.constants.SuperType;
|
|||
import mage.game.ExileZone;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetCard;
|
||||
import mage.target.common.TargetCardInHand;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
|
|
@ -41,8 +43,13 @@ public final class AzorsGateway extends CardImpl {
|
|||
// If cards with five or more different converted mana costs are exiled with Azor's Gateway,
|
||||
// you gain 5 life, untap Azor's Gateway, and transform it.
|
||||
this.addAbility(new TransformAbility());
|
||||
Ability ability = new SimpleActivatedAbility(new AzorsGatewayEffect(), new GenericManaCost(1));
|
||||
Ability ability = new SimpleActivatedAbility(new DrawCardSourceControllerEffect(1), new GenericManaCost(1));
|
||||
ability.addCost(new TapSourceCost());
|
||||
ability.addEffect(new AzorsGatewayEffect());
|
||||
ability.addEffect(new ConditionalOneShotEffect(
|
||||
new GainLifeEffect(5), AzorsGatewayCondition.instance, "If cards with five or more " +
|
||||
"different mana values are exiled with {this}, you gain 5 life, untap {this}, and transform it."
|
||||
).addEffect(new UntapSourceEffect()).addEffect(new TransformSourceEffect()));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
@ -56,13 +63,28 @@ public final class AzorsGateway extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
enum AzorsGatewayCondition implements Condition {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source));
|
||||
return exileZone != null
|
||||
&& exileZone
|
||||
.getCards(game)
|
||||
.stream()
|
||||
.map(MageObject::getManaValue)
|
||||
.distinct()
|
||||
.mapToInt(x -> 1)
|
||||
.sum() >= 5;
|
||||
}
|
||||
}
|
||||
|
||||
class AzorsGatewayEffect extends OneShotEffect {
|
||||
|
||||
AzorsGatewayEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "Draw a card, then exile a card from your hand. " +
|
||||
"If cards with five or more different mana values are exiled with {this}, " +
|
||||
"you gain 5 life, untap {this}, and transform it";
|
||||
this.staticText = ", then exile a card from your hand";
|
||||
}
|
||||
|
||||
private AzorsGatewayEffect(final AzorsGatewayEffect effect) {
|
||||
|
|
@ -76,37 +98,18 @@ class AzorsGatewayEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller == null) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player == null || player.getHand().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MageObject sourceObject = source.getSourceObject(game);
|
||||
if (sourceObject == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UUID exileId = CardUtil.getCardExileZoneId(game, source);
|
||||
|
||||
controller.drawCards(1, source, game);
|
||||
TargetCardInHand target = new TargetCardInHand();
|
||||
controller.choose(outcome, target, source, game);
|
||||
Card cardToExile = game.getCard(target.getFirstTarget());
|
||||
if (cardToExile != null) {
|
||||
controller.moveCardsToExile(cardToExile, source, game, true, exileId, sourceObject.getIdName());
|
||||
}
|
||||
Set<Integer> usedCMC = new HashSet<>();
|
||||
ExileZone exileZone = game.getExile().getExileZone(exileId);
|
||||
if (exileZone != null) {
|
||||
for (Card card : exileZone.getCards(game)) {
|
||||
usedCMC.add(card.getManaValue());
|
||||
}
|
||||
if (usedCMC.size() > 4) {
|
||||
controller.gainLife(4, game, source);
|
||||
new UntapSourceEffect().apply(game, source);
|
||||
new TransformSourceEffect().apply(game, source);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
TargetCard target = new TargetCardInHand();
|
||||
target.withChooseHint("to exile");
|
||||
player.choose(outcome, player.getHand(), target, source, game);
|
||||
Card card = game.getCard(target.getFirstTarget());
|
||||
return card != null && player.moveCardsToExile(
|
||||
card, source, game, true,
|
||||
CardUtil.getExileZoneId(game, source),
|
||||
CardUtil.getSourceLogName(game, source)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
46
Mage.Sets/src/mage/cards/a/AzulaAlwaysLies.java
Normal file
46
Mage.Sets/src/mage/cards/a/AzulaAlwaysLies.java
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.common.continuous.BoostTargetEffect;
|
||||
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.counters.CounterType;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AzulaAlwaysLies extends CardImpl {
|
||||
|
||||
public AzulaAlwaysLies(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{B}");
|
||||
|
||||
this.subtype.add(SubType.LESSON);
|
||||
|
||||
// Choose one or both --
|
||||
this.getSpellAbility().getModes().setMinModes(1);
|
||||
this.getSpellAbility().getModes().setMaxModes(2);
|
||||
|
||||
// * Target creature gets -1/-1 until end of turn.
|
||||
this.getSpellAbility().addEffect(new BoostTargetEffect(-1, -1));
|
||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent());
|
||||
|
||||
// * Put a +1/+1 counter on target creature.
|
||||
this.getSpellAbility().addMode(new Mode(new AddCountersTargetEffect(CounterType.P1P1.createInstance()))
|
||||
.addTarget(new TargetCreaturePermanent()));
|
||||
}
|
||||
|
||||
private AzulaAlwaysLies(final AzulaAlwaysLies card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AzulaAlwaysLies copy() {
|
||||
return new AzulaAlwaysLies(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -67,7 +67,7 @@ class BackFromTheBrinkCost extends CostImpl {
|
|||
|
||||
@Override
|
||||
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
||||
return this.getTargets().canChoose(controllerId, source, game);
|
||||
return canChooseOrAlreadyChosen(ability, source, controllerId, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
53
Mage.Sets/src/mage/cards/b/Badgermole.java
Normal file
53
Mage.Sets/src/mage/cards/b/Badgermole.java
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityAllEffect;
|
||||
import mage.abilities.effects.keyword.EarthbendTargetEffect;
|
||||
import mage.abilities.keyword.TrampleAbility;
|
||||
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.target.common.TargetControlledLandPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class Badgermole extends CardImpl {
|
||||
|
||||
public Badgermole(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}");
|
||||
|
||||
this.subtype.add(SubType.BADGER);
|
||||
this.subtype.add(SubType.MOLE);
|
||||
this.power = new MageInt(4);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
// When this creature enters, earthbend 2.
|
||||
Ability ability = new EntersBattlefieldTriggeredAbility(new EarthbendTargetEffect(2));
|
||||
ability.addTarget(new TargetControlledLandPermanent());
|
||||
this.addAbility(ability);
|
||||
|
||||
// Creatures you control with +1/+1 counters on them have trample.
|
||||
this.addAbility(new SimpleStaticAbility(new GainAbilityAllEffect(
|
||||
TrampleAbility.getInstance(), Duration.WhileOnBattlefield,
|
||||
StaticFilters.FILTER_CONTROLLED_CREATURES_P1P1
|
||||
)));
|
||||
}
|
||||
|
||||
private Badgermole(final Badgermole card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Badgermole copy() {
|
||||
return new Badgermole(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -111,7 +111,7 @@ class BagOfHoldingReturnCardsEffect extends OneShotEffect {
|
|||
return false;
|
||||
}
|
||||
ExileZone exileZone = game.getExile().getExileZone(
|
||||
CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter())
|
||||
CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC())
|
||||
);
|
||||
if (exileZone == null) {
|
||||
return true;
|
||||
|
|
|
|||
55
Mage.Sets/src/mage/cards/b/BagelAndSchmear.java
Normal file
55
Mage.Sets/src/mage/cards/b/BagelAndSchmear.java
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.ActivateAsSorceryActivatedAbility;
|
||||
import mage.abilities.costs.common.SacrificeSourceCost;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
|
||||
import mage.abilities.token.FoodAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.counters.CounterType;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class BagelAndSchmear extends CardImpl {
|
||||
|
||||
public BagelAndSchmear(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}");
|
||||
|
||||
this.subtype.add(SubType.FOOD);
|
||||
|
||||
// Share -- {W}, {T}, Sacrifice this artifact: Put a +1/+1 counter on up to one target creature. Draw a card. Activate only as a sorcery.
|
||||
Ability ability = new ActivateAsSorceryActivatedAbility(
|
||||
new AddCountersTargetEffect(CounterType.P1P1.createInstance()), new ManaCostsImpl<>("{W}")
|
||||
);
|
||||
ability.addCost(new TapSourceCost());
|
||||
ability.addCost(new SacrificeSourceCost());
|
||||
ability.addEffect(new DrawCardSourceControllerEffect(1));
|
||||
ability.addTarget(new TargetCreaturePermanent(0, 1));
|
||||
this.addAbility(ability.withFlavorWord("Share"));
|
||||
|
||||
// Nosh -- {2}, {T}, Sacrifice this artifact: You gain 3 life and draw a card.
|
||||
ability = new FoodAbility();
|
||||
ability.addEffect(new DrawCardSourceControllerEffect(1).concatBy("and"));
|
||||
this.addAbility(ability.withFlavorWord("Nosh"));
|
||||
}
|
||||
|
||||
private BagelAndSchmear(final BagelAndSchmear card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BagelAndSchmear copy() {
|
||||
return new BagelAndSchmear(this);
|
||||
}
|
||||
}
|
||||
// where's the lox?
|
||||
|
|
@ -3,7 +3,6 @@ package mage.cards.b;
|
|||
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.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.combat.CantBlockTargetEffect;
|
||||
|
|
@ -72,15 +71,15 @@ class BallistaWielderEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = game.getPermanent(source.getFirstTarget());
|
||||
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
|
||||
if (permanent == null) {
|
||||
Player player = game.getPlayer(source.getFirstTarget());
|
||||
Player player = game.getPlayer(getTargetPointer().getFirst(game, source));
|
||||
return player != null && player.damage(1, source, game) > 0;
|
||||
}
|
||||
if (permanent.damage(1, source, game) > 0) {
|
||||
game.addEffect(new CantBlockTargetEffect(Duration.EndOfTurn), source);
|
||||
return true;
|
||||
if (permanent.damage(1, source, game) <= 0) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
game.addEffect(new CantBlockTargetEffect(Duration.EndOfTurn), source);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ public final class BalthierAndFran extends CardImpl {
|
|||
= new FilterCreaturePermanent(SubType.VEHICLE, "a Vehicle crewed by {this} this turn");
|
||||
|
||||
static {
|
||||
filter.add(BalthierAndFranPredicate.instance);
|
||||
filter2.add(BalthierAndFranPredicate.instance);
|
||||
}
|
||||
|
||||
public BalthierAndFran(UUID ownerId, CardSetInfo setInfo) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package mage.cards.b;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.common.SacrificeSourceCost;
|
||||
|
|
@ -11,11 +9,15 @@ import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect;
|
|||
import mage.abilities.mana.ColorlessManaAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author North
|
||||
*/
|
||||
|
|
@ -24,7 +26,6 @@ public final class BantPanorama extends CardImpl {
|
|||
private static final FilterCard filter = new FilterCard("a basic Forest, Plains, or Island card");
|
||||
|
||||
static {
|
||||
filter.add(CardType.LAND.getPredicate());
|
||||
filter.add(SuperType.BASIC.getPredicate());
|
||||
filter.add(Predicates.or(
|
||||
SubType.FOREST.getPredicate(),
|
||||
|
|
|
|||
48
Mage.Sets/src/mage/cards/b/BarrelsOfBlastingJelly.java
Normal file
48
Mage.Sets/src/mage/cards/b/BarrelsOfBlastingJelly.java
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
package mage.cards.b;
|
||||
|
||||
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.DamageTargetEffect;
|
||||
import mage.abilities.effects.mana.AddManaOfAnyColorEffect;
|
||||
import mage.abilities.mana.LimitedTimesPerTurnActivatedManaAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Zone;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class BarrelsOfBlastingJelly extends CardImpl {
|
||||
|
||||
public BarrelsOfBlastingJelly(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}");
|
||||
|
||||
// {1}: Add one mana of any color. Activate only once each turn.
|
||||
this.addAbility(new LimitedTimesPerTurnActivatedManaAbility(
|
||||
Zone.BATTLEFIELD, new AddManaOfAnyColorEffect(), new GenericManaCost(1)
|
||||
));
|
||||
|
||||
// {5}, {T}, Sacrifice this artifact: It deals 5 damage to target creature.
|
||||
Ability ability = new SimpleActivatedAbility(new DamageTargetEffect(5, "it"), new GenericManaCost(5));
|
||||
ability.addCost(new TapSourceCost());
|
||||
ability.addCost(new SacrificeSourceCost());
|
||||
ability.addTarget(new TargetCreaturePermanent());
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
private BarrelsOfBlastingJelly(final BarrelsOfBlastingJelly card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BarrelsOfBlastingJelly copy() {
|
||||
return new BarrelsOfBlastingJelly(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -147,6 +147,7 @@ class BattleForBretagardTarget extends TargetPermanent {
|
|||
@Override
|
||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
||||
|
||||
Set<String> names = this.getTargets()
|
||||
.stream()
|
||||
.map(game::getPermanent)
|
||||
|
|
@ -159,6 +160,7 @@ class BattleForBretagardTarget extends TargetPermanent {
|
|||
Permanent permanent = game.getPermanent(uuid);
|
||||
return permanent == null || names.contains(permanent.getName());
|
||||
});
|
||||
|
||||
return possibleTargets;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ class BattlefieldScroungerCost extends CostImpl {
|
|||
|
||||
@Override
|
||||
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
||||
return this.getTargets().canChoose(controllerId, source, game);
|
||||
return canChooseOrAlreadyChosen(ability, source, controllerId, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -121,11 +121,11 @@ class BeamsplitterMageTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
private boolean checkNotSource(Permanent permanent, Game game) {
|
||||
// workaround for zcc not being set before first intervening if check
|
||||
if (this.getSourceObjectZoneChangeCounter() == 0) {
|
||||
if (this.getStackMomentSourceZCC() == 0) {
|
||||
return !permanent.getId().equals(this.getSourceId());
|
||||
}
|
||||
return !permanent.getId().equals(this.getSourceId())
|
||||
|| permanent.getZoneChangeCounter(game) != this.getSourceObjectZoneChangeCounter();
|
||||
|| permanent.getZoneChangeCounter(game) != this.getStackMomentSourceZCC();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
46
Mage.Sets/src/mage/cards/b/BeetleHeadedMerchants.java
Normal file
46
Mage.Sets/src/mage/cards/b/BeetleHeadedMerchants.java
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.AttacksTriggeredAbility;
|
||||
import mage.abilities.costs.common.SacrificeTargetCost;
|
||||
import mage.abilities.effects.common.DoIfCostPaid;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
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 java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class BeetleHeadedMerchants extends CardImpl {
|
||||
|
||||
public BeetleHeadedMerchants(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}");
|
||||
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.CITIZEN);
|
||||
this.power = new MageInt(5);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
// Whenever this creature attacks, you may sacrifice another creature or artifact. If you do, draw a card and put a +1/+1 counter on this creature.
|
||||
this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid(
|
||||
new DrawCardSourceControllerEffect(1),
|
||||
new SacrificeTargetCost(StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE_OR_ARTIFACT)
|
||||
).addEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance()).concatBy("and"))));
|
||||
}
|
||||
|
||||
private BeetleHeadedMerchants(final BeetleHeadedMerchants card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeetleHeadedMerchants copy() {
|
||||
return new BeetleHeadedMerchants(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@ public final class BeetleLegacyCriminal extends CardImpl {
|
|||
ability.addCost(new ExileSourceFromGraveCost());
|
||||
ability.addTarget(new TargetCreaturePermanent());
|
||||
ability.addEffect(new GainAbilityTargetEffect(FlyingAbility.getInstance())
|
||||
.setText("Put a +1/+1 counter on target creature"));
|
||||
.setText("It gains flying until end of turn"));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -66,12 +66,12 @@ class BeguilerOfWillsTarget extends TargetPermanent {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
||||
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||
Permanent permanent = game.getPermanent(id);
|
||||
int count = game.getBattlefield().countAll(this.filter, source.getControllerId(), game);
|
||||
|
||||
if (permanent != null && permanent.getPower().getValue() <= count) {
|
||||
return super.canTarget(controllerId, id, source, game);
|
||||
return super.canTarget(playerId, id, source, game);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
91
Mage.Sets/src/mage/cards/b/BeholdTheSinisterSix.java
Normal file
91
Mage.Sets/src/mage/cards/b/BeholdTheSinisterSix.java
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.common.FilterPermanentCard;
|
||||
import mage.game.Game;
|
||||
import mage.target.common.TargetCardInYourGraveyard;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class BeholdTheSinisterSix extends CardImpl {
|
||||
|
||||
public BeholdTheSinisterSix(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{6}{B}");
|
||||
|
||||
// Return up to six target creature cards with different names from your graveyard to the battlefield.
|
||||
this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect());
|
||||
this.getSpellAbility().addTarget(new BeholdTheSinisterSixTarget());
|
||||
}
|
||||
|
||||
private BeholdTheSinisterSix(final BeholdTheSinisterSix card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeholdTheSinisterSix copy() {
|
||||
return new BeholdTheSinisterSix(this);
|
||||
}
|
||||
}
|
||||
|
||||
class BeholdTheSinisterSixTarget extends TargetCardInYourGraveyard {
|
||||
|
||||
private static final FilterCard filter = new FilterPermanentCard("creature cards with different names");
|
||||
|
||||
BeholdTheSinisterSixTarget() {
|
||||
super(0, 6, filter, false);
|
||||
}
|
||||
|
||||
private BeholdTheSinisterSixTarget(final BeholdTheSinisterSixTarget target) {
|
||||
super(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeholdTheSinisterSixTarget copy() {
|
||||
return new BeholdTheSinisterSixTarget(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canTarget(UUID playerId, UUID id, Ability ability, Game game) {
|
||||
if (!super.canTarget(playerId, id, ability, game)) {
|
||||
return false;
|
||||
}
|
||||
Set<String> names = this.getTargets()
|
||||
.stream()
|
||||
.map(game::getCard)
|
||||
.map(MageObject::getName)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
Card card = game.getCard(id);
|
||||
return card != null && !names.contains(card.getName());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
||||
Set<String> names = this.getTargets()
|
||||
.stream()
|
||||
.map(game::getCard)
|
||||
.map(MageObject::getName)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
possibleTargets.removeIf(uuid -> {
|
||||
Card card = game.getCard(uuid);
|
||||
return card != null && names.contains(card.getName());
|
||||
});
|
||||
return possibleTargets;
|
||||
}
|
||||
}
|
||||
35
Mage.Sets/src/mage/cards/b/BendersWaterskin.java
Normal file
35
Mage.Sets/src/mage/cards/b/BendersWaterskin.java
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.continuous.UntapSourceDuringEachOtherPlayersUntapStepEffect;
|
||||
import mage.abilities.mana.AnyColorManaAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class BendersWaterskin extends CardImpl {
|
||||
|
||||
public BendersWaterskin(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
|
||||
|
||||
// Untap this artifact during each other player's untap step.
|
||||
this.addAbility(new SimpleStaticAbility(new UntapSourceDuringEachOtherPlayersUntapStepEffect()));
|
||||
|
||||
// {T}: Add one mana of any color.
|
||||
this.addAbility(new AnyColorManaAbility());
|
||||
}
|
||||
|
||||
private BendersWaterskin(final BendersWaterskin card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BendersWaterskin copy() {
|
||||
return new BendersWaterskin(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.PutIntoGraveFromAnywhereSourceAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.CantBeCounteredControlledEffect;
|
||||
import mage.abilities.effects.common.ExileSourceEffect;
|
||||
import mage.abilities.keyword.DisturbAbility;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
|
|
@ -39,7 +38,7 @@ public final class BenevolentGeist extends CardImpl {
|
|||
)));
|
||||
|
||||
// If Benevolent Geist would be put into a graveyard from anywhere, exile it instead.
|
||||
this.addAbility(new PutIntoGraveFromAnywhereSourceAbility(new ExileSourceEffect().setText("exile it instead")));
|
||||
this.addAbility(DisturbAbility.makeBackAbility());
|
||||
}
|
||||
|
||||
private BenevolentGeist(final BenevolentGeist card) {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import mage.constants.SagaChapter;
|
|||
import mage.constants.SubType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterBySubtypeCard;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
|
||||
|
|
@ -24,7 +23,7 @@ import java.util.UUID;
|
|||
*/
|
||||
public final class BindingTheOldGods extends CardImpl {
|
||||
|
||||
private static final FilterCard filter = new FilterBySubtypeCard(SubType.FOREST);
|
||||
private static final FilterCard filter = new FilterCard(SubType.FOREST);
|
||||
|
||||
public BindingTheOldGods(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}{G}");
|
||||
|
|
@ -33,15 +32,18 @@ public final class BindingTheOldGods extends CardImpl {
|
|||
|
||||
// (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)
|
||||
SagaAbility sagaAbility = new SagaAbility(this);
|
||||
|
||||
// I — Destroy target nonland permanent an opponent controls.
|
||||
sagaAbility.addChapterEffect(
|
||||
this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_I,
|
||||
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(filter), true)
|
||||
);
|
||||
|
||||
// III — Creatures you control gain deathtouch until end of turn.
|
||||
sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III,
|
||||
new GainAbilityControlledEffect(DeathtouchAbility.getInstance(), Duration.EndOfTurn,
|
||||
|
|
|
|||
64
Mage.Sets/src/mage/cards/b/BiorganicCarapace.java
Normal file
64
Mage.Sets/src/mage/cards/b/BiorganicCarapace.java
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.DealsCombatDamageTriggeredAbility;
|
||||
import mage.abilities.common.EntersBattlefieldAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
|
||||
import mage.abilities.effects.common.AttachEffect;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostEquippedEffect;
|
||||
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.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.predicate.permanent.ModifiedPredicate;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jmlundeen
|
||||
*/
|
||||
public final class BiorganicCarapace extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterControlledCreaturePermanent("each modified creature you control");
|
||||
|
||||
static {
|
||||
filter.add(ModifiedPredicate.instance);
|
||||
}
|
||||
|
||||
public BiorganicCarapace(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{W}{U}");
|
||||
|
||||
this.subtype.add(SubType.EQUIPMENT);
|
||||
|
||||
// When this Equipment enters, attach it to target creature you control.
|
||||
Ability ability = new EntersBattlefieldAbility(new AttachEffect(Outcome.BoostCreature));
|
||||
ability.addTarget(new TargetControlledCreaturePermanent());
|
||||
this.addAbility(ability);
|
||||
|
||||
// Equipped creature gets +2/+2 and has "Whenever this creature deals combat damage to a player, draw a card for each modified creature you control."
|
||||
Ability gainedAbility = new DealsCombatDamageTriggeredAbility(new DrawCardSourceControllerEffect(new PermanentsOnBattlefieldCount(filter)), false);
|
||||
Ability equipAbility = new SimpleStaticAbility(new BoostEquippedEffect(2, 2));
|
||||
equipAbility.addEffect(new GainAbilityAttachedEffect(gainedAbility, null)
|
||||
.concatBy("and"));
|
||||
this.addAbility(equipAbility);
|
||||
|
||||
// Equip {2}
|
||||
this.addAbility(new EquipAbility(2));
|
||||
}
|
||||
|
||||
private BiorganicCarapace(final BiorganicCarapace card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiorganicCarapace copy() {
|
||||
return new BiorganicCarapace(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -18,13 +18,11 @@ import mage.constants.CardType;
|
|||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.game.ExileZone;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import static mage.filter.StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE;
|
||||
|
|
@ -87,7 +85,7 @@ class BishopOfBindingExileEffect extends OneShotEffect {
|
|||
// the target creature won't be exiled.
|
||||
if (permanent != null) {
|
||||
new ExileTargetEffect(
|
||||
CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()), permanent.getIdName()
|
||||
CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()), permanent.getIdName()
|
||||
).apply(game, source);
|
||||
game.addDelayedTriggeredAbility(new OnLeaveReturnExiledAbility(), source);
|
||||
return true;
|
||||
|
|
@ -101,7 +99,7 @@ enum BishopOfBindingValue implements DynamicValue {
|
|||
|
||||
@Override
|
||||
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||
ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, sourceAbility.getSourceId(), sourceAbility.getSourceObjectZoneChangeCounter()));
|
||||
ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, sourceAbility.getSourceId(), sourceAbility.getStackMomentSourceZCC()));
|
||||
if (exileZone != null) {
|
||||
Card exiledCard = exileZone.getRandom(game);
|
||||
if (exiledCard != null) {
|
||||
|
|
|
|||
|
|
@ -12,11 +12,9 @@ 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.TargetPermanent;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
@ -65,7 +63,7 @@ enum BiteDownOnCrimeAdjuster implements CostAdjuster {
|
|||
@Override
|
||||
public void reduceCost(Ability ability, Game game) {
|
||||
if (CollectedEvidenceCondition.instance.apply(game, ability)
|
||||
|| (game.inCheckPlayableState() && collectEvidenceCost.canPay(ability, null, ability.getControllerId(), game))) {
|
||||
|| (game.inCheckPlayableState() && collectEvidenceCost.canPay(ability, ability, ability.getControllerId(), game))) {
|
||||
CardUtil.reduceCost(ability, 2);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
91
Mage.Sets/src/mage/cards/b/BlackCatCunningThief.java
Normal file
91
Mage.Sets/src/mage/cards/b/BlackCatCunningThief.java
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect;
|
||||
import mage.cards.*;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetCard;
|
||||
import mage.target.common.TargetOpponent;
|
||||
import mage.target.targetpointer.FixedTargets;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jmlundeen
|
||||
*/
|
||||
public final class BlackCatCunningThief extends CardImpl {
|
||||
|
||||
public BlackCatCunningThief(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.ROGUE);
|
||||
this.subtype.add(SubType.VILLAIN);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(3);
|
||||
|
||||
// When Black Cat enters, look at the top nine cards of target opponent's library, exile two of them face down, then put the rest on the bottom of their library in a random order. You may play the exiled cards for as long as they remain exiled. Mana of any type can be spent to cast spells this way.
|
||||
Ability ability = new EntersBattlefieldTriggeredAbility(new BlackCatCunningThiefEffect());
|
||||
ability.addTarget(new TargetOpponent());
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
private BlackCatCunningThief(final BlackCatCunningThief card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlackCatCunningThief copy() {
|
||||
return new BlackCatCunningThief(this);
|
||||
}
|
||||
}
|
||||
class BlackCatCunningThiefEffect extends OneShotEffect {
|
||||
|
||||
BlackCatCunningThiefEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "look at the top nine cards of target opponent's library, exile two of them face down, then put the rest on the bottom of their library in a random order. You may play the exiled cards for as long as they remain exiled. Mana of any type can be spent to cast spells this way";
|
||||
}
|
||||
|
||||
private BlackCatCunningThiefEffect(final BlackCatCunningThiefEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlackCatCunningThiefEffect copy() {
|
||||
return new BlackCatCunningThiefEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
Player opponent = game.getPlayer(getTargetPointer().getFirst(game, source));
|
||||
MageObject sourceObject = source.getSourceObject(game);
|
||||
if (controller == null || opponent == null || sourceObject == null) {
|
||||
return false;
|
||||
}
|
||||
Cards topCards = new CardsImpl();
|
||||
topCards.addAllCards(opponent.getLibrary().getTopCards(game, 9));
|
||||
TargetCard target = new TargetCard(2, 2, Zone.LIBRARY, new FilterCard("card to exile"));
|
||||
controller.choose(outcome, topCards, target, source, game);
|
||||
Cards exiledCards = new CardsImpl(target.getTargets().stream()
|
||||
.map(game::getCard)
|
||||
.collect(Collectors.toList()));
|
||||
new ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect(false, CastManaAdjustment.AS_THOUGH_ANY_MANA_TYPE)
|
||||
.setTargetPointer(new FixedTargets(exiledCards, game))
|
||||
.apply(game, source);
|
||||
topCards.retainZone(Zone.LIBRARY, game);
|
||||
// then put the rest on the bottom of that library in a random order
|
||||
controller.putCardsOnBottomOfLibrary(topCards, game, source, false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -59,7 +59,7 @@ enum BlazingEffigyCount implements DynamicValue {
|
|||
if (watcher == null) {
|
||||
return 3;
|
||||
}
|
||||
int effigyDamage = watcher.damageDoneTo(sourceAbility.getSourceId(), sourceAbility.getSourceObjectZoneChangeCounter() - 1, game);
|
||||
int effigyDamage = watcher.damageDoneTo(sourceAbility.getSourceId(), sourceAbility.getStackMomentSourceZCC() - 1, game);
|
||||
return CardUtil.overflowInc(3, effigyDamage);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,18 +49,18 @@ class BlazingHopeTarget extends TargetPermanent {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
||||
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||
Permanent permanent = game.getPermanent(id);
|
||||
if (permanent != null) {
|
||||
if (!isNotTarget()) {
|
||||
if (!permanent.canBeTargetedBy(game.getObject(source.getId()), controllerId, source, game)
|
||||
|| !permanent.canBeTargetedBy(game.getObject(source), controllerId, source, game)) {
|
||||
if (!permanent.canBeTargetedBy(game.getObject(source.getId()), playerId, source, game)
|
||||
|| !permanent.canBeTargetedBy(game.getObject(source), playerId, source, game)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null && permanent.getPower().getValue() >= controller.getLife()) {
|
||||
return filter.match(permanent, controllerId, source, game);
|
||||
return filter.match(permanent, playerId, source, game);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ public final class Blink extends CardImpl {
|
|||
this, SagaChapter.CHAPTER_IV,
|
||||
new CreateTokenEffect(new AlienAngelToken())
|
||||
);
|
||||
this.addAbility(sagaAbility);
|
||||
this.addAbility(sagaAbility); //TODO: These should be a single AddChapterEffect, but currently XMage does not support noncontiguous Saga chapters
|
||||
}
|
||||
|
||||
private Blink(final Blink card) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package mage.cards.b;
|
|||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.condition.common.YouGainedLifeCondition;
|
||||
|
|
@ -12,10 +11,14 @@ import mage.abilities.effects.common.GainLifeEffect;
|
|||
import mage.abilities.effects.common.LoseLifeOpponentsEffect;
|
||||
import mage.abilities.hint.ConditionHint;
|
||||
import mage.abilities.hint.Hint;
|
||||
import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.TargetController;
|
||||
import mage.game.permanent.token.BloodToken;
|
||||
import mage.watchers.common.PlayerGainedLifeWatcher;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -40,7 +43,7 @@ public final class BloodsoakedReveler extends CardImpl {
|
|||
this.addAbility(new BeginningOfEndStepTriggeredAbility(
|
||||
TargetController.YOU, new CreateTokenEffect(new BloodToken()),
|
||||
false, condition
|
||||
).addHint(hint));
|
||||
).addHint(hint), new PlayerGainedLifeWatcher());
|
||||
|
||||
// {4}{B}: Each opponent loses 2 life and you gain 2 life.
|
||||
Ability ability = new SimpleActivatedAbility(
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue