diff --git a/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java b/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java index 00ce3e7af75..8b517db7ee2 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java @@ -87,7 +87,7 @@ public class AddLandDialog extends MageDialog { landSetNames.add(expansionInfo.getName()); } if (landSetNames.isEmpty()) { - throw new IllegalArgumentException("No set with basic land was found"); + throw new IllegalArgumentException("No set with basic land was found (possible memory problems, need client restart)"); } if (landSetNames.size() > 1) { landSetNames.add(""); @@ -477,6 +477,7 @@ public class AddLandDialog extends MageDialog { }//GEN-LAST:event_btnSetFastSearchActionPerformed private void autoAddLands() { + // suggest lands amount for deck without lands int deckSize = ((Number) spnDeckSize.getValue()).intValue(); int[] lands = DeckBuildUtils.landCountSuggestion(deckSize, deck.getMaindeckCards()); spnPlains.setValue(lands[0]); diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java index 94a87f91fdb..0b091bd16fb 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java @@ -61,7 +61,7 @@ public class ScryfallImageSupportTokens { put("RNA/Treasure", "https://api.scryfall.com/cards/trna/12/en?format=image"); put("RNA/Zombie", "https://api.scryfall.com/cards/trna/3/en?format=image"); - //GRN + // GRN put("GRN/Angel", "https://api.scryfall.com/cards/tgrn/1/en?format=image"); put("GRN/Bird Illusion", "https://api.scryfall.com/cards/tgrn/3/en?format=image"); put("GRN/Elf Knight", "https://api.scryfall.com/cards/tgrn/6/en?format=image"); @@ -71,7 +71,7 @@ public class ScryfallImageSupportTokens { put("GRN/Soldier", "https://api.scryfall.com/cards/tgrn/2/en?format=image"); put("GRN/Emblem Vraska", "https://api.scryfall.com/cards/tgrn/8/en?format=image"); - //DOM + // DOM put("DOM/Cleric", "https://api.scryfall.com/cards/tdom/4/en?format=image"); put("DOM/Construct", "https://api.scryfall.com/cards/tdom/14/en?format=image"); put("DOM/Demon", "https://api.scryfall.com/cards/tdom/7/en?format=image"); @@ -89,7 +89,7 @@ public class ScryfallImageSupportTokens { put("DOM/Emblem Teferi", "https://api.scryfall.com/cards/tdom/16/en?format=image"); put("DOM/Zombie Knight", "https://api.scryfall.com/cards/tdom/5/en?format=image"); - //XLN + // XLN put("XLN/Dinosaur", "https://api.scryfall.com/cards/txln/5/en?format=image"); put("XLN/Illusion", "https://api.scryfall.com/cards/txln/2/en?format=image"); put("XLN/Merfolk", "https://api.scryfall.com/cards/txln/3/en?format=image"); @@ -101,12 +101,12 @@ public class ScryfallImageSupportTokens { put("XLN/Treasure/4", "https://api.scryfall.com/cards/txln/10/en?format=image"); put("XLN/Vampire", "https://api.scryfall.com/cards/txln/1/en?format=image"); - //HOU + // HOU put("HOU/Horse", "https://api.scryfall.com/cards/thou/10/en?format=image"); put("HOU/Insect", "https://api.scryfall.com/cards/thou/12/en?format=image"); put("HOU/Snake", "https://api.scryfall.com/cards/thou/11/en?format=image"); - //AKH - tokens + // AKH - tokens put("AKH/Beast", "https://api.scryfall.com/cards/takh/21/en?format=image"); put("AKH/Cat", "https://api.scryfall.com/cards/takh/16/en?format=image"); put("AKH/Drake", "https://api.scryfall.com/cards/takh/18/en?format=image"); @@ -117,7 +117,7 @@ public class ScryfallImageSupportTokens { put("AKH/Warrior", "https://api.scryfall.com/cards/takh/17/en?format=image"); put("AKH/Wurm", "https://api.scryfall.com/cards/takh/24/en?format=image"); put("AKH/Zombie", "https://api.scryfall.com/cards/takh/20/en?format=image"); - //AKH - embalm ability (token from card) + // AKH - embalm ability (token from card) put("AKH/Angel of Sanctions", "https://api.scryfall.com/cards/takh/1/en?format=image"); put("AKH/Anointer Priest", "https://api.scryfall.com/cards/takh/2/en?format=image"); put("AKH/Aven Initiate", "https://api.scryfall.com/cards/takh/3/en?format=image"); @@ -134,13 +134,13 @@ public class ScryfallImageSupportTokens { put("AKH/Unwavering Initiate", "https://api.scryfall.com/cards/takh/14/en?format=image"); put("AKH/Vizier of Many Faces", "https://api.scryfall.com/cards/takh/15/en?format=image"); - //AER + // AER put("AER/Etherium Cell", "https://api.scryfall.com/cards/taer/3/en?format=image"); put("AER/Gremlin", "https://api.scryfall.com/cards/taer/1/en?format=image"); put("AER/Ragavan", "https://api.scryfall.com/cards/taer/2/en?format=image"); put("AER/Emblem Tezzeret", "https://api.scryfall.com/cards/taer/4/en?format=image"); - //KLD + // KLD put("KLD/Beast", "https://api.scryfall.com/cards/tkld/1/en?format=image"); put("KLD/Emblem Chandra", "https://api.scryfall.com/cards/tkld/10/en?format=image"); put("KLD/Construct/1", "https://api.scryfall.com/cards/tkld/2/en?format=image"); @@ -154,7 +154,7 @@ public class ScryfallImageSupportTokens { put("KLD/Thopter/2", "https://api.scryfall.com/cards/tkld/8/en?format=image"); put("KLD/Thopter/3", "https://api.scryfall.com/cards/tkld/9/en?format=image"); - //EMN + // EMN put("EMN/Eldrazi Horror", "https://api.scryfall.com/cards/temn/1/en?format=image"); put("EMN/Human", "https://api.scryfall.com/cards/temn/7/en?format=image"); put("EMN/Human Wizard", "https://api.scryfall.com/cards/temn/2/en?format=image"); @@ -166,7 +166,7 @@ public class ScryfallImageSupportTokens { put("EMN/Zombie/3", "https://api.scryfall.com/cards/temn/5/en?format=image"); put("EMN/Zombie/4", "https://api.scryfall.com/cards/temn/6/en?format=image"); - //SOI + // SOI put("SOI/Angel", "https://api.scryfall.com/cards/tsoi/1/en?format=image"); put("SOI/Emblem Arlinn", "https://api.scryfall.com/cards/tsoi/18/en?format=image"); put("SOI/Clue/1", "https://api.scryfall.com/cards/tsoi/11/en?format=image"); @@ -186,7 +186,7 @@ public class ScryfallImageSupportTokens { put("SOI/Wolf", "https://api.scryfall.com/cards/tsoi/9/en?format=image"); put("SOI/Zombie", "https://api.scryfall.com/cards/tsoi/5/en?format=image"); - //OGW + // OGW put("OGW/Angel", "https://api.scryfall.com/cards/togw/7/en?format=image"); put("OGW/Eldrazi Scion/1", "https://api.scryfall.com/cards/togw/1/en?format=image"); put("OGW/Eldrazi Scion/2", "https://api.scryfall.com/cards/togw/2/en?format=image"); @@ -199,7 +199,7 @@ public class ScryfallImageSupportTokens { put("OGW/Plant", "https://api.scryfall.com/cards/togw/11/en?format=image"); put("OGW/Zombie", "https://api.scryfall.com/cards/togw/8/en?format=image"); - //BFZ + // BFZ put("BFZ/Dragon", "https://api.scryfall.com/cards/tbfz/8/en?format=image"); put("BFZ/Eldrazi", "https://api.scryfall.com/cards/tbfz/1/en?format=image"); put("BFZ/Eldrazi Scion/1", "https://api.scryfall.com/cards/tbfz/2/en?format=image"); @@ -319,7 +319,7 @@ public class ScryfallImageSupportTokens { put("C18/Worm", "https://api.scryfall.com/cards/tc18/18/en?format=image"); put("C18/Zombie", "https://api.scryfall.com/cards/tc18/9/en?format=image"); - //C19 + // C19 put("C19/Assassin", "https://api.scryfall.com/cards/tc19/9/en?format=image"); put("C19/Beast/1", "https://api.scryfall.com/cards/tc19/13/en?format=image"); put("C19/Beast/2", "https://api.scryfall.com/cards/tc19/14/en?format=image"); @@ -2167,7 +2167,7 @@ public class ScryfallImageSupportTokens { put("WOC/Spirit", "https://api.scryfall.com/cards/twoc/17/en?format=image"); put("WOC/Virtuous", "https://api.scryfall.com/cards/twoc/3/en?format=image"); - //WHO + // WHO put("WHO/Alien Insect", "https://api.scryfall.com/cards/twho/19/en?format=image"); // 8ED @@ -2491,6 +2491,9 @@ public class ScryfallImageSupportTokens { put("FDN/Emblem Vivien", "https://api.scryfall.com/cards/tfdn/25/en?format=image"); put("FDN/Zombie", "https://api.scryfall.com/cards/tfdn/15/en?format=image"); + // H17 + put("H17/Dragon", "https://api.scryfall.com/cards/h17/4/en?format=image"); + // generate supported sets supportedSets.clear(); for (String cardName : this.keySet()) { diff --git a/Mage.Client/src/test/java/mage/client/util/DownloaderTest.java b/Mage.Client/src/test/java/mage/client/util/DownloaderTest.java index 6898daf1ed5..68034c47597 100644 --- a/Mage.Client/src/test/java/mage/client/util/DownloaderTest.java +++ b/Mage.Client/src/test/java/mage/client/util/DownloaderTest.java @@ -16,20 +16,35 @@ public class DownloaderTest { @Test public void test_DownloadText_ByHttp() { - String s = XmageURLConnection.downloadText("http://google.com"); + String s = XmageURLConnection.downloadText("http://example.com"); Assert.assertTrue("must have text data", s.contains("")); } @Test public void test_DownloadText_ByHttps() { - String s = XmageURLConnection.downloadText("https://google.com"); + String s = XmageURLConnection.downloadText("https://example.com"); Assert.assertTrue("must have text data", s.contains("")); } + @Test + public void test_DownloadText_ByRedirectProtocol() { + // http to https restricted by design, see https://stackoverflow.com/a/1884427/1276632 + // it's not critical for a client (e.g. for images download), so no needs in custom implementation + // like xmage launcher does + String s = XmageURLConnection.downloadText("http://github.com"); + Assert.assertTrue("must have fail on https redirect (301 result)", s.isEmpty()); + } + + @Test + public void test_DownloadText_ByRedirectUri() { + String s = XmageURLConnection.downloadText("https://github.com/magefree/mage/issues/new"); + Assert.assertTrue("must have text data (redirect to login page)", s.contains("Sign in to GitHub")); + } + @Test public void test_DownloadFile_ByHttp() throws IOException { // use any public image here - InputStream stream = XmageURLConnection.downloadBinary("http://www.google.com/tia/tia.png"); + InputStream stream = XmageURLConnection.downloadBinary("http://xmage.today/images/xmage-logo.png"); Assert.assertNotNull(stream); BufferedImage image = ImageIO.read(stream); Assert.assertNotNull(stream); @@ -39,7 +54,7 @@ public class DownloaderTest { @Test public void test_DownloadFile_ByHttps() throws IOException { // use any public image here - InputStream stream = XmageURLConnection.downloadBinary("https://www.google.com/tia/tia.png"); + InputStream stream = XmageURLConnection.downloadBinary("https://xmage.today/images/xmage-logo.png"); Assert.assertNotNull(stream); BufferedImage image = ImageIO.read(stream); Assert.assertNotNull(stream); diff --git a/Mage.Common/src/main/java/mage/utils/MageVersion.java b/Mage.Common/src/main/java/mage/utils/MageVersion.java index 48892660657..0cd7917d144 100644 --- a/Mage.Common/src/main/java/mage/utils/MageVersion.java +++ b/Mage.Common/src/main/java/mage/utils/MageVersion.java @@ -18,7 +18,7 @@ public class MageVersion implements Serializable, Comparable { public static final int MAGE_VERSION_MAJOR = 1; public static final int MAGE_VERSION_MINOR = 4; public static final int MAGE_VERSION_RELEASE = 55; - public static final String MAGE_VERSION_RELEASE_INFO = "V2"; // V1, V1a, V1b for releases; V1-beta3, V1-beta4 for betas + public static final String MAGE_VERSION_RELEASE_INFO = "V3"; // V1, V1a, V1b for releases; V1-beta3, V1-beta4 for betas // strict mode // Each update requires a strict version diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java index d2be2b0444c..a0dfd0d541b 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Legacy.java @@ -59,6 +59,7 @@ public class Legacy extends Constructed { banned.add("Necropotence"); banned.add("Oath of Druids"); banned.add("Oko, Thief of Crowns"); + banned.add("Psychic Frog"); banned.add("Ragavan, Nimble Pilferer"); banned.add("Sensei's Divining Top"); banned.add("Skullclamp"); @@ -73,6 +74,7 @@ public class Legacy extends Constructed { banned.add("Treasure Cruise"); banned.add("Underworld Breach"); banned.add("Vampiric Tutor"); + banned.add("Vexing Bauble"); banned.add("Wheel of Fortune"); banned.add("Windfall"); banned.add("Wrenn and Six"); diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Modern.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Modern.java index 897a8368bd4..f079385508e 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Modern.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Modern.java @@ -23,6 +23,7 @@ public class Modern extends Constructed { } } + banned.add("Amped Raptor"); banned.add("Ancient Den"); banned.add("Arcum's Astrolabe"); banned.add("Birthing Pod"); @@ -35,21 +36,19 @@ public class Modern extends Constructed { banned.add("Dig Through Time"); banned.add("Dread Return"); banned.add("Eye of Ugin"); - banned.add("Faithless Looting"); banned.add("Field of the Dead"); banned.add("Fury"); banned.add("Gitaxian Probe"); banned.add("Glimpse of Nature"); banned.add("Golgari Grave-Troll"); banned.add("Great Furnace"); - banned.add("Green Sun's Zenith"); banned.add("Grief"); banned.add("Hogaak, Arisen Necropolis"); banned.add("Hypergenesis"); + banned.add("Jegantha, the Wellspring"); banned.add("Krark-Clan Ironworks"); banned.add("Lurrus of the Dream-Den"); banned.add("Mental Misstep"); - banned.add("Mox Opal"); banned.add("Mycosynth Lattice"); banned.add("Mystic Sanctuary"); banned.add("Nadu, Winged Wisdom"); @@ -64,8 +63,8 @@ public class Modern extends Constructed { banned.add("Sensei's Divining Top"); banned.add("Simian Spirit Guide"); banned.add("Skullclamp"); - banned.add("Splinter Twin"); banned.add("Summer Bloom"); + banned.add("The One Ring"); banned.add("Tibalt's Trickery"); banned.add("Treasure Cruise"); banned.add("Tree of Tales"); diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Pioneer.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Pioneer.java index bb3156d401f..4d86075f3ee 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Pioneer.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/Pioneer.java @@ -31,6 +31,7 @@ public class Pioneer extends Constructed { banned.add("Flooded Strand"); banned.add("Geological Appraiser"); banned.add("Inverter of Truth"); + banned.add("Jegantha, the Wellspring"); banned.add("Karn, the Great Creator"); banned.add("Kethis, the Hidden Hand"); banned.add("Leyline of Abundance"); diff --git a/Mage.Sets/src/mage/cards/a/AbzanCharm.java b/Mage.Sets/src/mage/cards/a/AbzanCharm.java index 0de055f435a..288f3e1c380 100644 --- a/Mage.Sets/src/mage/cards/a/AbzanCharm.java +++ b/Mage.Sets/src/mage/cards/a/AbzanCharm.java @@ -11,7 +11,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.ComparisonType; -import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.PowerPredicate; import mage.target.common.TargetCreaturePermanent; @@ -43,7 +42,7 @@ public final class AbzanCharm extends CardImpl { this.getSpellAbility().addMode(mode); // *Distribute two +1/+1 counters among one or two target creatures. - mode = new Mode(new DistributeCountersEffect(2, "one or two target creatures")); + mode = new Mode(new DistributeCountersEffect()); mode.addTarget(new TargetCreaturePermanentAmount(2)); this.getSpellAbility().addMode(mode); diff --git a/Mage.Sets/src/mage/cards/a/AerialVolley.java b/Mage.Sets/src/mage/cards/a/AerialVolley.java index 2f44c92fbef..557da14e0f2 100644 --- a/Mage.Sets/src/mage/cards/a/AerialVolley.java +++ b/Mage.Sets/src/mage/cards/a/AerialVolley.java @@ -26,7 +26,7 @@ public final class AerialVolley extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{G}"); // Aerial Volley deals 3 damage divided as you choose among one, two, or three target creatures with flying. - this.getSpellAbility().addEffect(new DamageMultiEffect(3)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(3, filter)); } diff --git a/Mage.Sets/src/mage/cards/a/AjaniMentorOfHeroes.java b/Mage.Sets/src/mage/cards/a/AjaniMentorOfHeroes.java index 02796fb08c5..ce244b2c975 100644 --- a/Mage.Sets/src/mage/cards/a/AjaniMentorOfHeroes.java +++ b/Mage.Sets/src/mage/cards/a/AjaniMentorOfHeroes.java @@ -9,7 +9,6 @@ import mage.abilities.effects.common.counter.DistributeCountersEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.counters.CounterType; import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.filter.predicate.Predicates; @@ -38,7 +37,7 @@ public final class AjaniMentorOfHeroes extends CardImpl { this.setStartingLoyalty(4); // +1: Distribute three +1/+1 counters among one, two, or three target creatures you control - Ability ability = new LoyaltyAbility(new DistributeCountersEffect(3, "one, two, or three target creatures you control"), 1); + Ability ability = new LoyaltyAbility(new DistributeCountersEffect(), 1); ability.addTarget(new TargetCreaturePermanentAmount(3, StaticFilters.FILTER_CONTROLLED_CREATURES)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/a/AjaniSleeperAgent.java b/Mage.Sets/src/mage/cards/a/AjaniSleeperAgent.java index 091d653edea..0ac62db118b 100644 --- a/Mage.Sets/src/mage/cards/a/AjaniSleeperAgent.java +++ b/Mage.Sets/src/mage/cards/a/AjaniSleeperAgent.java @@ -15,11 +15,9 @@ import mage.constants.*; import mage.abilities.keyword.CompleatedAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.counters.CounterType; import mage.game.Game; import mage.game.command.emblems.AjaniSleeperAgentEmblem; import mage.players.Player; -import mage.target.Target; import mage.target.common.TargetCreaturePermanentAmount; /** @@ -42,12 +40,10 @@ public final class AjaniSleeperAgent extends CardImpl { this.addAbility(new LoyaltyAbility(new AjaniSleeperAgentEffect(), 1)); // −3: Distribute three +1/+1 counters among up to three target creatures. They gain vigilance until end of turn. - Ability ability = new LoyaltyAbility(new DistributeCountersEffect(3, "up to three target creatures"), -3); + Ability ability = new LoyaltyAbility(new DistributeCountersEffect() + .setText("distribute three +1/+1 counters among up to three target creatures"), -3); ability.addEffect(new GainAbilityTargetEffect(VigilanceAbility.getInstance()).setText("They gain vigilance until end of turn")); - Target target = new TargetCreaturePermanentAmount(3); - target.setMinNumberOfTargets(0); - target.setMaxNumberOfTargets(3); - ability.addTarget(target); + ability.addTarget(new TargetCreaturePermanentAmount(3, 0, 3)); this.addAbility(ability); // −6: You get an emblem with "Whenever you cast a creature or planeswalker spell, target opponent gets two poison counters." diff --git a/Mage.Sets/src/mage/cards/a/AmethystDragon.java b/Mage.Sets/src/mage/cards/a/AmethystDragon.java index 426a3878ac4..fcb9ceedb6a 100644 --- a/Mage.Sets/src/mage/cards/a/AmethystDragon.java +++ b/Mage.Sets/src/mage/cards/a/AmethystDragon.java @@ -32,7 +32,7 @@ public final class AmethystDragon extends AdventureCard { // Explosive Crystal // Explosive Crystal deals 4 damage divided as you choose among any number of targets. - this.getSpellCard().getSpellAbility().addEffect(new DamageMultiEffect(4)); + this.getSpellCard().getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellCard().getSpellAbility().addTarget(new TargetAnyTargetAmount(4)); this.finalizeAdventure(); diff --git a/Mage.Sets/src/mage/cards/a/AngelOfSalvation.java b/Mage.Sets/src/mage/cards/a/AngelOfSalvation.java index 2eeb1a5a056..c43ac853406 100644 --- a/Mage.Sets/src/mage/cards/a/AngelOfSalvation.java +++ b/Mage.Sets/src/mage/cards/a/AngelOfSalvation.java @@ -34,7 +34,7 @@ public final class AngelOfSalvation extends CardImpl { this.addAbility(new ConvokeAbility()); // Flying this.addAbility(FlyingAbility.getInstance()); - // When Angel of Salvation enters the battlefield, prevent the next 5 damage that would be dealt this turn to any number of target creatures and/or players, divided as you choose. + // When Angel of Salvation enters, prevent the next 5 damage that would be dealt this turn to any number of targets, divided as you choose. Ability ability = new EntersBattlefieldTriggeredAbility(new PreventDamageToTargetMultiAmountEffect(Duration.EndOfTurn, 5)); ability.addTarget(new TargetAnyTargetAmount(5)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/a/ArcLightning.java b/Mage.Sets/src/mage/cards/a/ArcLightning.java index d27a09fd71d..8e79e37bb07 100644 --- a/Mage.Sets/src/mage/cards/a/ArcLightning.java +++ b/Mage.Sets/src/mage/cards/a/ArcLightning.java @@ -17,7 +17,7 @@ public final class ArcLightning extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{R}"); // Arc Lightning deals 3 damage divided as you choose among one, two, or three targets. - this.getSpellAbility().addEffect(new DamageMultiEffect(3)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(3)); } diff --git a/Mage.Sets/src/mage/cards/a/ArcMage.java b/Mage.Sets/src/mage/cards/a/ArcMage.java index e4f001a97f5..5087bdc013c 100644 --- a/Mage.Sets/src/mage/cards/a/ArcMage.java +++ b/Mage.Sets/src/mage/cards/a/ArcMage.java @@ -12,7 +12,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; import mage.target.common.TargetAnyTargetAmount; /** @@ -29,7 +28,7 @@ public final class ArcMage extends CardImpl { this.toughness = new MageInt(2); // {2}{R}, {tap}, Discard a card: Arc Mage deals 2 damage divided as you choose among one or two targets. - Ability ability = new SimpleActivatedAbility(new DamageMultiEffect(2), new ManaCostsImpl<>("{2}{R}")); + Ability ability = new SimpleActivatedAbility(new DamageMultiEffect(), new ManaCostsImpl<>("{2}{R}")); ability.addCost(new TapSourceCost()); ability.addCost(new DiscardCardCost()); ability.addTarget(new TargetAnyTargetAmount(2)); diff --git a/Mage.Sets/src/mage/cards/a/ArmamentCorps.java b/Mage.Sets/src/mage/cards/a/ArmamentCorps.java index 454359f62e7..3a1ca243565 100644 --- a/Mage.Sets/src/mage/cards/a/ArmamentCorps.java +++ b/Mage.Sets/src/mage/cards/a/ArmamentCorps.java @@ -9,7 +9,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanentAmount; @@ -28,7 +27,7 @@ public final class ArmamentCorps extends CardImpl { this.toughness = new MageInt(4); // When Armament Corps enters the battlefield, distribute two +1/+1 counters among one or two target creatures you control. - Ability ability = new EntersBattlefieldTriggeredAbility(new DistributeCountersEffect(2, "one or two target creatures you control"), false); + Ability ability = new EntersBattlefieldTriggeredAbility(new DistributeCountersEffect(), false); ability.addTarget(new TargetCreaturePermanentAmount(2, StaticFilters.FILTER_CONTROLLED_CREATURES)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/ArrowVolleyTrap.java b/Mage.Sets/src/mage/cards/a/ArrowVolleyTrap.java index a846575e5d5..4dca2f81887 100644 --- a/Mage.Sets/src/mage/cards/a/ArrowVolleyTrap.java +++ b/Mage.Sets/src/mage/cards/a/ArrowVolleyTrap.java @@ -27,7 +27,7 @@ public final class ArrowVolleyTrap extends CardImpl { this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl<>("{1}{W}"), ArrowVolleyTrapCondition.instance)); // Arrow Volley Trap deals 5 damage divided as you choose among any number of target attacking creatures. - this.getSpellAbility().addEffect(new DamageMultiEffect(5)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(5, StaticFilters.FILTER_ATTACKING_CREATURES)); } diff --git a/Mage.Sets/src/mage/cards/a/AureliasFury.java b/Mage.Sets/src/mage/cards/a/AureliasFury.java index 1febd70e70c..9f84a9c4aa8 100644 --- a/Mage.Sets/src/mage/cards/a/AureliasFury.java +++ b/Mage.Sets/src/mage/cards/a/AureliasFury.java @@ -59,10 +59,10 @@ public final class AureliasFury extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{R}{W}"); - // Aurelia's Fury deals X damage divided as you choose among any number of target creatures and/or players. + // Aurelia's Fury deals X damage divided as you choose among any number of targets. // Tap each creature dealt damage this way. Players dealt damage this way can't cast noncreature spells this turn. DynamicValue xValue = GetXValue.instance; - this.getSpellAbility().addEffect(new DamageMultiEffect(xValue)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addEffect(new AureliasFuryEffect()); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(xValue)); this.getSpellAbility().addWatcher(new AureliasFuryDamagedByWatcher()); diff --git a/Mage.Sets/src/mage/cards/a/AvacynsJudgment.java b/Mage.Sets/src/mage/cards/a/AvacynsJudgment.java index 5d6601dfba0..4e9ddc806b9 100644 --- a/Mage.Sets/src/mage/cards/a/AvacynsJudgment.java +++ b/Mage.Sets/src/mage/cards/a/AvacynsJudgment.java @@ -31,9 +31,9 @@ public final class AvacynsJudgment extends CardImpl { ability.setRuleAtTheTop(true); this.addAbility(ability); - // Avacyn's Judgment deals 2 damage divided as you choose among any number of target creatures and/or players. If Avacyn's Judgment's madness cost was paid, it deals X damage divided as you choose among those creatures and/or players instead. + // Avacyn's Judgment deals 2 damage divided as you choose among any number of targets. If this spell's madness cost was paid, it deals X damage divided as you choose among those creatures and/or players instead. DynamicValue xValue = new AvacynsJudgmentManacostVariableValue(); - Effect effect = new DamageMultiEffect(xValue); + Effect effect = new DamageMultiEffect(); effect.setText("{this} deals 2 damage divided as you choose among any number of targets. If this spell's madness cost was paid, it deals X damage divided as you choose among those permanents and/or players instead."); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(xValue)); diff --git a/Mage.Sets/src/mage/cards/a/AwakenTheMaelstrom.java b/Mage.Sets/src/mage/cards/a/AwakenTheMaelstrom.java index 79b45c6a0c0..ff4ba76a560 100644 --- a/Mage.Sets/src/mage/cards/a/AwakenTheMaelstrom.java +++ b/Mage.Sets/src/mage/cards/a/AwakenTheMaelstrom.java @@ -115,8 +115,6 @@ class AwakenTheMaelstromEffect extends OneShotEffect { return; } TargetPermanentAmount target = new TargetCreaturePermanentAmount(3, StaticFilters.FILTER_CONTROLLED_CREATURE); - target.setMinNumberOfTargets(1); - target.setMaxNumberOfTargets(3); target.withNotTarget(true); target.withChooseHint("to distribute counters"); target.chooseTarget(outcome, player.getId(), source, game); diff --git a/Mage.Sets/src/mage/cards/b/BannerOfKinship.java b/Mage.Sets/src/mage/cards/b/BannerOfKinship.java new file mode 100644 index 00000000000..24bfa8eaaf2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BannerOfKinship.java @@ -0,0 +1,113 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ChooseCreatureTypeEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author PurpleCrowbar + */ +public final class BannerOfKinship extends CardImpl { + + public BannerOfKinship(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{5}"); + + // As this artifact enters, choose a creature type. This artifact enters with a fellowship counter on it for each creature you control of the chosen type. + AsEntersBattlefieldAbility ability = new AsEntersBattlefieldAbility(new ChooseCreatureTypeEffect(Outcome.BoostCreature)); + ability.addEffect(new BannerOfKinshipEffect()); + this.addAbility(ability); + + // Creatures you control of the chosen type get +1/+1 for each fellowship counter on this artifact. + this.addAbility(new SimpleStaticAbility(new BannerOfKinshipBoostEffect())); + } + + private BannerOfKinship(final BannerOfKinship card) { + super(card); + } + + @Override + public BannerOfKinship copy() { + return new BannerOfKinship(this); + } +} + +class BannerOfKinshipEffect extends OneShotEffect { + + BannerOfKinshipEffect() { + super(Outcome.BoostCreature); + this.staticText = "This artifact enters with a fellowship counter on it for each creature you control of the chosen type"; + } + + private BannerOfKinshipEffect(final BannerOfKinshipEffect effect) { + super(effect); + } + + @Override + public BannerOfKinshipEffect copy() { + return new BannerOfKinshipEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanentEntering(source.getSourceId()); + if (controller == null || permanent == null) { + return false; + } + SubType subtype = (SubType) game.getState().getValue(source.getSourceId() + "_type"); + int amount = game.getBattlefield().getAllActivePermanents(new FilterControlledCreaturePermanent(subtype), source.getControllerId(), game).size(); + if (amount > 0) { + permanent.addCounters(CounterType.FELLOWSHIP.createInstance(amount), source.getControllerId(), source, game); + } + return true; + } +} + +class BannerOfKinshipBoostEffect extends ContinuousEffectImpl { + + BannerOfKinshipBoostEffect() { + super(Duration.WhileOnBattlefield, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.BoostCreature); + staticText = "Creatures you control of the chosen type get +1/+1 for each fellowship counter on this artifact"; + } + + private BannerOfKinshipBoostEffect(final BannerOfKinshipBoostEffect effect) { + super(effect); + } + + @Override + public BannerOfKinshipBoostEffect copy() { + return new BannerOfKinshipBoostEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); + if (permanent != null) { + SubType subtype = (SubType) game.getState().getValue(permanent.getId() + "_type"); + if (subtype != null) { + for (Permanent perm : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURES, source.getControllerId(), game)) { + if (perm.hasSubtype(subtype, game)) { + int boost = permanent.getCounters(game).getCount(CounterType.FELLOWSHIP); + perm.addPower(boost); + perm.addToughness(boost); + } + } + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BiogenicUpgrade.java b/Mage.Sets/src/mage/cards/b/BiogenicUpgrade.java index af5253deaee..8543e1101c4 100644 --- a/Mage.Sets/src/mage/cards/b/BiogenicUpgrade.java +++ b/Mage.Sets/src/mage/cards/b/BiogenicUpgrade.java @@ -26,10 +26,7 @@ public final class BiogenicUpgrade extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{G}{G}"); // Distribute three +1/+1 counters among one, two, or three target creatures, then double the number of +1/+1 counters on each of those creatures. - this.getSpellAbility().addEffect(new DistributeCountersEffect( - 3, - "one, two, or three target creatures" - )); + this.getSpellAbility().addEffect(new DistributeCountersEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(3)); this.getSpellAbility().addEffect(new BiogenicUpgradeEffect()); } diff --git a/Mage.Sets/src/mage/cards/b/BlasterHulk.java b/Mage.Sets/src/mage/cards/b/BlasterHulk.java index 057abb8c510..5c76d07c028 100644 --- a/Mage.Sets/src/mage/cards/b/BlasterHulk.java +++ b/Mage.Sets/src/mage/cards/b/BlasterHulk.java @@ -46,7 +46,7 @@ public final class BlasterHulk extends CardImpl { // Whenever Blaster Hulk attacks, you get {E}{E}, then you may pay eight {E}. When you do, Blaster Hulk deals 8 damage divided as you choose among up to eight targets. Ability ability = new AttacksTriggeredAbility(new GetEnergyCountersControllerEffect(2)); - ReflexiveTriggeredAbility reflexiveAbility = new ReflexiveTriggeredAbility(new DamageMultiEffect(8) + ReflexiveTriggeredAbility reflexiveAbility = new ReflexiveTriggeredAbility(new DamageMultiEffect() .setText("{this} deals 8 damage divided as you choose among up to eight targets"), false); reflexiveAbility.addTarget(new TargetAnyTargetAmount(8)); ability.addEffect(new DoWhenCostPaid(reflexiveAbility, new PayEnergyCost(8), diff --git a/Mage.Sets/src/mage/cards/b/BlessingOfFrost.java b/Mage.Sets/src/mage/cards/b/BlessingOfFrost.java index e1b11556cae..c20d9a720d1 100644 --- a/Mage.Sets/src/mage/cards/b/BlessingOfFrost.java +++ b/Mage.Sets/src/mage/cards/b/BlessingOfFrost.java @@ -79,8 +79,7 @@ class BlessingOfFrostEffect extends OneShotEffect { int snow = ManaPaidSourceWatcher.getSnowPaid(source.getId(), game); int potentialTarget = game.getBattlefield().count(StaticFilters.FILTER_CONTROLLED_CREATURE, player.getId(), source, game); if (snow > 0 && potentialTarget > 0) { - TargetAmount target = new TargetCreaturePermanentAmount(snow, StaticFilters.FILTER_CONTROLLED_CREATURE); - target.setMinNumberOfTargets(1); + TargetAmount target = new TargetCreaturePermanentAmount(snow, 0, snow, StaticFilters.FILTER_CONTROLLED_CREATURE); target.withNotTarget(true); target.chooseTarget(outcome, player.getId(), source, game); for (UUID targetId : target.getTargets()) { diff --git a/Mage.Sets/src/mage/cards/b/BlessingsOfNature.java b/Mage.Sets/src/mage/cards/b/BlessingsOfNature.java index b6f13ca0c49..20dbef1ef91 100644 --- a/Mage.Sets/src/mage/cards/b/BlessingsOfNature.java +++ b/Mage.Sets/src/mage/cards/b/BlessingsOfNature.java @@ -5,7 +5,6 @@ import mage.abilities.keyword.MiracleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.counters.CounterType; import mage.target.common.TargetCreaturePermanentAmount; import java.util.UUID; @@ -19,7 +18,7 @@ public final class BlessingsOfNature extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{G}"); // Distribute four +1/+1 counters among any number of target creatures. - this.getSpellAbility().addEffect(new DistributeCountersEffect(4, "any number of target creatures")); + this.getSpellAbility().addEffect(new DistributeCountersEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(4)); this.addAbility(new MiracleAbility("{G}")); diff --git a/Mage.Sets/src/mage/cards/b/BogardanHellkite.java b/Mage.Sets/src/mage/cards/b/BogardanHellkite.java index 2ea935eafa8..7d3443dabbc 100644 --- a/Mage.Sets/src/mage/cards/b/BogardanHellkite.java +++ b/Mage.Sets/src/mage/cards/b/BogardanHellkite.java @@ -28,9 +28,14 @@ public final class BogardanHellkite extends CardImpl { this.power = new MageInt(5); this.toughness = new MageInt(5); + // Flash this.addAbility(FlashAbility.getInstance()); + + // Flying this.addAbility(FlyingAbility.getInstance()); - Ability ability = new EntersBattlefieldTriggeredAbility(new DamageMultiEffect(5, "it"), false); + + // When Bogardan Hellkite enters, it deals 5 damage divided as you choose among any number of targets. + Ability ability = new EntersBattlefieldTriggeredAbility(new DamageMultiEffect("it"), false); ability.addTarget(new TargetAnyTargetAmount(5)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/b/Boulderfall.java b/Mage.Sets/src/mage/cards/b/Boulderfall.java index aa052a79005..48835dbc38e 100644 --- a/Mage.Sets/src/mage/cards/b/Boulderfall.java +++ b/Mage.Sets/src/mage/cards/b/Boulderfall.java @@ -17,7 +17,7 @@ public final class Boulderfall extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{6}{R}{R}"); // Boulderfall deals 5 damage divided as you choose among any number of targets. - this.getSpellAbility().addEffect(new DamageMultiEffect(5)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(5)); } diff --git a/Mage.Sets/src/mage/cards/b/BountyOfTheHunt.java b/Mage.Sets/src/mage/cards/b/BountyOfTheHunt.java index 082ba4df231..77e146b7e18 100644 --- a/Mage.Sets/src/mage/cards/b/BountyOfTheHunt.java +++ b/Mage.Sets/src/mage/cards/b/BountyOfTheHunt.java @@ -7,7 +7,6 @@ import mage.abilities.effects.common.counter.DistributeCountersEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.counters.CounterType; import mage.filter.common.FilterOwnedCard; import mage.filter.predicate.mageobject.ColorPredicate; import mage.target.common.TargetCardInHand; @@ -34,10 +33,7 @@ public final class BountyOfTheHunt extends CardImpl { this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(filter)))); // Distribute three +1/+1 counters among one, two, or three target creatures. For each +1/+1 counter you put on a creature this way, remove a +1/+1 counter from that creature at the beginning of the next cleanup step. - this.getSpellAbility().addEffect(new DistributeCountersEffect( - 3, - "one, two, or three target creatures" - ).withRemoveAtEndOfTurn()); + this.getSpellAbility().addEffect(new DistributeCountersEffect().withRemoveAtEndOfTurn()); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(3)); } diff --git a/Mage.Sets/src/mage/cards/b/Broodlord.java b/Mage.Sets/src/mage/cards/b/Broodlord.java index 1119984f9b4..734099c31fb 100644 --- a/Mage.Sets/src/mage/cards/b/Broodlord.java +++ b/Mage.Sets/src/mage/cards/b/Broodlord.java @@ -16,7 +16,7 @@ import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.Target; -import mage.target.common.TargetPermanentAmount; +import mage.target.common.TargetCreaturePermanentAmount; import java.util.UUID; @@ -37,7 +37,7 @@ public final class Broodlord extends CardImpl { // Brood Telepathy -- When Broodlord enters the battlefield, distribute X +1/+1 counters among any number of other target creatures you control. Ability ability = new EntersBattlefieldTriggeredAbility(new BroodlordEffect()); - ability.addTarget(new TargetPermanentAmount(GetXValue.instance, StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE)); + ability.addTarget(new TargetCreaturePermanentAmount(GetXValue.instance, StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE)); this.addAbility(ability.withFlavorWord("Brood Telepathy")); } diff --git a/Mage.Sets/src/mage/cards/c/CaptainAmericaFirstAvenger.java b/Mage.Sets/src/mage/cards/c/CaptainAmericaFirstAvenger.java index 9b8da42a407..34ecb776194 100644 --- a/Mage.Sets/src/mage/cards/c/CaptainAmericaFirstAvenger.java +++ b/Mage.Sets/src/mage/cards/c/CaptainAmericaFirstAvenger.java @@ -53,11 +53,11 @@ public final class CaptainAmericaFirstAvenger extends CardImpl { // Throw ... — {3}, Unattach an Equipment from Captain America: He deals damage equal to that Equipment’s mana value divided as you choose among one, two, or three targets. Ability ability = new SimpleActivatedAbility( - new DamageMultiEffect(CaptainAmericaFirstAvengerValue.instance).setText( + new DamageMultiEffect().setText( "he deals damage equal to that Equipment's mana value divided as you choose among one, two, or three targets."), new GenericManaCost(3)); ability.addCost(new CaptainAmericaFirstAvengerUnattachCost()); - ability.addTarget(new TargetAnyTargetAmount(CaptainAmericaFirstAvengerValue.instance, 3)); + ability.addTarget(new TargetAnyTargetAmount(CaptainAmericaFirstAvengerValue.instance, 1, 3)); this.addAbility(ability.withFlavorWord("Throw ...")); // ... Catch — At the beginning of combat on your turn, attach up to one target Equipment you control to Captain America. diff --git a/Mage.Sets/src/mage/cards/c/CaseOfTheTrampledGarden.java b/Mage.Sets/src/mage/cards/c/CaseOfTheTrampledGarden.java index bc7153a94ce..127b95a330e 100644 --- a/Mage.Sets/src/mage/cards/c/CaseOfTheTrampledGarden.java +++ b/Mage.Sets/src/mage/cards/c/CaseOfTheTrampledGarden.java @@ -26,7 +26,7 @@ import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetAttackingCreature; -import mage.target.common.TargetPermanentAmount; +import mage.target.common.TargetCreaturePermanentAmount; /** * @@ -40,11 +40,8 @@ public final class CaseOfTheTrampledGarden extends CardImpl { this.subtype.add(SubType.CASE); // When this Case enters the battlefield, distribute two +1/+1 counters among one or two target creatures you control. - Ability initialAbility = new EntersBattlefieldTriggeredAbility(new DistributeCountersEffect(2, - "one or two target creatures you control")); - TargetPermanentAmount target = new TargetPermanentAmount(2, StaticFilters.FILTER_CONTROLLED_CREATURES); - target.setMinNumberOfTargets(1); - initialAbility.addTarget(target); + Ability initialAbility = new EntersBattlefieldTriggeredAbility(new DistributeCountersEffect()); + initialAbility.addTarget(new TargetCreaturePermanentAmount(2, StaticFilters.FILTER_CONTROLLED_CREATURES)); // To solve -- Creatures you control have total power 8 or greater. // Solved -- Whenever you attack, put a +1/+1 counter on target attacking creature. It gains trample until end of turn. Ability solvedAbility = new ConditionalTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/c/ChandraFlameshaper.java b/Mage.Sets/src/mage/cards/c/ChandraFlameshaper.java index 477a9432f55..0f5222ca23d 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraFlameshaper.java +++ b/Mage.Sets/src/mage/cards/c/ChandraFlameshaper.java @@ -50,7 +50,7 @@ public final class ChandraFlameshaper extends CardImpl { // -4: Chandra deals 8 damage divided as you choose among any number of target creatures and/or planeswalkers. Ability minusFourAbility = new LoyaltyAbility( - new DamageMultiEffect(8, "{this}"), -4 + new DamageMultiEffect(), -4 ); minusFourAbility.addTarget(new TargetCreatureOrPlaneswalkerAmount(8)); this.addAbility(minusFourAbility); diff --git a/Mage.Sets/src/mage/cards/c/ChandrasPyrohelix.java b/Mage.Sets/src/mage/cards/c/ChandrasPyrohelix.java index e760da10870..04f08ae92f9 100644 --- a/Mage.Sets/src/mage/cards/c/ChandrasPyrohelix.java +++ b/Mage.Sets/src/mage/cards/c/ChandrasPyrohelix.java @@ -17,7 +17,7 @@ public final class ChandrasPyrohelix extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); // Chandra's Pyrohelix deals 2 damage divided as you choose among one or two targets. - this.getSpellAbility().addEffect(new DamageMultiEffect(2)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(2)); } diff --git a/Mage.Sets/src/mage/cards/c/Conflagrate.java b/Mage.Sets/src/mage/cards/c/Conflagrate.java index 5b328705cb5..ca97bdb4e04 100644 --- a/Mage.Sets/src/mage/cards/c/Conflagrate.java +++ b/Mage.Sets/src/mage/cards/c/Conflagrate.java @@ -24,7 +24,7 @@ public final class Conflagrate extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{X}{R}"); // Conflagrate deals X damage divided as you choose among any number of targets. - this.getSpellAbility().addEffect(new DamageMultiEffect(GetXValue.instance)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(GetXValue.instance)); // Flashback-{R}{R}, Discard X cards. diff --git a/Mage.Sets/src/mage/cards/c/Contagion.java b/Mage.Sets/src/mage/cards/c/Contagion.java index 0e27f3f1b90..1369f0f881a 100644 --- a/Mage.Sets/src/mage/cards/c/Contagion.java +++ b/Mage.Sets/src/mage/cards/c/Contagion.java @@ -38,10 +38,7 @@ public final class Contagion extends CardImpl { // Distribute two -2/-1 counters among one or two target creatures. this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(2)); - this.getSpellAbility().addEffect(new DistributeCountersEffect( - CounterType.M2M1, 2, - "one or two target creatures" - )); + this.getSpellAbility().addEffect(new DistributeCountersEffect(CounterType.M2M1)); } private Contagion(final Contagion card) { diff --git a/Mage.Sets/src/mage/cards/c/CourtOfGarenbrig.java b/Mage.Sets/src/mage/cards/c/CourtOfGarenbrig.java index bb9072ea778..db12c9ee9a3 100644 --- a/Mage.Sets/src/mage/cards/c/CourtOfGarenbrig.java +++ b/Mage.Sets/src/mage/cards/c/CourtOfGarenbrig.java @@ -31,15 +31,9 @@ public final class CourtOfGarenbrig extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new BecomesMonarchSourceEffect()).addHint(MonarchHint.instance)); // At the beginning of your upkeep, distribute two +1/+1 counters among up to two target creatures. Then if you're the monarch, double the number of +1/+1 counters on each creature you control. - Ability ability = new BeginningOfUpkeepTriggeredAbility( - new DistributeCountersEffect( - 2, "up to two target creatures" - ) - ); - TargetCreaturePermanentAmount target = new TargetCreaturePermanentAmount(2); - target.setMinNumberOfTargets(0); - target.setMaxNumberOfTargets(2); - ability.addTarget(target); + Ability ability = new BeginningOfUpkeepTriggeredAbility(new DistributeCountersEffect() + .setText("distribute two +1/+1 counters among up to two target creatures")); + ability.addTarget(new TargetCreaturePermanentAmount(2, 0, 2)); ability.addEffect(new ConditionalOneShotEffect( new DoubleCounterOnEachPermanentEffect(CounterType.P1P1, StaticFilters.FILTER_CONTROLLED_CREATURE), MonarchIsSourceControllerCondition.instance diff --git a/Mage.Sets/src/mage/cards/c/CuratorsWard.java b/Mage.Sets/src/mage/cards/c/CuratorsWard.java index f77e6a62905..e420d69ae12 100644 --- a/Mage.Sets/src/mage/cards/c/CuratorsWard.java +++ b/Mage.Sets/src/mage/cards/c/CuratorsWard.java @@ -64,6 +64,7 @@ class CuratorsWardTriggeredAbility extends TriggeredAbilityImpl { public CuratorsWardTriggeredAbility() { super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(2), false); + setLeavesTheBattlefieldTrigger(true); } private CuratorsWardTriggeredAbility(final CuratorsWardTriggeredAbility ability) { diff --git a/Mage.Sets/src/mage/cards/d/DefendTheCelestus.java b/Mage.Sets/src/mage/cards/d/DefendTheCelestus.java index cf520b7eb23..ba11c5a1cb8 100644 --- a/Mage.Sets/src/mage/cards/d/DefendTheCelestus.java +++ b/Mage.Sets/src/mage/cards/d/DefendTheCelestus.java @@ -4,7 +4,6 @@ import mage.abilities.effects.common.counter.DistributeCountersEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanentAmount; @@ -19,10 +18,7 @@ public final class DefendTheCelestus extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}{G}"); // Distribute three +1/+1 counters among one, two, or three target creatures you control. - this.getSpellAbility().addEffect(new DistributeCountersEffect( - 3, - "one, two, or three target creatures you control" - )); + this.getSpellAbility().addEffect(new DistributeCountersEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount( 3, StaticFilters.FILTER_CONTROLLED_CREATURES )); diff --git a/Mage.Sets/src/mage/cards/d/DeftDismissal.java b/Mage.Sets/src/mage/cards/d/DeftDismissal.java index 53a76683cc4..03d5acbbc43 100644 --- a/Mage.Sets/src/mage/cards/d/DeftDismissal.java +++ b/Mage.Sets/src/mage/cards/d/DeftDismissal.java @@ -18,7 +18,7 @@ public final class DeftDismissal extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{W}"); // Deft Dismissal deals 3 damage divided as you choose among one, two, or three target attacking or blocking creatures. - this.getSpellAbility().addEffect(new DamageMultiEffect(3)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(3, StaticFilters.FILTER_ATTACKING_OR_BLOCKING_CREATURES)); } diff --git a/Mage.Sets/src/mage/cards/d/DragonlordAtarka.java b/Mage.Sets/src/mage/cards/d/DragonlordAtarka.java index def1083412f..38878c22ff1 100644 --- a/Mage.Sets/src/mage/cards/d/DragonlordAtarka.java +++ b/Mage.Sets/src/mage/cards/d/DragonlordAtarka.java @@ -43,11 +43,8 @@ public final class DragonlordAtarka extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // When Dragonlord Atarka enters the battlefield, it deals 5 damage divided as you choose among any number of target creatures and/or planeswalkers your opponents control. - Ability ability = new EntersBattlefieldTriggeredAbility(new DamageMultiEffect(5, "it"), false); - TargetCreatureOrPlaneswalkerAmount target = new TargetCreatureOrPlaneswalkerAmount(5, filter); - target.setMinNumberOfTargets(1); - target.setMaxNumberOfTargets(5); - ability.addTarget(target); + Ability ability = new EntersBattlefieldTriggeredAbility(new DamageMultiEffect("it"), false); + ability.addTarget(new TargetCreatureOrPlaneswalkerAmount(5, filter)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/e/Electrolyze.java b/Mage.Sets/src/mage/cards/e/Electrolyze.java index 3590b7067c3..45b502d181f 100644 --- a/Mage.Sets/src/mage/cards/e/Electrolyze.java +++ b/Mage.Sets/src/mage/cards/e/Electrolyze.java @@ -18,7 +18,7 @@ public final class Electrolyze extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}{R}"); // Electrolyze deals 2 damage divided as you choose among one or two targets. - this.getSpellAbility().addEffect(new DamageMultiEffect(2)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(2)); // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("
")); diff --git a/Mage.Sets/src/mage/cards/e/ElusiveOtter.java b/Mage.Sets/src/mage/cards/e/ElusiveOtter.java index fdc288de3bd..53c557f82a4 100644 --- a/Mage.Sets/src/mage/cards/e/ElusiveOtter.java +++ b/Mage.Sets/src/mage/cards/e/ElusiveOtter.java @@ -10,9 +10,7 @@ import mage.cards.AdventureCard; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.counters.CounterType; import mage.filter.StaticFilters; -import mage.target.Target; import mage.target.common.TargetCreaturePermanentAmount; import java.util.UUID; @@ -37,14 +35,9 @@ public final class ElusiveOtter extends AdventureCard { // Grove's Bounty // Distribute X +1/+1 counters among any number of target creatures you control. - this.getSpellCard().getSpellAbility().addEffect(new DistributeCountersEffect( - CounterType.P1P1, GetXValue.instance, - "any number of target creatures you control" - )); - Target target = new TargetCreaturePermanentAmount(GetXValue.instance, StaticFilters.FILTER_CONTROLLED_CREATURES); - target.setMinNumberOfTargets(0); - target.setMaxNumberOfTargets(Integer.MAX_VALUE); - this.getSpellCard().getSpellAbility().addTarget(target); + this.getSpellCard().getSpellAbility().addEffect(new DistributeCountersEffect()); + this.getSpellCard().getSpellAbility().addTarget( + new TargetCreaturePermanentAmount(GetXValue.instance, StaticFilters.FILTER_CONTROLLED_CREATURES)); this.finalizeAdventure(); } diff --git a/Mage.Sets/src/mage/cards/e/ElvenRite.java b/Mage.Sets/src/mage/cards/e/ElvenRite.java index 030277f47db..7dcb8db2700 100644 --- a/Mage.Sets/src/mage/cards/e/ElvenRite.java +++ b/Mage.Sets/src/mage/cards/e/ElvenRite.java @@ -6,7 +6,6 @@ import mage.abilities.effects.common.counter.DistributeCountersEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.counters.CounterType; import mage.target.common.TargetCreaturePermanentAmount; /** @@ -19,7 +18,7 @@ public final class ElvenRite extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{G}"); // Distribute two +1/+1 counters among one or two target creatures. - this.getSpellAbility().addEffect(new DistributeCountersEffect(2, "one or two target creatures")); + this.getSpellAbility().addEffect(new DistributeCountersEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(2)); } diff --git a/Mage.Sets/src/mage/cards/e/Embolden.java b/Mage.Sets/src/mage/cards/e/Embolden.java index 92fbf5a8b0d..0e1525d8358 100644 --- a/Mage.Sets/src/mage/cards/e/Embolden.java +++ b/Mage.Sets/src/mage/cards/e/Embolden.java @@ -9,7 +9,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.TimingRule; import mage.target.common.TargetAnyTargetAmount; /** @@ -21,7 +20,7 @@ public final class Embolden extends CardImpl { public Embolden(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}"); - // Prevent the next 4 damage that would be dealt this turn to any number of target creatures and/or players, divided as you choose. + // Prevent the next 4 damage that would be dealt this turn to any number of targets, divided as you choose. this.getSpellAbility().addEffect(new PreventDamageToTargetMultiAmountEffect(Duration.EndOfTurn, 4)); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(4)); diff --git a/Mage.Sets/src/mage/cards/f/FeastOfTheVictoriousDead.java b/Mage.Sets/src/mage/cards/f/FeastOfTheVictoriousDead.java index 9529fdde0d0..cc289f7797b 100644 --- a/Mage.Sets/src/mage/cards/f/FeastOfTheVictoriousDead.java +++ b/Mage.Sets/src/mage/cards/f/FeastOfTheVictoriousDead.java @@ -77,8 +77,7 @@ class FeastOfTheVictoriousDeadEffect extends OneShotEffect { if (player == null || game.getBattlefield().count(StaticFilters.FILTER_CONTROLLED_CREATURE, player.getId(), source, game) < 1) { return false; } - TargetPermanentAmount target = new TargetCreaturePermanentAmount(amount, StaticFilters.FILTER_CONTROLLED_CREATURE); - target.setMinNumberOfTargets(1); + TargetPermanentAmount target = new TargetCreaturePermanentAmount(amount, 1, amount, StaticFilters.FILTER_CONTROLLED_CREATURE); target.withNotTarget(true); target.withChooseHint("to distribute " + amount + " counters"); target.chooseTarget(outcome, player.getId(), source, game); diff --git a/Mage.Sets/src/mage/cards/f/FieryJustice.java b/Mage.Sets/src/mage/cards/f/FieryJustice.java index 252a927406f..9bea53b34c5 100644 --- a/Mage.Sets/src/mage/cards/f/FieryJustice.java +++ b/Mage.Sets/src/mage/cards/f/FieryJustice.java @@ -22,7 +22,7 @@ public final class FieryJustice extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{R}{G}{W}"); // Fiery Justice deals 5 damage divided as you choose among any number of targets. Target opponent gains 5 life. - this.getSpellAbility().addEffect(new DamageMultiEffect(5)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(5)); Effect effect = new GainLifeTargetEffect(5); effect.setTargetPointer(new SecondTargetPointer()); diff --git a/Mage.Sets/src/mage/cards/f/FightWithFire.java b/Mage.Sets/src/mage/cards/f/FightWithFire.java index 91c66490787..3d09f8144de 100644 --- a/Mage.Sets/src/mage/cards/f/FightWithFire.java +++ b/Mage.Sets/src/mage/cards/f/FightWithFire.java @@ -29,7 +29,7 @@ public final class FightWithFire extends CardImpl { // Fight with Fire deals 5 damage to target creature. If this spell was kicked, it deals 10 damage divided as you choose among any number of targets instead. (Those targets can include players and planeswalkers.) this.getSpellAbility().addEffect(new ConditionalOneShotEffect( - new DamageMultiEffect(10), + new DamageMultiEffect(), new DamageTargetEffect(5), KickedCondition.ONCE, "{this} deals 5 damage to target creature. If this spell was kicked, " diff --git a/Mage.Sets/src/mage/cards/f/FireAtWill.java b/Mage.Sets/src/mage/cards/f/FireAtWill.java index e910437823f..77893d8ec34 100644 --- a/Mage.Sets/src/mage/cards/f/FireAtWill.java +++ b/Mage.Sets/src/mage/cards/f/FireAtWill.java @@ -19,7 +19,7 @@ public final class FireAtWill extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R/W}{R/W}{R/W}"); // Fire at Will deals 3 damage divided as you choose among one, two, or three target attacking or blocking creatures. - this.getSpellAbility().addEffect(new DamageMultiEffect(3)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(3, StaticFilters.FILTER_ATTACKING_OR_BLOCKING_CREATURES)); } diff --git a/Mage.Sets/src/mage/cards/f/FireCovenant.java b/Mage.Sets/src/mage/cards/f/FireCovenant.java index 4ae64d84009..a485ae46cbf 100644 --- a/Mage.Sets/src/mage/cards/f/FireCovenant.java +++ b/Mage.Sets/src/mage/cards/f/FireCovenant.java @@ -26,7 +26,7 @@ public final class FireCovenant extends CardImpl { // Fire Covenant deals X damage divided as you choose among any number of target creatures. DynamicValue xValue = GetXValue.instance; - this.getSpellAbility().addEffect(new DamageMultiEffect(xValue)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(xValue)); } diff --git a/Mage.Sets/src/mage/cards/f/FireIce.java b/Mage.Sets/src/mage/cards/f/FireIce.java index afe9d77b15f..19dc510f23a 100644 --- a/Mage.Sets/src/mage/cards/f/FireIce.java +++ b/Mage.Sets/src/mage/cards/f/FireIce.java @@ -18,7 +18,7 @@ public final class FireIce extends SplitCard { // Fire // Fire deals 2 damage divided as you choose among one or two targets. - getLeftHalfCard().getSpellAbility().addEffect(new DamageMultiEffect(2, "Fire")); + getLeftHalfCard().getSpellAbility().addEffect(new DamageMultiEffect()); getLeftHalfCard().getSpellAbility().addTarget(new TargetAnyTargetAmount(2)); // Ice diff --git a/Mage.Sets/src/mage/cards/f/FlamesOfTheFirebrand.java b/Mage.Sets/src/mage/cards/f/FlamesOfTheFirebrand.java index 7100668c180..767540f7bd6 100644 --- a/Mage.Sets/src/mage/cards/f/FlamesOfTheFirebrand.java +++ b/Mage.Sets/src/mage/cards/f/FlamesOfTheFirebrand.java @@ -17,7 +17,7 @@ public final class FlamesOfTheFirebrand extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{R}"); // Flames of the Firebrand deals 3 damage divided as you choose among one, two, or three targets. - this.getSpellAbility().addEffect(new DamageMultiEffect(3)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(3)); } diff --git a/Mage.Sets/src/mage/cards/f/Flameshot.java b/Mage.Sets/src/mage/cards/f/Flameshot.java index 15fde82d4ad..ee0f42e2c21 100644 --- a/Mage.Sets/src/mage/cards/f/Flameshot.java +++ b/Mage.Sets/src/mage/cards/f/Flameshot.java @@ -32,7 +32,7 @@ public final class Flameshot extends CardImpl { this.addAbility(new AlternativeCostSourceAbility(new DiscardTargetCost(new TargetCardInHand(filter)))); // Flameshot deals 3 damage divided as you choose among one, two, or three target creatures. - this.getSpellAbility().addEffect(new DamageMultiEffect(3)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(3)); } diff --git a/Mage.Sets/src/mage/cards/f/ForkedBolt.java b/Mage.Sets/src/mage/cards/f/ForkedBolt.java index 167d4999165..9ded9196f48 100644 --- a/Mage.Sets/src/mage/cards/f/ForkedBolt.java +++ b/Mage.Sets/src/mage/cards/f/ForkedBolt.java @@ -17,7 +17,7 @@ public final class ForkedBolt extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}"); // Forked Bolt deals 2 damage divided as you choose among one or two targets. - this.getSpellAbility().addEffect(new DamageMultiEffect(2)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(2)); } diff --git a/Mage.Sets/src/mage/cards/f/ForkedLightning.java b/Mage.Sets/src/mage/cards/f/ForkedLightning.java index 0d14651ab6a..b08ce5a3c1f 100644 --- a/Mage.Sets/src/mage/cards/f/ForkedLightning.java +++ b/Mage.Sets/src/mage/cards/f/ForkedLightning.java @@ -6,7 +6,6 @@ import mage.abilities.effects.common.DamageMultiEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.target.Target; import mage.target.common.TargetCreaturePermanentAmount; /** @@ -19,9 +18,8 @@ public final class ForkedLightning extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}"); // Forked Lightning deals 4 damage divided as you choose among one, two, or three target creatures. - this.getSpellAbility().addEffect(new DamageMultiEffect(4) - .setText("{this} deals 4 damage divided as you choose among one, two, or three target creatures")); - Target target=new TargetCreaturePermanentAmount(4);target.setMaxNumberOfTargets(3);this.getSpellAbility().addTarget(target); + this.getSpellAbility().addEffect(new DamageMultiEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(4, 1, 3)); } private ForkedLightning(final ForkedLightning card) { diff --git a/Mage.Sets/src/mage/cards/f/Fury.java b/Mage.Sets/src/mage/cards/f/Fury.java index 03c3ef9a723..edc969baa55 100644 --- a/Mage.Sets/src/mage/cards/f/Fury.java +++ b/Mage.Sets/src/mage/cards/f/Fury.java @@ -43,7 +43,7 @@ public final class Fury extends CardImpl { // When Fury enters the battlefield, it deals 4 damage divided as you choose among any number of target creatures and/or planeswalkers. Ability ability = new EntersBattlefieldTriggeredAbility( - new DamageMultiEffect(4, "it") + new DamageMultiEffect("it") ); ability.addTarget(new TargetCreatureOrPlaneswalkerAmount(4)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/g/GangOfDevils.java b/Mage.Sets/src/mage/cards/g/GangOfDevils.java index a782b65ce5c..59a30a4613b 100644 --- a/Mage.Sets/src/mage/cards/g/GangOfDevils.java +++ b/Mage.Sets/src/mage/cards/g/GangOfDevils.java @@ -26,7 +26,7 @@ public final class GangOfDevils extends CardImpl { this.toughness = new MageInt(3); // When Gang of Devils dies, it deals 3 damage divided as you choose among one, two, or three targets. - Ability ability = new DiesSourceTriggeredAbility(new DamageMultiEffect(3, "it")); + Ability ability = new DiesSourceTriggeredAbility(new DamageMultiEffect("it")); ability.addTarget(new TargetAnyTargetAmount(3)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GhiredsBelligerence.java b/Mage.Sets/src/mage/cards/g/GhiredsBelligerence.java index 2d5cb7bfd5c..7b048c93cc1 100644 --- a/Mage.Sets/src/mage/cards/g/GhiredsBelligerence.java +++ b/Mage.Sets/src/mage/cards/g/GhiredsBelligerence.java @@ -46,7 +46,7 @@ public final class GhiredsBelligerence extends CardImpl { class GhiredsBelligerenceEffect extends OneShotEffect { - private static final DamageMultiEffect effect = new DamageMultiEffect(GetXValue.instance); + private static final DamageMultiEffect effect = new DamageMultiEffect(); GhiredsBelligerenceEffect() { super(Outcome.Benefit); diff --git a/Mage.Sets/src/mage/cards/g/GlintWeaver.java b/Mage.Sets/src/mage/cards/g/GlintWeaver.java index 2d8f0e0d398..f99e13ef97a 100644 --- a/Mage.Sets/src/mage/cards/g/GlintWeaver.java +++ b/Mage.Sets/src/mage/cards/g/GlintWeaver.java @@ -11,7 +11,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.counters.CounterType; import mage.target.common.TargetCreaturePermanentAmount; import java.util.UUID; @@ -32,9 +31,7 @@ public final class GlintWeaver extends CardImpl { this.addAbility(ReachAbility.getInstance()); // When Glint Weaver enters the battlefield, distribute three +1/+1 counters among one, two, or three target creatures, then you gain life equal to the greatest toughness among creatures you control. - Ability ability = new EntersBattlefieldTriggeredAbility(new DistributeCountersEffect( - 3, "one, two, or three target creatures" - )); + Ability ability = new EntersBattlefieldTriggeredAbility(new DistributeCountersEffect()); ability.addEffect(new GainLifeEffect(GreatestToughnessAmongControlledCreaturesValue.instance) .setText(", then you gain life equal to the greatest toughness among creatures you control")); ability.addTarget(new TargetCreaturePermanentAmount(3)); diff --git a/Mage.Sets/src/mage/cards/g/GracefulAntelope.java b/Mage.Sets/src/mage/cards/g/GracefulAntelope.java index bdb48ae5327..ea00f389dc7 100644 --- a/Mage.Sets/src/mage/cards/g/GracefulAntelope.java +++ b/Mage.Sets/src/mage/cards/g/GracefulAntelope.java @@ -28,6 +28,7 @@ public final class GracefulAntelope extends CardImpl { // Plainswalk this.addAbility(new PlainswalkAbility()); + // Whenever Graceful Antelope deals combat damage to a player, you may have target land become a Plains until Graceful Antelope leaves the battlefield. Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new BecomesBasicLandTargetEffect(Duration.UntilSourceLeavesBattlefield, SubType.PLAINS), true); ability.addTarget(new TargetLandPermanent()); diff --git a/Mage.Sets/src/mage/cards/h/HailOfArrows.java b/Mage.Sets/src/mage/cards/h/HailOfArrows.java index 939e29198a9..8495476ac79 100644 --- a/Mage.Sets/src/mage/cards/h/HailOfArrows.java +++ b/Mage.Sets/src/mage/cards/h/HailOfArrows.java @@ -19,7 +19,7 @@ public final class HailOfArrows extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{X}{W}"); // Hail of Arrows deals X damage divided as you choose among any number of target attacking creatures. - this.getSpellAbility().addEffect(new DamageMultiEffect(GetXValue.instance)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(GetXValue.instance, StaticFilters.FILTER_ATTACKING_CREATURES)); } diff --git a/Mage.Sets/src/mage/cards/h/HavengulLaboratory.java b/Mage.Sets/src/mage/cards/h/HavengulLaboratory.java index 76ccb05d0c5..766da704cd2 100644 --- a/Mage.Sets/src/mage/cards/h/HavengulLaboratory.java +++ b/Mage.Sets/src/mage/cards/h/HavengulLaboratory.java @@ -42,7 +42,7 @@ public final class HavengulLaboratory extends CardImpl { ability.addCost(new TapSourceCost()); this.addAbility(ability); - // At the beginning of your end step, if you sacrificed three or more Clues this turn, transform Hawkins National Laboratory. + // At the beginning of your end step, if you sacrificed three or more Clues this turn, transform Havengul Laboratory. this.addAbility(new TransformAbility()); this.addAbility(new BeginningOfEndStepTriggeredAbility( TargetController.YOU, new TransformSourceEffect(), diff --git a/Mage.Sets/src/mage/cards/h/HavengulMystery.java b/Mage.Sets/src/mage/cards/h/HavengulMystery.java index 505472cd0e5..bb1dbe2aa08 100644 --- a/Mage.Sets/src/mage/cards/h/HavengulMystery.java +++ b/Mage.Sets/src/mage/cards/h/HavengulMystery.java @@ -22,6 +22,7 @@ import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; +import mage.util.CardUtil; import java.util.HashSet; import java.util.Set; @@ -35,7 +36,6 @@ public final class HavengulMystery extends CardImpl { public HavengulMystery(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); this.supertype.add(SuperType.LEGENDARY); - this.nightCard = true; // When this land transforms into Havengul Mystery, return target creature card from your graveyard to the battlefield. @@ -63,7 +63,7 @@ public final class HavengulMystery extends CardImpl { } static String makeKey(Ability source, Game game) { - return "HavengulMystery_" + source.getSourceId() + '_' + source.getSourceObjectZoneChangeCounter(); + return "HavengulMystery_" + source.getSourceId() + '_' + CardUtil.getActualSourceObjectZoneChangeCounter(game, source); } } @@ -112,6 +112,7 @@ class HavengulMysteryLeavesAbility extends TriggeredAbilityImpl { HavengulMysteryLeavesAbility() { super(Zone.BATTLEFIELD, new TransformSourceEffect()); + setLeavesTheBattlefieldTrigger(true); } private HavengulMysteryLeavesAbility(final HavengulMysteryLeavesAbility ability) { @@ -131,7 +132,12 @@ class HavengulMysteryLeavesAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - Set morSet = (Set) game.getState().getValue(HavengulMystery.makeKey(this, game)); + if (zEvent.getFromZone() != Zone.BATTLEFIELD) { + return false; + } + + String key = HavengulMystery.makeKey(this, game); + Set morSet = (Set) game.getState().getValue(key); return morSet != null && !morSet.isEmpty() && morSet.stream().anyMatch(mor -> mor.refersTo(zEvent.getTarget(), game)); diff --git a/Mage.Sets/src/mage/cards/i/IgniteDisorder.java b/Mage.Sets/src/mage/cards/i/IgniteDisorder.java index f28100eb6f9..c3f499e2eba 100644 --- a/Mage.Sets/src/mage/cards/i/IgniteDisorder.java +++ b/Mage.Sets/src/mage/cards/i/IgniteDisorder.java @@ -29,7 +29,7 @@ public final class IgniteDisorder extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{R}"); // Ignite Disorder deals 3 damage divided as you choose among one, two, or three target white and/or blue creatures. - this.getSpellAbility().addEffect(new DamageMultiEffect(3)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(3, filter)); } diff --git a/Mage.Sets/src/mage/cards/i/ImpactResonance.java b/Mage.Sets/src/mage/cards/i/ImpactResonance.java index ab458592a25..294498ed54b 100644 --- a/Mage.Sets/src/mage/cards/i/ImpactResonance.java +++ b/Mage.Sets/src/mage/cards/i/ImpactResonance.java @@ -29,7 +29,7 @@ public final class ImpactResonance extends CardImpl { // Impact Resonance deals X damage divided as you choose among any number of target creatures, where X is the greatest amount of damage dealt by a source to a permanent or player this turn. DynamicValue xValue = GreatestAmountOfDamageDealtValue.instance; - Effect effect = new DamageMultiEffect(xValue); + Effect effect = new DamageMultiEffect(); effect.setText("{this} deals X damage divided as you choose among any number of target creatures, where X is the greatest amount of damage dealt by a source to a permanent or player this turn"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(xValue)); diff --git a/Mage.Sets/src/mage/cards/i/InfernalHarvest.java b/Mage.Sets/src/mage/cards/i/InfernalHarvest.java index 641cf103078..6188702c0b3 100644 --- a/Mage.Sets/src/mage/cards/i/InfernalHarvest.java +++ b/Mage.Sets/src/mage/cards/i/InfernalHarvest.java @@ -37,7 +37,7 @@ public final class InfernalHarvest extends CardImpl { this.getSpellAbility().addCost(new InfernalHarvestAdditionalCost()); // Infernal Harvest deals X damage divided as you choose among any number of target creatures. - this.getSpellAbility().addEffect(new DamageMultiEffect(GetXValue.instance)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(GetXValue.instance)); } diff --git a/Mage.Sets/src/mage/cards/i/InfernoTitan.java b/Mage.Sets/src/mage/cards/i/InfernoTitan.java index c26e2b72051..793e3a4614e 100644 --- a/Mage.Sets/src/mage/cards/i/InfernoTitan.java +++ b/Mage.Sets/src/mage/cards/i/InfernoTitan.java @@ -30,7 +30,7 @@ public final class InfernoTitan extends CardImpl { this.addAbility(new SimpleActivatedAbility(new BoostSourceEffect(1, 0, Duration.EndOfTurn), new ColoredManaCost(ColoredManaSymbol.R))); // Whenever Inferno Titan enters the battlefield or attacks, it deals 3 damage divided as you choose among one, two, or three targets. - Ability ability = new EntersBattlefieldOrAttacksSourceTriggeredAbility(new DamageMultiEffect(3, "it")); + Ability ability = new EntersBattlefieldOrAttacksSourceTriggeredAbility(new DamageMultiEffect("it")); ability.addTarget(new TargetAnyTargetAmount(3)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/i/InvokeJustice.java b/Mage.Sets/src/mage/cards/i/InvokeJustice.java index f2377b002a0..e4389f72ddd 100644 --- a/Mage.Sets/src/mage/cards/i/InvokeJustice.java +++ b/Mage.Sets/src/mage/cards/i/InvokeJustice.java @@ -85,7 +85,7 @@ class InvokeJusticeEffect extends OneShotEffect { if (!game.getBattlefield().contains(filter, source, game, 1)) { return false; } - TargetPermanentAmount target = new TargetPermanentAmount(4, filter); + TargetPermanentAmount target = new TargetPermanentAmount(4, 0, filter); target.withNotTarget(true); target.chooseTarget(outcome, player.getId(), source, game); for (UUID targetId : target.getTargets()) { diff --git a/Mage.Sets/src/mage/cards/i/ItlimocCradleOfTheSun.java b/Mage.Sets/src/mage/cards/i/ItlimocCradleOfTheSun.java index e237ffdd3f2..a1140a281d8 100644 --- a/Mage.Sets/src/mage/cards/i/ItlimocCradleOfTheSun.java +++ b/Mage.Sets/src/mage/cards/i/ItlimocCradleOfTheSun.java @@ -1,10 +1,11 @@ - package mage.cards.i; import java.util.UUID; import mage.Mana; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; import mage.abilities.mana.DynamicManaAbility; import mage.abilities.mana.GreenManaAbility; import mage.cards.CardImpl; @@ -19,7 +20,9 @@ import mage.filter.StaticFilters; */ public final class ItlimocCradleOfTheSun extends CardImpl { - + private static final Hint hint = new ValueHint( + "Number of creatures you control", new PermanentsOnBattlefieldCount(StaticFilters.FILTER_PERMANENT_CREATURE_CONTROLLED) + ); public ItlimocCradleOfTheSun(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); @@ -33,7 +36,7 @@ public final class ItlimocCradleOfTheSun extends CardImpl { this.addAbility(new GreenManaAbility()); // {T}: Add {G} for each creature you control. - this.addAbility(new DynamicManaAbility(Mana.GreenMana(1), new PermanentsOnBattlefieldCount(StaticFilters.FILTER_PERMANENT_CREATURE_CONTROLLED))); + this.addAbility(new DynamicManaAbility(Mana.GreenMana(1), new PermanentsOnBattlefieldCount(StaticFilters.FILTER_PERMANENT_CREATURE_CONTROLLED)).addHint(hint)); } private ItlimocCradleOfTheSun(final ItlimocCradleOfTheSun card) { diff --git a/Mage.Sets/src/mage/cards/j/JadeSeedstones.java b/Mage.Sets/src/mage/cards/j/JadeSeedstones.java index 149827d2016..55cde970259 100644 --- a/Mage.Sets/src/mage/cards/j/JadeSeedstones.java +++ b/Mage.Sets/src/mage/cards/j/JadeSeedstones.java @@ -7,9 +7,8 @@ import mage.abilities.keyword.CraftAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.counters.CounterType; import mage.filter.StaticFilters; -import mage.target.common.TargetPermanentAmount; +import mage.target.common.TargetCreaturePermanentAmount; import java.util.UUID; @@ -23,12 +22,8 @@ public final class JadeSeedstones extends CardImpl { this.secondSideCardClazz = mage.cards.j.JadeheartAttendant.class; // When Jade Seedstones enters the battlefield, distribute three +1/+1 counters among one, two, or three target creatures you control. - Ability ability = new EntersBattlefieldTriggeredAbility(new DistributeCountersEffect( - 3, "one, two, or three target creatures you control" - )); - TargetPermanentAmount target = new TargetPermanentAmount(3, StaticFilters.FILTER_CONTROLLED_CREATURES); - target.setMinNumberOfTargets(1); - ability.addTarget(target); + Ability ability = new EntersBattlefieldTriggeredAbility(new DistributeCountersEffect()); + ability.addTarget(new TargetCreaturePermanentAmount(3, StaticFilters.FILTER_CONTROLLED_CREATURES)); this.addAbility(ability); // Craft with creature {5}{G}{G} diff --git a/Mage.Sets/src/mage/cards/j/JawsOfStone.java b/Mage.Sets/src/mage/cards/j/JawsOfStone.java index c286b9a1adc..a06432dfb35 100644 --- a/Mage.Sets/src/mage/cards/j/JawsOfStone.java +++ b/Mage.Sets/src/mage/cards/j/JawsOfStone.java @@ -30,7 +30,7 @@ public final class JawsOfStone extends CardImpl { // Jaws of Stone deals X damage divided as you choose among any number of targets, where X is the number of Mountains you control as you cast this spell. PermanentsOnBattlefieldCount mountains = new PermanentsOnBattlefieldCount(filter, null); - Effect effect = new DamageMultiEffect(mountains); + Effect effect = new DamageMultiEffect(); effect.setText(rule); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(mountains)); diff --git a/Mage.Sets/src/mage/cards/j/JuganTheRisingStar.java b/Mage.Sets/src/mage/cards/j/JuganTheRisingStar.java index effc7db6288..cc5d51ca700 100644 --- a/Mage.Sets/src/mage/cards/j/JuganTheRisingStar.java +++ b/Mage.Sets/src/mage/cards/j/JuganTheRisingStar.java @@ -13,7 +13,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.counters.CounterType; import mage.target.common.TargetCreaturePermanentAmount; /** @@ -33,7 +32,7 @@ public final class JuganTheRisingStar extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // When Jugan, the Rising Star dies, you may distribute five +1/+1 counters among any number of target creatures. - Ability ability = new DiesSourceTriggeredAbility(new DistributeCountersEffect(5, "any number of target creatures"), true); + Ability ability = new DiesSourceTriggeredAbility(new DistributeCountersEffect(), true); ability.addTarget(new TargetCreaturePermanentAmount(5)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/k/KuldothaFlamefiend.java b/Mage.Sets/src/mage/cards/k/KuldothaFlamefiend.java index c844636eea9..cdc235bbb37 100644 --- a/Mage.Sets/src/mage/cards/k/KuldothaFlamefiend.java +++ b/Mage.Sets/src/mage/cards/k/KuldothaFlamefiend.java @@ -11,7 +11,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.filter.StaticFilters; -import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetAnyTargetAmount; /** @@ -29,7 +28,7 @@ public final class KuldothaFlamefiend extends CardImpl { // When Kuldotha Flamefiend enters the battlefield, you may sacrifice an artifact. If you do, Kuldotha Flamefiend deals 4 damage divided as you choose among any number of targets. EntersBattlefieldTriggeredAbility ability = - new EntersBattlefieldTriggeredAbility(new DoIfCostPaid(new DamageMultiEffect(4), new SacrificeTargetCost(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT_AN)), false); + new EntersBattlefieldTriggeredAbility(new DoIfCostPaid(new DamageMultiEffect(), new SacrificeTargetCost(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACT_AN)), false); ability.addTarget(new TargetAnyTargetAmount(4)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/l/LathielTheBounteousDawn.java b/Mage.Sets/src/mage/cards/l/LathielTheBounteousDawn.java index 47bf24fade8..20e73478ede 100644 --- a/Mage.Sets/src/mage/cards/l/LathielTheBounteousDawn.java +++ b/Mage.Sets/src/mage/cards/l/LathielTheBounteousDawn.java @@ -15,10 +15,8 @@ import mage.abilities.keyword.LifelinkAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.game.Game; -import mage.target.TargetAmount; import mage.target.common.TargetCreaturePermanentAmount; import mage.watchers.common.PlayerGainedLifeWatcher; @@ -44,18 +42,13 @@ public final class LathielTheBounteousDawn extends CardImpl { // At the beginning of each end step, if you gained life this turn, distribute up to that many +1/+1 counters among any number of other target creatures. Ability ability = new ConditionalInterveningIfTriggeredAbility( - new BeginningOfEndStepTriggeredAbility(TargetController.ANY, new DistributeCountersEffect( - 1, "" - ), false), + new BeginningOfEndStepTriggeredAbility(TargetController.ANY, new DistributeCountersEffect(), false), condition, "At the beginning of each end step, if you gained life this turn, " + "distribute up to that many +1/+1 counters among any number of other target creatures." ); - TargetAmount target = new TargetCreaturePermanentAmount( + ability.addTarget(new TargetCreaturePermanentAmount( LathielTheBounteousDawnValue.instance, - StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE - ); - target.setMinNumberOfTargets(0); - ability.addTarget(target); + StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE)); this.addAbility(ability.addHint(LathielTheBounteousDawnValue.getHint()), new PlayerGainedLifeWatcher()); } diff --git a/Mage.Sets/src/mage/cards/l/LivingInferno.java b/Mage.Sets/src/mage/cards/l/LivingInferno.java index 347f6af772f..163246c0dad 100644 --- a/Mage.Sets/src/mage/cards/l/LivingInferno.java +++ b/Mage.Sets/src/mage/cards/l/LivingInferno.java @@ -11,7 +11,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.Target; @@ -57,7 +56,8 @@ enum LivingInfernoAdjuster implements TargetAdjuster { Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(ability.getSourceId()); if (sourcePermanent != null) { ability.getTargets().clear(); - ability.addTarget(new TargetCreaturePermanentAmount(sourcePermanent.getPower().getValue())); + int power = sourcePermanent.getPower().getValue(); + ability.addTarget(new TargetCreaturePermanentAmount(power, 0, power)); } } } diff --git a/Mage.Sets/src/mage/cards/l/LukkaBoundToRuin.java b/Mage.Sets/src/mage/cards/l/LukkaBoundToRuin.java index 2a76293cae0..09d0f88c46c 100644 --- a/Mage.Sets/src/mage/cards/l/LukkaBoundToRuin.java +++ b/Mage.Sets/src/mage/cards/l/LukkaBoundToRuin.java @@ -6,7 +6,6 @@ import mage.Mana; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.condition.Condition; -import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; @@ -51,11 +50,10 @@ public class LukkaBoundToRuin extends CardImpl { // −4: Lukka deals X damage divided as you choose among any number of target creatures and/or planeswalkers, // where X is the greatest power among creatures you controlled as you activated this ability. - DynamicValue xValue = GreatestPowerAmongControlledCreaturesValue.instance; - DamageMultiEffect damageMultiEffect = new DamageMultiEffect(xValue); + DamageMultiEffect damageMultiEffect = new DamageMultiEffect(); damageMultiEffect.setText("Lukka deals X damage divided as you choose " + "among any number of target creatures and/or planeswalkers, " + - "where X is the greatest power among creatures you controlled as you activated this ability."); + "where X is the greatest power among creatures you control as you activate this ability."); ability = new LoyaltyAbility(damageMultiEffect, -4); ability.setTargetAdjuster(LukkaBoundToRuinAdjuster.instance); this.addAbility(ability); @@ -135,9 +133,6 @@ enum LukkaBoundToRuinAdjuster implements TargetAdjuster { // Maximum targets is equal to the damage - as each target need to be assigned at least 1 damage ability.getTargets().clear(); int xValue = GreatestPowerAmongControlledCreaturesValue.instance.calculate(game, ability, null); - TargetCreatureOrPlaneswalkerAmount targetCreatureOrPlaneswalkerAmount = new TargetCreatureOrPlaneswalkerAmount(xValue); - targetCreatureOrPlaneswalkerAmount.setMinNumberOfTargets(0); - targetCreatureOrPlaneswalkerAmount.setMaxNumberOfTargets(xValue); - ability.addTarget(targetCreatureOrPlaneswalkerAmount); + ability.addTarget(new TargetCreatureOrPlaneswalkerAmount(xValue, 0, xValue)); } } diff --git a/Mage.Sets/src/mage/cards/m/MagicMissile.java b/Mage.Sets/src/mage/cards/m/MagicMissile.java index b64132d2951..2fce2dc2503 100644 --- a/Mage.Sets/src/mage/cards/m/MagicMissile.java +++ b/Mage.Sets/src/mage/cards/m/MagicMissile.java @@ -21,7 +21,7 @@ public final class MagicMissile extends CardImpl { this.addAbility(new CantBeCounteredSourceAbility().setRuleAtTheTop(true)); // Magic Missile deals 3 damage divided as you choose among one, two, or three targets. - this.getSpellAbility().addEffect(new DamageMultiEffect(3)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(3)); } diff --git a/Mage.Sets/src/mage/cards/m/MagmaOpus.java b/Mage.Sets/src/mage/cards/m/MagmaOpus.java index fc202b234d1..a7586a6fb28 100644 --- a/Mage.Sets/src/mage/cards/m/MagmaOpus.java +++ b/Mage.Sets/src/mage/cards/m/MagmaOpus.java @@ -30,7 +30,7 @@ public final class MagmaOpus extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{6}{U}{R}"); // Magma Opus deals 4 damage divided as you choose among any number of targets. Tap two target permanents. Create a 4/4 blue and red Elemental creature token. Draw two cards. - this.getSpellAbility().addEffect(new DamageMultiEffect(4)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(4).withChooseHint("damage")); this.getSpellAbility().addEffect(new TapTargetEffect("tap two target permanents").setTargetPointer(new SecondTargetPointer())); this.getSpellAbility().addTarget(new TargetPermanent(2, StaticFilters.FILTER_PERMANENTS).withChooseHint("tap")); diff --git a/Mage.Sets/src/mage/cards/m/MagmaticCore.java b/Mage.Sets/src/mage/cards/m/MagmaticCore.java index b53cf52cfe3..d2ba2ad0773 100644 --- a/Mage.Sets/src/mage/cards/m/MagmaticCore.java +++ b/Mage.Sets/src/mage/cards/m/MagmaticCore.java @@ -30,7 +30,7 @@ public final class MagmaticCore extends CardImpl { // At the beginning of your end step, Magmatic Core deals X damage divided as you choose among any number of target creatures, where X is the number of age counters on it. DynamicValue value = new CountersSourceCount(CounterType.AGE); Ability ability = new BeginningOfEndStepTriggeredAbility( - new DamageMultiEffect(value) + new DamageMultiEffect() .setText("{this} deals X damage divided as you choose " + "among any number of target creatures," + " where X is the number of age counters on it.") diff --git a/Mage.Sets/src/mage/cards/m/MeteorShower.java b/Mage.Sets/src/mage/cards/m/MeteorShower.java index bef6bc4aaf1..1f30e90576f 100644 --- a/Mage.Sets/src/mage/cards/m/MeteorShower.java +++ b/Mage.Sets/src/mage/cards/m/MeteorShower.java @@ -22,7 +22,8 @@ public final class MeteorShower extends CardImpl { // Meteor Shower deals X plus 1 damage divided as you choose among any number of targets. DynamicValue xValue = new IntPlusDynamicValue(1, GetXValue.instance); - this.getSpellAbility().addEffect(new DamageMultiEffect(xValue)); + this.getSpellAbility().addEffect(new DamageMultiEffect() + .setText("{this} deals X plus 1 damage divided as you choose among any number of targets")); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(xValue)); } diff --git a/Mage.Sets/src/mage/cards/m/MeteorSwarm.java b/Mage.Sets/src/mage/cards/m/MeteorSwarm.java index 6b3c7bb7c35..fd64f60794a 100644 --- a/Mage.Sets/src/mage/cards/m/MeteorSwarm.java +++ b/Mage.Sets/src/mage/cards/m/MeteorSwarm.java @@ -19,10 +19,11 @@ public final class MeteorSwarm extends CardImpl { // Meteor Swarm deals 8 damage divided as you choose among X target creatures and/or planeswalkers. this.getSpellAbility().addEffect( - new DamageMultiEffect(8). + new DamageMultiEffect(). setText("{this} deals 8 damage divided as you choose among X target creatures and/or planeswalkers.") ); - this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalkerAmount(8)); + // Minimum number of targets will be overridden to X by the adjuster + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalkerAmount(8, 1, 8)); this.getSpellAbility().setTargetAdjuster(new XTargetsCountAdjuster()); } diff --git a/Mage.Sets/src/mage/cards/m/MoggMob.java b/Mage.Sets/src/mage/cards/m/MoggMob.java index 31fcc189b0f..132f92715f3 100644 --- a/Mage.Sets/src/mage/cards/m/MoggMob.java +++ b/Mage.Sets/src/mage/cards/m/MoggMob.java @@ -27,7 +27,7 @@ public final class MoggMob extends CardImpl { // Sacrifice Mogg Mob: It deals 3 damage divided as you choose among one, two, or three targets. Ability ability = new SimpleActivatedAbility( - new DamageMultiEffect(3, "it"), new SacrificeSourceCost() + new DamageMultiEffect("it"), new SacrificeSourceCost() ); ability.addTarget(new TargetAnyTargetAmount(3)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/m/MonstrousOnslaught.java b/Mage.Sets/src/mage/cards/m/MonstrousOnslaught.java index d3ae6ca0562..2a5b2b02ee8 100644 --- a/Mage.Sets/src/mage/cards/m/MonstrousOnslaught.java +++ b/Mage.Sets/src/mage/cards/m/MonstrousOnslaught.java @@ -22,7 +22,7 @@ public final class MonstrousOnslaught extends CardImpl { // Monstrous Onslaught deals X damage divided as you choose among any number of target creatures, where X is the greatest power among creatures you control as you cast Monstrous Onslaught. DynamicValue xValue = GreatestPowerAmongControlledCreaturesValue.instance; - Effect effect = new DamageMultiEffect(xValue); + Effect effect = new DamageMultiEffect(); effect.setText("{this} deals X damage divided as you choose among any number of target creatures, where X is the greatest power among creatures you control as you cast this spell"); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(xValue)); diff --git a/Mage.Sets/src/mage/cards/m/MyojinOfToweringMight.java b/Mage.Sets/src/mage/cards/m/MyojinOfToweringMight.java index 7191e76fad9..f4148079935 100644 --- a/Mage.Sets/src/mage/cards/m/MyojinOfToweringMight.java +++ b/Mage.Sets/src/mage/cards/m/MyojinOfToweringMight.java @@ -44,10 +44,8 @@ public final class MyojinOfToweringMight extends CardImpl { ), new CastFromHandWatcher()); // Remove an indestructible counter from Myojin of Towering Might: Distribute eight +1/+1 counters among any number of target creatures you control. They gain trample until end of turn. - Ability ability = new SimpleActivatedAbility(new DistributeCountersEffect( - 8, - "any number of target creatures you control" - ), new RemoveCountersSourceCost(CounterType.INDESTRUCTIBLE.createInstance())); + Ability ability = new SimpleActivatedAbility(new DistributeCountersEffect(), + new RemoveCountersSourceCost(CounterType.INDESTRUCTIBLE.createInstance())); ability.addEffect(new GainAbilityTargetEffect( TrampleAbility.getInstance(), Duration.EndOfTurn ).setText("They gain trample until end of turn")); diff --git a/Mage.Sets/src/mage/cards/m/MythosOfVadrok.java b/Mage.Sets/src/mage/cards/m/MythosOfVadrok.java index fe65693100a..8db5a31abe4 100644 --- a/Mage.Sets/src/mage/cards/m/MythosOfVadrok.java +++ b/Mage.Sets/src/mage/cards/m/MythosOfVadrok.java @@ -11,7 +11,6 @@ import mage.abilities.effects.common.DamageMultiEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.ColoredManaSymbol; import mage.constants.Duration; import mage.constants.Outcome; import mage.game.Game; @@ -34,7 +33,7 @@ public final class MythosOfVadrok extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}{R}"); // Mythos of Vadrok deals 5 damage divided as you choose among any number of target creatures and/or planeswalkers. If {W}{U} was spent to cast this spell, until your next turn, those permanents can't attack or block and their activated abilities can't be activated. - this.getSpellAbility().addEffect(new DamageMultiEffect(5)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalkerAmount(5)); this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new MythosOfVadrokEffect(), condition, "If {W}{U} was spent to cast this spell, " + diff --git a/Mage.Sets/src/mage/cards/n/NahirisSacrifice.java b/Mage.Sets/src/mage/cards/n/NahirisSacrifice.java index 24d17a718b1..9bb7df08c61 100644 --- a/Mage.Sets/src/mage/cards/n/NahirisSacrifice.java +++ b/Mage.Sets/src/mage/cards/n/NahirisSacrifice.java @@ -41,7 +41,7 @@ public final class NahirisSacrifice extends CardImpl { this.getSpellAbility().addCost(new SacrificeXManaValueCost(filter,true)); // Nahiri’s Sacrifice deals X damage divided as you choose among any number of target creatures. - Effect effect = new DamageMultiEffect(GetXValue.instance); + Effect effect = new DamageMultiEffect(); effect.setText("{this} deals X damage divided as you choose among any number of target creatures."); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(GetXValue.instance)); diff --git a/Mage.Sets/src/mage/cards/n/NumaJoragaChieftain.java b/Mage.Sets/src/mage/cards/n/NumaJoragaChieftain.java index 16dc5790cdd..04c6abef386 100644 --- a/Mage.Sets/src/mage/cards/n/NumaJoragaChieftain.java +++ b/Mage.Sets/src/mage/cards/n/NumaJoragaChieftain.java @@ -13,11 +13,10 @@ import mage.abilities.keyword.PartnerAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.game.Game; import mage.players.Player; -import mage.target.common.TargetCreaturePermanentAmount; +import mage.target.common.TargetPermanentAmount; import java.util.UUID; @@ -88,10 +87,10 @@ class NumaJoragaChieftainEffect extends OneShotEffect { return false; } ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( - new DistributeCountersEffect(costX, ""), + new DistributeCountersEffect(), false, "distribute " + costX + " +1/+1 counters among any number of target Elves" ); - ability.addTarget(new TargetCreaturePermanentAmount(costX, filter)); + ability.addTarget(new TargetPermanentAmount(costX, 0, filter)); game.fireReflexiveTriggeredAbility(ability, source); return true; } diff --git a/Mage.Sets/src/mage/cards/o/OmnivorousFlytrap.java b/Mage.Sets/src/mage/cards/o/OmnivorousFlytrap.java index 7a5c2f998f8..88422143946 100644 --- a/Mage.Sets/src/mage/cards/o/OmnivorousFlytrap.java +++ b/Mage.Sets/src/mage/cards/o/OmnivorousFlytrap.java @@ -35,7 +35,7 @@ public final class OmnivorousFlytrap extends CardImpl { // Delirium -- Whenever Omnivorous Flytrap enters or attacks, if there are four or more card types among cards in your graveyard, distribute two +1/+1 counters among one or two target creatures. Then if there are six or more card types among cards in your graveyard, double the number of +1/+1 counters on those creatures. Ability ability = new EntersBattlefieldOrAttacksSourceTriggeredAbility( - new DistributeCountersEffect(CounterType.P1P1, 2, "one or two target creatures")) + new DistributeCountersEffect()) .withInterveningIf(DeliriumCondition.instance); ability.addEffect(new ConditionalOneShotEffect( new OmnivorousFlytrapEffect(), diff --git a/Mage.Sets/src/mage/cards/o/OnduKnotmaster.java b/Mage.Sets/src/mage/cards/o/OnduKnotmaster.java index 421ac8229eb..f6ef3b944e6 100644 --- a/Mage.Sets/src/mage/cards/o/OnduKnotmaster.java +++ b/Mage.Sets/src/mage/cards/o/OnduKnotmaster.java @@ -50,12 +50,7 @@ public final class OnduKnotmaster extends AdventureCard { // Throw a Line // Distribute two +1/+1 counters among one or two target creatures. - this.getSpellCard().getSpellAbility().addEffect( - new DistributeCountersEffect( - 2, - "one or two target creatures" - ) - ); + this.getSpellCard().getSpellAbility().addEffect(new DistributeCountersEffect()); this.getSpellCard().getSpellAbility().addTarget(new TargetCreaturePermanentAmount(2)); this.finalizeAdventure(); diff --git a/Mage.Sets/src/mage/cards/o/OrcaSiegeDemon.java b/Mage.Sets/src/mage/cards/o/OrcaSiegeDemon.java index 7bdb7569ddf..1f66c6c921f 100644 --- a/Mage.Sets/src/mage/cards/o/OrcaSiegeDemon.java +++ b/Mage.Sets/src/mage/cards/o/OrcaSiegeDemon.java @@ -38,7 +38,7 @@ public final class OrcaSiegeDemon extends CardImpl { this.addAbility(new DiesCreatureTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance()), false, true)); // When Orca dies, it deals damage equal to its power divided as you choose among any number of targets. - Ability ability = new DiesSourceTriggeredAbility(new DamageMultiEffect(SourcePermanentPowerValue.NOT_NEGATIVE) + Ability ability = new DiesSourceTriggeredAbility(new DamageMultiEffect() .setText("it deals damage equal to its power divided as you choose among any number of targets.")); ability.addTarget(new TargetAnyTargetAmount(SourcePermanentPowerValue.NOT_NEGATIVE)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/p/PicnicRuiner.java b/Mage.Sets/src/mage/cards/p/PicnicRuiner.java index e8ef4ec3cd9..a5c00fb58a7 100644 --- a/Mage.Sets/src/mage/cards/p/PicnicRuiner.java +++ b/Mage.Sets/src/mage/cards/p/PicnicRuiner.java @@ -12,7 +12,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanentAmount; @@ -40,14 +39,9 @@ public final class PicnicRuiner extends AdventureCard { // Stolen Goodies // Distribute three +1/+1 counters among any number of target creatures you control. - this.getSpellCard().getSpellAbility().addEffect( - new DistributeCountersEffect( - 3, - "any number of target creatures you control" - ) - ); + this.getSpellCard().getSpellAbility().addEffect(new DistributeCountersEffect()); this.getSpellCard().getSpellAbility().addTarget( - new TargetCreaturePermanentAmount(3, StaticFilters.FILTER_CONTROLLED_CREATURES) + new TargetCreaturePermanentAmount(3, 0, 3, StaticFilters.FILTER_CONTROLLED_CREATURES) ); this.finalizeAdventure(); diff --git a/Mage.Sets/src/mage/cards/p/PollenRemedy.java b/Mage.Sets/src/mage/cards/p/PollenRemedy.java index a1e3503b4de..628abc57020 100644 --- a/Mage.Sets/src/mage/cards/p/PollenRemedy.java +++ b/Mage.Sets/src/mage/cards/p/PollenRemedy.java @@ -27,13 +27,13 @@ public final class PollenRemedy extends CardImpl { // Kicker-Sacrifice a land. this.addAbility(new KickerAbility(new SacrificeTargetCost(StaticFilters.FILTER_LAND))); - // Prevent the next 3 damage that would be dealt this turn to any number of target creatures and/or players, divided as you choose. + // Prevent the next 3 damage that would be dealt this turn to any number of targets, divided as you choose. // If Pollen Remedy was kicked, prevent the next 6 damage this way instead. Effect effect = new ConditionalReplacementEffect(new PreventDamageToTargetMultiAmountEffect(Duration.EndOfTurn, 6), KickedCondition.ONCE, new PreventDamageToTargetMultiAmountEffect(Duration.EndOfTurn, 3)); effect.setText("Prevent the next 3 damage that would be dealt this turn to any number of targets, divided as you choose. If this spell was kicked, prevent the next 6 damage this way instead."); this.getSpellAbility().addEffect(effect); - this.getSpellAbility().addTarget(new TargetAnyTargetAmount(3)); + this.getSpellAbility().addTarget(new TargetAnyTargetAmount(3, 0, 3)); this.getSpellAbility().setTargetAdjuster(new ConditionalTargetAdjuster(KickedCondition.ONCE, new TargetAnyTargetAmount(6))); } diff --git a/Mage.Sets/src/mage/cards/p/PolukranosWorldEater.java b/Mage.Sets/src/mage/cards/p/PolukranosWorldEater.java index 9739ad31699..99a6152f271 100644 --- a/Mage.Sets/src/mage/cards/p/PolukranosWorldEater.java +++ b/Mage.Sets/src/mage/cards/p/PolukranosWorldEater.java @@ -79,7 +79,7 @@ enum PolukranosWorldEaterAdjuster implements TargetAdjuster { public void adjustTargets(Ability ability, Game game) { int xValue = ((BecomesMonstrousSourceTriggeredAbility) ability).getMonstrosityValue(); ability.getTargets().clear(); - ability.addTarget(new TargetCreaturePermanentAmount(xValue, StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); + ability.addTarget(new TargetCreaturePermanentAmount(xValue, 0, xValue, StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE)); } } diff --git a/Mage.Sets/src/mage/cards/p/Portcullis.java b/Mage.Sets/src/mage/cards/p/Portcullis.java index 60b3fb061ab..a85b6c4c20a 100644 --- a/Mage.Sets/src/mage/cards/p/Portcullis.java +++ b/Mage.Sets/src/mage/cards/p/Portcullis.java @@ -33,8 +33,7 @@ public final class Portcullis extends CardImpl { public Portcullis(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); - // Whenever a creature enters the battlefield, if there are two or more other creatures on the battlefield, exile that creature. - // Return that card to the battlefield under its owner's control when Portcullis leaves the battlefield. + // Whenever a creature enters the battlefield, if there are two or more other creatures on the battlefield, exile that creature. Return that card to the battlefield under its owner's control when Portcullis leaves the battlefield. String rule = "Whenever a creature enters the battlefield, if there are two or more other creatures on the battlefield, exile that creature."; String rule2 = " Return that card to the battlefield under its owner's control when {this} leaves the battlefield."; TriggeredAbility ability = new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new PortcullisExileEffect(), diff --git a/Mage.Sets/src/mage/cards/p/Pyrokinesis.java b/Mage.Sets/src/mage/cards/p/Pyrokinesis.java index b86e2277b50..47bcdb06697 100644 --- a/Mage.Sets/src/mage/cards/p/Pyrokinesis.java +++ b/Mage.Sets/src/mage/cards/p/Pyrokinesis.java @@ -33,7 +33,7 @@ public final class Pyrokinesis extends CardImpl { this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(filter)))); // Pyrokinesis deals 4 damage divided as you choose among any number of target creatures. - this.getSpellAbility().addEffect(new DamageMultiEffect(4)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(4)); } diff --git a/Mage.Sets/src/mage/cards/p/Pyrotechnics.java b/Mage.Sets/src/mage/cards/p/Pyrotechnics.java index 354c0e384b7..5e071a833d3 100644 --- a/Mage.Sets/src/mage/cards/p/Pyrotechnics.java +++ b/Mage.Sets/src/mage/cards/p/Pyrotechnics.java @@ -19,7 +19,7 @@ public final class Pyrotechnics extends CardImpl { // Pyrotechnics deals 4 damage divided as you choose among any number of targets. - this.getSpellAbility().addEffect(new DamageMultiEffect(4)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(4)); } diff --git a/Mage.Sets/src/mage/cards/q/QuilledGreatwurm.java b/Mage.Sets/src/mage/cards/q/QuilledGreatwurm.java new file mode 100644 index 00000000000..ead1c64e929 --- /dev/null +++ b/Mage.Sets/src/mage/cards/q/QuilledGreatwurm.java @@ -0,0 +1,114 @@ +package mage.cards.q; + +import mage.MageIdentifier; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsDamageToAnyTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MyTurnCondition; +import mage.abilities.costs.Cost; +import mage.abilities.costs.Costs; +import mage.abilities.costs.CostsImpl; +import mage.abilities.costs.common.RemoveCounterCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalTriggeredAbility; +import mage.abilities.dynamicvalue.common.SavedDamageValue; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.CounterAnyPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author PurpleCrowbar + */ +public final class QuilledGreatwurm extends CardImpl { + + public QuilledGreatwurm(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{G}"); + this.subtype.add(SubType.WURM); + this.power = new MageInt(7); + this.toughness = new MageInt(7); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Whenever a creature you control deals combat damage during your turn, put that many +1/+1 counters on it. + this.addAbility(new ConditionalTriggeredAbility(new DealsDamageToAnyTriggeredAbility( + Zone.BATTLEFIELD, new AddCountersTargetEffect( + CounterType.P1P1.createInstance(), SavedDamageValue.MANY + ), StaticFilters.FILTER_CONTROLLED_A_CREATURE, SetTargetPointer.PERMANENT, true, false + ), MyTurnCondition.instance, "Whenever a creature you control deals combat damage during your turn, put that many +1/+1 counters on it")); + + // You may cast this card from your graveyard by removing six counters from among creatures you control in addition to paying its other costs. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new QuilledGreatwurmEffect()).setIdentifier(MageIdentifier.QuilledGreatwurmAlternateCast)); + } + + private QuilledGreatwurm(final QuilledGreatwurm card) { + super(card); + } + + @Override + public QuilledGreatwurm copy() { + return new QuilledGreatwurm(this); + } +} + +class QuilledGreatwurmEffect extends AsThoughEffectImpl { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(); + + static { + filter.add(CounterAnyPredicate.instance); + } + + QuilledGreatwurmEffect() { + super(AsThoughEffectType.CAST_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfGame, Outcome.Benefit); + this.staticText = "you may cast {this} from your graveyard by removing six counters " + + "from among creatures you control in addition to paying its other costs"; + } + + private QuilledGreatwurmEffect(final QuilledGreatwurmEffect effect) { + super(effect); + } + + @Override + public QuilledGreatwurmEffect copy() { + return new QuilledGreatwurmEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectControllerId, Game game) { + if (!source.getSourceId().equals(objectId) + || !source.isControlledBy(affectControllerId) + || game.getState().getZone(objectId) != Zone.GRAVEYARD) { + return false; + } + Player controller = game.getPlayer(affectControllerId); + if (controller == null) { + return false; + } + Costs costs = new CostsImpl<>(); + costs.add(new RemoveCounterCost(new TargetControlledCreaturePermanent(1, 6, filter, true), null, 6)); + controller.setCastSourceIdWithAlternateMana( + objectId, new ManaCostsImpl<>("{4}{G}{G}"), costs, + MageIdentifier.QuilledGreatwurmAlternateCast + ); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/q/QuirionBeastcaller.java b/Mage.Sets/src/mage/cards/q/QuirionBeastcaller.java index 3cb8f79460c..18d5aa5db0a 100644 --- a/Mage.Sets/src/mage/cards/q/QuirionBeastcaller.java +++ b/Mage.Sets/src/mage/cards/q/QuirionBeastcaller.java @@ -14,7 +14,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.counters.CounterType; import mage.filter.StaticFilters; -import mage.target.common.TargetPermanentAmount; +import mage.target.common.TargetCreaturePermanentAmount; /** * @@ -39,10 +39,8 @@ public final class QuirionBeastcaller extends CardImpl { // When Quirion Beastcaller dies, distribute X +1/+1 counters among any number of target creatures you control, where X is the number of +1/+1 counters on Quirion Beastcaller. Ability ability = new DiesSourceTriggeredAbility(new DistributeCountersEffect( - // Amount here is only used for text generation. Real amount is set in target. - 1, "any number of target creatures you control" ).setText("distribute X +1/+1 counters among any number of target creatures you control, where X is the number of +1/+1 counters on {this}")); - ability.addTarget(new TargetPermanentAmount(new CountersSourceCount(CounterType.P1P1), StaticFilters.FILTER_CONTROLLED_CREATURES)); + ability.addTarget(new TargetCreaturePermanentAmount(new CountersSourceCount(CounterType.P1P1), StaticFilters.FILTER_CONTROLLED_CREATURES)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/r/RalCallerOfStorms.java b/Mage.Sets/src/mage/cards/r/RalCallerOfStorms.java index 97cf64715f1..1d050a8241e 100644 --- a/Mage.Sets/src/mage/cards/r/RalCallerOfStorms.java +++ b/Mage.Sets/src/mage/cards/r/RalCallerOfStorms.java @@ -40,7 +40,7 @@ public final class RalCallerOfStorms extends CardImpl { )); // -2: Ral, Caller of Storms deals 3 damage divided as you choose among one, two, or three targets. - Ability ability = new LoyaltyAbility(new DamageMultiEffect(3), -2); + Ability ability = new LoyaltyAbility(new DamageMultiEffect(), -2); ability.addTarget(new TargetAnyTargetAmount(3)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/r/RalLeylineProdigy.java b/Mage.Sets/src/mage/cards/r/RalLeylineProdigy.java index d0d24fb8de8..d5bd0c3d84c 100644 --- a/Mage.Sets/src/mage/cards/r/RalLeylineProdigy.java +++ b/Mage.Sets/src/mage/cards/r/RalLeylineProdigy.java @@ -75,7 +75,7 @@ public final class RalLeylineProdigy extends CardImpl { this.addAbility(new LoyaltyAbility(new RalLeylineProdigyCostReductionEffect(), 1)); // -2: Ral deals 2 damage divided as you choose among one or two targets. Draw a card if you control a blue permanent other than Ral. - Ability ability = new LoyaltyAbility(new DamageMultiEffect(2), -2); + Ability ability = new LoyaltyAbility(new DamageMultiEffect(), -2); ability.addTarget(new TargetAnyTargetAmount(2)); ability.addEffect(new ConditionalOneShotEffect( new DrawCardSourceControllerEffect(1), diff --git a/Mage.Sets/src/mage/cards/r/RavenousGigantotherium.java b/Mage.Sets/src/mage/cards/r/RavenousGigantotherium.java index 263d13b95db..eeaacab7a2b 100644 --- a/Mage.Sets/src/mage/cards/r/RavenousGigantotherium.java +++ b/Mage.Sets/src/mage/cards/r/RavenousGigantotherium.java @@ -73,13 +73,13 @@ class RavenousGigantotheriumAbility extends EntersBattlefieldTriggeredAbility { } int power = Math.max(permanent.getPower().getValue(), 0); this.getEffects().clear(); - this.addEffect(new DamageMultiEffect(power)); + this.addEffect(new DamageMultiEffect()); this.addEffect(new RavenousGigantotheriumEffect()); this.getTargets().clear(); if (power < 1) { return true; } - this.addTarget(new TargetCreaturePermanentAmount(power)); + this.addTarget(new TargetCreaturePermanentAmount(power, 0, power)); return true; } diff --git a/Mage.Sets/src/mage/cards/r/Remedy.java b/Mage.Sets/src/mage/cards/r/Remedy.java index a8ac3d73ea9..ee2837a7ce7 100644 --- a/Mage.Sets/src/mage/cards/r/Remedy.java +++ b/Mage.Sets/src/mage/cards/r/Remedy.java @@ -19,7 +19,7 @@ public final class Remedy extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}"); - // Prevent the next 5 damage that would be dealt this turn to any number of target creatures and/or players, divided as you choose. + // Prevent the next 5 damage that would be dealt this turn to any number of targets, divided as you choose. this.getSpellAbility().addEffect(new PreventDamageToTargetMultiAmountEffect(Duration.EndOfTurn, 5)); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(5)); } diff --git a/Mage.Sets/src/mage/cards/r/RockSlide.java b/Mage.Sets/src/mage/cards/r/RockSlide.java index 72e8b3ddf5e..9ff56d7f9c1 100644 --- a/Mage.Sets/src/mage/cards/r/RockSlide.java +++ b/Mage.Sets/src/mage/cards/r/RockSlide.java @@ -30,7 +30,7 @@ public final class RockSlide extends CardImpl { // Rock Slide deals X damage divided as you choose among any number of target attacking or blocking creatures without flying. DynamicValue xValue = GetXValue.instance; - this.getSpellAbility().addEffect(new DamageMultiEffect(xValue)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(xValue, filter)); } diff --git a/Mage.Sets/src/mage/cards/r/RoilsRetribution.java b/Mage.Sets/src/mage/cards/r/RoilsRetribution.java index d3ac9b51a63..dc0b863a96a 100644 --- a/Mage.Sets/src/mage/cards/r/RoilsRetribution.java +++ b/Mage.Sets/src/mage/cards/r/RoilsRetribution.java @@ -18,7 +18,7 @@ public final class RoilsRetribution extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{W}{W}"); // Roil's Retribution deals 5 damage divided as you choose among any number of target attacking or blocking creatures. - this.getSpellAbility().addEffect(new DamageMultiEffect(5)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(5, StaticFilters.FILTER_ATTACKING_OR_BLOCKING_CREATURES)); } diff --git a/Mage.Sets/src/mage/cards/r/RollingThunder.java b/Mage.Sets/src/mage/cards/r/RollingThunder.java index c3fded58cdb..91e77c9cb19 100644 --- a/Mage.Sets/src/mage/cards/r/RollingThunder.java +++ b/Mage.Sets/src/mage/cards/r/RollingThunder.java @@ -20,7 +20,7 @@ public final class RollingThunder extends CardImpl { // Rolling Thunder deals X damage divided as you choose among any number of targets. DynamicValue xValue = GetXValue.instance; - this.getSpellAbility().addEffect(new DamageMultiEffect(xValue)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(xValue)); } diff --git a/Mage.Sets/src/mage/cards/s/SamutTheTested.java b/Mage.Sets/src/mage/cards/s/SamutTheTested.java index 6d8a013c20d..118d7db9f74 100644 --- a/Mage.Sets/src/mage/cards/s/SamutTheTested.java +++ b/Mage.Sets/src/mage/cards/s/SamutTheTested.java @@ -45,7 +45,7 @@ public final class SamutTheTested extends CardImpl { this.addAbility(ability); // -2: Samut, the Tested deals 2 damage divided as you choose among one or two targets. - effect = new DamageMultiEffect(2); + effect = new DamageMultiEffect(); ability = new LoyaltyAbility(effect, -2); ability.addTarget(new TargetAnyTargetAmount(2)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/SerrasHymn.java b/Mage.Sets/src/mage/cards/s/SerrasHymn.java index fc97f30a71b..2c3d583e6ed 100644 --- a/Mage.Sets/src/mage/cards/s/SerrasHymn.java +++ b/Mage.Sets/src/mage/cards/s/SerrasHymn.java @@ -12,7 +12,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; import mage.counters.CounterType; import mage.target.common.TargetAnyTargetAmount; @@ -22,7 +21,7 @@ import mage.target.common.TargetAnyTargetAmount; */ public final class SerrasHymn extends CardImpl { - private static final String rule = "Prevent the next X damage that would be dealt this turn to any number of target creatures and/or players, divided as you choose, where X is the number of verse counters on {this}."; + private static final String rule = "Prevent the next X damage that would be dealt this turn to any number of targets, divided as you choose, where X is the number of verse counters on {this}."; public SerrasHymn(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); @@ -31,7 +30,7 @@ public final class SerrasHymn extends CardImpl { this.addAbility(new BeginningOfUpkeepTriggeredAbility( new AddCountersSourceEffect(CounterType.VERSE.createInstance(), true), true)); - // Sacrifice Serra's Hymn: Prevent the next X damage that would be dealt this turn to any number of target creatures and/or players, divided as you choose, where X is the number of verse counters on Serra's Hymn. + // Sacrifice Serra's Hymn: Prevent the next X damage that would be dealt this turn to any number of targets, divided as you choose, where X is the number of verse counters on Serra's Hymn. Ability ability = new SimpleActivatedAbility( new PreventDamageToTargetMultiAmountEffect( Duration.EndOfTurn, diff --git a/Mage.Sets/src/mage/cards/s/ShamblingSwarm.java b/Mage.Sets/src/mage/cards/s/ShamblingSwarm.java index d3f9dc9c8f9..9aae9bfbf55 100644 --- a/Mage.Sets/src/mage/cards/s/ShamblingSwarm.java +++ b/Mage.Sets/src/mage/cards/s/ShamblingSwarm.java @@ -27,7 +27,7 @@ public final class ShamblingSwarm extends CardImpl { // When Shambling Swarm dies, distribute three -1/-1 counters among one, two, or three target creatures. For each -1/-1 counter you put on a creature this way, remove a -1/-1 counter from that creature at the beginning of the next end step. Ability ability = new DiesSourceTriggeredAbility(new DistributeCountersEffect( - CounterType.M1M1, 3, "one, two, or three target creatures" + CounterType.M1M1 ).withRemoveAtEndOfTurn(), false); ability.addTarget(new TargetCreaturePermanentAmount(3)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/ShatterskullSmashing.java b/Mage.Sets/src/mage/cards/s/ShatterskullSmashing.java index f7003404553..55e33b1e636 100644 --- a/Mage.Sets/src/mage/cards/s/ShatterskullSmashing.java +++ b/Mage.Sets/src/mage/cards/s/ShatterskullSmashing.java @@ -5,9 +5,6 @@ import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.condition.Condition; import mage.abilities.costs.common.PayLifeCost; import mage.abilities.decorator.ConditionalOneShotEffect; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.MultipliedValue; -import mage.abilities.dynamicvalue.common.GetXValue; import mage.abilities.effects.common.DamageMultiEffect; import mage.abilities.effects.common.TapSourceUnlessPaysEffect; import mage.abilities.mana.RedManaAbility; @@ -28,8 +25,6 @@ import java.util.UUID; */ public final class ShatterskullSmashing extends ModalDoubleFacedCard { - private static final DynamicValue xValue = new MultipliedValue(GetXValue.instance, 2); - public ShatterskullSmashing(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, new SubType[]{}, "{X}{R}{R}", @@ -42,7 +37,7 @@ public final class ShatterskullSmashing extends ModalDoubleFacedCard { // Shatterskull Smashing deals X damage divided as you choose among up to two target creatures and/or planeswalkers. If X is 6 or more, Shatterskull Smashing deals twice X damage divided as you choose among them instead. this.getLeftHalfCard().getSpellAbility().addEffect(new ConditionalOneShotEffect( - new DamageMultiEffect(xValue), new DamageMultiEffect(GetXValue.instance), + new DamageMultiEffect(), new DamageMultiEffect(), ShatterskullSmashingCondition.instance, "{this} deals X damage divided as you choose " + "among up to two target creatures and/or planeswalkers. If X is 6 or more, " + "{this} deals twice X damage divided as you choose among them instead." @@ -90,12 +85,10 @@ enum ShatterskullSmashingAdjuster implements TargetAdjuster { ability.getTargets().clear(); TargetAmount target; if (CardUtil.getSourceCostsTag(game, ability, "X", 0) >= 6) { - target = new TargetCreatureOrPlaneswalkerAmount(2 * CardUtil.getSourceCostsTag(game, ability, "X", 0)); + target = new TargetCreatureOrPlaneswalkerAmount(2 * CardUtil.getSourceCostsTag(game, ability, "X", 0), 0, 2); } else { - target = new TargetCreatureOrPlaneswalkerAmount(CardUtil.getSourceCostsTag(game, ability, "X", 0)); + target = new TargetCreatureOrPlaneswalkerAmount(CardUtil.getSourceCostsTag(game, ability, "X", 0), 0, 2); } - target.setMinNumberOfTargets(0); - target.setMaxNumberOfTargets(2); ability.addTarget(target); } } diff --git a/Mage.Sets/src/mage/cards/s/SkarrganHellkite.java b/Mage.Sets/src/mage/cards/s/SkarrganHellkite.java index 816e660ae3c..0e312aca483 100644 --- a/Mage.Sets/src/mage/cards/s/SkarrganHellkite.java +++ b/Mage.Sets/src/mage/cards/s/SkarrganHellkite.java @@ -38,7 +38,7 @@ public final class SkarrganHellkite extends CardImpl { // {3}{R}: Skarrgan Hellkite deals 2 damage divided as you choose among one or two targets. Activate this ability only if Skarrgan Hellkite has a +1/+1 counter on it. Ability ability = new ConditionalActivatedAbility( - Zone.BATTLEFIELD, new DamageMultiEffect(2), + Zone.BATTLEFIELD, new DamageMultiEffect(), new ManaCostsImpl<>("{3}{R}"), new SourceHasCounterCondition(CounterType.P1P1), "{3}{R}: {this} deals 2 damage divided as you choose among one or two targets. " + "Activate only if {this} has a +1/+1 counter on it." diff --git a/Mage.Sets/src/mage/cards/s/SkirkVolcanist.java b/Mage.Sets/src/mage/cards/s/SkirkVolcanist.java index 536733d44c2..c6a173a3eff 100644 --- a/Mage.Sets/src/mage/cards/s/SkirkVolcanist.java +++ b/Mage.Sets/src/mage/cards/s/SkirkVolcanist.java @@ -12,9 +12,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.filter.common.FilterControlledLandPermanent; -import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetCreaturePermanentAmount; -import mage.target.common.TargetSacrifice; /** * @@ -38,7 +36,7 @@ public final class SkirkVolcanist extends CardImpl { this.addAbility(new MorphAbility(this, new SacrificeTargetCost(2, filter))); // When Skirk Volcanist is turned face up, it deals 3 damage divided as you choose among one, two, or three target creatures. - Ability ability = new TurnedFaceUpSourceTriggeredAbility(new DamageMultiEffect(3, "it")); + Ability ability = new TurnedFaceUpSourceTriggeredAbility(new DamageMultiEffect("it")); ability.addTarget(new TargetCreaturePermanentAmount(3)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SludgeStrider.java b/Mage.Sets/src/mage/cards/s/SludgeStrider.java index cc5d9e0fd68..02827d7907c 100644 --- a/Mage.Sets/src/mage/cards/s/SludgeStrider.java +++ b/Mage.Sets/src/mage/cards/s/SludgeStrider.java @@ -61,6 +61,7 @@ class SludgeStriderTriggeredAbility extends TriggeredAbilityImpl { public SludgeStriderTriggeredAbility() { // setting optional = false because DoIfCostPaid already asks the player super(Zone.BATTLEFIELD, new DoIfCostPaid(new SludgeStriderEffect(), new GenericManaCost(1)), false); + setLeavesTheBattlefieldTrigger(true); } private SludgeStriderTriggeredAbility(final SludgeStriderTriggeredAbility ability) { @@ -69,7 +70,8 @@ class SludgeStriderTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD || event.getType() == GameEvent.EventType.ZONE_CHANGE; + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD + || event.getType() == GameEvent.EventType.ZONE_CHANGE; } @Override diff --git a/Mage.Sets/src/mage/cards/s/SplendidAgony.java b/Mage.Sets/src/mage/cards/s/SplendidAgony.java index b553a39d4a7..4db2104cf7d 100644 --- a/Mage.Sets/src/mage/cards/s/SplendidAgony.java +++ b/Mage.Sets/src/mage/cards/s/SplendidAgony.java @@ -19,7 +19,7 @@ public final class SplendidAgony extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}"); // Distribute two -1/-1 counters among one or two target creatures. - getSpellAbility().addEffect(new DistributeCountersEffect(CounterType.M1M1, 2, "one or two target creatures")); + getSpellAbility().addEffect(new DistributeCountersEffect(CounterType.M1M1)); getSpellAbility().addTarget(new TargetCreaturePermanentAmount(2)); } diff --git a/Mage.Sets/src/mage/cards/s/SpreadingFlames.java b/Mage.Sets/src/mage/cards/s/SpreadingFlames.java index 133249d54d1..eb3243c635a 100644 --- a/Mage.Sets/src/mage/cards/s/SpreadingFlames.java +++ b/Mage.Sets/src/mage/cards/s/SpreadingFlames.java @@ -18,7 +18,7 @@ public final class SpreadingFlames extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{6}{R}"); // Spreading Flames deals 6 damage divided as you choose among any number of target creatures. - this.getSpellAbility().addEffect(new DamageMultiEffect(6)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(6)); } diff --git a/Mage.Sets/src/mage/cards/s/StormTheSeedcore.java b/Mage.Sets/src/mage/cards/s/StormTheSeedcore.java index 39433a1b5d7..85c34246101 100644 --- a/Mage.Sets/src/mage/cards/s/StormTheSeedcore.java +++ b/Mage.Sets/src/mage/cards/s/StormTheSeedcore.java @@ -8,10 +8,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanentAmount; -import mage.target.common.TargetPermanentAmount; import java.util.UUID; @@ -23,15 +21,10 @@ public final class StormTheSeedcore extends CardImpl { public StormTheSeedcore(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{G}{G}"); - // Distribute four +1/+1 counter among up to four target creatures you control. Creatures you control gain vigilance and trample until end of turn. - this.getSpellAbility().addEffect(new DistributeCountersEffect( - 4, - "up to four target creatures you control" - )); - TargetPermanentAmount target = new TargetCreaturePermanentAmount(4, StaticFilters.FILTER_CONTROLLED_CREATURES); - target.setMinNumberOfTargets(0); - target.setMaxNumberOfTargets(4); - this.getSpellAbility().addTarget(target); + // Distribute four +1/+1 counters among up to four target creatures you control. Creatures you control gain vigilance and trample until end of turn. + this.getSpellAbility().addEffect(new DistributeCountersEffect() + .setText("distribute four +1/+1 counters among up to four target creatures you control")); + this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(4, StaticFilters.FILTER_CONTROLLED_CREATURES)); this.getSpellAbility().addEffect(new GainAbilityControlledEffect( VigilanceAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES diff --git a/Mage.Sets/src/mage/cards/s/StumpsquallHydra.java b/Mage.Sets/src/mage/cards/s/StumpsquallHydra.java index fa390531622..810734314ec 100644 --- a/Mage.Sets/src/mage/cards/s/StumpsquallHydra.java +++ b/Mage.Sets/src/mage/cards/s/StumpsquallHydra.java @@ -20,7 +20,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetAmount; -import mage.target.common.TargetCreatureOrPlaneswalkerAmount; +import mage.target.common.TargetPermanentAmount; import java.util.UUID; @@ -96,8 +96,7 @@ class StumpsquallHydraEffect extends OneShotEffect { return false; } - TargetAmount targetAmount = new TargetCreatureOrPlaneswalkerAmount(xValue, filter); - targetAmount.setMinNumberOfTargets(1); + TargetAmount targetAmount = new TargetPermanentAmount(xValue, 1, filter); targetAmount.withNotTarget(true); targetAmount.chooseTarget(outcome, player.getId(), source, game); for (UUID targetId : targetAmount.getTargets()) { diff --git a/Mage.Sets/src/mage/cards/s/SunderingStroke.java b/Mage.Sets/src/mage/cards/s/SunderingStroke.java index 17fde8221a9..d4744fb24da 100644 --- a/Mage.Sets/src/mage/cards/s/SunderingStroke.java +++ b/Mage.Sets/src/mage/cards/s/SunderingStroke.java @@ -25,12 +25,12 @@ public final class SunderingStroke extends CardImpl { // Sundering Stroke deals 7 damage divided as you choose among one, two, or three targets. If at least seven red mana was spent to cast this spell, instead Sundering Stroke deals 7 damage to each of those permanents and/or players. this.getSpellAbility().addEffect(new ConditionalOneShotEffect( - new DamageTargetEffect(7), new DamageMultiEffect(7), SunderingStrokeCondtition.instance, + new DamageTargetEffect(7), new DamageMultiEffect(), SunderingStrokeCondtition.instance, "{this} deals 7 damage divided as you choose among one, two, or three targets. " + "If at least seven red mana was spent to cast this spell, " + "instead {this} deals 7 damage to each of those permanents and/or players" )); - this.getSpellAbility().addTarget(new TargetAnyTargetAmount(7, 3)); + this.getSpellAbility().addTarget(new TargetAnyTargetAmount(7, 1, 3)); this.getSpellAbility().addHint(new StaticHint( "(You have to choose how 7 damage is divided even if you spend seven red mana.)" )); diff --git a/Mage.Sets/src/mage/cards/t/TheGrandEvolution.java b/Mage.Sets/src/mage/cards/t/TheGrandEvolution.java index 49ae80faade..cfcde708afd 100644 --- a/Mage.Sets/src/mage/cards/t/TheGrandEvolution.java +++ b/Mage.Sets/src/mage/cards/t/TheGrandEvolution.java @@ -14,13 +14,12 @@ import mage.cards.CardSetInfo; import mage.cards.Cards; import mage.cards.CardsImpl; import mage.constants.*; -import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; import mage.target.TargetPermanent; -import mage.target.common.TargetPermanentAmount; +import mage.target.common.TargetCreaturePermanentAmount; import java.util.UUID; @@ -45,10 +44,8 @@ public final class TheGrandEvolution extends CardImpl { // II -- Distribute seven +1/+1 counters among any number of target creatures you control. sagaAbility.addChapterEffect( this, SagaChapter.CHAPTER_II, SagaChapter.CHAPTER_II, - new DistributeCountersEffect( - 7, - "any number of target creatures you control" - ), new TargetPermanentAmount(7, StaticFilters.FILTER_CONTROLLED_CREATURES) + new DistributeCountersEffect(), + new TargetCreaturePermanentAmount(7, StaticFilters.FILTER_CONTROLLED_CREATURES) ); // III -- Until end of turn, creatures you control gain "{1}: This creature fights target creature you don't control." Exile The Grand Evolution, then return it to the battlefield. diff --git a/Mage.Sets/src/mage/cards/t/ThoughtGorger.java b/Mage.Sets/src/mage/cards/t/ThoughtGorger.java index dd9aa9f1801..ddb2e2a6bd2 100644 --- a/Mage.Sets/src/mage/cards/t/ThoughtGorger.java +++ b/Mage.Sets/src/mage/cards/t/ThoughtGorger.java @@ -4,14 +4,15 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; @@ -37,7 +38,10 @@ public final class ThoughtGorger extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new ThoughtGorgerEffectEnters())); // When Thought Gorger leaves the battlefield, draw a card for each +1/+1 counter on it. - this.addAbility(new LeavesBattlefieldTriggeredAbility(new ThoughtGorgerEffectLeaves(), false)); + this.addAbility(new LeavesBattlefieldTriggeredAbility( + new DrawCardSourceControllerEffect(new CountersSourceCount(CounterType.P1P1)) + .setText("draw a card for each +1/+1 counter on it"), false + )); } private ThoughtGorger(final ThoughtGorger card) { @@ -83,33 +87,3 @@ class ThoughtGorgerEffectEnters extends OneShotEffect { return true; } } - -class ThoughtGorgerEffectLeaves extends OneShotEffect { - - ThoughtGorgerEffectLeaves() { - super(Outcome.Neutral); - this.staticText = "draw a card for each +1/+1 counter on it."; - } - - private ThoughtGorgerEffectLeaves(final ThoughtGorgerEffectLeaves effect) { - super(effect); - } - - @Override - public ThoughtGorgerEffectLeaves copy() { - return new ThoughtGorgerEffectLeaves(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - Permanent thoughtGorgerLastState = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - int numberCounters = thoughtGorgerLastState.getCounters(game).getCount(CounterType.P1P1); - if (player != null) { - player.drawCards(numberCounters, source, game); - return true; - } - return false; - } -} - diff --git a/Mage.Sets/src/mage/cards/t/ToothyImaginaryFriend.java b/Mage.Sets/src/mage/cards/t/ToothyImaginaryFriend.java index 02a45c84c73..aa448f99579 100644 --- a/Mage.Sets/src/mage/cards/t/ToothyImaginaryFriend.java +++ b/Mage.Sets/src/mage/cards/t/ToothyImaginaryFriend.java @@ -2,11 +2,9 @@ package mage.cards.t; import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.DrawCardControllerTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.keyword.PartnerWithAbility; @@ -15,10 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.Zone; import mage.counters.CounterType; -import mage.game.Game; -import mage.game.permanent.Permanent; /** * @@ -42,8 +37,9 @@ public final class ToothyImaginaryFriend extends CardImpl { // When Toothy leaves the battlefield, draw a card for each +1/+1 counter on it. this.addAbility(new LeavesBattlefieldTriggeredAbility( - new DrawCardSourceControllerEffect(new ToothyImaginaryFriendCountersCount(CounterType.P1P1)) - .setText("draw a card for each +1/+1 counter on it"), false)); + new DrawCardSourceControllerEffect(new CountersSourceCount(CounterType.P1P1)) + .setText("draw a card for each +1/+1 counter on it"), false + )); } private ToothyImaginaryFriend(final ToothyImaginaryFriend card) { @@ -55,40 +51,3 @@ public final class ToothyImaginaryFriend extends CardImpl { return new ToothyImaginaryFriend(this); } } - -class ToothyImaginaryFriendCountersCount implements DynamicValue { - - private final String counterName; - - public ToothyImaginaryFriendCountersCount(CounterType counter) { - this.counterName = counter.getName(); - } - - private ToothyImaginaryFriendCountersCount(final ToothyImaginaryFriendCountersCount countersCount) { - this.counterName = countersCount.counterName; - } - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - Permanent permanent = (Permanent) game.getLastKnownInformation(sourceAbility.getSourceId(), Zone.BATTLEFIELD); - if (permanent != null) { - return permanent.getCounters(game).getCount(counterName); - } - return 0; - } - - @Override - public ToothyImaginaryFriendCountersCount copy() { - return new ToothyImaginaryFriendCountersCount(this); - } - - @Override - public String toString() { - return "1"; - } - - @Override - public String getMessage() { - return counterName + " counter on {this}"; - } -} diff --git a/Mage.Sets/src/mage/cards/t/TwinBolt.java b/Mage.Sets/src/mage/cards/t/TwinBolt.java index bc9f455a484..6291a51a68d 100644 --- a/Mage.Sets/src/mage/cards/t/TwinBolt.java +++ b/Mage.Sets/src/mage/cards/t/TwinBolt.java @@ -17,7 +17,7 @@ public final class TwinBolt extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); // Twin Bolt deals 2 damage divided as you choose among one or two targets. - this.getSpellAbility().addEffect(new DamageMultiEffect(2)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(2)); } diff --git a/Mage.Sets/src/mage/cards/u/UndercityUpheaval.java b/Mage.Sets/src/mage/cards/u/UndercityUpheaval.java index ea27f64da27..3fff4a7c64e 100644 --- a/Mage.Sets/src/mage/cards/u/UndercityUpheaval.java +++ b/Mage.Sets/src/mage/cards/u/UndercityUpheaval.java @@ -12,7 +12,6 @@ import mage.cards.CardSetInfo; import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.Duration; -import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanentAmount; @@ -30,7 +29,7 @@ public final class UndercityUpheaval extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}{G}"); // Undergrowth -- Distribute X +1/+1 counters among any number of target creatures you control, where X is the number of creature cards in your graveyard as you cast this spell. Creatures you control gain vigilance until end of turn. - this.getSpellAbility().addEffect(new DistributeCountersEffect(1, "") + this.getSpellAbility().addEffect(new DistributeCountersEffect() .setText("distribute X +1/+1 counters among any number of target creatures you control, " + "where X is the number of creature cards in your graveyard as you cast this spell")); this.getSpellAbility().addEffect(new GainAbilityControlledEffect( diff --git a/Mage.Sets/src/mage/cards/u/UrzaAcademyHeadmaster.java b/Mage.Sets/src/mage/cards/u/UrzaAcademyHeadmaster.java index 6032b4ee8c1..0b091865b6b 100644 --- a/Mage.Sets/src/mage/cards/u/UrzaAcademyHeadmaster.java +++ b/Mage.Sets/src/mage/cards/u/UrzaAcademyHeadmaster.java @@ -144,7 +144,7 @@ class UrzaAcademyHeadmasterRandomEffect extends OneShotEffect { break; case 2: // AJANI MENTOR OF HEROES 1 sb.append("Distribute three +1/+1 counters among one, two, or three target creatures you control."); - effects.add(new DistributeCountersEffect(3, "one, two, or three target creatures you control")); + effects.add(new DistributeCountersEffect()); target = new TargetCreaturePermanentAmount(3, filter1); break; case 3: // NICOL BOLAS PLANESWALKER 1 diff --git a/Mage.Sets/src/mage/cards/v/VastwoodHydra.java b/Mage.Sets/src/mage/cards/v/VastwoodHydra.java index 47057fdc8f3..e59fc7a16ee 100644 --- a/Mage.Sets/src/mage/cards/v/VastwoodHydra.java +++ b/Mage.Sets/src/mage/cards/v/VastwoodHydra.java @@ -88,8 +88,7 @@ class VastwoodHydraDistributeEffect extends OneShotEffect { return false; } - TargetPermanentAmount target = new TargetCreaturePermanentAmount(amount, StaticFilters.FILTER_CONTROLLED_CREATURE); - target.setMinNumberOfTargets(1); + TargetPermanentAmount target = new TargetCreaturePermanentAmount(amount, 0, amount, StaticFilters.FILTER_CONTROLLED_CREATURE); target.withNotTarget(true); target.withChooseHint("to distribute " + amount + " counters"); target.chooseTarget(outcome, player.getId(), source, game); diff --git a/Mage.Sets/src/mage/cards/v/VerdurousGearhulk.java b/Mage.Sets/src/mage/cards/v/VerdurousGearhulk.java index b17752c6191..d1dc069424b 100644 --- a/Mage.Sets/src/mage/cards/v/VerdurousGearhulk.java +++ b/Mage.Sets/src/mage/cards/v/VerdurousGearhulk.java @@ -10,7 +10,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.target.common.TargetCreaturePermanentAmount; @@ -31,7 +30,7 @@ public final class VerdurousGearhulk extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // When Verdurous Gearhulk enters the battlefield, distribute four +1/+1 counters among any number of target creatures you control. - Ability ability = new EntersBattlefieldTriggeredAbility(new DistributeCountersEffect(4, "any number of target creatures you control"), false); + Ability ability = new EntersBattlefieldTriggeredAbility(new DistributeCountersEffect(), false); ability.addTarget(new TargetCreaturePermanentAmount(4, StaticFilters.FILTER_CONTROLLED_CREATURES)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/v/ViolentEruption.java b/Mage.Sets/src/mage/cards/v/ViolentEruption.java index d5aac94e74e..4bbe8162cda 100644 --- a/Mage.Sets/src/mage/cards/v/ViolentEruption.java +++ b/Mage.Sets/src/mage/cards/v/ViolentEruption.java @@ -19,7 +19,7 @@ public final class ViolentEruption extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{R}{R}{R}"); // Violent Eruption deals 4 damage divided as you choose among any number of targets. - this.getSpellAbility().addEffect(new DamageMultiEffect(4)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(4)); // Madness {1}{R}{R} diff --git a/Mage.Sets/src/mage/cards/v/ViralSpawning.java b/Mage.Sets/src/mage/cards/v/ViralSpawning.java index f92ab2b7b4f..1124790accc 100644 --- a/Mage.Sets/src/mage/cards/v/ViralSpawning.java +++ b/Mage.Sets/src/mage/cards/v/ViralSpawning.java @@ -33,7 +33,7 @@ public final class ViralSpawning extends CardImpl { Zone.GRAVEYARD, new ConditionalContinuousEffect( new GainAbilitySourceEffect( - new FlashbackAbility(this, new ManaCostsImpl<>("{2}{G}")), Duration.Custom, true + new FlashbackAbility(this, new ManaCostsImpl<>("{2}{G}")), Duration.WhileInGraveyard, true ), CorruptedCondition.instance, "as long as an opponent has three or more " + "poison counters and {this} is in your graveyard, it has flashback {2}{G}" ) diff --git a/Mage.Sets/src/mage/cards/v/VivienArkbowRanger.java b/Mage.Sets/src/mage/cards/v/VivienArkbowRanger.java index 44b54206a5a..27dcad1bf4b 100644 --- a/Mage.Sets/src/mage/cards/v/VivienArkbowRanger.java +++ b/Mage.Sets/src/mage/cards/v/VivienArkbowRanger.java @@ -14,7 +14,6 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.constants.SuperType; -import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetCreatureOrPlaneswalker; @@ -35,16 +34,13 @@ public final class VivienArkbowRanger extends CardImpl { this.setStartingLoyalty(4); // +1: Distribute two +1/+1 counters among up to two target creatures. They gain trample until end of turn. - Ability ability = new LoyaltyAbility(new DistributeCountersEffect( - 2, "up to two target creatures"), 1); + Ability ability = new LoyaltyAbility(new DistributeCountersEffect() + .setText("distribute two +1/+1 counters among up to two target creatures"), 1); ability.addEffect(new GainAbilityTargetEffect( TrampleAbility.getInstance(), Duration.EndOfTurn, "They gain trample until end of turn" )); - TargetCreaturePermanentAmount target = new TargetCreaturePermanentAmount(2); - target.setMinNumberOfTargets(0); - target.setMaxNumberOfTargets(2); - ability.addTarget(target); + ability.addTarget(new TargetCreaturePermanentAmount(2, 0, 2)); this.addAbility(ability); // −3: Target creature you control deals damage equal to its power to target creature or planeswalker. diff --git a/Mage.Sets/src/mage/cards/v/VolcanicWind.java b/Mage.Sets/src/mage/cards/v/VolcanicWind.java index 495b083e130..6eea593fff4 100644 --- a/Mage.Sets/src/mage/cards/v/VolcanicWind.java +++ b/Mage.Sets/src/mage/cards/v/VolcanicWind.java @@ -25,7 +25,7 @@ public final class VolcanicWind extends CardImpl { // Volcanic Wind deals X damage divided as you choose among any number of target creatures, where X is the number of creatures as you cast Volcanic Wind. PermanentsOnBattlefieldCount creatures = new PermanentsOnBattlefieldCount(filter, null); - Effect effect = new DamageMultiEffect(creatures); + Effect effect = new DamageMultiEffect(); effect.setText(rule); this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetCreaturePermanentAmount(creatures)); diff --git a/Mage.Sets/src/mage/cards/v/VolleyOfBoulders.java b/Mage.Sets/src/mage/cards/v/VolleyOfBoulders.java index a5d545f66f1..ae3faa99e8a 100644 --- a/Mage.Sets/src/mage/cards/v/VolleyOfBoulders.java +++ b/Mage.Sets/src/mage/cards/v/VolleyOfBoulders.java @@ -7,7 +7,6 @@ import mage.abilities.keyword.FlashbackAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.TimingRule; import mage.target.common.TargetAnyTargetAmount; /** @@ -20,7 +19,7 @@ public final class VolleyOfBoulders extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{8}{R}"); // Volley of Boulders deals 6 damage divided as you choose among any number of targets. - this.getSpellAbility().addEffect(new DamageMultiEffect(6)); + this.getSpellAbility().addEffect(new DamageMultiEffect()); this.getSpellAbility().addTarget(new TargetAnyTargetAmount(6)); // Flashback {R}{R}{R}{R}{R}{R} this.addAbility(new FlashbackAbility(this, new ManaCostsImpl<>("{R}{R}{R}{R}{R}{R}"))); diff --git a/Mage.Sets/src/mage/cards/w/WurmskinForger.java b/Mage.Sets/src/mage/cards/w/WurmskinForger.java index 88a1b282c0d..67689ee8c74 100644 --- a/Mage.Sets/src/mage/cards/w/WurmskinForger.java +++ b/Mage.Sets/src/mage/cards/w/WurmskinForger.java @@ -10,7 +10,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.counters.CounterType; import mage.target.common.TargetCreaturePermanentAmount; /** @@ -27,7 +26,7 @@ public final class WurmskinForger extends CardImpl { this.toughness = new MageInt(2); // When Wurmskin Forger enters the battlefield, distribute three +1/+1 counters among one, two, or three target creatures. - Ability ability = new EntersBattlefieldTriggeredAbility(new DistributeCountersEffect(3, "one, two, or three target creatures"), false); + Ability ability = new EntersBattlefieldTriggeredAbility(new DistributeCountersEffect(), false); ability.addTarget(new TargetCreaturePermanentAmount(3)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/y/YannikScavengingSentinel.java b/Mage.Sets/src/mage/cards/y/YannikScavengingSentinel.java index 648aa7cecc1..9c4997e3944 100644 --- a/Mage.Sets/src/mage/cards/y/YannikScavengingSentinel.java +++ b/Mage.Sets/src/mage/cards/y/YannikScavengingSentinel.java @@ -13,7 +13,6 @@ import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; @@ -99,11 +98,11 @@ class YannikScavengingSentinelEffect extends OneShotEffect { game.addDelayedTriggeredAbility(new OnLeaveReturnExiledAbility(), source); if (game.getState().getZone(permanent.getId()) != Zone.BATTLEFIELD) { ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( - new DistributeCountersEffect(power, ""), false, + new DistributeCountersEffect(), false, "distribute X +1/+1 counters among any number of target creatures, " + "where X is the exiled creature's power" ); - ability.addTarget(new TargetCreaturePermanentAmount(power)); + ability.addTarget(new TargetCreaturePermanentAmount(power, 0, power)); game.fireReflexiveTriggeredAbility(ability, source); } return true; diff --git a/Mage.Sets/src/mage/cards/y/YesManPersonalSecuritron.java b/Mage.Sets/src/mage/cards/y/YesManPersonalSecuritron.java new file mode 100644 index 00000000000..0db344a9708 --- /dev/null +++ b/Mage.Sets/src/mage/cards/y/YesManPersonalSecuritron.java @@ -0,0 +1,117 @@ +package mage.cards.y; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.condition.common.MyTurnCondition; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.dynamicvalue.common.CountersSourceCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenTargetEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.TargetPlayerGainControlSourceEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.hint.common.MyTurnHint; +import mage.constants.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.SoldierToken; +import mage.target.common.TargetOpponent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author Grath + */ +public final class YesManPersonalSecuritron extends CardImpl { + + public YesManPersonalSecuritron(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.ROBOT); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // {T}: Target opponent gains control of Yes Man, Personal Securitron. When they do, you draw two cards and put a quest counter on Yes Man. Activate only during your turn. + Ability ability = new ActivateIfConditionActivatedAbility(Zone.BATTLEFIELD, + new YesManPersonalSecuritronControlEffect(), new TapSourceCost(), MyTurnCondition.instance); + ability.addTarget(new TargetOpponent()); + ability.addHint(MyTurnHint.instance); + this.addAbility(ability); + + // Wild Card -- When Yes Man leaves the battlefield, its owner creates a tapped 1/1 white Soldier creature token for each quest counter on it. + this.addAbility(new LeavesBattlefieldTriggeredAbility(new YesManPersonalSecuritronLeavesEffect(), false)); + } + + private YesManPersonalSecuritron(final YesManPersonalSecuritron card) { + super(card); + } + + @Override + public YesManPersonalSecuritron copy() { + return new YesManPersonalSecuritron(this); + } +} + +class YesManPersonalSecuritronControlEffect extends TargetPlayerGainControlSourceEffect { + YesManPersonalSecuritronControlEffect() { + super(); + this.staticText = "Target opponent gains control of {this}. When they do, you draw two cards and put a quest counter on Yes Man."; + } + + private YesManPersonalSecuritronControlEffect(final YesManPersonalSecuritronControlEffect effect) { + super(effect); + } + + @Override + public YesManPersonalSecuritronControlEffect copy() { + return new YesManPersonalSecuritronControlEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + boolean controlChanged = super.apply(game, source); + if (!controlChanged) { + return false; + } + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(new DrawCardSourceControllerEffect(2), false, "When they do, you draw two cards and put a quest counter on Yes Man."); + OneShotEffect counterEffect = new AddCountersTargetEffect(CounterType.QUEST.createInstance()); + counterEffect.setTargetPointer(new FixedTarget(source.getSourceId())); + ability.addEffect(counterEffect); + game.fireReflexiveTriggeredAbility(ability, source); + return true; + } +} + +class YesManPersonalSecuritronLeavesEffect extends CreateTokenTargetEffect { + YesManPersonalSecuritronLeavesEffect() { + super(new SoldierToken(), new CountersSourceCount(CounterType.QUEST), true); + this.staticText = "its owner creates a tapped 1/1 white Soldier creature token for each quest counter on it."; + } + + private YesManPersonalSecuritronLeavesEffect(final YesManPersonalSecuritronLeavesEffect effect) { + super(effect); + } + + @Override + public YesManPersonalSecuritronLeavesEffect copy() { + return new YesManPersonalSecuritronLeavesEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent yesman = game.getPermanentOrLKIBattlefield(source.getSourceId()); + if (yesman == null) { + return false; + } + this.setTargetPointer(new FixedTarget(yesman.getOwnerId())); + return super.apply(game, source); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/Fallout.java b/Mage.Sets/src/mage/sets/Fallout.java index d8c805944a4..e3b4309e94e 100644 --- a/Mage.Sets/src/mage/sets/Fallout.java +++ b/Mage.Sets/src/mage/sets/Fallout.java @@ -430,6 +430,7 @@ public final class Fallout extends ExpansionSet { cards.add(new SetCardInfo("Windbrisk Heights", 315, Rarity.RARE, mage.cards.w.WindbriskHeights.class)); cards.add(new SetCardInfo("Winding Constrictor", 223, Rarity.UNCOMMON, mage.cards.w.WindingConstrictor.class)); cards.add(new SetCardInfo("Woodland Cemetery", 316, Rarity.RARE, mage.cards.w.WoodlandCemetery.class)); + cards.add(new SetCardInfo("Yes Man, Personal Securitron", 29, Rarity.RARE, mage.cards.y.YesManPersonalSecuritron.class)); cards.add(new SetCardInfo("Young Deathclaws", 125, Rarity.UNCOMMON, mage.cards.y.YoungDeathclaws.class)); cards.removeIf(setCardInfo -> IkoriaLairOfBehemoths.mutateNames.contains(setCardInfo.getName())); // remove when mutate is implemented diff --git a/Mage.Sets/src/mage/sets/Foundations.java b/Mage.Sets/src/mage/sets/Foundations.java index 13f782234a4..467adecad79 100644 --- a/Mage.Sets/src/mage/sets/Foundations.java +++ b/Mage.Sets/src/mage/sets/Foundations.java @@ -60,6 +60,9 @@ public final class Foundations extends ExpansionSet { cards.add(new SetCardInfo("Ballyrush Banneret", 567, Rarity.COMMON, mage.cards.b.BallyrushBanneret.class)); cards.add(new SetCardInfo("Balmor, Battlemage Captain", 237, Rarity.UNCOMMON, mage.cards.b.BalmorBattlemageCaptain.class)); cards.add(new SetCardInfo("Banishing Light", 138, Rarity.COMMON, mage.cards.b.BanishingLight.class)); + cards.add(new SetCardInfo("Banner of Kinship", 127, Rarity.RARE, mage.cards.b.BannerOfKinship.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Banner of Kinship", 352, Rarity.RARE, mage.cards.b.BannerOfKinship.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Banner of Kinship", 484, Rarity.RARE, mage.cards.b.BannerOfKinship.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Basilisk Collar", 669, Rarity.RARE, mage.cards.b.BasiliskCollar.class)); cards.add(new SetCardInfo("Battle-Rattle Shaman", 533, Rarity.UNCOMMON, mage.cards.b.BattleRattleShaman.class)); cards.add(new SetCardInfo("Battlesong Berserker", 78, Rarity.UNCOMMON, mage.cards.b.BattlesongBerserker.class)); @@ -376,6 +379,9 @@ public final class Foundations extends ExpansionSet { cards.add(new SetCardInfo("Pyromancer's Goggles", 677, Rarity.MYTHIC, mage.cards.p.PyromancersGoggles.class)); cards.add(new SetCardInfo("Quakestrider Ceratops", 110, Rarity.UNCOMMON, mage.cards.q.QuakestriderCeratops.class)); cards.add(new SetCardInfo("Quick Study", 513, Rarity.COMMON, mage.cards.q.QuickStudy.class)); + cards.add(new SetCardInfo("Quilled Greatwurm", 111, Rarity.MYTHIC, mage.cards.q.QuilledGreatwurm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Quilled Greatwurm", 339, Rarity.MYTHIC, mage.cards.q.QuilledGreatwurm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Quilled Greatwurm", 473, Rarity.MYTHIC, mage.cards.q.QuilledGreatwurm.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Quick-Draw Katana", 130, Rarity.COMMON, mage.cards.q.QuickDrawKatana.class)); cards.add(new SetCardInfo("Raging Redcap", 543, Rarity.COMMON, mage.cards.r.RagingRedcap.class)); cards.add(new SetCardInfo("Raise the Past", 22, Rarity.RARE, mage.cards.r.RaiseThePast.class)); diff --git a/Mage.Sets/src/mage/sets/JumpstartHistoricHorizons.java b/Mage.Sets/src/mage/sets/JumpstartHistoricHorizons.java index 39b54ca1d8b..b2c2b9b6e99 100644 --- a/Mage.Sets/src/mage/sets/JumpstartHistoricHorizons.java +++ b/Mage.Sets/src/mage/sets/JumpstartHistoricHorizons.java @@ -302,7 +302,7 @@ public final class JumpstartHistoricHorizons extends ExpansionSet { cards.add(new SetCardInfo("Servant of the Scale", 635, Rarity.COMMON, mage.cards.s.ServantOfTheScale.class)); cards.add(new SetCardInfo("Shambleshark", 714, Rarity.COMMON, mage.cards.s.Shambleshark.class)); cards.add(new SetCardInfo("Shelter", 133, Rarity.COMMON, mage.cards.s.Shelter.class)); - cards.add(new SetCardInfo("Shivan Drgaon", 788, Rarity.RARE, mage.cards.s.ShivanDragon.class)); + cards.add(new SetCardInfo("Shivan Dragon", 788, Rarity.RARE, mage.cards.s.ShivanDragon.class)); cards.add(new SetCardInfo("Shiv's Embrace", 511, Rarity.UNCOMMON, mage.cards.s.ShivsEmbrace.class)); cards.add(new SetCardInfo("Shoreline Scout", 12, Rarity.UNCOMMON, mage.cards.s.ShorelineScout.class)); cards.add(new SetCardInfo("Sinister Starfish", 383, Rarity.COMMON, mage.cards.s.SinisterStarfish.class)); diff --git a/Mage.Sets/src/mage/sets/UniversesWithin.java b/Mage.Sets/src/mage/sets/UniversesWithin.java index 895ee348ca3..1e748c59444 100644 --- a/Mage.Sets/src/mage/sets/UniversesWithin.java +++ b/Mage.Sets/src/mage/sets/UniversesWithin.java @@ -16,7 +16,6 @@ public final class UniversesWithin extends ExpansionSet { } private UniversesWithin() { - // The set name is a placeholder and will likely change super("Universes Within", "SLX", ExpansionSet.buildDate(2022, 3, 3), SetType.SUPPLEMENTAL); this.hasBasicLands = false; this.hasBoosters = false; diff --git a/Mage.Tests/src/test/java/org/mage/test/AI/basic/TargetPriorityTest.java b/Mage.Tests/src/test/java/org/mage/test/AI/basic/TargetPriorityTest.java index d84216dc63c..17dc5e580e3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/AI/basic/TargetPriorityTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/AI/basic/TargetPriorityTest.java @@ -223,8 +223,8 @@ public class TargetPriorityTest extends CardTestPlayerBaseAI { @Test public void test_targetAmount_NormalCase() { - Ability ability = new SimpleActivatedAbility(Zone.ALL, new DamageMultiEffect(3), new ManaCostsImpl<>("{R}")); - ability.addTarget(new TargetCreaturePermanentAmount(3)); + Ability ability = new SimpleActivatedAbility(Zone.ALL, new DamageMultiEffect(), new ManaCostsImpl<>("{R}")); + ability.addTarget(new TargetCreaturePermanentAmount(3, 0, 3)); addCustomCardWithAbility("damage 3", playerA, ability); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); // @@ -250,8 +250,8 @@ public class TargetPriorityTest extends CardTestPlayerBaseAI { public void test_targetAmount_BadCase() { // choose targets as enters battlefield (e.g. can't be canceled) SpellAbility spell = new SpellAbility(new ManaCostsImpl<>("{R}"), "damage 3", Zone.HAND); - Ability ability = new EntersBattlefieldTriggeredAbility(new DamageMultiEffect(3)); - ability.addTarget(new TargetCreaturePermanentAmount(3)); + Ability ability = new EntersBattlefieldTriggeredAbility(new DamageMultiEffect()); + ability.addTarget(new TargetCreaturePermanentAmount(3, 0, 3)); addCustomCardWithSpell(playerA, spell, ability, CardType.ENCHANTMENT); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); // @@ -283,8 +283,8 @@ public class TargetPriorityTest extends CardTestPlayerBaseAI { public void test_targetAmount_Performance() { int cardsMultiplier = 3; - Ability ability = new SimpleActivatedAbility(Zone.ALL, new DamageMultiEffect(3), new ManaCostsImpl<>("{R}")); - ability.addTarget(new TargetCreaturePermanentAmount(3)); + Ability ability = new SimpleActivatedAbility(Zone.ALL, new DamageMultiEffect(), new ManaCostsImpl<>("{R}")); + ability.addTarget(new TargetCreaturePermanentAmount(3, 0, 3)); addCustomCardWithAbility("damage 3", playerA, ability); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); // diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GraftTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GraftTest.java index ee779a84fd8..f74ec6b68cf 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GraftTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GraftTest.java @@ -1,5 +1,3 @@ - - package org.mage.test.cards.abilities.keywords; import mage.constants.PhaseStep; @@ -59,7 +57,6 @@ public class GraftTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Sporeback Troll", 1); assertPowerToughness(playerA, "Sporeback Troll", 2, 2); assertCounterCount("Sporeback Troll", CounterType.P1P1, 2); - } @Test @@ -102,6 +99,5 @@ public class GraftTest extends CardTestPlayerBase { assertPowerToughness(playerA, "Cytoplast Root-Kin", 4, 4); assertCounterCount("Sporeback Troll", CounterType.P1P1, 2); assertCounterCount("Cytoplast Root-Kin", CounterType.P1P1, 4); - } - + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/AnimateDeadTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/AnimateDeadTest.java index 3aaa6ead773..b5dcb5310e1 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/AnimateDeadTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/enchantments/AnimateDeadTest.java @@ -3,6 +3,7 @@ package org.mage.test.cards.enchantments; import mage.constants.PhaseStep; import mage.constants.Zone; import org.junit.Test; +import org.mage.test.player.TestPlayer; import org.mage.test.serverside.base.CardTestPlayerBase; /** @@ -192,6 +193,7 @@ public class AnimateDeadTest extends CardTestPlayerBase { addCard(Zone.GRAVEYARD, playerB, "Dragonlord Atarka", 1); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Animate Dead", "Dragonlord Atarka"); + addTarget(playerA, TestPlayer.TARGET_SKIP); setStopAt(2, PhaseStep.BEGIN_COMBAT); execute(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m12/GrandAbolisherTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m12/GrandAbolisherTest.java new file mode 100644 index 00000000000..36bbd128649 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m12/GrandAbolisherTest.java @@ -0,0 +1,54 @@ +package org.mage.test.cards.single.m12; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class GrandAbolisherTest extends CardTestPlayerBase { + + @Test + public void test_MakeSureItWorksFromBattlefieldOnly() { + skipInitShuffling(); + + // related bug: #13099 + String castAbility = "Cast Consider"; + + // During your turn, your opponents can't cast spells or activate abilities of artifacts, creatures, or enchantments. + addCard(Zone.LIBRARY, playerB, "Grand Abolisher", 1); // {W}{W} + addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); + // + // Draw a card. + addCard(Zone.HAND, playerA, "Consider", 1); // {U} + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + + String info = "turn 1 (opponent, in library - no restriction)"; + checkHandCardCount(info, 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Grand Abolisher", 0); + checkPlayableAbility(info, 1, PhaseStep.POSTCOMBAT_MAIN, playerA, castAbility, true); + + info = "turn 2 (own, in hand - no restriction)"; + checkHandCardCount(info, 2, PhaseStep.PRECOMBAT_MAIN, playerB, "Grand Abolisher", 1); + checkPlayableAbility(info, 2, PhaseStep.POSTCOMBAT_MAIN, playerA, castAbility, true); + + info = "turn 3 (opponent, in hand - no restriction)"; + checkHandCardCount(info, 3, PhaseStep.PRECOMBAT_MAIN, playerB, "Grand Abolisher", 1); + checkPlayableAbility(info, 3, PhaseStep.POSTCOMBAT_MAIN, playerA, castAbility, true); + + info = "turn 4 (own, in battlefield - must have restriction)"; + castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Grand Abolisher"); + waitStackResolved(4, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount(info, 4, PhaseStep.PRECOMBAT_MAIN, playerB, "Grand Abolisher", 1); + checkPlayableAbility(info, 4, PhaseStep.POSTCOMBAT_MAIN, playerA, castAbility, false); + + info = "turn 5 (opponent, in battlefield - no restriction)"; + checkPermanentCount(info, 5, PhaseStep.PRECOMBAT_MAIN, playerB, "Grand Abolisher", 1); + checkPlayableAbility(info, 5, PhaseStep.POSTCOMBAT_MAIN, playerA, castAbility, true); + + setStrictChooseMode(true); + setStopAt(5, PhaseStep.END_TURN); + execute(); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/NulldrifterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/NulldrifterTest.java new file mode 100644 index 00000000000..7d507886f06 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/NulldrifterTest.java @@ -0,0 +1,160 @@ +package org.mage.test.cards.single.mh3; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps; + +/** + * @author JayDi85 + */ +public class NulldrifterTest extends CardTestPlayerBaseWithAIHelps { + + @Test + public void test_AllMana_UseNormalCost() { + // When you cast this spell, draw two cards. + // Evoke {2}{U} (You may cast this spell for its evoke cost. If you do, it’s sacrificed when it enters.) + addCard(Zone.HAND, playerA, "Nulldrifter", 1); // {7} + addCard(Zone.BATTLEFIELD, playerA, "Island", 7); + + // select normal cost + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nulldrifter"); + setChoice(playerA, "Cast with no alternative cost"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Nulldrifter", 1); + assertGraveyardCount(playerA, "Nulldrifter", 0); + assertHandCount(playerA, 2); + assertTappedCount("Island", true, 7); + } + + @Test + public void test_AllMana_UseEvokeCost_Human() { + // When you cast this spell, draw two cards. + // Evoke {2}{U} (You may cast this spell for its evoke cost. If you do, it’s sacrificed when it enters.) + addCard(Zone.HAND, playerA, "Nulldrifter", 1); // {7} + addCard(Zone.BATTLEFIELD, playerA, "Island", 7); + + // select evoke cost + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nulldrifter"); + setChoice(playerA, "Cast with Evoke alternative cost"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Nulldrifter", 0); + assertGraveyardCount(playerA, "Nulldrifter", 1); + assertHandCount(playerA, 2); + assertTappedCount("Island", true, 3); + } + + @Test + @Ignore // TODO: implement alternative cost choose by AI instead random + public void test_AllMana_UseEvokeCost_AI() { + // When you cast this spell, draw two cards. + // Evoke {2}{U} (You may cast this spell for its evoke cost. If you do, it’s sacrificed when it enters.) + addCard(Zone.HAND, playerA, "Nulldrifter", 1); // {7} + addCard(Zone.BATTLEFIELD, playerA, "Island", 7); + + // make sure AI will use evoke cost + aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Nulldrifter", 0); + assertGraveyardCount(playerA, "Nulldrifter", 1); + assertHandCount(playerA, 2); + assertTappedCount("Island", true, 3); + } + + @Test + public void test_OnlyEvoke_UseEvokeCost_Human() { + // When you cast this spell, draw two cards. + // Evoke {2}{U} (You may cast this spell for its evoke cost. If you do, it’s sacrificed when it enters.) + addCard(Zone.HAND, playerA, "Nulldrifter", 1); // {7} + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + + // select evoke cost + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nulldrifter"); + setChoice(playerA, "Cast with Evoke alternative cost"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Nulldrifter", 0); + assertGraveyardCount(playerA, "Nulldrifter", 1); + assertHandCount(playerA, 2); + assertTappedCount("Island", true, 3); + } + + @Test + @Ignore // TODO: implement alternative cost choose by AI instead random + public void test_OnlyEvoke_UseEvokeCost_AI() { + // When you cast this spell, draw two cards. + // Evoke {2}{U} (You may cast this spell for its evoke cost. If you do, it’s sacrificed when it enters.) + addCard(Zone.HAND, playerA, "Nulldrifter", 1); // {7} + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + + // make sure AI will use evoke cost + aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Nulldrifter", 0); + assertGraveyardCount(playerA, "Nulldrifter", 1); + assertHandCount(playerA, 2); + assertTappedCount("Island", true, 3); + } + + @Test + public void test_OnlyNormalCost_UseNormalCost_Human() { + // When you cast this spell, draw two cards. + // Evoke {2}{U} (You may cast this spell for its evoke cost. If you do, it’s sacrificed when it enters.) + addCard(Zone.HAND, playerA, "Nulldrifter", 1); // {7} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 7); + + // select normal cost + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Nulldrifter"); + setChoice(playerA, "Cast with no alternative cost"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Nulldrifter", 1); + assertGraveyardCount(playerA, "Nulldrifter", 0); + assertHandCount(playerA, 2); + assertTappedCount("Mountain", true, 7); + } + + @Test + @Ignore // TODO: implement alternative cost choose by AI instead random + public void test_OnlyNormalCost_UseNormalCost_AI() { + // When you cast this spell, draw two cards. + // Evoke {2}{U} (You may cast this spell for its evoke cost. If you do, it’s sacrificed when it enters.) + addCard(Zone.HAND, playerA, "Nulldrifter", 1); // {7} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 7); + + // make sure AI will use normal cost + aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Nulldrifter", 1); + assertGraveyardCount(playerA, "Nulldrifter", 0); + assertHandCount(playerA, 2); + assertTappedCount("Mountain", true, 7); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/slx/HavengulLaboratoryTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/slx/HavengulLaboratoryTest.java new file mode 100644 index 00000000000..748a0894374 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/slx/HavengulLaboratoryTest.java @@ -0,0 +1,63 @@ +package org.mage.test.cards.single.slx; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class HavengulLaboratoryTest extends CardTestPlayerBase { + + @Test + public void test_LeavesAndTransform() { + addCustomEffect_TargetDestroy(playerA); + + // Havengul Laboratory + // At the beginning of your end step, if you sacrificed three or more Clues this turn, transform Havengul Laboratory. + // Havengul Mystery + // When this land transforms into Havengul Mystery, return target creature card from your graveyard to the battlefield. + // When the creature put onto the battlefield with Havengul Mystery leaves the battlefield, transform Havengul Mystery. + addCard(Zone.BATTLEFIELD, playerA, "Havengul Laboratory"); + addCard(Zone.GRAVEYARD, playerA, "Grizzly Bears"); + // + // Draw a card. Investigate. + addCard(Zone.HAND, playerA, "Deduce", 3); // instant {1}{U} + addCard(Zone.BATTLEFIELD, playerA, "Island", 3 * 2); + // + // Clue Token + // {2}, Sacrifice this artifact: Draw a card. + addCard(Zone.BATTLEFIELD, playerA, "Island", 3 * 2); + + // prepare x3 clues + checkPermanentCount("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Havengul Laboratory", 1); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Deduce"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Deduce"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Deduce"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + checkPermanentCount("on prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Clue Token", 3); + + // use all clues and transform + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}, Sacrifice"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}, Sacrifice"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}, Sacrifice"); + addTarget(playerA, "Grizzly Bears"); // return to battlefield on transform + waitStackResolved(1, PhaseStep.END_TURN); + checkPermanentCount("on transform", 1, PhaseStep.END_TURN, playerA, "Havengul Laboratory", 0); + checkPermanentCount("on transform", 1, PhaseStep.END_TURN, playerA, "Havengul Mystery", 1); + checkPermanentCount("on transform", 1, PhaseStep.END_TURN, playerA, "Grizzly Bears", 1); + + // destroy and transform back to Havengul Laboratory + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "target destroy", "Grizzly Bears"); + waitStackResolved(2, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("on leaves", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Havengul Laboratory", 1); + checkPermanentCount("on leaves", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Havengul Mystery", 0); + checkPermanentCount("on leaves", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 0); + checkGraveyardCount("on leaves", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 1); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.END_TURN); + execute(); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/who/CrackInTimeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/who/CrackInTimeTest.java new file mode 100644 index 00000000000..a835773568d --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/who/CrackInTimeTest.java @@ -0,0 +1,39 @@ +package org.mage.test.cards.single.who; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class CrackInTimeTest extends CardTestPlayerBase { + + @Test + public void test_ExileUntilLeaves() { + addCustomEffect_TargetDestroy(playerA); + + // When Crack in Time enters the battlefield and at the beginning of your precombat main phase, + // exile target creature an opponent controls until Crack in Time leaves the battlefield. + addCard(Zone.HAND, playerA, "Crack in Time"); // {3}{W} + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1); + + // exile + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Crack in Time"); + addTarget(playerA, "Balduvian Bears"); // exile on etb + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkExileCount("on exile", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Balduvian Bears", 1); + + // destroy and return + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "target destroy", "Crack in Time"); + waitStackResolved(1, PhaseStep.POSTCOMBAT_MAIN); + checkExileCount("on return", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Balduvian Bears", 0); + checkPermanentCount("on return", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Balduvian Bears", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/RadCounterTriggerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/RadCounterTriggerTest.java index 3e74ffca4af..c0cd6026d75 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/RadCounterTriggerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/RadCounterTriggerTest.java @@ -1,4 +1,4 @@ -package org.mage.test.cards.single.pip; +package org.mage.test.cards.triggers; import mage.constants.PhaseStep; import mage.constants.Zone; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/SacrificeDiesTriggerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/SacrificeDiesTriggerTest.java index eec48b69efa..82b1912c671 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/SacrificeDiesTriggerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/SacrificeDiesTriggerTest.java @@ -413,9 +413,9 @@ public class SacrificeDiesTriggerTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Silvercoat Lion", 1); } - // bug #9688 @Test - public void testIndustrialAdvancement() { + public void test_IndustrialAdvancement() { + // bug #9688 skipInitShuffling(); addCard(Zone.BATTLEFIELD, playerA, "Industrial Advancement"); // At the beginning of your end step, you may sacrifice a creature. If you do, look at the top X cards of your @@ -438,7 +438,99 @@ public class SacrificeDiesTriggerTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Lone Missionary", 1); assertGraveyardCount(playerA, "Guardian Automaton", 1); assertLife(playerA, 27); - } + @Test + public void test_SingleEvent_SavraQueenOfTheGolgari_SacrificeAnother() { + // Whenever you sacrifice a black creature, you may pay 2 life. If you do, each other player sacrifices a creature. + // Whenever you sacrifice a green creature, you may gain 2 life. + addCard(Zone.BATTLEFIELD, playerA, "Savra, Queen of the Golgari"); // {2}{B}{G} + // + // {2}, {T}, Sacrifice a creature: Draw a card. + addCard(Zone.BATTLEFIELD, playerA, "Phyrexian Vault", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + // + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); + + // sacrifice another creature + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}, {T}, Sacrifice"); + setChoice(playerA, "Grizzly Bears"); // to sacrifice + setChoice(playerA, true); // use gain life + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 20 + 2); // from green trigger + } + + @Test + public void test_SingleEvent_SavraQueenOfTheGolgari_SacrificeItself() { + // make sure it works on itself, bug #13089 + + // Whenever you sacrifice a black creature, you may pay 2 life. If you do, each other player sacrifices a creature. + // Whenever you sacrifice a green creature, you may gain 2 life. + addCard(Zone.BATTLEFIELD, playerA, "Savra, Queen of the Golgari"); // {2}{B}{G} + // + // {2}, {T}, Sacrifice a creature: Draw a card. + addCard(Zone.BATTLEFIELD, playerA, "Phyrexian Vault", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + + // sacrifice itself (must catch x2 triggers from savra) + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}, {T}, Sacrifice"); + setChoice(playerA, "Savra, Queen of the Golgari"); + setChoice(playerA, "Whenever you sacrifice a black creature"); // x2 triggers order from savra + setChoice(playerA, true); // use gain life from green trigger + setChoice(playerA, false); // ignore black trigger + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 20 + 2); // from green trigger + } + + @Test + public void test_BatchEvent_ForgeNeverwinterCharlatan_SacrificeAnother() { + // Whenever one or more players sacrifice one or more creatures, you create a tapped Treasure token. + // This ability triggers only once each turn. + addCard(Zone.BATTLEFIELD, playerA, "Forge, Neverwinter Charlatan"); + // + // {2}, {T}, Sacrifice a creature: Draw a card. + addCard(Zone.BATTLEFIELD, playerA, "Phyrexian Vault", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + // + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears"); + + // sacrifice another creature + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}, {T}, Sacrifice"); + setChoice(playerA, "Grizzly Bears"); // to sacrifice + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertTokenCount(playerA, "Treasure Token", 1); + } + + @Test + public void test_BatchEvent_ForgeNeverwinterCharlatan_SacrificeItself() { + // Whenever one or more players sacrifice one or more creatures, you create a tapped Treasure token. + // This ability triggers only once each turn. + addCard(Zone.BATTLEFIELD, playerA, "Forge, Neverwinter Charlatan"); + // + // {2}, {T}, Sacrifice a creature: Draw a card. + addCard(Zone.BATTLEFIELD, playerA, "Phyrexian Vault", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + + // sacrifice itself + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}, {T}, Sacrifice"); + setChoice(playerA, "Forge, Neverwinter Charlatan"); // to sacrifice + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertTokenCount(playerA, "Treasure Token", 1); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/deck/DeckAutoLandsTest.java b/Mage.Tests/src/test/java/org/mage/test/serverside/deck/DeckAutoLandsTest.java new file mode 100644 index 00000000000..35c08188042 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/deck/DeckAutoLandsTest.java @@ -0,0 +1,166 @@ +package org.mage.test.serverside.deck; + +import mage.cards.decks.Deck; +import mage.cards.decks.DeckCardInfo; +import mage.cards.decks.DeckCardLists; +import mage.game.GameException; +import mage.util.DeckBuildUtils; +import mage.util.TournamentUtil; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.MageTestPlayerBase; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Testing lands suggestion and adding. It used in: + * - client side: for auto-lands suggest button + * - server side: for AI and invalid deck's timeout + * + * @author JayDi85 + */ +public class DeckAutoLandsTest extends MageTestPlayerBase { + + @Test + public void test_LandsSuggest_OneColor() { + Deck deck = prepareDeck(Arrays.asList( + new DeckCardInfo("Grizzly Bears", "129", "S99", 40) + )); + assertSuggestedLands("one color", deck, 60, 0, 0, 0, 0, 20); + } + + @Test + public void test_LandsSuggest_TwoColors() { + Deck deck = prepareDeck(Arrays.asList( + new DeckCardInfo("Arbalest Engineers", "206", "BRO", 40) + )); + assertSuggestedLands("two colors", deck, 60, 0, 0, 0, 10, 10); + } + + @Test + public void test_LandsSuggest_ThreeColors() { + Deck deck = prepareDeck(Arrays.asList( + new DeckCardInfo("Adun Oakenshield", "216", "LEG", 40) + )); + assertSuggestedLands("tree colors", deck, 60, 0, 0, 7, 7, 6); + } + + @Test + public void test_LandsSuggest_FiveColors() { + Deck deck = prepareDeck(Arrays.asList( + new DeckCardInfo("Atogatog", "286", "ODY", 40) + )); + assertSuggestedLands("five colors", deck, 60, 4, 4, 4, 4, 4); + } + + @Test + public void test_LandsSuggest_NoColors() { + Deck deck = prepareDeck(Arrays.asList( + new DeckCardInfo("Abstruse Archaic", "712", "CMM", 40) + )); + assertSuggestedLands("no colors", deck, 60, 4, 4, 4, 4, 4); + } + + @Test + public void test_LandsSuggest_NoNeedLands() { + Deck deck = prepareDeck(Arrays.asList( + new DeckCardInfo("Grizzly Bears", "129", "S99", 40) + )); + assertSuggestedLands("no need lands - 39", deck, 39, 0, 0, 0, 0, 0); + assertSuggestedLands("no need lands - 40", deck, 40, 0, 0, 0, 0, 0); + assertSuggestedLands("no need lands - 41", deck, 41, 0, 0, 0, 0, 1); + } + + private Deck prepareDeck(List cards) { + DeckCardLists source = new DeckCardLists(); + source.getCards().addAll(cards); + Deck deck = null; + try { + deck = Deck.load(source, true); + } catch (GameException e) { + Assert.fail("Can't prepare deck: " + cards); + } + return deck; + } + + private void assertSuggestedLands( + String info, + Deck deck, + int needTotal, + int needPlains, + int needIslands, + int needSwamps, + int needMountains, + int needForests) { + int[] lands = DeckBuildUtils.landCountSuggestion(needTotal, deck.getMaindeckCards()); + List current = new ArrayList<>(Arrays.asList(lands[0], lands[1], lands[2], lands[3], lands[4])); + List need = new ArrayList<>(Arrays.asList(needPlains, needIslands, needSwamps, needMountains, needForests)); + Assert.assertTrue(info + " - wrong deck size", deck.getMaindeckCards().size() + current.stream().mapToInt(x -> x).sum() >= needTotal); + Assert.assertEquals(info + " - wrong lands count (WUBRG)", need, current); + } + + @Test + public void test_PossibleSets_OneCompatibleSet() { + Deck deck = prepareDeck(Arrays.asList( + new DeckCardInfo("Grizzly Bears", "129", "S99", 1) + )); + assertPossibleSets("one compatible set", deck, Arrays.asList("S99")); + } + + @Test + public void test_PossibleSets_MakeSureNoSnowLands() { + Deck deck = prepareDeck(Arrays.asList( + new DeckCardInfo("Grizzly Bears", "129", "S99", 1), + new DeckCardInfo("Abominable Treefolk", "194", "MH1", 1) // MH1 with snow lands + )); + assertPossibleSets("no snow lands", deck, Arrays.asList("S99")); + } + + @Test + public void test_PossibleSets_MultipleCompatibleSets() { + Deck deck = prepareDeck(Arrays.asList( + new DeckCardInfo("Grizzly Bears", "129", "S99", 1), + new DeckCardInfo("Akki Raider", "92", "BOK", 1), // BOK without lands, but with boosters + new DeckCardInfo("Grizzly Bears", "169", "POR", 1), + new DeckCardInfo("Aggravated Assault", "25", "MP2", 1) // MP2 without lands and boosters + )); + assertPossibleSets("multiple compatible sets", deck, Arrays.asList("POR", "S99")); + } + + @Test + public void test_PossibleSets_CompatibleBlocks() { + // BOK from Kamigawa block, so it must look at: + // * CHK - Champions of Kamigawa - has lands + // * SOK - Saviors of Kamigawa - no lands + // * BOK - Betrayers of Kamigawa - no lands + Deck deck = prepareDeck(Arrays.asList( + new DeckCardInfo("Akki Raider", "92", "BOK", 1), // BOK without lands, but with boosters + new DeckCardInfo("Aggravated Assault", "25", "MP2", 1) // MP2 without lands and boosters + )); + assertPossibleSets("compatible block", deck, Arrays.asList("CHK")); + } + + @Test + public void test_PossibleSets_NoCompatibleSetsOrBlocks() { + Deck deck = prepareDeck(Arrays.asList( + new DeckCardInfo("Amulet of Kroog", "36", "ATQ", 1) // ATQ without lands + )); + // must find 2 random sets + List possibleSets1 = TournamentUtil.getLandSetCodeForDeckSets(deck.getExpansionSetCodes()).stream().sorted().collect(Collectors.toList()); + List possibleSets2 = TournamentUtil.getLandSetCodeForDeckSets(deck.getExpansionSetCodes()).stream().sorted().collect(Collectors.toList()); + Assert.assertEquals("must find 1 random set, try 1", 1, possibleSets1.size()); + Assert.assertEquals("must find 1 random set, try 2", 1, possibleSets2.size()); + Assert.assertNotEquals("must find random sets, try 3", possibleSets1.get(0), possibleSets2.get(0)); + } + + private void assertPossibleSets( + String info, + Deck deck, + List needSets) { + List possibleSets = TournamentUtil.getLandSetCodeForDeckSets(deck.getExpansionSetCodes()).stream().sorted().collect(Collectors.toList()); + Assert.assertEquals(info + " - wrong possible sets", needSets, possibleSets); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/deck/DeckValidatorTest.java b/Mage.Tests/src/test/java/org/mage/test/serverside/deck/DeckValidatorTest.java index ad9be74689f..b1cb8bcf99f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/deck/DeckValidatorTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/deck/DeckValidatorTest.java @@ -305,7 +305,7 @@ public class DeckValidatorTest extends MageTestPlayerBase { validator.getErrorsList().clear(); deckList.clear(); - deckList.add(new DeckValidationUtil.CardNameAmount("Green Sun's Zenith", 4)); + deckList.add(new DeckValidationUtil.CardNameAmount("Grief", 4)); deckList.add(new DeckValidationUtil.CardNameAmount("Mountain", 56)); validationSuccessful = testDeckValid(validator, deckList); Assert.assertFalse(validator.getErrorsListInfo(), validationSuccessful); diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java index 2f42b8867a0..36fad30c164 100644 --- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java +++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java @@ -13,6 +13,7 @@ import mage.abilities.condition.Condition; import mage.abilities.costs.Cost; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; +import mage.abilities.effects.common.ExileUntilSourceLeavesEffect; import mage.abilities.effects.common.FightTargetsEffect; import mage.abilities.effects.common.counter.ProliferateEffect; import mage.abilities.effects.keyword.ScryEffect; @@ -74,6 +75,7 @@ public class VerifyCardDataTest { private static final String FULL_ABILITIES_CHECK_SET_CODES = "MH3;M3C"; // check ability text due mtgjson, can use multiple sets like MAT;CMD or * for all private static final boolean CHECK_ONLY_ABILITIES_TEXT = false; // use when checking text locally, suppresses unnecessary checks and output messages + private static final boolean CHECK_COPYABLE_FIELDS = true; // disable for better verify test performance private static final boolean AUTO_FIX_SAMPLE_DECKS = false; // debug only: auto-fix sample decks by test_checkSampleDecks test run @@ -1632,7 +1634,9 @@ public class VerifyCardDataTest { checkRarityAndBasicLands(card, ref); checkMissingAbilities(card, ref); checkWrongSymbolsInRules(card); - checkCardCanBeCopied(card); + if (CHECK_COPYABLE_FIELDS) { + checkCardCanBeCopied(card); + } } checkWrongAbilitiesText(card, ref, cardIndex); } @@ -1994,6 +1998,10 @@ public class VerifyCardDataTest { ignoredCards.add("Infested Thrinax"); ignoredCards.add("Xira, the Golden Sting"); ignoredCards.add("Mawloc"); + ignoredCards.add("Crack in Time"); + ignoredCards.add("Mysterious Limousine"); + ignoredCards.add("Graceful Antelope"); + ignoredCards.add("Portcullis"); List ignoredAbilities = new ArrayList<>(); ignoredAbilities.add("roll"); // roll die effects ignoredAbilities.add("with \"When"); // token creating effects @@ -2009,6 +2017,18 @@ public class VerifyCardDataTest { if (triggeredAbility == null) { continue; } + + // ignore exile effects + // example 1: exile up to one other target nonland permanent until Constable of the Realm leaves the battlefield. + if (ability.getAllEffects().stream().anyMatch(e -> e instanceof ExileUntilSourceLeavesEffect)) { + continue; + } + // example 2: When Hostage Taker enters the battlefield, exile another target artifact or creature until Hostage Taker leaves the battlefield + if (ability instanceof EntersBattlefieldTriggeredAbility) { + continue; + } + + // search and check dies related abilities String rules = triggeredAbility.getRule(); if (ignoredAbilities.stream().anyMatch(rules::contains)) { @@ -2022,7 +2042,6 @@ public class VerifyCardDataTest { && rules.contains("graveyard") && rules.contains("from the battlefield"); boolean isLeavesBattlefield = rules.contains("leaves the battlefield"); - isLeavesBattlefield = false; // TODO: remove and fix all bad cards if (triggeredAbility.isLeavesTheBattlefieldTrigger()) { // TODO: add check for wrongly enabled settings too? } else { diff --git a/Mage/src/main/java/mage/MageIdentifier.java b/Mage/src/main/java/mage/MageIdentifier.java index f25b87718a7..afb1416331e 100644 --- a/Mage/src/main/java/mage/MageIdentifier.java +++ b/Mage/src/main/java/mage/MageIdentifier.java @@ -75,7 +75,8 @@ public enum MageIdentifier { OfferingAlternateCast, TheRuinousPowersAlternateCast, FiresOfMountDoomAlternateCast, - PrimalPrayersAlternateCast; + PrimalPrayersAlternateCast, + QuilledGreatwurmAlternateCast; /** * Additional text if there is need to differentiate two very similar effects diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index 242a25f863e..e965fadc827 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -466,10 +466,16 @@ public abstract class AbilityImpl implements Ability { } /** - * @return true if choices for the activation were made (can be to activate with the regular cost) + * @return false to stop activation process, e.g. on wrong data/choices */ @Override public boolean activateAlternateOrAdditionalCosts(MageObject sourceObject, Set allowedIdentifiers, boolean noMana, Player controller, Game game) { + // alternative or additional costs supported for spells or activated abilities only + if (!this.getAbilityType().isActivatedAbility() + && !this.getAbilityType().isPlayCardAbility()) { + return true; + } + boolean canUseAlternativeCost = true; boolean canUseAdditionalCost = true; @@ -1284,6 +1290,7 @@ public abstract class AbilityImpl implements Ability { switch (e.getType()) { case DESTROYED_PERMANENT: case EXPLOITED_CREATURE: + case SACRIFICED_PERMANENT: return true; case ZONE_CHANGE: return ((ZoneChangeEvent) e).getFromZone() == Zone.BATTLEFIELD; diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java b/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java index a119c31c1bb..212300758ed 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java @@ -395,6 +395,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge break; case DESTROYED_PERMANENT: case EXPLOITED_CREATURE: + case SACRIFICED_PERMANENT: if (isLeavesTheBattlefieldTrigger() && game.checkShortLivingLKI(affectedSourceId, Zone.BATTLEFIELD)) { affectedSourceObject = game.getPermanentOrLKIBattlefield(affectedSourceId); } diff --git a/Mage/src/main/java/mage/abilities/common/delayed/OnLeaveReturnExiledAbility.java b/Mage/src/main/java/mage/abilities/common/delayed/OnLeaveReturnExiledAbility.java index a80d200e7c0..33afa3d86df 100644 --- a/Mage/src/main/java/mage/abilities/common/delayed/OnLeaveReturnExiledAbility.java +++ b/Mage/src/main/java/mage/abilities/common/delayed/OnLeaveReturnExiledAbility.java @@ -40,6 +40,7 @@ public class OnLeaveReturnExiledAbility extends DelayedTriggeredAbility { super(new ReturnExiledPermanentsEffect(zone), Duration.OneUse); this.usesStack = false; this.setRuleVisible(false); + setLeavesTheBattlefieldTrigger(true); } protected OnLeaveReturnExiledAbility(final OnLeaveReturnExiledAbility ability) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenTargetEffect.java index a1d2b36e89a..0238ad8c2b3 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenTargetEffect.java @@ -35,7 +35,11 @@ public class CreateTokenTargetEffect extends OneShotEffect { } public CreateTokenTargetEffect(Token token, DynamicValue amount) { - this(token, amount, false, false); + this(token, amount, false); + } + + public CreateTokenTargetEffect(Token token, DynamicValue amount, boolean tapped) { + this(token, amount, tapped, false); } public CreateTokenTargetEffect(Token token, DynamicValue amount, boolean tapped, boolean attacking) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/DamageMultiEffect.java b/Mage/src/main/java/mage/abilities/effects/common/DamageMultiEffect.java index 653be78f3e7..c6d78cee01b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DamageMultiEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DamageMultiEffect.java @@ -4,13 +4,13 @@ import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; +import mage.target.TargetAmount; import java.util.*; @@ -19,32 +19,21 @@ import java.util.*; */ public class DamageMultiEffect extends OneShotEffect { - protected DynamicValue amount; private String sourceName = "{this}"; private final Set damagedSet = new HashSet<>(); - public DamageMultiEffect(int amount) { - this(StaticValue.get(amount)); - } - - public DamageMultiEffect(int amount, String whoDealDamageName) { - this(StaticValue.get(amount), whoDealDamageName); - } - - public DamageMultiEffect(DynamicValue amount, String whoDealDamageName) { - this(amount); - this.sourceName = whoDealDamageName; - } - - public DamageMultiEffect(DynamicValue amount) { + public DamageMultiEffect() { super(Outcome.Damage); - this.amount = amount; + } + + public DamageMultiEffect(String whoDealDamageName) { + super(Outcome.Damage); + this.sourceName = whoDealDamageName; } protected DamageMultiEffect(final DamageMultiEffect effect) { super(effect); this.damagedSet.addAll(effect.damagedSet); - this.amount = effect.amount; this.sourceName = effect.sourceName; } @@ -84,16 +73,16 @@ public class DamageMultiEffect extends OneShotEffect { StringBuilder sb = new StringBuilder(sourceName); sb.append(" deals "); - String amountString = amount.toString(); - sb.append(amountString); - sb.append(" damage divided as you choose among "); - sb.append(amountString.equals("2") ? "one or two " : amountString.equals("3") ? "one, two, or three " : "any number of "); - - String targetName = mode.getTargets().get(0).getTargetName(); - if (!targetName.contains("target")) { - sb.append("target "); + Target target = mode.getTargets().get(0); + if (!(target instanceof TargetAmount)) { + throw new IllegalStateException("Must use TargetAmount"); } - sb.append(targetName); + TargetAmount targetAmount = (TargetAmount) target; + + DynamicValue amount = targetAmount.getAmount(); + sb.append(amount.toString()); + sb.append(" damage divided as you choose among "); + sb.append(targetAmount.getDescription()); return sb.toString(); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/counter/DistributeCountersEffect.java b/Mage/src/main/java/mage/abilities/effects/common/counter/DistributeCountersEffect.java index d1ee03ce613..30620d8c69d 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/counter/DistributeCountersEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/counter/DistributeCountersEffect.java @@ -12,6 +12,7 @@ import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.Target; +import mage.target.TargetAmount; import mage.util.CardUtil; import java.util.UUID; @@ -22,34 +23,24 @@ import java.util.UUID; public class DistributeCountersEffect extends OneShotEffect { private final CounterType counterType; - private final DynamicValue amount; private boolean removeAtEndOfTurn = false; - private final String targetDescription; /** * Distribute +1/+1 counters among targets */ - public DistributeCountersEffect(int amount, String targetDescription) { - this(CounterType.P1P1, StaticValue.get(amount), targetDescription); + public DistributeCountersEffect() { + this(CounterType.P1P1); } - public DistributeCountersEffect(CounterType counterType, int amount, String targetDescription) { - this(counterType, StaticValue.get(amount), targetDescription); - } - - public DistributeCountersEffect(CounterType counterType, DynamicValue amount, String targetDescription) { + public DistributeCountersEffect(CounterType counterType) { super(Outcome.BoostCreature); this.counterType = counterType; - this.amount = amount; - this.targetDescription = targetDescription; } protected DistributeCountersEffect(final DistributeCountersEffect effect) { super(effect); this.counterType = effect.counterType; - this.amount = effect.amount; this.removeAtEndOfTurn = effect.removeAtEndOfTurn; - this.targetDescription = effect.targetDescription; } @Override @@ -90,9 +81,16 @@ public class DistributeCountersEffect extends OneShotEffect { if (staticText != null && !staticText.isEmpty()) { return staticText; } + Target target = mode.getTargets().get(0); + if (!(target instanceof TargetAmount)) { + throw new IllegalStateException("Must use TargetAmount"); + } + TargetAmount targetAmount = (TargetAmount) target; + DynamicValue amount = targetAmount.getAmount(); + String name = counterType.getName(); String number = (amount instanceof StaticValue) ? CardUtil.numberToText(((StaticValue) amount).getValue()) : amount.toString(); - String text = "distribute " + number + ' ' + name + " counters among " + targetDescription; + String text = "distribute " + number + ' ' + name + " counters among " + targetAmount.getDescription(); if (removeAtEndOfTurn) { text += ". For each " + name + " counter you put on a creature this way, remove a " + name + " counter from that creature at the beginning of the next cleanup step."; diff --git a/Mage/src/main/java/mage/counters/CounterType.java b/Mage/src/main/java/mage/counters/CounterType.java index d1003b4bce0..dcb5353e8b3 100644 --- a/Mage/src/main/java/mage/counters/CounterType.java +++ b/Mage/src/main/java/mage/counters/CounterType.java @@ -81,6 +81,7 @@ public enum CounterType { FATE("fate"), FEATHER("feather"), FEEDING("feeding"), + FELLOWSHIP("fellowship"), FETCH("fetch"), FILIBUSTER("filibuster"), FINALITY("finality"), diff --git a/Mage/src/main/java/mage/target/TargetAmount.java b/Mage/src/main/java/mage/target/TargetAmount.java index 6bd1e51c510..68107f825a8 100644 --- a/Mage/src/main/java/mage/target/TargetAmount.java +++ b/Mage/src/main/java/mage/target/TargetAmount.java @@ -2,6 +2,7 @@ package mage.target; import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.StaticValue; import mage.constants.Outcome; import mage.game.Game; import mage.players.Player; @@ -18,8 +19,10 @@ public abstract class TargetAmount extends TargetImpl { DynamicValue amount; int remainingAmount; - protected TargetAmount(DynamicValue amount) { + protected TargetAmount(DynamicValue amount, int minNumberOfTargets, int maxNumberOfTargets) { this.amount = amount; + setMinNumberOfTargets(minNumberOfTargets); + setMaxNumberOfTargets(maxNumberOfTargets); } protected TargetAmount(final TargetAmount target) { @@ -65,6 +68,10 @@ public abstract class TargetAmount extends TargetImpl { amountWasSet = true; } + public DynamicValue getAmount() { + return amount; + } + public int getAmountTotal(Game game, Ability source) { return amount.calculate(game, source, null); } @@ -176,4 +183,24 @@ public abstract class TargetAmount extends TargetImpl { remainingAmount -= (amount - this.getTargetAmount(targetId)); this.setTargetAmount(targetId, amount, game); } + + @Override + protected boolean getUseAnyNumber() { + int min = getMinNumberOfTargets(); + int max = getMaxNumberOfTargets(); + if (min != 0) { + return false; + } + if (max == Integer.MAX_VALUE) { + return true; + } + // For a TargetAmount with a min of 0: + // A max that equals the amount, when the amount is a StaticValue, + // usually represents "any number of target __s", since you can't target more than the amount. + // + // 601.2d. If the spell requires the player to divide or distribute an effect + // (such as damage or counters) among one or more targets, the player announces the division. + // Each of these targets must receive at least one of whatever is being divided. + return amount instanceof StaticValue && max == ((StaticValue) amount).getValue(); + } } diff --git a/Mage/src/main/java/mage/target/TargetImpl.java b/Mage/src/main/java/mage/target/TargetImpl.java index 12386ee5180..1a937a54ef4 100644 --- a/Mage/src/main/java/mage/target/TargetImpl.java +++ b/Mage/src/main/java/mage/target/TargetImpl.java @@ -109,17 +109,22 @@ public abstract class TargetImpl implements Target { sb.append(" or more "); } else if (!targetName.startsWith("X ") && (min != 1 || max != 1)) { targetName = targetName.replace("another", "other"); //If non-singular, use "other" instead of "another" - if (min < max && max != Integer.MAX_VALUE) { - if (min == 1 && max == 2) { - sb.append("one or "); - } else if (min == 1 && max == 3) { - sb.append("one, two, or "); - } else { - sb.append("up to "); + + if (getUseAnyNumber()) { + sb.append(("any number of ")); + } else { + if (min < max && max != Integer.MAX_VALUE) { + if (min == 1 && max == 2) { + sb.append("one or "); + } else if (min == 1 && max == 3) { + sb.append("one, two, or "); + } else { + sb.append("up to "); + } } + sb.append(CardUtil.numberToText(max)); + sb.append(' '); } - sb.append(CardUtil.numberToText(max)); - sb.append(' '); } boolean addTargetWord = false; if (!isNotTarget()) { @@ -127,7 +132,8 @@ public abstract class TargetImpl implements Target { if (targetName.contains("target ")) { addTargetWord = false; } else if (targetName.endsWith("any target") - || targetName.endsWith("any other target")) { + || targetName.endsWith("any other target") + || targetName.endsWith("targets")) { addTargetWord = false; } // endsWith needs to be specific. @@ -144,6 +150,15 @@ public abstract class TargetImpl implements Target { return sb.toString(); } + /** + * Used for generating text description. Needed so that subclasses may override. + */ + protected boolean getUseAnyNumber() { + int min = getMinNumberOfTargets(); + int max = getMaxNumberOfTargets(); + return min == 0 && max == Integer.MAX_VALUE; + } + @Override public String getMessage(Game game) { // UI choose message diff --git a/Mage/src/main/java/mage/target/common/TargetAnyTargetAmount.java b/Mage/src/main/java/mage/target/common/TargetAnyTargetAmount.java index 8132871fd93..0e7173c81c5 100644 --- a/Mage/src/main/java/mage/target/common/TargetAnyTargetAmount.java +++ b/Mage/src/main/java/mage/target/common/TargetAnyTargetAmount.java @@ -14,25 +14,45 @@ public class TargetAnyTargetAmount extends TargetPermanentOrPlayerAmount { private static final FilterPermanentOrPlayer defaultFilter = new FilterAnyTarget("targets"); + /** + * IMPORTANT: Use more specific constructor if {@code amount} is not always the same number!
+ * {@code minNumberOfTargets} defaults to zero for {@code amount} > 3, otherwise to one, in line with typical templating.
+ * {@code maxNumberOfTargets} defaults to {@code amount}. + * + * @see TargetAnyTargetAmount#TargetAnyTargetAmount(int, int, int) + */ public TargetAnyTargetAmount(int amount) { - this(amount, 0); + this(amount, amount > 3 ? 0 : 1, amount); } - public TargetAnyTargetAmount(int amount, int maxNumberOfTargets) { - // 107.1c If a rule or ability instructs a player to choose “any number,” that player may choose - // any positive number or zero, unless something (such as damage or counters) is being divided - // or distributed among “any number” of players and/or objects. In that case, a nonzero number - // of players and/or objects must be chosen if possible. - this(StaticValue.get(amount), maxNumberOfTargets); - this.minNumberOfTargets = 1; + /** + * @param amount Amount of stuff (e.g. damage) to distribute. + * @param minNumberOfTargets Minimum number of targets. + * @param maxNumberOfTargets Maximum number of targets. Should be set to {@code amount} if no lower max is needed. + */ + public TargetAnyTargetAmount(int amount, int minNumberOfTargets, int maxNumberOfTargets) { + this(StaticValue.get(amount), minNumberOfTargets, maxNumberOfTargets); } + /** + * {@code minNumberOfTargets} defaults to zero.
+ * {@code maxNumberOfTargets} defaults to Integer.MAX_VALUE. + * + * @see TargetAnyTargetAmount#TargetAnyTargetAmount(DynamicValue, int, int) + */ public TargetAnyTargetAmount(DynamicValue amount) { - this(amount, 0); + this(amount, 0, Integer.MAX_VALUE); } - public TargetAnyTargetAmount(DynamicValue amount, int maxNumberOfTargets) { - super(amount, maxNumberOfTargets); + /** + * @param amount Amount of stuff (e.g. damage) to distribute. + * @param minNumberOfTargets Minimum number of targets. + * @param maxNumberOfTargets Maximum number of targets. Since {@code amount} is dynamic, + * should be set to Integer.MAX_VALUE if no lower max is needed. + * (Game will always prevent choosing more than {@code amount} targets.) + */ + public TargetAnyTargetAmount(DynamicValue amount, int minNumberOfTargets, int maxNumberOfTargets) { + super(amount, minNumberOfTargets, maxNumberOfTargets); this.zone = Zone.ALL; this.filter = defaultFilter; this.targetName = filter.getMessage(); diff --git a/Mage/src/main/java/mage/target/common/TargetCreatureOrPlaneswalkerAmount.java b/Mage/src/main/java/mage/target/common/TargetCreatureOrPlaneswalkerAmount.java index 4932e92cb47..a508ead9533 100644 --- a/Mage/src/main/java/mage/target/common/TargetCreatureOrPlaneswalkerAmount.java +++ b/Mage/src/main/java/mage/target/common/TargetCreatureOrPlaneswalkerAmount.java @@ -1,6 +1,6 @@ package mage.target.common; -import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreatureOrPlaneswalkerPermanent; import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent; /** @@ -11,12 +11,62 @@ public class TargetCreatureOrPlaneswalkerAmount extends TargetPermanentAmount { private static final FilterCreatureOrPlaneswalkerPermanent defaultFilter = new FilterCreatureOrPlaneswalkerPermanent("target creatures and/or planeswalkers"); + /** + * IMPORTANT: Use more specific constructor if {@code amount} is not always the same number!
+ * {@code minNumberOfTargets} defaults to zero for {@code amount} > 3, otherwise to one, in line with typical templating.
+ * {@code maxNumberOfTargets} defaults to {@code amount}.
+ * {@code filter} defaults to all creature and planeswalker permanents. ("target creatures and/or planeswalkers") + * + * @see TargetCreatureOrPlaneswalkerAmount#TargetCreatureOrPlaneswalkerAmount(int, int, int, FilterCreatureOrPlaneswalkerPermanent) + */ public TargetCreatureOrPlaneswalkerAmount(int amount) { - super(amount, defaultFilter); + this(amount, defaultFilter); } - public TargetCreatureOrPlaneswalkerAmount(int amount, FilterPermanent filter) { - super(amount, filter); + /** + * IMPORTANT: Use more specific constructor if {@code amount} is not always the same number!
+ * {@code minNumberOfTargets} defaults to zero for {@code amount} > 3, otherwise to one, in line with typical templating.
+ * {@code maxNumberOfTargets} defaults to {@code amount}. + * + * @see TargetCreatureOrPlaneswalkerAmount#TargetCreatureOrPlaneswalkerAmount(int, int, int, FilterCreatureOrPlaneswalkerPermanent) + */ + public TargetCreatureOrPlaneswalkerAmount(int amount, FilterCreatureOrPlaneswalkerPermanent filter) { + this(amount, amount > 3 ? 0 : 1, amount, filter); + } + + /** + * {@code filter} defaults to all creature and planeswalker permanents. ("target creatures and/or planeswalkers") + * + * @see TargetCreatureOrPlaneswalkerAmount#TargetCreatureOrPlaneswalkerAmount(int, int, int, FilterCreatureOrPlaneswalkerPermanent) + */ + public TargetCreatureOrPlaneswalkerAmount(int amount, int minNumberOfTargets, int maxNumberOfTargets) { + this(amount, minNumberOfTargets, maxNumberOfTargets, defaultFilter); + } + + /** + * @param amount Amount of stuff (e.g. damage, counters) to distribute. + * @param minNumberOfTargets Minimum number of targets. + * @param maxNumberOfTargets Maximum number of targets. If no lower max is needed, set to {@code amount}. + * @param filter Filter for creatures and/or planeswalkers that something will be distributed amongst. + */ + public TargetCreatureOrPlaneswalkerAmount(int amount, int minNumberOfTargets, int maxNumberOfTargets, FilterCreatureOrPlaneswalkerPermanent filter) { + super(amount, minNumberOfTargets, maxNumberOfTargets, filter); + } + + /** + * IMPORTANT: Use more specific constructor if {@code amount} is not always the same number! + * + * @see TargetCreatureOrPlaneswalkerAmount#TargetCreatureOrPlaneswalkerAmount(int, FilterCreatureOrPlaneswalkerPermanent) + */ + public TargetCreatureOrPlaneswalkerAmount(int amount, FilterControlledCreatureOrPlaneswalkerPermanent filter) { + this(amount, amount > 3 ? 0 : 1, amount, filter); + } + + /** + * @see TargetCreatureOrPlaneswalkerAmount#TargetCreatureOrPlaneswalkerAmount(int, int, int, FilterCreatureOrPlaneswalkerPermanent) + */ + public TargetCreatureOrPlaneswalkerAmount(int amount, int minNumberOfTargets, int maxNumberOfTargets, FilterControlledCreatureOrPlaneswalkerPermanent filter) { + super(amount, minNumberOfTargets, maxNumberOfTargets, filter); } private TargetCreatureOrPlaneswalkerAmount(final TargetCreatureOrPlaneswalkerAmount target) { diff --git a/Mage/src/main/java/mage/target/common/TargetCreaturePermanentAmount.java b/Mage/src/main/java/mage/target/common/TargetCreaturePermanentAmount.java index 3367668ccce..143d6f75b55 100644 --- a/Mage/src/main/java/mage/target/common/TargetCreaturePermanentAmount.java +++ b/Mage/src/main/java/mage/target/common/TargetCreaturePermanentAmount.java @@ -1,7 +1,7 @@ package mage.target.common; import mage.abilities.dynamicvalue.DynamicValue; -import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; /** @@ -11,20 +11,88 @@ public class TargetCreaturePermanentAmount extends TargetPermanentAmount { private static final FilterCreaturePermanent defaultFilter = new FilterCreaturePermanent("target creatures"); + /** + * IMPORTANT: Use more specific constructor if {@code amount} is not always the same number!
+ * {@code minNumberOfTargets} defaults to zero for {@code amount} > 3, otherwise to one, in line with typical templating.
+ * {@code maxNumberOfTargets} defaults to {@code amount}.
+ * {@code filter} defaults to all creature permanents. ("target creatures") + * + * @see TargetCreaturePermanentAmount#TargetCreaturePermanentAmount(int, int, int, FilterCreaturePermanent) + */ public TargetCreaturePermanentAmount(int amount) { - super(amount, defaultFilter); + this(amount, defaultFilter); } + /** + * IMPORTANT: Use more specific constructor if {@code amount} is not always the same number!
+ * {@code minNumberOfTargets} defaults to zero for {@code amount} > 3, otherwise to one, in line with typical templating.
+ * {@code maxNumberOfTargets} defaults to {@code amount}. + * + * @see TargetCreaturePermanentAmount#TargetCreaturePermanentAmount(int, int, int, FilterCreaturePermanent) + */ + public TargetCreaturePermanentAmount(int amount, FilterCreaturePermanent filter) { + this(amount, amount > 3 ? 0 : 1, amount, filter); + } + + /** + * {@code filter} defaults to all creature permanents. ("target creatures") + * + * @see TargetCreaturePermanentAmount#TargetCreaturePermanentAmount(int, int, int, FilterCreaturePermanent) + */ + public TargetCreaturePermanentAmount(int amount, int minNumberOfTargets, int maxNumberOfTargets) { + this(amount, minNumberOfTargets, maxNumberOfTargets, defaultFilter); + } + + /** + * @param amount Amount of stuff (e.g. damage, counters) to distribute. + * @param minNumberOfTargets Minimum number of targets. + * @param maxNumberOfTargets Maximum number of targets. If no lower max is needed, set to {@code amount}. + * @param filter Filter for creatures that something will be distributed amongst. + */ + public TargetCreaturePermanentAmount(int amount, int minNumberOfTargets, int maxNumberOfTargets, FilterCreaturePermanent filter) { + super(amount, minNumberOfTargets, maxNumberOfTargets, filter); + } + + /** + * {@code filter} defaults to all creature permanents. ("target creatures") + * + * @see TargetCreaturePermanentAmount#TargetCreaturePermanentAmount(DynamicValue, FilterCreaturePermanent) + */ public TargetCreaturePermanentAmount(DynamicValue amount) { this(amount, defaultFilter); } - public TargetCreaturePermanentAmount(int amount, FilterPermanent filter) { - super(amount, filter); + /** + * Minimum number of targets will be zero, and max will be set to Integer.MAX_VALUE. + * + * @param amount Amount of stuff (e.g. damage, counters) to distribute. + * @param filter Filter for creatures that something will be distributed amongst. + */ + public TargetCreaturePermanentAmount(DynamicValue amount, FilterCreaturePermanent filter) { + super(amount, 0, filter); } - public TargetCreaturePermanentAmount(DynamicValue amount, FilterPermanent filter) { - super(amount, filter); + /** + * IMPORTANT: Use more specific constructor if {@code amount} is not always the same number! + * + * @see TargetCreaturePermanentAmount#TargetCreaturePermanentAmount(int, FilterCreaturePermanent) + */ + public TargetCreaturePermanentAmount(int amount, FilterControlledCreaturePermanent filter) { + this(amount, amount > 3 ? 0 : 1, amount, filter); + } + + /** + * @see TargetCreaturePermanentAmount#TargetCreaturePermanentAmount(int, int, int, FilterCreaturePermanent) + */ + public TargetCreaturePermanentAmount(int amount, int minNumberOfTargets, int maxNumberOfTargets, FilterControlledCreaturePermanent filter) { + super(amount, minNumberOfTargets, maxNumberOfTargets, filter); + } + + /** + * @see TargetCreaturePermanentAmount#TargetCreaturePermanentAmount(DynamicValue, FilterCreaturePermanent) + */ + public TargetCreaturePermanentAmount(DynamicValue amount, FilterControlledCreaturePermanent filter) { + super(amount, 0, filter); } private TargetCreaturePermanentAmount(final TargetCreaturePermanentAmount target) { diff --git a/Mage/src/main/java/mage/target/common/TargetPermanentAmount.java b/Mage/src/main/java/mage/target/common/TargetPermanentAmount.java index daac1cbc93b..9525f7b11cc 100644 --- a/Mage/src/main/java/mage/target/common/TargetPermanentAmount.java +++ b/Mage/src/main/java/mage/target/common/TargetPermanentAmount.java @@ -5,7 +5,6 @@ import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; import mage.constants.Zone; -import mage.filter.Filter; import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.permanent.Permanent; @@ -23,16 +22,41 @@ public class TargetPermanentAmount extends TargetAmount { protected final FilterPermanent filter; - public TargetPermanentAmount(int amount, FilterPermanent filter) { - // 107.1c If a rule or ability instructs a player to choose “any number,” that player may choose - // any positive number or zero, unless something (such as damage or counters) is being divided - // or distributed among “any number” of players and/or objects. In that case, a nonzero number - // of players and/or objects must be chosen if possible. - this(StaticValue.get(amount), filter); + /** + * {@code maxNumberOfTargets} defaults to {@code amount}. + * + * @see TargetPermanentAmount#TargetPermanentAmount(DynamicValue, int, int, FilterPermanent) + */ + public TargetPermanentAmount(int amount, int minNumberOfTargets, FilterPermanent filter) { + this(amount, minNumberOfTargets, amount, filter); } - public TargetPermanentAmount(DynamicValue amount, FilterPermanent filter) { - super(amount); + /** + * {@code maxNumberOfTargets} defaults to Integer.MAX_VALUE. + * + * @see TargetPermanentAmount#TargetPermanentAmount(DynamicValue, int, int, FilterPermanent) + */ + public TargetPermanentAmount(DynamicValue amount, int minNumberOfTargets, FilterPermanent filter) { + this(amount, minNumberOfTargets, Integer.MAX_VALUE, filter); + } + + /** + * @see TargetPermanentAmount#TargetPermanentAmount(DynamicValue, int, int, FilterPermanent) + */ + public TargetPermanentAmount(int amount, int minNumberOfTargets, int maxNumberOfTargets, FilterPermanent filter) { + this(StaticValue.get(amount), minNumberOfTargets, maxNumberOfTargets, filter); + } + + /** + * @param amount Amount of stuff (e.g. damage, counters) to distribute. + * @param minNumberOfTargets Minimum number of targets. + * @param maxNumberOfTargets Maximum number of targets. If no lower max is needed: + * set to {@code amount} if amount is static; otherwise, set to Integer.MAX_VALUE. + * (Game will always prevent distributing among more than {@code amount} permanents.) + * @param filter Filter for permanents that something will be distributed amongst. + */ + public TargetPermanentAmount(DynamicValue amount, int minNumberOfTargets, int maxNumberOfTargets, FilterPermanent filter) { + super(amount, minNumberOfTargets, maxNumberOfTargets); this.zone = Zone.ALL; this.filter = filter; this.targetName = filter.getMessage(); @@ -49,7 +73,7 @@ public class TargetPermanentAmount extends TargetAmount { } @Override - public Filter getFilter() { + public FilterPermanent getFilter() { return this.filter; } diff --git a/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayerAmount.java b/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayerAmount.java index 9e42eb94fb6..a1e91b817cc 100644 --- a/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayerAmount.java +++ b/Mage/src/main/java/mage/target/common/TargetPermanentOrPlayerAmount.java @@ -22,9 +22,8 @@ public abstract class TargetPermanentOrPlayerAmount extends TargetAmount { protected FilterPermanentOrPlayer filter; - TargetPermanentOrPlayerAmount(DynamicValue amount, int maxNumberOfTargets) { - super(amount); - this.maxNumberOfTargets = maxNumberOfTargets; + TargetPermanentOrPlayerAmount(DynamicValue amount, int minNumberOfTargets, int maxNumberOfTargets) { + super(amount, minNumberOfTargets, maxNumberOfTargets); } TargetPermanentOrPlayerAmount(final TargetPermanentOrPlayerAmount target) { diff --git a/Mage/src/main/java/mage/util/DeckBuildUtils.java b/Mage/src/main/java/mage/util/DeckBuildUtils.java index c7bae4d8175..20d264666ce 100644 --- a/Mage/src/main/java/mage/util/DeckBuildUtils.java +++ b/Mage/src/main/java/mage/util/DeckBuildUtils.java @@ -6,12 +6,12 @@ import java.util.Set; public final class DeckBuildUtils { + /** + * Returns the number of basic lands suggested to complete a deck + * as an array of five ints: plains, islands, swamps, mountains, forests + * Total number of lands always sufficient to reach deckSize + */ public static int[] landCountSuggestion(int deckSize, Set deckList) { - /* - Returns the number of basic lands suggested to complete a deck - as an array of five ints: plains, islands, swamps, mountains, forests - Total number of lands always sufficient to reach deckSize - */ int plains = 0, islands = 0, swamps = 0, mountains = 0, forests = 0; int landsNeeded = deckSize - deckList.size(); if (landsNeeded > 0) { @@ -51,9 +51,4 @@ public final class DeckBuildUtils { } return new int[] {plains, islands, swamps, mountains, forests}; } - - // Hide constructor - not to be instantiated - private DeckBuildUtils() { - } - } diff --git a/Mage/src/main/java/mage/util/TournamentUtil.java b/Mage/src/main/java/mage/util/TournamentUtil.java index 165c59d9d04..f9e3d0025ae 100644 --- a/Mage/src/main/java/mage/util/TournamentUtil.java +++ b/Mage/src/main/java/mage/util/TournamentUtil.java @@ -15,14 +15,12 @@ public final class TournamentUtil { /** * Tries to calculate the most appropriate sets to add basic lands for cards of a deck * - * @param setCodesDeck - * @return setCode for lands + * @param setCodesDeck all sets in current deck */ - public static Set getLandSetCodeForDeckSets(Collection setCodesDeck) { - Set landSetCodes = new HashSet<>(); - // decide from which sets basic lands are taken from + + // from deck's sets for (String setCode : setCodesDeck) { ExpansionInfo expansionInfo = ExpansionRepository.instance.getSetByCode(setCode); if (expansionInfo.hasBasicLands() && !CardRepository.haveSnowLands(setCode)) { @@ -30,7 +28,7 @@ public final class TournamentUtil { } } - // if sets have no basic land, take land from block + // from deck's blocks if (landSetCodes.isEmpty()) { for (String setCode : setCodesDeck) { ExpansionInfo expansionInfo = ExpansionRepository.instance.getSetByCode(setCode); @@ -42,10 +40,9 @@ public final class TournamentUtil { } } } - // if still no set with lands found, take one by random + + // from random if (landSetCodes.isEmpty()) { - // if sets have no basic lands and also it has no parent or parent has no lands get last set with lands - // select a set with basic lands by random List basicLandSets = ExpansionRepository.instance.getSetsWithBasicLandsByReleaseDate() .stream() .filter(exp -> !CardRepository.haveSnowLands(exp.getCode())) @@ -56,7 +53,7 @@ public final class TournamentUtil { } if (landSetCodes.isEmpty()) { - throw new IllegalArgumentException("No set with basic land was found"); + throw new IllegalArgumentException("No set with basic land was found (possible memory problems, need server restart)"); } return landSetCodes; } diff --git a/Mage/src/main/resources/tokens-database.txt b/Mage/src/main/resources/tokens-database.txt index 97af92fe948..643a4eefd12 100644 --- a/Mage/src/main/resources/tokens-database.txt +++ b/Mage/src/main/resources/tokens-database.txt @@ -231,11 +231,7 @@ |Generate|TOK:ARB|Bird Soldier|||BirdSoldierToken| |Generate|TOK:ARB|Dragon|||DragonBroodmotherDragonToken| |Generate|TOK:ARB|Lizard|||LizardToken| -|Generate|TOK:ARB|Zombie Wizard|||ZombieWizardToken| -|Generate|TOK:ARN|Bird|||RukhEggBirdToken| |Generate|TOK:ARN|Djinn|||DjinnToken| -|Generate|TOK:ATQ|Assembly-Worker|||AssemblyWorkerToken| -|Generate|TOK:ATQ|Tetravite|||TetraviteToken| |Generate|TOK:AVR|Angel|||AngelToken| |Generate|TOK:AVR|Demon|||DemonToken| |Generate|TOK:AVR|Human|1||RedHumanToken| @@ -401,12 +397,6 @@ |Generate|TOK:C19|Wurm|||WurmToken| |Generate|TOK:C19|Zombie|1||ZombieToken| |Generate|TOK:C19|Zombie|2||ZombieToken| -|Generate|TOK:CHK|Dragon Spirit|||TatsumaDragonToken| -|Generate|TOK:CHK|Elemental|||SeedGuardianToken| -|Generate|TOK:CHK|Illusion|||MelokuTheCloudedMirrorToken| -|Generate|TOK:CHK|Rat|||RatToken| -|Generate|TOK:CHK|Snake|||SnakeToken| -|Generate|TOK:CHR|Snake|||SerpentGeneratorSnakeToken| |Generate|TOK:CMA|Beast|1||BeastToken| |Generate|TOK:CMA|Beast|2||BeastToken2| |Generate|TOK:CMA|Dragon|||DragonToken2| @@ -445,7 +435,6 @@ |Generate|TOK:CNS|Zombie|||ZombieToken| |Generate|TOK:CON|Angel|||AngelToken| |Generate|TOK:CON|Elemental|||ElementalTokenWithHaste| -|Generate|TOK:DD2|Elemental Shaman|||ElementalShamanToken| |Generate|TOK:DVD|Demon|||DemonFlyingToken| |Generate|TOK:DVD|Spirit|||SpiritWhiteToken| |Generate|TOK:DVD|Thrull|||BreedingPitThrullToken| @@ -453,7 +442,6 @@ |Generate|TOK:GVL|Beast|1||BeastToken| |Generate|TOK:GVL|Beast|2||BeastToken2| |Generate|TOK:GVL|Elephant|||ElephantToken| -|Generate|TOK:JVC|Elemental Shaman|||ElementalShamanToken| |Generate|TOK:DDC|Demon|||DemonFlyingToken| |Generate|TOK:DDC|Spirit|||SpiritWhiteToken| |Generate|TOK:DDC|Thrull|||BreedingPitThrullToken| @@ -485,12 +473,6 @@ |Generate|TOK:DDQ|Spirit|||SpiritWhiteToken| |Generate|TOK:DDQ|Zombie|||ZombieToken| |Generate|TOK:DGM|Elemental|||VoiceOfResurgenceToken| -|Generate|TOK:DIS|Bird|||WhiteBlueBirdToken| -|Generate|TOK:DIS|Drake|||LeafdrakeRoostDrakeToken| -|Generate|TOK:DIS|Elemental|||ResearchDevelopmentToken| -|Generate|TOK:DIS|Goblin|||RakdosGuildmageGoblinToken| -|Generate|TOK:DIS|Saproling|||SaprolingToken| -|Generate|TOK:DIS|Snake|||PatagiaViperSnakeToken| |Generate|TOK:DKA|Human|||HumanToken| |Generate|TOK:DKA|Vampire|||SorinLordOfInnistradVampireToken| |Generate|TOK:DOM|Cleric|||BelzenlokClericToken| @@ -507,14 +489,6 @@ |Generate|TOK:DOM|Saproling|3||SaprolingToken| |Generate|TOK:DOM|Soldier|||SoldierToken| |Generate|TOK:DOM|Zombie Knight|||ZombieKnightToken| -|Generate|TOK:DRB|Saproling|||SaprolingToken| -|Generate|TOK:DST|Beast|||BeastToken| -|Generate|TOK:DST|Elemental|1||WandOfTheElementsFirstToken| -|Generate|TOK:DST|Elemental|2||WandOfTheElementsSecondToken| -|Generate|TOK:DST|Wirefly|||WireflyToken| -|Generate|TOK:DST|Insect|||InsectToken| -|Generate|TOK:DST|Myr|||MyrToken| -|Generate|TOK:DST|Spawn|||SpawningPitToken| |Generate|TOK:DTK|Djinn Monk|||DjinnMonkToken| |Generate|TOK:DTK|Dragon|||DragonToken| |Generate|TOK:DTK|Goblin|||GoblinToken| @@ -576,9 +550,6 @@ |Generate|TOK:GTC|Soldier|||SoldierTokenWithHaste| |Generate|TOK:GTC|Spirit|||WhiteBlackSpiritToken| |Generate|TOK:H17|Dragon|||DragonTokenGold| -|Generate|TOK:HML|Plant Wall|||KelpToken| -|Generate|TOK:HML|Serf|||SerfToken| -|Generate|TOK:HML|Skeleton|||SkeletonRegenerateToken| |Generate|TOK:HOU|Horse|||CrestedSunmareToken| |Generate|TOK:HOU|Insect|||TheLocustGodInsectToken| |Generate|TOK:HOU|Snake|||RhonassLastStandToken| @@ -593,11 +564,6 @@ #|Generate|TOK:HOU|Sinuous Striker|||| #|Generate|TOK:HOU|Steadfast Sentinel|||| #|Generate|TOK:HOU|Sunscourge Champion|||| -|Generate|TOK:ICE|Caribou|||CaribouToken| -|Generate|TOK:INV|Bird|||BlueBirdToken| -|Generate|TOK:INV|Elephant|||ElephantToken| -|Generate|TOK:INV|Reflection|||ReflectionToken| -|Generate|TOK:INV|Saproling|||SaprolingToken| |Generate|TOK:ISD|Angel|||AngelToken| |Generate|TOK:ISD|Demon|||DemonToken| |Generate|TOK:ISD|Homunculus|||StitchersApprenticeHomunculusToken| @@ -616,12 +582,6 @@ |Generate|TOK:JOU|Sphinx|||HourOfNeedSphinxToken| |Generate|TOK:JOU|Spider|||RenownedWeaverSpiderToken| |Generate|TOK:JOU|Zombie|||RitualOfTheReturnedZombieToken| -|Generate|TOK:JUD|Bear|||BearToken| -|Generate|TOK:JUD|Bird|||BirdToken| -|Generate|TOK:JUD|Elemental Cat|||ElementalCatToken| -|Generate|TOK:JUD|Elephant|||ElephantToken| -|Generate|TOK:JUD|Spirit|||SpiritWhiteToken| -|Generate|TOK:JUD|Wurm|||WurmToken| |Generate|TOK:KLD|Beast|||ArchitectOfTheUntamedBeastToken| |Generate|TOK:KLD|Construct|1||OviyaPashiriSageLifecrafterToken| |Generate|TOK:KLD|Construct|2||MetallurgicSummoningsConstructToken| @@ -641,9 +601,6 @@ |Generate|TOK:KTK|Warrior|1||WarriorToken| |Generate|TOK:KTK|Warrior|2||WarriorToken| |Generate|TOK:KTK|Zombie|||ZombieToken| -|Generate|TOK:LEG|Demon|||MinorDemonToken| -|Generate|TOK:LEG|Sand Warrior|||HazezonTamarSandWarriorToken| -|Generate|TOK:LEG|Snake|||SerpentGeneratorSnakeToken| # LGN don't have tokens, from wiki: A Sliver token for Brood Sliver and a Goblin token for Warbreak Trumpeter were featured as a Magic Player Reward. |Generate|TOK:LRW|Avatar|||AvatarToken| |Generate|TOK:LRW|Beast|||BeastToken| @@ -775,15 +732,6 @@ |Generate|TOK:MOR|Faerie Rogue|||FaerieRogueToken| |Generate|TOK:MOR|Giant Warrior|||GiantWarriorToken| |Generate|TOK:MOR|Treefolk Shaman|||TreefolkShamanToken| -|Generate|TOK:MRD|Beast|||OneDozenEyesBeastToken| -|Generate|TOK:MRD|Demon|||ReignOfThePitToken| -|Generate|TOK:MRD|Elemental|||ElementalTokenWithHaste| -|Generate|TOK:MRD|Insect|||InsectToken| -|Generate|TOK:MRD|Myr|||MyrToken| -|Generate|TOK:MRD|Pentavite|||PentaviteToken| -|Generate|TOK:MRD|Pest|||PestToken| -|Generate|TOK:MRD|Soldier|||SoldierToken| -|Generate|TOK:MRD|Spirit|||SpiritWhiteToken| |Generate|TOK:NPH|Beast|||BeastToken| |Generate|TOK:NPH|Phyrexian Goblin|||PhyrexianGoblinHasteToken| |Generate|TOK:NPH|Phyrexian Golem|||PhyrexianGolemToken| @@ -896,16 +844,8 @@ |Generate|TOK:THS|Soldier|1||SoldierToken| |Generate|TOK:THS|Soldier|2||SoldierToken| |Generate|TOK:THS|Soldier|3||AkroanSoldierToken| -|Generate|TOK:TOR|Squirrel|||SquirrelToken| |Generate|TOK:UST|Dragon|||DragonTokenGold| |Generate|TOK:UST|Storm Crow|||StormCrowToken| -|Generate|TOK:V10|Wolf|||WolfToken| -|Generate|TOK:V11|Faerie Rogue|||OonaQueenFaerieToken| -|Generate|TOK:VIS|Butterfly|||ButterflyToken| -|Generate|TOK:VIS|Insect|||InsectToken| -|Generate|TOK:VIS|Prism|||PrismToken| -|Generate|TOK:VIS|Snake|||SnakeToken| -|Generate|TOK:WTH|Squirrel|||SquirrelToken| |Generate|TOK:WWK|Construct|||StoneTrapIdolToken| |Generate|TOK:WWK|Dragon|||DragonToken2| |Generate|TOK:WWK|Elephant|||ElephantToken| @@ -1102,11 +1042,6 @@ |Generate|TOK:M21|Weird|||WeirdToken2| |Generate|TOK:M21|Zombie|||ZombieToken| -# JMP -# Jumpstart uses tokens and emblems from M21 set, -# TODO: check scryfall for JMP tokens after set's release -|Generate|TOK:JMP|Unicorn|||UnicornToken| - # ZNR |Generate|TOK:ZNR|Angel Warrior|||AngelWarriorToken| |Generate|TOK:ZNR|Cat|||CatToken3|