diff --git a/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java b/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java index 2f9d35332ed..c2f57c3b0c3 100644 --- a/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java +++ b/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java @@ -575,7 +575,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg // Constants private static final int DEFAULT_COUNT_LABEL_HEIGHT = 40; // can contain 1 or 2 lines - public static final int GRID_PADDING = 20; + public static final int GRID_PADDING = 12; private static final ImageIcon INSERT_ROW_ICON = new ImageIcon(DragCardGrid.class.getClassLoader().getResource("editor_insert_row.png")); private static final ImageIcon INSERT_COL_ICON = new ImageIcon(DragCardGrid.class.getClassLoader().getResource("editor_insert_col.png")); diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java index e91491d577f..5fc2c4b0f2b 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java @@ -467,6 +467,7 @@ public class ScryfallImageSupportCards { // add("MD1"); // Modern Event Deck // add("DD3"); // Duel Decks Anthology // add("PZ1"); // Legendary Cube + add("PLG20"); // Love Your LGS 2020 add("IKO"); // Ikoria: Lair of Behemoths add("C20"); // Commander 2020 add("M21"); // Core Set 2021 @@ -516,11 +517,13 @@ public class ScryfallImageSupportCards { add("NCC"); // New Capenna Commander add("SLX"); // Universes Within add("CLB"); // Commander Legends: Battle for Baldur's Gate + add("PLG22"); // Love Your LGS 2022 add("2X2"); // Double Masters 2022 add("SCH"); // Store Championships add("DMU"); // Dominaria United add("DMC"); // Dominaria United Commander add("YDMU"); // Alchemy: Dominaria + add("PRCQ"); // Regional Championship Qualifiers 2022 add("40K"); // Warhammer 40,000 Commander add("UNF"); // Unfinity add("GN3"); // Game Night: Free-for-All @@ -548,9 +551,11 @@ public class ScryfallImageSupportCards { add("30A"); // 30th Anniversary Edition add("P30A"); // 30th Anniversary Play Promos add("P30M"); // 30th Anniversary Misc Promos + add("P30H"); // 30th Anniversary History Promos add("PEWK"); // Eternal Weekend add("LTR"); // The Lord of the Rings: Tales of Middle-Earth add("LTC"); // Tales of Middle-Earth Commander + add("PF23"); // MagicFest 2023 add("CMM"); // Commander Masters add("WHO"); // Doctor Who add("WOE"); // Wilds of Eldraine @@ -561,10 +566,13 @@ public class ScryfallImageSupportCards { add("REX"); // Jurassic World Collection add("SPG"); // Special Guests add("PW24"); // Wizards Play Network 2024 + add("PF24"); // MagicFest 2024 add("RVR"); // Ravnica Remastered + add("PL24"); // Year of the Dragon 2024 add("PIP"); // Fallout add("MKM"); // Murders at Karlov Manor add("MKC"); // Murders at Karlov Manor Commander + add("PSS4"); // MKM Standard Showdown add("CLU"); // Ravnica: Clue Edition add("OTJ"); // Outlaws of Thunder Junction add("OTC"); // Outlaws of Thunder Junction Commander @@ -576,6 +584,7 @@ public class ScryfallImageSupportCards { add("ACR"); // Assassin's Creed add("BLB"); // Bloomburrow add("BLC"); // Bloomburrow Commander + add("PLG24"); // Love Your LGS 2024 add("PCBB"); // Cowboy Bebop add("MB2"); // Mystery Booster 2 add("DSK"); // Duskmourn: House of Horror @@ -587,6 +596,7 @@ public class ScryfallImageSupportCards { add("PSPL"); // Spotlight Series add("INR"); // Innistrad Remastered add("PF25"); // MagicFest 2025 + add("PL25"); // Year of the Snake 2025 add("DFT"); // Aetherdrift add("DRC"); // Aetherdrift Commander add("PLG25"); // Love Your LGS 2025 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 a707edc60c9..f53f63eea97 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 @@ -2818,6 +2818,14 @@ public class ScryfallImageSupportTokens { put("EOC/Shapeshifter", "https://api.scryfall.com/cards/teoc/2/en?format=image"); put("EOC/Thopter", "https://api.scryfall.com/cards/teoc/16/en?format=image"); + // SPM + put("SPM/Food", "https://api.scryfall.com/cards/tspm/5?format=image"); + put("SPM/Human", "https://api.scryfall.com/cards/tspm/4?format=image"); + put("SPM/Illusion", "https://api.scryfall.com/cards/tspm/2?format=image"); + put("SPM/Robot", "https://api.scryfall.com/cards/tspm/6?format=image"); + put("SPM/Spider", "https://api.scryfall.com/cards/tspm/3?format=image"); + put("SPM/Treasure", "https://api.scryfall.com/cards/tspm/7?format=image"); + // JVC put("JVC/Elemental Shaman", "https://api.scryfall.com/cards/tjvc/4?format=image"); @@ -2931,6 +2939,12 @@ public class ScryfallImageSupportTokens { // PL23 put("PL23/Food", "https://api.scryfall.com/cards/pl23/2?format=image"); + // PL24 + put("PL24/Dragon", "https://api.scryfall.com/cards/pl24/3?format=image"); + + // PL25 + put("PL25/Snake", "https://api.scryfall.com/cards/pl25/2?format=image"); + // generate supported sets supportedSets.clear(); for (String cardName : this.keySet()) { diff --git a/Mage.Sets/src/mage/cards/a/AgentVenom.java b/Mage.Sets/src/mage/cards/a/AgentVenom.java new file mode 100644 index 00000000000..9217071f4a2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AgentVenom.java @@ -0,0 +1,66 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.LoseLifeSourceControllerEffect; +import mage.abilities.keyword.FlashAbility; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.filter.predicate.permanent.TokenPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AgentVenom extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent("another nontoken creature you control"); + + static { + filter.add(AnotherPredicate.instance); + filter.add(TokenPredicate.FALSE); + } + + public AgentVenom(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SYMBIOTE); + this.subtype.add(SubType.SOLDIER); + this.subtype.add(SubType.HERO); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Menace + this.addAbility(new MenaceAbility()); + + // Whenever another nontoken creature you control dies, you draw a card and lose 1 life. + Ability ability = new DiesCreatureTriggeredAbility( + new DrawCardSourceControllerEffect(1, true), false, filter + ); + ability.addEffect(new LoseLifeSourceControllerEffect(1).setText("and lose 1 life")); + this.addAbility(ability); + } + + private AgentVenom(final AgentVenom card) { + super(card); + } + + @Override + public AgentVenom copy() { + return new AgentVenom(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AlabornZealot.java b/Mage.Sets/src/mage/cards/a/AlabornZealot.java index 9c84b4a111f..e9055f4c35c 100644 --- a/Mage.Sets/src/mage/cards/a/AlabornZealot.java +++ b/Mage.Sets/src/mage/cards/a/AlabornZealot.java @@ -27,9 +27,9 @@ public final class AlabornZealot extends CardImpl { // When Alaborn Zealot blocks a creature, destroy that creature and Alaborn Zealot. TriggeredAbility ability = new BlocksCreatureTriggeredAbility( - new DestroyTargetEffect().setText("destroy that creature") + new DestroyTargetEffect().setText("destroy both") ); - ability.addEffect(new DestroySourceEffect().setText("and {this}")); + ability.addEffect(new DestroySourceEffect().setText(" creatures")); ability.setTriggerPhrase("When {this} blocks a creature, "); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/a/AlienSymbiosis.java b/Mage.Sets/src/mage/cards/a/AlienSymbiosis.java new file mode 100644 index 00000000000..99b3985117a --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AlienSymbiosis.java @@ -0,0 +1,101 @@ +package mage.cards.a; + +import mage.MageIdentifier; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.Costs; +import mage.abilities.costs.CostsImpl; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.AddCardSubtypeAttachedEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class AlienSymbiosis extends CardImpl { + + public AlienSymbiosis(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(new EnchantAbility(auraTarget)); + + // Enchanted creature gets +1/+1, has menace, and is a Symbiote in addition to its other types. + Ability ability = new SimpleStaticAbility(new BoostEnchantedEffect(1, 1)); + ability.addEffect(new GainAbilityAttachedEffect(new MenaceAbility(), null).concatBy(",")); + ability.addEffect(new AddCardSubtypeAttachedEffect(SubType.SYMBIOTE, AttachmentType.AURA).concatBy(",").setText("and is a Symbiote in addition to its other types")); + this.addAbility(ability); + + // You may cast this card from your graveyard by discarding a card in addition to paying its other costs. + this.addAbility(new SimpleStaticAbility(Zone.GRAVEYARD, new AlienSymbiosisGraveyardEffect())); + } + + private AlienSymbiosis(final AlienSymbiosis card) { + super(card); + } + + @Override + public AlienSymbiosis copy() { + return new AlienSymbiosis(this); + } +} +class AlienSymbiosisGraveyardEffect extends AsThoughEffectImpl { + + AlienSymbiosisGraveyardEffect() { + super(AsThoughEffectType.CAST_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfGame, Outcome.PutCreatureInPlay); + this.staticText = "You may cast this card from your graveyard by discarding a card in addition to paying its other costs."; + } + + private AlienSymbiosisGraveyardEffect(final AlienSymbiosisGraveyardEffect effect) { + super(effect); + } + + @Override + public AlienSymbiosisGraveyardEffect copy() { + return new AlienSymbiosisGraveyardEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + if (!objectId.equals(source.getSourceId()) || !source.isControlledBy(affectedControllerId) + || game.getState().getZone(source.getSourceId()) != Zone.GRAVEYARD) { + return false; + } + Player controller = game.getPlayer(affectedControllerId); + if (controller != null) { + Costs costs = new CostsImpl<>(); + costs.add(new DiscardCardCost()); + controller.setCastSourceIdWithAlternateMana(objectId, new ManaCostsImpl<>("{1}{B}"), costs, + MageIdentifier.AlienSymbiosisAlternateCast); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/cards/a/AltairIbnLaAhad.java b/Mage.Sets/src/mage/cards/a/AltairIbnLaAhad.java index cd64eedaa1b..7aef812ebd2 100644 --- a/Mage.Sets/src/mage/cards/a/AltairIbnLaAhad.java +++ b/Mage.Sets/src/mage/cards/a/AltairIbnLaAhad.java @@ -117,7 +117,7 @@ class AltairIbnLaAhadTokenEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Set cards = game .getExile() - .getAllCards(game, source.getControllerId()) + .getCardsOwned(game, source.getControllerId()) .stream() .filter(card -> card.getCounters(game).containsKey(CounterType.MEMORY)) .filter(card -> card.isCreature(game)) diff --git a/Mage.Sets/src/mage/cards/a/AmazingAcrobatics.java b/Mage.Sets/src/mage/cards/a/AmazingAcrobatics.java new file mode 100644 index 00000000000..b843c829900 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AmazingAcrobatics.java @@ -0,0 +1,47 @@ +package mage.cards.a; + +import mage.abilities.Mode; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; +import mage.target.TargetSpell; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class AmazingAcrobatics extends CardImpl { + + public AmazingAcrobatics(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}{U}"); + + + // Choose one or both -- + this.getSpellAbility().getModes().setMinModes(1); + this.getSpellAbility().getModes().setMaxModes(2); + + // * Counter target spell. + this.getSpellAbility().addEffect(new CounterTargetEffect()); + this.getSpellAbility().addTarget(new TargetSpell()); + + // * Tap one or two target creatures. + Mode mode = new Mode(new TapTargetEffect()); + mode.addTarget(new TargetPermanent(1, 2, StaticFilters.FILTER_PERMANENT_CREATURES)); + this.getSpellAbility().getModes().addMode(mode); + } + + private AmazingAcrobatics(final AmazingAcrobatics card) { + super(card); + } + + @Override + public AmazingAcrobatics copy() { + return new AmazingAcrobatics(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AmbushCommander.java b/Mage.Sets/src/mage/cards/a/AmbushCommander.java index 036e316e46f..69d8b124cc3 100644 --- a/Mage.Sets/src/mage/cards/a/AmbushCommander.java +++ b/Mage.Sets/src/mage/cards/a/AmbushCommander.java @@ -14,7 +14,6 @@ import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterControlledPermanent; import mage.game.permanent.token.custom.CreatureToken; -import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -42,7 +41,11 @@ public final class AmbushCommander extends CardImpl { ContinuousEffect effect = new BecomesCreatureAllEffect( new CreatureToken(1, 1, "1/1 green Elf creatures").withColor("G").withSubType(SubType.ELF), "lands", filter2, Duration.WhileOnBattlefield, true); - effect.getDependencyTypes().add(DependencyType.BecomeForest); + effect.addDependedToType(DependencyType.BecomeForest); + effect.addDependedToType(DependencyType.BecomeIsland); + effect.addDependedToType(DependencyType.BecomeMountain); + effect.addDependedToType(DependencyType.BecomePlains); + effect.addDependedToType(DependencyType.BecomeSwamp); this.addAbility(new SimpleStaticAbility(effect)); // {1}{G}, Sacrifice an Elf: Target creature gets +3/+3 until end of turn. diff --git a/Mage.Sets/src/mage/cards/a/AngelOfDestiny.java b/Mage.Sets/src/mage/cards/a/AngelOfDestiny.java index bf0df6ed71b..3e97627ad2c 100644 --- a/Mage.Sets/src/mage/cards/a/AngelOfDestiny.java +++ b/Mage.Sets/src/mage/cards/a/AngelOfDestiny.java @@ -114,7 +114,7 @@ class AngelOfDestinyLoseEffect extends OneShotEffect { return false; } Set playerSet = watcher.getPlayers(new MageObjectReference( - source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game + source.getSourceId(), source.getStackMomentSourceZCC(), game )); if (playerSet == null) { return false; diff --git a/Mage.Sets/src/mage/cards/a/AngelicFavor.java b/Mage.Sets/src/mage/cards/a/AngelicFavor.java index 299134f4395..a89f9ecabd1 100644 --- a/Mage.Sets/src/mage/cards/a/AngelicFavor.java +++ b/Mage.Sets/src/mage/cards/a/AngelicFavor.java @@ -28,7 +28,7 @@ import mage.target.common.TargetControlledPermanent; */ public final class AngelicFavor extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("If you control a Plains"); + private static final FilterPermanent filter = new FilterPermanent("you control a Plains"); static { filter.add(SubType.PLAINS.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/a/ArachnePsionicWeaver.java b/Mage.Sets/src/mage/cards/a/ArachnePsionicWeaver.java new file mode 100644 index 00000000000..d80351d2164 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/ArachnePsionicWeaver.java @@ -0,0 +1,70 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.ChooseCardTypeEffect; +import mage.abilities.effects.common.LookAtTargetPlayerHandEffect; +import mage.abilities.effects.common.cost.SpellsCostIncreasingAllEffect; +import mage.abilities.keyword.WebSlingingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ChosenCardTypePredicate; +import mage.target.common.TargetOpponent; + +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * + * @author Jmlundeen + */ +public final class ArachnePsionicWeaver extends CardImpl { + + private static final FilterCard filter = new FilterCard("spells of the chosen type"); + + static { + filter.add(ChosenCardTypePredicate.TRUE); + } + + public ArachnePsionicWeaver(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIDER); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.HERO); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Web-slinging {W} + this.addAbility(new WebSlingingAbility(this, "{W}")); + + // As Arachne enters, look at target opponent's hand, then choose a noncreature card type. + List types = Arrays.stream(CardType.values()).filter(cardType -> cardType != CardType.CREATURE) + .collect(Collectors.toList()); + Ability ability = new AsEntersBattlefieldAbility(new LookAtTargetPlayerHandEffect()); + ability.addEffect(new ChooseCardTypeEffect(Outcome.Benefit, types) + .setText("choose a noncreature card type") + .concatBy(", then")); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + + // Spells of the chosen type cost {1} more to cast. + this.addAbility(new SimpleStaticAbility(new SpellsCostIncreasingAllEffect(1, filter, TargetController.ANY))); + } + + private ArachnePsionicWeaver(final ArachnePsionicWeaver card) { + super(card); + } + + @Override + public ArachnePsionicWeaver copy() { + return new ArachnePsionicWeaver(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/AranaHeartOfTheSpider.java b/Mage.Sets/src/mage/cards/a/AranaHeartOfTheSpider.java new file mode 100644 index 00000000000..cf4d01e8367 --- /dev/null +++ b/Mage.Sets/src/mage/cards/a/AranaHeartOfTheSpider.java @@ -0,0 +1,63 @@ +package mage.cards.a; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; +import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility; +import mage.abilities.effects.common.ExileTopXMayPlayUntilEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.ModifiedPredicate; +import mage.target.common.TargetAttackingCreature; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class AranaHeartOfTheSpider extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent("modified creature you control"); + + static { + filter.add(ModifiedPredicate.instance); + } + + public AranaHeartOfTheSpider(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIDER); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.HERO); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Whenever you attack, put a +1/+1 counter on target attacking creature. + Ability ability = new AttacksWithCreaturesTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), 1 + ); + ability.addTarget(new TargetAttackingCreature()); + this.addAbility(ability); + + // Whenever a modified creature you control deals combat damage to a player, exile the top card of your library. You may play that card this turn. + this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility( + new ExileTopXMayPlayUntilEffect(1, Duration.EndOfTurn), + filter, false, SetTargetPointer.NONE, true + )); + } + + private AranaHeartOfTheSpider(final AranaHeartOfTheSpider card) { + super(card); + } + + @Override + public AranaHeartOfTheSpider copy() { + return new AranaHeartOfTheSpider(this); + } +} diff --git a/Mage.Sets/src/mage/cards/a/ArchiveTrap.java b/Mage.Sets/src/mage/cards/a/ArchiveTrap.java index 1b431a97a91..9c560216f5b 100644 --- a/Mage.Sets/src/mage/cards/a/ArchiveTrap.java +++ b/Mage.Sets/src/mage/cards/a/ArchiveTrap.java @@ -95,7 +95,7 @@ enum OpponentSearchesLibCondition implements Condition { @Override public String toString() { - return "If an opponent searched their library this turn"; + return "an opponent searched their library this turn"; } } diff --git a/Mage.Sets/src/mage/cards/a/ArgivianAvenger.java b/Mage.Sets/src/mage/cards/a/ArgivianAvenger.java index 14af8a092ce..da01177a4f1 100644 --- a/Mage.Sets/src/mage/cards/a/ArgivianAvenger.java +++ b/Mage.Sets/src/mage/cards/a/ArgivianAvenger.java @@ -5,7 +5,7 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.GainsChoiceOfAbilitiesEffect; -import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.DeathtouchAbility; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.HasteAbility; @@ -13,6 +13,7 @@ import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Duration; import mage.constants.SubType; import java.util.UUID; @@ -30,7 +31,7 @@ public final class ArgivianAvenger extends CardImpl { this.toughness = new MageInt(5); // {1}: Until end of turn, Argivian Avenger gets -1/-1 and gains your choice of flying, vigilance, deathtouch, or haste. - Ability ability = new SimpleActivatedAbility(new BoostTargetEffect(-1, -1) + Ability ability = new SimpleActivatedAbility(new BoostSourceEffect(-1, -1, Duration.EndOfTurn) .setText("Until end of turn, {this} gets -1/-1"), new GenericManaCost(1)); ability.addEffect(new GainsChoiceOfAbilitiesEffect(GainsChoiceOfAbilitiesEffect.TargetType.Source, "", false, FlyingAbility.getInstance(), VigilanceAbility.getInstance(), DeathtouchAbility.getInstance(), HasteAbility.getInstance()) diff --git a/Mage.Sets/src/mage/cards/a/ArrowVolleyTrap.java b/Mage.Sets/src/mage/cards/a/ArrowVolleyTrap.java index 4dca2f81887..fb45fc33d75 100644 --- a/Mage.Sets/src/mage/cards/a/ArrowVolleyTrap.java +++ b/Mage.Sets/src/mage/cards/a/ArrowVolleyTrap.java @@ -52,6 +52,6 @@ enum ArrowVolleyTrapCondition implements Condition { @Override public String toString() { - return "If four or more creatures are attacking"; + return "four or more creatures are attacking"; } } diff --git a/Mage.Sets/src/mage/cards/a/AshayaSoulOfTheWild.java b/Mage.Sets/src/mage/cards/a/AshayaSoulOfTheWild.java index 8f726adaa2a..a0c80d4df31 100644 --- a/Mage.Sets/src/mage/cards/a/AshayaSoulOfTheWild.java +++ b/Mage.Sets/src/mage/cards/a/AshayaSoulOfTheWild.java @@ -64,6 +64,7 @@ class AshayaSoulOfTheWildEffect extends ContinuousEffectImpl { staticText = "Nontoken creatures you control are Forest lands in addition to their other types"; this.dependendToTypes.add(DependencyType.BecomeCreature); this.dependencyTypes.add(DependencyType.BecomeForest); + this.dependencyTypes.add(DependencyType.BecomeNonbasicLand); } private AshayaSoulOfTheWildEffect(final AshayaSoulOfTheWildEffect effect) { diff --git a/Mage.Sets/src/mage/cards/a/AshiokNightmareMuse.java b/Mage.Sets/src/mage/cards/a/AshiokNightmareMuse.java index 88d6668ca6c..3a24468a040 100644 --- a/Mage.Sets/src/mage/cards/a/AshiokNightmareMuse.java +++ b/Mage.Sets/src/mage/cards/a/AshiokNightmareMuse.java @@ -11,6 +11,7 @@ import mage.constants.*; import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.filter.predicate.Predicates; +import mage.filter.predicate.card.FaceDownPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.AshiokNightmareMuseToken; @@ -20,8 +21,6 @@ import mage.target.common.TargetNonlandPermanent; import mage.util.CardUtil; import java.util.UUID; -import mage.filter.predicate.card.FaceDownPredicate; -import mage.filter.predicate.card.OwnerIdPredicate; /** * @author TheElk801 @@ -96,6 +95,10 @@ class AshiokNightmareMuseBounceEffect extends OneShotEffect { class AshiokNightmareMuseCastEffect extends OneShotEffect { private static final FilterCard filter = new FilterCard("face-up cards your opponents own from exile"); + static { + filter.add(TargetController.OPPONENT.getOwnerPredicate()); + filter.add(Predicates.not(FaceDownPredicate.instance)); + } AshiokNightmareMuseCastEffect() { super(Outcome.Benefit); @@ -117,11 +120,10 @@ class AshiokNightmareMuseCastEffect extends OneShotEffect { if (controller == null) { return false; } - // card is owned by an opponent and is face up - filter.add(Predicates.not(new OwnerIdPredicate(controller.getId()))); - filter.add(Predicates.not(FaceDownPredicate.instance)); CardUtil.castMultipleWithAttributeForFree( - controller, source, game, new CardsImpl(game.getExile().getCards(filter, game)), + controller, source, game, new CardsImpl( + game.getExile().getCardsInRange(filter, controller.getId(), source, game) + ), StaticFilters.FILTER_CARD, 3 ); return true; diff --git a/Mage.Sets/src/mage/cards/a/AshioksErasure.java b/Mage.Sets/src/mage/cards/a/AshioksErasure.java index a8a4273cb13..4033aad9e33 100644 --- a/Mage.Sets/src/mage/cards/a/AshioksErasure.java +++ b/Mage.Sets/src/mage/cards/a/AshioksErasure.java @@ -90,7 +90,7 @@ class AshioksErasureExileEffect extends OneShotEffect { || spell == null) { return false; } - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); return controller.moveCardsToExile(spell, source, game, true, exileId, sourceObject.getIdName()); } } @@ -126,7 +126,7 @@ class AshioksErasureReplacementEffect extends ContinuousRuleModifyingEffectImpl || card == null) { return false; } - UUID exileZone = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileZone = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); ExileZone exile = game.getExile().getExileZone(exileZone); if (exile == null) { diff --git a/Mage.Sets/src/mage/cards/a/AuramancersGuise.java b/Mage.Sets/src/mage/cards/a/AuramancersGuise.java index c2f037a973d..f7482ba11f6 100644 --- a/Mage.Sets/src/mage/cards/a/AuramancersGuise.java +++ b/Mage.Sets/src/mage/cards/a/AuramancersGuise.java @@ -3,7 +3,7 @@ package mage.cards.a; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; @@ -12,19 +12,25 @@ import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.filter.FilterPermanent; +import mage.filter.predicate.permanent.AttachedToAttachedPredicate; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; -import java.util.Objects; -import java.util.Optional; import java.util.UUID; /** * @author spjspj */ public final class AuramancersGuise extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent("Aura attached to it"); + + static { + filter.add(SubType.AURA.getPredicate()); + filter.add(AttachedToAttachedPredicate.instance); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, 2); public AuramancersGuise(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}{U}"); @@ -39,7 +45,7 @@ public final class AuramancersGuise extends CardImpl { // Enchanted creature gets +2/+2 for each Aura attached to it and has vigilance. Ability ability = new SimpleStaticAbility(new BoostEnchantedEffect( - AuramancersGuiseValue.instance, AuramancersGuiseValue.instance, Duration.WhileOnBattlefield + xValue, xValue, Duration.WhileOnBattlefield )); ability.addEffect(new GainAbilityAttachedEffect( VigilanceAbility.getInstance(), AttachmentType.AURA @@ -57,40 +63,3 @@ public final class AuramancersGuise extends CardImpl { } } -enum AuramancersGuiseValue implements DynamicValue { - instance; - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - Permanent permanent = Optional - .ofNullable(sourceAbility.getSourcePermanentIfItStillExists(game)) - .map(Permanent::getAttachedTo) - .map(game::getPermanent) - .orElse(null); - return permanent != null - ? 2 * permanent - .getAttachments() - .stream() - .map(game::getPermanent) - .filter(Objects::nonNull) - .filter(p -> p.hasSubtype(SubType.AURA, game)) - .mapToInt(x -> 1) - .sum() - : 0; - } - - @Override - public AuramancersGuiseValue copy() { - return this; - } - - @Override - public String getMessage() { - return "for each Aura attached to it"; - } - - @Override - public String toString() { - return "2"; - } -} diff --git a/Mage.Sets/src/mage/cards/a/AuratouchedMage.java b/Mage.Sets/src/mage/cards/a/AuratouchedMage.java index 1a06f66a7e8..17c958cf8b0 100644 --- a/Mage.Sets/src/mage/cards/a/AuratouchedMage.java +++ b/Mage.Sets/src/mage/cards/a/AuratouchedMage.java @@ -73,7 +73,7 @@ class AuratouchedMageEffect extends OneShotEffect { Card aura = game.getCard(target.getFirstTarget()); Permanent auratouchedMage = source.getSourcePermanentIfItStillExists(game); if (aura != null && auratouchedMage != null - && game.getState().getZoneChangeCounter(source.getSourceId()) == source.getSourceObjectZoneChangeCounter()) { + && game.getState().getZoneChangeCounter(source.getSourceId()) == source.getStackMomentSourceZCC()) { game.getState().setValue("attachTo:" + aura.getId(), auratouchedMage); if (controller.moveCards(aura, Zone.BATTLEFIELD, source, game)) { auratouchedMage.addAttachment(aura.getId(), source, game); diff --git a/Mage.Sets/src/mage/cards/b/BagOfHolding.java b/Mage.Sets/src/mage/cards/b/BagOfHolding.java index 5b56b21b6fe..8705dc5cdd4 100644 --- a/Mage.Sets/src/mage/cards/b/BagOfHolding.java +++ b/Mage.Sets/src/mage/cards/b/BagOfHolding.java @@ -111,7 +111,7 @@ class BagOfHoldingReturnCardsEffect extends OneShotEffect { return false; } ExileZone exileZone = game.getExile().getExileZone( - CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()) + CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()) ); if (exileZone == null) { return true; diff --git a/Mage.Sets/src/mage/cards/b/BagelAndSchmear.java b/Mage.Sets/src/mage/cards/b/BagelAndSchmear.java new file mode 100644 index 00000000000..329fff0f0c5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BagelAndSchmear.java @@ -0,0 +1,55 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.token.FoodAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class BagelAndSchmear extends CardImpl { + + public BagelAndSchmear(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); + + this.subtype.add(SubType.FOOD); + + // Share -- {W}, {T}, Sacrifice this artifact: Put a +1/+1 counter on up to one target creature. Draw a card. Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), new ManaCostsImpl<>("{W}") + ); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeSourceCost()); + ability.addEffect(new DrawCardSourceControllerEffect(1)); + ability.addTarget(new TargetCreaturePermanent(0, 1)); + this.addAbility(ability.withFlavorWord("Share")); + + // Nosh -- {2}, {T}, Sacrifice this artifact: You gain 3 life and draw a card. + ability = new FoodAbility(); + ability.addEffect(new DrawCardSourceControllerEffect(1).concatBy("and")); + this.addAbility(ability.withFlavorWord("Nosh")); + } + + private BagelAndSchmear(final BagelAndSchmear card) { + super(card); + } + + @Override + public BagelAndSchmear copy() { + return new BagelAndSchmear(this); + } +} +// where's the lox? diff --git a/Mage.Sets/src/mage/cards/b/BalothCageTrap.java b/Mage.Sets/src/mage/cards/b/BalothCageTrap.java index f13adbffaba..98f55dca4f3 100644 --- a/Mage.Sets/src/mage/cards/b/BalothCageTrap.java +++ b/Mage.Sets/src/mage/cards/b/BalothCageTrap.java @@ -68,6 +68,6 @@ enum BalothCageTrapCondition implements Condition { @Override public String toString() { - return "If an opponent had an artifact enter the battlefield under their control this turn"; + return "an opponent had an artifact enter the battlefield under their control this turn"; } } diff --git a/Mage.Sets/src/mage/cards/b/BalthierAndFran.java b/Mage.Sets/src/mage/cards/b/BalthierAndFran.java index f3bb72fec8e..30412e58d21 100644 --- a/Mage.Sets/src/mage/cards/b/BalthierAndFran.java +++ b/Mage.Sets/src/mage/cards/b/BalthierAndFran.java @@ -36,7 +36,7 @@ public final class BalthierAndFran extends CardImpl { = new FilterCreaturePermanent(SubType.VEHICLE, "a Vehicle crewed by {this} this turn"); static { - filter.add(BalthierAndFranPredicate.instance); + filter2.add(BalthierAndFranPredicate.instance); } public BalthierAndFran(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/b/BandTogether.java b/Mage.Sets/src/mage/cards/b/BandTogether.java index cab380356ca..e7dc695688d 100644 --- a/Mage.Sets/src/mage/cards/b/BandTogether.java +++ b/Mage.Sets/src/mage/cards/b/BandTogether.java @@ -1,17 +1,13 @@ package mage.cards.b; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.TargetsDamageTargetsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.other.AnotherTargetPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.Target; import mage.target.TargetPermanent; @@ -27,19 +23,19 @@ public final class BandTogether extends CardImpl { static { filter.add(new AnotherTargetPredicate(1)); - filter2.add(new AnotherTargetPredicate(2)); + filter2.add(new AnotherTargetPredicate(3)); } public BandTogether(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}"); // Up to two target creatures you control each deal damage equal to their power to another target creature. - this.getSpellAbility().addEffect(new BandTogetherEffect()); + this.getSpellAbility().addEffect(new TargetsDamageTargetsEffect(true)); Target target = new TargetPermanent(0, 2, filter, false); target.setTargetTag(1); this.getSpellAbility().addTarget(target); target = new TargetPermanent(1, 1, filter2, false); - target.setTargetTag(2); + target.setTargetTag(3); this.getSpellAbility().addTarget(target); } @@ -52,48 +48,3 @@ public final class BandTogether extends CardImpl { return new BandTogether(this); } } - -class BandTogetherEffect extends OneShotEffect { - - BandTogetherEffect() { - super(Outcome.Benefit); - this.staticText = "Up to two target creatures you control each deal damage equal to their power to another target creature."; - } - - private BandTogetherEffect(final BandTogetherEffect effect) { - super(effect); - } - - @Override - public BandTogetherEffect copy() { - return new BandTogetherEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - if (source.getTargets().size() < 2) { - return false; - } - - Target damageTarget = source.getTargets().get(0); - Target destTarget = source.getTargets().get(1); - if (damageTarget.getTargets().isEmpty() || destTarget.getTargets().isEmpty()) { - return false; - } - - Permanent permanentDamage1 = damageTarget.getTargets().isEmpty() ? null : game.getPermanent(damageTarget.getTargets().get(0)); - Permanent permanentDamage2 = damageTarget.getTargets().size() < 2 ? null : game.getPermanent(damageTarget.getTargets().get(1)); - Permanent permanentDest = game.getPermanent(destTarget.getTargets().get(0)); - if (permanentDest == null) { - return false; - } - - if (permanentDamage1 != null) { - permanentDest.damage(permanentDamage1.getPower().getValue(), permanentDamage1.getId(), source, game, false, true); - } - if (permanentDamage2 != null) { - permanentDest.damage(permanentDamage2.getPower().getValue(), permanentDamage2.getId(), source, game, false, true); - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/b/BeamsplitterMage.java b/Mage.Sets/src/mage/cards/b/BeamsplitterMage.java index 1bdb93421cf..e1282b210d6 100644 --- a/Mage.Sets/src/mage/cards/b/BeamsplitterMage.java +++ b/Mage.Sets/src/mage/cards/b/BeamsplitterMage.java @@ -121,11 +121,11 @@ class BeamsplitterMageTriggeredAbility extends TriggeredAbilityImpl { private boolean checkNotSource(Permanent permanent, Game game) { // workaround for zcc not being set before first intervening if check - if (this.getSourceObjectZoneChangeCounter() == 0) { + if (this.getStackMomentSourceZCC() == 0) { return !permanent.getId().equals(this.getSourceId()); } return !permanent.getId().equals(this.getSourceId()) - || permanent.getZoneChangeCounter(game) != this.getSourceObjectZoneChangeCounter(); + || permanent.getZoneChangeCounter(game) != this.getStackMomentSourceZCC(); } @Override diff --git a/Mage.Sets/src/mage/cards/b/BeholdTheSinisterSix.java b/Mage.Sets/src/mage/cards/b/BeholdTheSinisterSix.java new file mode 100644 index 00000000000..a37ca39c4bb --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BeholdTheSinisterSix.java @@ -0,0 +1,91 @@ +package mage.cards.b; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterCard; +import mage.filter.common.FilterPermanentCard; +import mage.game.Game; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class BeholdTheSinisterSix extends CardImpl { + + public BeholdTheSinisterSix(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{6}{B}"); + + // Return up to six target creature cards with different names from your graveyard to the battlefield. + this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()); + this.getSpellAbility().addTarget(new BeholdTheSinisterSixTarget()); + } + + private BeholdTheSinisterSix(final BeholdTheSinisterSix card) { + super(card); + } + + @Override + public BeholdTheSinisterSix copy() { + return new BeholdTheSinisterSix(this); + } +} + +class BeholdTheSinisterSixTarget extends TargetCardInYourGraveyard { + + private static final FilterCard filter = new FilterPermanentCard("creature cards with different names"); + + BeholdTheSinisterSixTarget() { + super(0, 6, filter, false); + } + + private BeholdTheSinisterSixTarget(final BeholdTheSinisterSixTarget target) { + super(target); + } + + @Override + public BeholdTheSinisterSixTarget copy() { + return new BeholdTheSinisterSixTarget(this); + } + + @Override + public boolean canTarget(UUID playerId, UUID id, Ability ability, Game game) { + if (!super.canTarget(playerId, id, ability, game)) { + return false; + } + Set names = this.getTargets() + .stream() + .map(game::getCard) + .map(MageObject::getName) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + Card card = game.getCard(id); + return card != null && !names.contains(card.getName()); + } + + + @Override + public Set possibleTargets(UUID sourceControllerId, Ability source, Game game) { + Set possibleTargets = super.possibleTargets(sourceControllerId, source, game); + Set names = this.getTargets() + .stream() + .map(game::getCard) + .map(MageObject::getName) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + possibleTargets.removeIf(uuid -> { + Card card = game.getCard(uuid); + return card != null && names.contains(card.getName()); + }); + return possibleTargets; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BiorganicCarapace.java b/Mage.Sets/src/mage/cards/b/BiorganicCarapace.java new file mode 100644 index 00000000000..388deedc3c8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BiorganicCarapace.java @@ -0,0 +1,60 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAttachToTarget; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.ModifiedPredicate; + +import java.util.UUID; + +/** + * @author Jmlundeen + */ +public final class BiorganicCarapace extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent("each modified creature you control"); + + static { + filter.add(ModifiedPredicate.instance); + } + + public BiorganicCarapace(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{W}{U}"); + + this.subtype.add(SubType.EQUIPMENT); + + // When this Equipment enters, attach it to target creature you control. + this.addAbility(new EntersBattlefieldAttachToTarget()); + + // Equipped creature gets +2/+2 and has "Whenever this creature deals combat damage to a player, draw a card for each modified creature you control." + Ability gainedAbility = new DealsCombatDamageTriggeredAbility(new DrawCardSourceControllerEffect(new PermanentsOnBattlefieldCount(filter)), false); + Ability equipAbility = new SimpleStaticAbility(new BoostEquippedEffect(2, 2)); + equipAbility.addEffect(new GainAbilityAttachedEffect(gainedAbility, null) + .concatBy("and")); + this.addAbility(equipAbility); + + // Equip {2} + this.addAbility(new EquipAbility(2)); + } + + private BiorganicCarapace(final BiorganicCarapace card) { + super(card); + } + + @Override + public BiorganicCarapace copy() { + return new BiorganicCarapace(this); + } +} diff --git a/Mage.Sets/src/mage/cards/b/Biotransference.java b/Mage.Sets/src/mage/cards/b/Biotransference.java index 5f13df83656..7c1abf38f4e 100644 --- a/Mage.Sets/src/mage/cards/b/Biotransference.java +++ b/Mage.Sets/src/mage/cards/b/Biotransference.java @@ -88,7 +88,7 @@ class BiotransferenceEffect extends ContinuousEffectImpl { } } // in Exile - for (Card card : game.getState().getExile().getAllCards(game, source.getControllerId())) { + for (Card card : game.getState().getExile().getCardsOwned(game, source.getControllerId())) { if (card.isCreature(game) && !card.isArtifact(game)) { card.addCardType(game, CardType.ARTIFACT); } diff --git a/Mage.Sets/src/mage/cards/b/BishopOfBinding.java b/Mage.Sets/src/mage/cards/b/BishopOfBinding.java index cdfe4492213..df6167235e0 100644 --- a/Mage.Sets/src/mage/cards/b/BishopOfBinding.java +++ b/Mage.Sets/src/mage/cards/b/BishopOfBinding.java @@ -18,13 +18,11 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.SubType; -import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.game.ExileZone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; -import mage.target.common.TargetCreaturePermanent; import mage.util.CardUtil; import static mage.filter.StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE; @@ -87,7 +85,7 @@ class BishopOfBindingExileEffect extends OneShotEffect { // the target creature won't be exiled. if (permanent != null) { new ExileTargetEffect( - CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()), permanent.getIdName() + CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()), permanent.getIdName() ).apply(game, source); game.addDelayedTriggeredAbility(new OnLeaveReturnExiledAbility(), source); return true; @@ -101,7 +99,7 @@ enum BishopOfBindingValue implements DynamicValue { @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { - ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, sourceAbility.getSourceId(), sourceAbility.getSourceObjectZoneChangeCounter())); + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, sourceAbility.getSourceId(), sourceAbility.getStackMomentSourceZCC())); if (exileZone != null) { Card exiledCard = exileZone.getRandom(game); if (exiledCard != null) { diff --git a/Mage.Sets/src/mage/cards/b/BlackCatCunningThief.java b/Mage.Sets/src/mage/cards/b/BlackCatCunningThief.java new file mode 100644 index 00000000000..3560d34dc26 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BlackCatCunningThief.java @@ -0,0 +1,91 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect; +import mage.cards.*; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetOpponent; +import mage.target.targetpointer.FixedTargets; + +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * + * @author Jmlundeen + */ +public final class BlackCatCunningThief extends CardImpl { + + public BlackCatCunningThief(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ROGUE); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // When Black Cat enters, look at the top nine cards of target opponent's library, exile two of them face down, then put the rest on the bottom of their library in a random order. You may play the exiled cards for as long as they remain exiled. Mana of any type can be spent to cast spells this way. + Ability ability = new EntersBattlefieldTriggeredAbility(new BlackCatCunningThiefEffect()); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + } + + private BlackCatCunningThief(final BlackCatCunningThief card) { + super(card); + } + + @Override + public BlackCatCunningThief copy() { + return new BlackCatCunningThief(this); + } +} +class BlackCatCunningThiefEffect extends OneShotEffect { + + BlackCatCunningThiefEffect() { + super(Outcome.Benefit); + this.staticText = "look at the top nine cards of target opponent's library, exile two of them face down, then put the rest on the bottom of their library in a random order. You may play the exiled cards for as long as they remain exiled. Mana of any type can be spent to cast spells this way"; + } + + private BlackCatCunningThiefEffect(final BlackCatCunningThiefEffect effect) { + super(effect); + } + + @Override + public BlackCatCunningThiefEffect copy() { + return new BlackCatCunningThiefEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player opponent = game.getPlayer(getTargetPointer().getFirst(game, source)); + MageObject sourceObject = source.getSourceObject(game); + if (controller == null || opponent == null || sourceObject == null) { + return false; + } + Cards topCards = new CardsImpl(); + topCards.addAllCards(opponent.getLibrary().getTopCards(game, 9)); + TargetCard target = new TargetCard(2, 2, Zone.LIBRARY, new FilterCard("card to exile")); + controller.choose(outcome, topCards, target, source, game); + Cards exiledCards = new CardsImpl(target.getTargets().stream() + .map(game::getCard) + .collect(Collectors.toList())); + new ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect(false, CastManaAdjustment.AS_THOUGH_ANY_MANA_TYPE) + .setTargetPointer(new FixedTargets(exiledCards, game)) + .apply(game, source); + topCards.retainZone(Zone.LIBRARY, game); + // then put the rest on the bottom of that library in a random order + controller.putCardsOnBottomOfLibrary(topCards, game, source, false); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/b/BlasphemousEdict.java b/Mage.Sets/src/mage/cards/b/BlasphemousEdict.java index 4521e46fb75..bef73b91266 100644 --- a/Mage.Sets/src/mage/cards/b/BlasphemousEdict.java +++ b/Mage.Sets/src/mage/cards/b/BlasphemousEdict.java @@ -28,7 +28,7 @@ public final class BlasphemousEdict extends CardImpl { // You may pay {B} rather than pay this spell's mana cost if there are thirteen or more creatures on the battlefield. Ability ability = new AlternativeCostSourceAbility(new ManaCostsImpl<>("{B}"), new PermanentsOnTheBattlefieldCondition( - new FilterCreaturePermanent("If there are thirteen or more creatures on the battlefield"), + new FilterCreaturePermanent("there are thirteen or more creatures on the battlefield"), ComparisonType.OR_GREATER, 13, false diff --git a/Mage.Sets/src/mage/cards/b/BlazingEffigy.java b/Mage.Sets/src/mage/cards/b/BlazingEffigy.java index c64d4e9826f..4723a25efcc 100644 --- a/Mage.Sets/src/mage/cards/b/BlazingEffigy.java +++ b/Mage.Sets/src/mage/cards/b/BlazingEffigy.java @@ -59,7 +59,7 @@ enum BlazingEffigyCount implements DynamicValue { if (watcher == null) { return 3; } - int effigyDamage = watcher.damageDoneTo(sourceAbility.getSourceId(), sourceAbility.getSourceObjectZoneChangeCounter() - 1, game); + int effigyDamage = watcher.damageDoneTo(sourceAbility.getSourceId(), sourceAbility.getStackMomentSourceZCC() - 1, game); return CardUtil.overflowInc(3, effigyDamage); } diff --git a/Mage.Sets/src/mage/cards/b/Blight.java b/Mage.Sets/src/mage/cards/b/Blight.java index f38512ea5b8..afb7d151ab3 100644 --- a/Mage.Sets/src/mage/cards/b/Blight.java +++ b/Mage.Sets/src/mage/cards/b/Blight.java @@ -1,4 +1,3 @@ - package mage.cards.b; import java.util.UUID; @@ -34,7 +33,9 @@ public final class Blight extends CardImpl { this.addAbility(ability); // When enchanted land becomes tapped, destroy it. - this.addAbility(new BecomesTappedAttachedTriggeredAbility(new DestroyAttachedToEffect("it"), "enchanted land")); + this.addAbility(new BecomesTappedAttachedTriggeredAbility( + new DestroyAttachedToEffect("it"), "enchanted land" + ).setTriggerPhrase("When enchanted land becomes tapped, ")); } private Blight(final Blight card) { diff --git a/Mage.Sets/src/mage/cards/b/BogardanPhoenix.java b/Mage.Sets/src/mage/cards/b/BogardanPhoenix.java index 4e7eb4308e6..7bd42f29c23 100644 --- a/Mage.Sets/src/mage/cards/b/BogardanPhoenix.java +++ b/Mage.Sets/src/mage/cards/b/BogardanPhoenix.java @@ -74,11 +74,11 @@ class BogardanPhoenixEffect extends OneShotEffect { if (permanent == null || controller == null || permanent.getZoneChangeCounter(game) + 1 - != source.getSourceObjectZoneChangeCounter()) { + != source.getStackMomentSourceZCC()) { return false; } Card card = game.getCard(permanent.getId()); - if (card == null || card.getZoneChangeCounter(game) != source.getSourceObjectZoneChangeCounter()) { + if (card == null || card.getZoneChangeCounter(game) != source.getStackMomentSourceZCC()) { return false; } if (permanent.getCounters(game).containsKey(CounterType.DEATH)) { diff --git a/Mage.Sets/src/mage/cards/b/BrassKnuckles.java b/Mage.Sets/src/mage/cards/b/BrassKnuckles.java index 830e18e7cf1..6faa50a2209 100644 --- a/Mage.Sets/src/mage/cards/b/BrassKnuckles.java +++ b/Mage.Sets/src/mage/cards/b/BrassKnuckles.java @@ -1,8 +1,8 @@ package mage.cards.b; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.CastSourceTriggeredAbility; import mage.abilities.effects.common.CopySourceSpellEffect; @@ -13,20 +13,25 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.AttachmentType; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.SubType; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.filter.FilterPermanent; +import mage.filter.predicate.permanent.AttachedToAttachedPredicate; -import java.util.Collection; -import java.util.Objects; -import java.util.Optional; import java.util.UUID; -import java.util.stream.Stream; /** * @author TheElk801 */ public final class BrassKnuckles extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent("Equipment attached to it"); + + static { + filter.add(SubType.EQUIPMENT.getPredicate()); + filter.add(AttachedToAttachedPredicate.instance); + } + + private static final Condition twoEquipmentAttachedToAttached = new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.OR_GREATER, 2); public BrassKnuckles(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); @@ -40,7 +45,7 @@ public final class BrassKnuckles extends CardImpl { this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( new GainAbilityAttachedEffect( DoubleStrikeAbility.getInstance(), AttachmentType.EQUIPMENT - ), BrassKnucklesCondition.instance, "equipped creature has double strike " + + ), twoEquipmentAttachedToAttached, "equipped creature has double strike " + "as long as two or more Equipment are attached to it" ))); @@ -57,23 +62,3 @@ public final class BrassKnuckles extends CardImpl { return new BrassKnuckles(this); } } - -enum BrassKnucklesCondition implements Condition { - instance; - - @Override - public boolean apply(Game game, Ability source) { - return Optional - .ofNullable(source.getSourcePermanentIfItStillExists(game)) - .map(Permanent::getAttachedTo) - .map(game::getPermanent) - .map(Permanent::getAttachments) - .map(Collection::stream) - .map(stream -> stream.map(game::getPermanent)) - .map(stream -> stream.filter(Objects::nonNull)) - .map(stream -> stream.filter(p -> p.hasSubtype(SubType.EQUIPMENT, game))) - .map(Stream::count) - .map(x -> x >= 2) - .orElse(false); - } -} diff --git a/Mage.Sets/src/mage/cards/c/CallToTheGrave.java b/Mage.Sets/src/mage/cards/c/CallToTheGrave.java index 42dbde627e9..ac3fcd5af06 100644 --- a/Mage.Sets/src/mage/cards/c/CallToTheGrave.java +++ b/Mage.Sets/src/mage/cards/c/CallToTheGrave.java @@ -9,6 +9,7 @@ import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.common.FilterCreaturePermanent; @@ -28,7 +29,7 @@ public final class CallToTheGrave extends CardImpl { } private static final Condition condition = new PermanentsOnTheBattlefieldCondition( - new FilterCreaturePermanent("no creatures are on the battlefield"), false + new FilterCreaturePermanent("no creatures are on the battlefield"), ComparisonType.EQUAL_TO, 0, false ); public CallToTheGrave(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/c/CallousBloodmage.java b/Mage.Sets/src/mage/cards/c/CallousBloodmage.java index 9459c1ee304..3a71cc6cd02 100644 --- a/Mage.Sets/src/mage/cards/c/CallousBloodmage.java +++ b/Mage.Sets/src/mage/cards/c/CallousBloodmage.java @@ -40,7 +40,7 @@ public final class CallousBloodmage extends CardImpl { ability.addMode(mode); // • Exile target player's graveyard. - mode = new Mode(new ExileGraveyardAllTargetPlayerEffect().setText("exile target player's graveyard")); + mode = new Mode(new ExileGraveyardAllTargetPlayerEffect()); mode.addTarget(new TargetPlayer()); ability.addMode(mode); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/c/CandlekeepInspiration.java b/Mage.Sets/src/mage/cards/c/CandlekeepInspiration.java index 88514dd0d89..6fedd940d9e 100644 --- a/Mage.Sets/src/mage/cards/c/CandlekeepInspiration.java +++ b/Mage.Sets/src/mage/cards/c/CandlekeepInspiration.java @@ -65,7 +65,7 @@ enum CandlekeepInspirationValue implements DynamicValue { .getCards(game) .stream(), game.getExile() - .getAllCards(game, sourceAbility.getControllerId()) + .getCardsOwned(game, sourceAbility.getControllerId()) .stream() ) .filter(Objects::nonNull) diff --git a/Mage.Sets/src/mage/cards/c/CarnageCrimsonChaos.java b/Mage.Sets/src/mage/cards/c/CarnageCrimsonChaos.java new file mode 100644 index 00000000000..12860badb5b --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CarnageCrimsonChaos.java @@ -0,0 +1,116 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksEachCombatStaticAbility; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.MayhemAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class CarnageCrimsonChaos extends CardImpl { + + private static final FilterCard filter = new FilterCard("creature card with mana value 3 or less"); + + static { + filter.add(new ManaValuePredicate(ComparisonType.OR_LESS, 3)); + } + + public CarnageCrimsonChaos(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SYMBIOTE); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // When Carnage enters, return target creature card with mana value 3 or less from your graveyard to the battlefield. It gains "This creature attacks each combat if able" and "When this creature deals combat damage to a player, sacrifice it." + Ability ability = new EntersBattlefieldTriggeredAbility(new CarnageCrimsonChaosReturnEffect()); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + + + // Mayhem {B}{R} + this.addAbility(new MayhemAbility(this, "{B}{R}")); + + } + + private CarnageCrimsonChaos(final CarnageCrimsonChaos card) { + super(card); + } + + @Override + public CarnageCrimsonChaos copy() { + return new CarnageCrimsonChaos(this); + } +} + +class CarnageCrimsonChaosReturnEffect extends OneShotEffect { + + CarnageCrimsonChaosReturnEffect() { + super(Outcome.PutCreatureInPlay); + this.staticText = "return target creature card with mana value 3 or less from your graveyard to the battlefield. It gains \"This creature attacks each combat if able\" and \"When this creature deals combat damage to a player, sacrifice it.\""; + } + + protected CarnageCrimsonChaosReturnEffect(final CarnageCrimsonChaosReturnEffect effect) { + super(effect); + } + + @Override + public CarnageCrimsonChaosReturnEffect copy() { + return new CarnageCrimsonChaosReturnEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + + Card card = game.getCard(source.getFirstTarget()); + if (card == null) { + return false; + } + controller.moveCards(card, Zone.BATTLEFIELD, source, game); + Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game); + if (permanent == null) { + return false; + } + Ability attacksEachTurnAbility = new AttacksEachCombatStaticAbility(); + Ability damageTriggerAbility = new DealsCombatDamageToAPlayerTriggeredAbility(new SacrificeSourceEffect()); + ContinuousEffect effectOne = new GainAbilityTargetEffect(attacksEachTurnAbility, Duration.WhileOnBattlefield); + effectOne.setTargetPointer(new FixedTarget(permanent, game)); + ContinuousEffect effectTwo = new GainAbilityTargetEffect(damageTriggerAbility, Duration.WhileOnBattlefield); + effectTwo.setTargetPointer(new FixedTarget(permanent, game)); + game.addEffect(effectOne, source); + game.addEffect(effectTwo, source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/c/CelestialDawn.java b/Mage.Sets/src/mage/cards/c/CelestialDawn.java index 397cf682d29..a7a7db29db7 100644 --- a/Mage.Sets/src/mage/cards/c/CelestialDawn.java +++ b/Mage.Sets/src/mage/cards/c/CelestialDawn.java @@ -63,6 +63,8 @@ class CelestialDawnToPlainsEffect extends ContinuousEffectImpl { CelestialDawnToPlainsEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); this.staticText = "Lands you control are Plains"; + this.dependendToTypes.add(DependencyType.BecomeNonbasicLand); + this.dependencyTypes.add(DependencyType.BecomePlains); } private CelestialDawnToPlainsEffect(final CelestialDawnToPlainsEffect effect) { @@ -126,7 +128,7 @@ class CelestialDawnToWhiteEffect extends ContinuousEffectImpl { } } // Exile - for (Card card : game.getExile().getAllCards(game)) { + for (Card card : game.getExile().getCardsInRange(game, controller.getId())) { if (card.isOwnedBy(controller.getId())) { setColor(card.getColor(game), game); } diff --git a/Mage.Sets/src/mage/cards/c/CemeteryGatekeeper.java b/Mage.Sets/src/mage/cards/c/CemeteryGatekeeper.java index cfc09b66f3c..9ec04a37b8b 100644 --- a/Mage.Sets/src/mage/cards/c/CemeteryGatekeeper.java +++ b/Mage.Sets/src/mage/cards/c/CemeteryGatekeeper.java @@ -86,7 +86,7 @@ class CemeteryGatekeeperEffect extends OneShotEffect { controller.choose(outcome, target, source, game); Card card = game.getCard(target.getFirstTarget()); if (card != null) { - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); MageObject sourceObject = source.getSourceObject(game); String exileName = sourceObject == null ? null : sourceObject.getIdName(); return controller.moveCardsToExile(card, source, game, true, exileId, exileName); diff --git a/Mage.Sets/src/mage/cards/c/CemeteryIlluminator.java b/Mage.Sets/src/mage/cards/c/CemeteryIlluminator.java index 31ffac3f823..550529caeb5 100644 --- a/Mage.Sets/src/mage/cards/c/CemeteryIlluminator.java +++ b/Mage.Sets/src/mage/cards/c/CemeteryIlluminator.java @@ -92,7 +92,7 @@ class CemeteryIlluminatorExileEffect extends OneShotEffect { controller.choose(outcome, target, source, game); Card card = game.getCard(target.getFirstTarget()); if (card != null) { - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); MageObject sourceObject = source.getSourceObject(game); String exileName = sourceObject == null ? null : sourceObject.getIdName(); return controller.moveCardsToExile(card, source, game, true, exileId, exileName); diff --git a/Mage.Sets/src/mage/cards/c/CemeteryProtector.java b/Mage.Sets/src/mage/cards/c/CemeteryProtector.java index 199dd0cf567..8c7cb4a13d1 100644 --- a/Mage.Sets/src/mage/cards/c/CemeteryProtector.java +++ b/Mage.Sets/src/mage/cards/c/CemeteryProtector.java @@ -87,7 +87,7 @@ class CemeteryProtectorEffect extends OneShotEffect { controller.choose(outcome, target, source, game); Card card = game.getCard(target.getFirstTarget()); if (card != null) { - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); MageObject sourceObject = source.getSourceObject(game); String exileName = sourceObject == null ? null : sourceObject.getIdName(); return controller.moveCardsToExile(card, source, game, true, exileId, exileName); diff --git a/Mage.Sets/src/mage/cards/c/CemeteryProwler.java b/Mage.Sets/src/mage/cards/c/CemeteryProwler.java index 835e90b1c21..5df6f0900e6 100644 --- a/Mage.Sets/src/mage/cards/c/CemeteryProwler.java +++ b/Mage.Sets/src/mage/cards/c/CemeteryProwler.java @@ -79,7 +79,7 @@ class CemeteryProwlerExileEffect extends OneShotEffect { controller.choose(outcome, target, source, game); Card card = game.getCard(target.getFirstTarget()); if (card != null) { - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); MageObject sourceObject = source.getSourceObject(game); String exileName = sourceObject == null ? null : sourceObject.getIdName(); return controller.moveCardsToExile(card, source, game, true, exileId, exileName); diff --git a/Mage.Sets/src/mage/cards/c/ChainerNightmareAdept.java b/Mage.Sets/src/mage/cards/c/ChainerNightmareAdept.java index 95111180226..9a31aa06474 100644 --- a/Mage.Sets/src/mage/cards/c/ChainerNightmareAdept.java +++ b/Mage.Sets/src/mage/cards/c/ChainerNightmareAdept.java @@ -141,14 +141,14 @@ class ChainerNightmareAdeptWatcher extends Watcher { return false; } MageObjectReference mor = new MageObjectReference( - source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game + source.getSourceId(), source.getStackMomentSourceZCC(), game ); return morMap.computeIfAbsent(mor, m -> new HashMap<>()).getOrDefault(playerId, 0) > 0; } void addPlayable(Ability source, Game game) { MageObjectReference mor = new MageObjectReference( - source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game + source.getSourceId(), source.getStackMomentSourceZCC(), game ); morMap.computeIfAbsent(mor, m -> new HashMap<>()) .compute(source.getControllerId(), CardUtil::setOrIncrementValue); diff --git a/Mage.Sets/src/mage/cards/c/ChameleonMasterOfDisguise.java b/Mage.Sets/src/mage/cards/c/ChameleonMasterOfDisguise.java new file mode 100644 index 00000000000..e4aa44f5842 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChameleonMasterOfDisguise.java @@ -0,0 +1,65 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.effects.common.CopyPermanentEffect; +import mage.abilities.keyword.MayhemAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.util.functions.CopyApplier; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ChameleonMasterOfDisguise extends CardImpl { + + private static final CopyApplier applier = new CopyApplier() { + @Override + public boolean apply(Game game, MageObject blueprint, Ability source, UUID copyToObjectId) { + blueprint.setName("Chameleon, Master of Disguise"); + return true; + } + + @Override + public String getText() { + return ", except his name is Chameleon, Master of Disguise"; + } + }; + + public ChameleonMasterOfDisguise(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SHAPESHIFTER); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // You may have Chameleon enter as a copy of a creature you control, except his name is Chameleon, Master of Disguise. + this.addAbility(new EntersBattlefieldAbility(new CopyPermanentEffect( + StaticFilters.FILTER_CONTROLLED_CREATURE, applier + ), true)); + + // Mayhem {2}{U} + this.addAbility(new MayhemAbility(this, "{2}{U}")); + } + + private ChameleonMasterOfDisguise(final ChameleonMasterOfDisguise card) { + super(card); + } + + @Override + public ChameleonMasterOfDisguise copy() { + return new ChameleonMasterOfDisguise(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ChandraDressedToKill.java b/Mage.Sets/src/mage/cards/c/ChandraDressedToKill.java index ed2fd8e518e..a80ea00f467 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraDressedToKill.java +++ b/Mage.Sets/src/mage/cards/c/ChandraDressedToKill.java @@ -97,7 +97,7 @@ class ChandraDressedToKillExile1Effect extends OneShotEffect { if (card == null) { return false; } - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); MageObject sourceObject = source.getSourceObject(game); String exileName = sourceObject == null ? null : sourceObject.getIdName(); controller.moveCardsToExile(card, source, game, true, exileId, exileName); @@ -135,7 +135,7 @@ class ChandraDressedToKillExile5Effect extends OneShotEffect { if (cards.isEmpty()) { return false; } - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); MageObject sourceObject = source.getSourceObject(game); String exileName = sourceObject == null ? null : sourceObject.getIdName(); controller.moveCardsToExile(cards, source, game, true, exileId, exileName); diff --git a/Mage.Sets/src/mage/cards/c/CheeringCrowd.java b/Mage.Sets/src/mage/cards/c/CheeringCrowd.java new file mode 100644 index 00000000000..efeb7790ea7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CheeringCrowd.java @@ -0,0 +1,100 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.costs.common.PutCountersSourceCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.triggers.BeginningOfFirstMainTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class CheeringCrowd extends CardImpl { + + public CheeringCrowd(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R/G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CITIZEN); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // At the beginning of each player's first main phase, that player may put a +1/+1 counter on this creature. If they do, they add {C} for each counter on it. + this.addAbility(new BeginningOfFirstMainTriggeredAbility(TargetController.EACH_PLAYER, new CheeringCrowdDoIfPaidEffect(), false)); + } + + private CheeringCrowd(final CheeringCrowd card) { + super(card); + } + + @Override + public CheeringCrowd copy() { + return new CheeringCrowd(this); + } +} +class CheeringCrowdDoIfPaidEffect extends DoIfCostPaid { + + CheeringCrowdDoIfPaidEffect() { + super(new CheeringCrowdEffect(), new PutCountersSourceCost(CounterType.P1P1.createInstance()), + "Put a +1/+1 counter on Cheering Crowd and add {C} for each counter on it?", true); + staticText = "that player may put a +1/+1 counter on this creature. If they do, they add {C} for each counter on it"; + } + + private CheeringCrowdDoIfPaidEffect(final CheeringCrowdDoIfPaidEffect effect) { + super(effect); + } + + @Override + public CheeringCrowdDoIfPaidEffect copy() { + return new CheeringCrowdDoIfPaidEffect(this); + } + + @Override + protected Player getPayingPlayer(Game game, Ability source) { + return game.getPlayer(getTargetPointer().getFirst(game, source)); + } +} + +class CheeringCrowdEffect extends OneShotEffect { + + public CheeringCrowdEffect() { + super(Outcome.PutManaInPool); + staticText = "they add {C} for each counter on it"; + } + + protected CheeringCrowdEffect(final CheeringCrowdEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); + Permanent permanent = source.getSourcePermanentOrLKI(game); + if (player == null || permanent == null) { + return false; + } + int counterCounter = permanent.getCounters(game).getCount(CounterType.P1P1); + player.getManaPool().addMana(Mana.ColorlessMana(counterCounter), game, source); + return true; + } + + @Override + public CheeringCrowdEffect copy() { + return new CheeringCrowdEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ChissGoriaForgeTyrant.java b/Mage.Sets/src/mage/cards/c/ChissGoriaForgeTyrant.java index 6eca6368d9e..c66f3b52b4c 100644 --- a/Mage.Sets/src/mage/cards/c/ChissGoriaForgeTyrant.java +++ b/Mage.Sets/src/mage/cards/c/ChissGoriaForgeTyrant.java @@ -164,7 +164,7 @@ class ChissGoriaForgeTyrantAffinityEffect extends ContinuousEffectImpl { return false; } - for (Card card : game.getExile().getAllCardsByRange(game, source.getControllerId())) { + for (Card card : game.getExile().getCardsInRange(game, source.getControllerId())) { if (morSet.contains(new MageObjectReference(card, game)) && card.isArtifact(game)) { game.getState().addOtherAbility(card, new AffinityForArtifactsAbility()); } diff --git a/Mage.Sets/src/mage/cards/c/ChoArrimLegate.java b/Mage.Sets/src/mage/cards/c/ChoArrimLegate.java index 083156338f3..8d2278f2383 100644 --- a/Mage.Sets/src/mage/cards/c/ChoArrimLegate.java +++ b/Mage.Sets/src/mage/cards/c/ChoArrimLegate.java @@ -41,7 +41,7 @@ public final class ChoArrimLegate extends CardImpl { this.addAbility(ProtectionAbility.from(ObjectColor.BLACK)); // If an opponent controls a Swamp and you control a Plains, you may cast this spell without paying its mana cost. - Condition condition = new CompoundCondition("If an opponent controls a Swamp and you control a Plains", + Condition condition = new CompoundCondition("an opponent controls a Swamp and you control a Plains", new OpponentControlsPermanentCondition(filterSwamp), new PermanentsOnTheBattlefieldCondition(filterPlains)); this.addAbility(new AlternativeCostSourceAbility(null, condition)); diff --git a/Mage.Sets/src/mage/cards/c/ChromaticLantern.java b/Mage.Sets/src/mage/cards/c/ChromaticLantern.java index db50ca986ef..1a8a7a561d2 100644 --- a/Mage.Sets/src/mage/cards/c/ChromaticLantern.java +++ b/Mage.Sets/src/mage/cards/c/ChromaticLantern.java @@ -3,13 +3,14 @@ package mage.cards.c; import java.util.UUID; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.mana.AnyColorManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.DependencyType; import mage.constants.Duration; -import mage.constants.Zone; import mage.filter.StaticFilters; /** @@ -21,7 +22,10 @@ public final class ChromaticLantern extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); // Lands you control have "{T}: Add one mana of any color." - this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect(new AnyColorManaAbility(), Duration.WhileOnBattlefield, StaticFilters.FILTER_LANDS, false))); + ContinuousEffect effect = new GainAbilityControlledEffect(new AnyColorManaAbility(), Duration.WhileOnBattlefield, StaticFilters.FILTER_LANDS, false); + effect.getDependedToTypes().add(DependencyType.BecomeNonbasicLand); + this.addAbility(new SimpleStaticAbility(effect)); + // {T}: Add one mana of any color. this.addAbility(new AnyColorManaAbility()); diff --git a/Mage.Sets/src/mage/cards/c/CityPigeon.java b/Mage.Sets/src/mage/cards/c/CityPigeon.java new file mode 100644 index 00000000000..ad9305069b4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CityPigeon.java @@ -0,0 +1,42 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.FoodToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CityPigeon extends CardImpl { + + public CityPigeon(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}"); + + this.subtype.add(SubType.BIRD); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When this creature leaves the battlefield, create a Food token. + this.addAbility(new LeavesBattlefieldTriggeredAbility(new CreateTokenEffect(new FoodToken()))); + } + + private CityPigeon(final CityPigeon card) { + super(card); + } + + @Override + public CityPigeon copy() { + return new CityPigeon(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/ClockworkBeetle.java b/Mage.Sets/src/mage/cards/c/ClockworkBeetle.java index e513d181cb4..54c08ad4fb0 100644 --- a/Mage.Sets/src/mage/cards/c/ClockworkBeetle.java +++ b/Mage.Sets/src/mage/cards/c/ClockworkBeetle.java @@ -66,7 +66,7 @@ class ClockworkBeetleEffect extends OneShotEffect { Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null) { Effect effect = new RemoveCounterTargetEffect(CounterType.P1P1.createInstance()); - effect.setTargetPointer(new FixedTarget(source.getSourceId(), source.getSourceObjectZoneChangeCounter())); + effect.setTargetPointer(new FixedTarget(source.getSourceId(), source.getStackMomentSourceZCC())); game.addDelayedTriggeredAbility(new AtTheEndOfCombatDelayedTriggeredAbility(effect), source); return true; } diff --git a/Mage.Sets/src/mage/cards/c/CobraTrap.java b/Mage.Sets/src/mage/cards/c/CobraTrap.java index 99cd7e9cd7f..7994947e6e4 100644 --- a/Mage.Sets/src/mage/cards/c/CobraTrap.java +++ b/Mage.Sets/src/mage/cards/c/CobraTrap.java @@ -60,7 +60,7 @@ enum CobraTrapCondition implements Condition { @Override public String toString() { - return "If a noncreature permanent under your control was destroyed this turn by a spell or ability an opponent controlled"; + return "a noncreature permanent under your control was destroyed this turn by a spell or ability an opponent controlled"; } } diff --git a/Mage.Sets/src/mage/cards/c/ComboAttack.java b/Mage.Sets/src/mage/cards/c/ComboAttack.java index 8acc59d0cf4..ba36b21c8db 100644 --- a/Mage.Sets/src/mage/cards/c/ComboAttack.java +++ b/Mage.Sets/src/mage/cards/c/ComboAttack.java @@ -1,15 +1,11 @@ package mage.cards.c; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.TargetsDamageTargetsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.filter.FilterPermanent; import mage.filter.common.FilterTeamCreaturePermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -26,9 +22,9 @@ public final class ComboAttack extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{G}"); // Two target creatures your team controls each deal damage equal to their power to target creature. - this.getSpellAbility().addEffect(new ComboAttackEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(2, filter)); - this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addEffect(new TargetsDamageTargetsEffect(true)); + this.getSpellAbility().addTarget(new TargetPermanent(2, filter).setTargetTag(1)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent().setTargetTag(3)); } private ComboAttack(final ComboAttack card) { @@ -40,41 +36,3 @@ public final class ComboAttack extends CardImpl { return new ComboAttack(this); } } - -class ComboAttackEffect extends OneShotEffect { - - ComboAttackEffect() { - super(Outcome.Benefit); - this.staticText = "Two target creatures your team controls each deal damage equal to their power to target creature"; - } - - private ComboAttackEffect(final ComboAttackEffect effect) { - super(effect); - } - - @Override - public ComboAttackEffect copy() { - return new ComboAttackEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - if (source.getTargets().size() < 2) { - return false; - } - Permanent permanent3 = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if (permanent3 == null) { - return false; - } - // You can’t cast Combo Attack without targeting two creatures your team controls. - // If one of those creatures is an illegal target as Combo Attack resolves, - // the other will still deal damage equal to its power. (2018-06-08) - for (UUID id : source.getTargets().get(0).getTargets()) { - Permanent permanent = game.getPermanent(id); - if (permanent != null) { - permanent3.damage(permanent.getPower().getValue(), permanent.getId(), source, game); - } - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/c/CommonCrook.java b/Mage.Sets/src/mage/cards/c/CommonCrook.java new file mode 100644 index 00000000000..25d18e58d38 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CommonCrook.java @@ -0,0 +1,40 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.TreasureToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CommonCrook extends CardImpl { + + public CommonCrook(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ROGUE); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // When this creature dies, create a Treasure token. + this.addAbility(new DiesSourceTriggeredAbility(new CreateTokenEffect(new TreasureToken()))); + } + + private CommonCrook(final CommonCrook card) { + super(card); + } + + @Override + public CommonCrook copy() { + return new CommonCrook(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CompleteTheCircuit.java b/Mage.Sets/src/mage/cards/c/CompleteTheCircuit.java index 9d7ec3b3735..4669ae89e0d 100644 --- a/Mage.Sets/src/mage/cards/c/CompleteTheCircuit.java +++ b/Mage.Sets/src/mage/cards/c/CompleteTheCircuit.java @@ -1,8 +1,7 @@ package mage.cards.c; -import mage.abilities.Ability; -import mage.abilities.common.delayed.CopyNextSpellDelayedTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.common.delayed.CastNextSpellDelayedTriggeredAbility; +import mage.abilities.effects.common.CopyTargetStackObjectEffect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.continuous.CastAsThoughItHadFlashAllEffect; import mage.abilities.keyword.ConvokeAbility; @@ -10,11 +9,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.filter.FilterCard; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.stack.Spell; import java.util.UUID; @@ -40,10 +36,9 @@ public final class CompleteTheCircuit extends CardImpl { // When you next cast an instant or sorcery spell this turn, copy that spell twice. You may choose new targets for the copies. this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect( - new CopyNextSpellDelayedTriggeredAbility( - StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, - new CompleteTheCircuitEffect(), "When you next cast an instant or sorcery spell " + - "this turn, copy that spell twice. You may choose new targets for the copies." + new CastNextSpellDelayedTriggeredAbility( + new CopyTargetStackObjectEffect(false, true, true, 2, null), + StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, true ) ).concatBy("
")); } @@ -57,29 +52,3 @@ public final class CompleteTheCircuit extends CardImpl { return new CompleteTheCircuit(this); } } - -class CompleteTheCircuitEffect extends OneShotEffect { - - CompleteTheCircuitEffect() { - super(Outcome.Benefit); - } - - private CompleteTheCircuitEffect(final CompleteTheCircuitEffect effect) { - super(effect); - } - - @Override - public CompleteTheCircuitEffect copy() { - return new CompleteTheCircuitEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Spell spell = (Spell) getValue("spellCast"); - if (spell != null) { - spell.createCopyOnStack(game, source, source.getControllerId(), true, 2); - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/c/ConsignToMemory.java b/Mage.Sets/src/mage/cards/c/ConsignToMemory.java index 0e28b8b29a2..eb8e853108a 100644 --- a/Mage.Sets/src/mage/cards/c/ConsignToMemory.java +++ b/Mage.Sets/src/mage/cards/c/ConsignToMemory.java @@ -6,11 +6,10 @@ import mage.abilities.keyword.ReplicateAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.FilterSpell; import mage.filter.FilterStackObject; import mage.filter.predicate.Predicate; -import mage.filter.predicate.mageobject.ColorlessPredicate; import mage.game.Game; +import mage.game.stack.Spell; import mage.game.stack.StackObject; import mage.target.TargetStackObject; @@ -31,7 +30,6 @@ public final class ConsignToMemory extends CardImpl { public ConsignToMemory(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}"); - // Replicate {1} this.addAbility(new ReplicateAbility("{1}")); @@ -53,18 +51,11 @@ public final class ConsignToMemory extends CardImpl { enum ConsignToMemoryPredicate implements Predicate { instance; - private static final FilterSpell filterSpell = new FilterSpell("colorless spell"); - - static { - filterSpell.add(ColorlessPredicate.instance); - } - @Override public boolean apply(StackObject input, Game game) { - if (input instanceof Ability) { - Ability ability = (Ability) input; - return ability.getAbilityType().isTriggeredAbility(); + if (input instanceof Spell) { + return input.getColor(game).isColorless(); } - return filterSpell.match(input, game); + return input instanceof Ability && ((Ability) input).isTriggeredAbility(); } } diff --git a/Mage.Sets/src/mage/cards/c/Conspiracy.java b/Mage.Sets/src/mage/cards/c/Conspiracy.java index c05affa5d3b..f7577401d22 100644 --- a/Mage.Sets/src/mage/cards/c/Conspiracy.java +++ b/Mage.Sets/src/mage/cards/c/Conspiracy.java @@ -92,14 +92,14 @@ public final class Conspiracy extends CardImpl { } } // in Exile - for (Card card : game.getState().getExile().getAllCards(game)) { - if (card.isOwnedBy(controller.getId()) && card.isCreature(game)) { + for (Card card : game.getState().getExile().getCardsOwned(game, controller.getId())) { + if (card.isCreature(game)) { setCreatureSubtype(card, subType, game); } } // in Library (e.g. for Mystical Teachings) for (Card card : controller.getLibrary().getCards(game)) { - if (card.isOwnedBy(controller.getId()) && card.isCreature(game)) { + if (card.isCreature(game)) { setCreatureSubtype(card, subType, game); } } diff --git a/Mage.Sets/src/mage/cards/c/Conversion.java b/Mage.Sets/src/mage/cards/c/Conversion.java index 73afd85c63a..41cedbc0a28 100644 --- a/Mage.Sets/src/mage/cards/c/Conversion.java +++ b/Mage.Sets/src/mage/cards/c/Conversion.java @@ -54,6 +54,12 @@ public final class Conversion extends CardImpl { ConversionEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); this.staticText = "All Mountains are Plains"; + this.dependendToTypes.add(DependencyType.BecomeForest); + this.dependendToTypes.add(DependencyType.BecomeIsland); + this.dependendToTypes.add(DependencyType.BecomeMountain); + this.dependendToTypes.add(DependencyType.BecomePlains); + this.dependendToTypes.add(DependencyType.BecomeSwamp); + this.dependencyTypes.add(DependencyType.BecomePlains); } private ConversionEffect(final ConversionEffect effect) { diff --git a/Mage.Sets/src/mage/cards/c/CoordinatedClobbering.java b/Mage.Sets/src/mage/cards/c/CoordinatedClobbering.java index 01b66f18355..21e6436de84 100644 --- a/Mage.Sets/src/mage/cards/c/CoordinatedClobbering.java +++ b/Mage.Sets/src/mage/cards/c/CoordinatedClobbering.java @@ -1,21 +1,15 @@ package mage.cards.c; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.TargetsDamageTargetsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.common.TargetOpponentsCreaturePermanent; -import java.util.List; -import java.util.Objects; import java.util.UUID; -import java.util.stream.Collectors; /** * @author TheElk801 @@ -26,11 +20,12 @@ public final class CoordinatedClobbering extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{G}"); // Tap one or two target untapped creatures you control. They each deal damage equal to their power to target creature an opponent controls. - this.getSpellAbility().addEffect(new CoordinatedClobberingEffect()); + this.getSpellAbility().addEffect(new TapTargetEffect()); + this.getSpellAbility().addEffect(new TargetsDamageTargetsEffect(false)); this.getSpellAbility().addTarget(new TargetPermanent( 1, 2, StaticFilters.FILTER_CONTROLLED_UNTAPPED_CREATURES - )); - this.getSpellAbility().addTarget(new TargetOpponentsCreaturePermanent()); + ).setTargetTag(1)); + this.getSpellAbility().addTarget(new TargetOpponentsCreaturePermanent().setTargetTag(3)); } private CoordinatedClobbering(final CoordinatedClobbering card) { @@ -42,48 +37,4 @@ public final class CoordinatedClobbering extends CardImpl { return new CoordinatedClobbering(this); } } - -class CoordinatedClobberingEffect extends OneShotEffect { - - CoordinatedClobberingEffect() { - super(Outcome.Benefit); - staticText = "tap one or two target untapped creatures you control. " + - "They each deal damage equal to their power to target creature an opponent controls"; - } - - private CoordinatedClobberingEffect(final CoordinatedClobberingEffect effect) { - super(effect); - } - - @Override - public CoordinatedClobberingEffect copy() { - return new CoordinatedClobberingEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - List permanents = this - .getTargetPointer() - .getTargets(game, source) - .stream() - .map(game::getPermanent) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - if (permanents.isEmpty()) { - return false; - } - for (Permanent permanent : permanents) { - permanent.tap(source, game); - } - Permanent targetPermanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if (targetPermanent == null) { - return true; - } - game.processAction(); - for (Permanent permanent : permanents) { - targetPermanent.damage(permanent.getPower().getValue(), permanent.getId(), source, game); - } - return true; - } -} // flame on! diff --git a/Mage.Sets/src/mage/cards/c/CorrosiveOoze.java b/Mage.Sets/src/mage/cards/c/CorrosiveOoze.java index 2979160128e..d06923b0a46 100644 --- a/Mage.Sets/src/mage/cards/c/CorrosiveOoze.java +++ b/Mage.Sets/src/mage/cards/c/CorrosiveOoze.java @@ -79,7 +79,7 @@ class CorrosiveOozeEffect extends OneShotEffect { if (watcher == null) { return false; } - MageObjectReference sourceMor = new MageObjectReference(source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game); + MageObjectReference sourceMor = new MageObjectReference(source.getSourceId(), source.getStackMomentSourceZCC(), game); List equipments = watcher.getEquipmentsToDestroy(sourceMor) .stream() .map(mor -> mor.getPermanent(game)) diff --git a/Mage.Sets/src/mage/cards/c/CosmicSpiderMan.java b/Mage.Sets/src/mage/cards/c/CosmicSpiderMan.java new file mode 100644 index 00000000000..dcddeabb67a --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CosmicSpiderMan.java @@ -0,0 +1,77 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.*; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CosmicSpiderMan extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.SPIDER, "Spiders"); + + public CosmicSpiderMan(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{U}{B}{R}{G}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIDER); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.HERO); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // At the beginning of combat on your turn, other Spiders you control gain flying, first strike, trample, lifelink, and haste until end of turn. + Ability ability = new BeginningOfCombatTriggeredAbility(new GainAbilityControlledEffect( + FlyingAbility.getInstance(), Duration.EndOfTurn, filter, true + ).setText("other Spiders you control gain flying")); + ability.addEffect(new GainAbilityControlledEffect( + FirstStrikeAbility.getInstance(), Duration.EndOfTurn, filter, true + ).setText(", first strike")); + ability.addEffect(new GainAbilityControlledEffect( + TrampleAbility.getInstance(), Duration.EndOfTurn, filter, true + ).setText(", trample")); + ability.addEffect(new GainAbilityControlledEffect( + LifelinkAbility.getInstance(), Duration.EndOfTurn, filter, true + ).setText(", lifelink")); + ability.addEffect(new GainAbilityControlledEffect( + HasteAbility.getInstance(), Duration.EndOfTurn, filter, true + ).setText(", and haste until end of turn")); + this.addAbility(ability); + } + + private CosmicSpiderMan(final CosmicSpiderMan card) { + super(card); + } + + @Override + public CosmicSpiderMan copy() { + return new CosmicSpiderMan(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/Cosmogoyf.java b/Mage.Sets/src/mage/cards/c/Cosmogoyf.java index ac336b28f9d..0d3d326a7ef 100644 --- a/Mage.Sets/src/mage/cards/c/Cosmogoyf.java +++ b/Mage.Sets/src/mage/cards/c/Cosmogoyf.java @@ -56,7 +56,7 @@ enum CosmogoyfValue implements DynamicValue { @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { - return game.getState().getExile().getAllCards(game, sourceAbility.getControllerId()).size(); + return game.getState().getExile().getCardsOwned(game, sourceAbility.getControllerId()).size(); } @Override diff --git a/Mage.Sets/src/mage/cards/c/CostumeCloset.java b/Mage.Sets/src/mage/cards/c/CostumeCloset.java new file mode 100644 index 00000000000..4d152009b7e --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CostumeCloset.java @@ -0,0 +1,62 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.LeavesBattlefieldAllTriggeredAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.common.counter.MoveCountersFromSourceToTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.ModifiedPredicate; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class CostumeCloset extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent("a modified creature you control"); + + static { + filter.add(ModifiedPredicate.instance); + } + + public CostumeCloset(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{W}"); + + // This artifact enters with two +1/+1 counters on it. + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance(2)), + "with two +1/+1 counters on it" + )); + + // {T}: Move a +1/+1 counter from this artifact onto target creature you control. Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility( + new MoveCountersFromSourceToTargetEffect(), new TapSourceCost() + ); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + + // Whenever a modified creature you control leaves the battlefield, put a +1/+1 counter on this artifact. + this.addAbility(new LeavesBattlefieldAllTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), filter + )); + } + + private CostumeCloset(final CostumeCloset card) { + super(card); + } + + @Override + public CostumeCloset copy() { + return new CostumeCloset(this); + } +} diff --git a/Mage.Sets/src/mage/cards/c/CustodyBattle.java b/Mage.Sets/src/mage/cards/c/CustodyBattle.java index 8ff892631d4..4b2a3f30eb1 100644 --- a/Mage.Sets/src/mage/cards/c/CustodyBattle.java +++ b/Mage.Sets/src/mage/cards/c/CustodyBattle.java @@ -131,7 +131,7 @@ class CustodyBattleUnlessPaysEffect extends OneShotEffect { return true; } } - if (source.getSourceObjectZoneChangeCounter() == game.getState().getZoneChangeCounter(source.getSourceId()) + if (source.getStackMomentSourceZCC() == game.getState().getZoneChangeCounter(source.getSourceId()) && game.getState().getZone(source.getSourceId()) == Zone.BATTLEFIELD) { ContinuousEffect effect = new GiveControlEffect(); effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); diff --git a/Mage.Sets/src/mage/cards/c/CyclopeanTomb.java b/Mage.Sets/src/mage/cards/c/CyclopeanTomb.java index 2de29c123d3..ad96ccb040e 100644 --- a/Mage.Sets/src/mage/cards/c/CyclopeanTomb.java +++ b/Mage.Sets/src/mage/cards/c/CyclopeanTomb.java @@ -147,7 +147,7 @@ class CyclopeanTombEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - MageObjectReference mor = new MageObjectReference(source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game); + MageObjectReference mor = new MageObjectReference(source.getSourceId(), source.getStackMomentSourceZCC(), game); CyclopeanTombCounterWatcher watcher = game.getState().getWatcher(CyclopeanTombCounterWatcher.class); if (controller != null && watcher != null) { diff --git a/Mage.Sets/src/mage/cards/d/DailyBugleBuilding.java b/Mage.Sets/src/mage/cards/d/DailyBugleBuilding.java new file mode 100644 index 00000000000..79491405489 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DailyBugleBuilding.java @@ -0,0 +1,52 @@ +package mage.cards.d; + +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.abilities.mana.AnyColorManaAbility; +import mage.abilities.mana.ColorlessManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DailyBugleBuilding extends CardImpl { + + public DailyBugleBuilding(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // {T}: Add {C}. + this.addAbility(new ColorlessManaAbility()); + + // {1}, {T}: Add one mana of any color. + Ability ability = new AnyColorManaAbility(new GenericManaCost(1)); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + + // Smear Campaign -- {1}, {T}: Target legendary creature gains menace until end of turn. Activate only as a sorcery. + ability = new ActivateAsSorceryActivatedAbility( + new GainAbilityTargetEffect(new MenaceAbility(false)), new GenericManaCost(1) + ); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_LEGENDARY)); + this.addAbility(ability.withFlavorWord("Smear Campaign")); + } + + private DailyBugleBuilding(final DailyBugleBuilding card) { + super(card); + } + + @Override + public DailyBugleBuilding copy() { + return new DailyBugleBuilding(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DamageControlCrew.java b/Mage.Sets/src/mage/cards/d/DamageControlCrew.java new file mode 100644 index 00000000000..d4ff8c8d47d --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DamageControlCrew.java @@ -0,0 +1,62 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.target.TargetPermanent; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DamageControlCrew extends CardImpl { + + private static final FilterCard filter = new FilterCard("card with mana value 4 or greater from your graveyard"); + + static { + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 3)); + } + + public DamageControlCrew(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CITIZEN); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When this creature enters, choose one -- + // * Repair -- Return target card with mana value 4 or greater from your graveyard to your hand. + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect()); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + ability.withFirstModeFlavorWord("Repair"); + + // * Impound -- Exile target artifact or enchantment. + ability.addMode(new Mode(new ExileTargetEffect()) + .addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)) + .withFlavorWord("Impound")); + this.addAbility(ability); + } + + private DamageControlCrew(final DamageControlCrew card) { + super(card); + } + + @Override + public DamageControlCrew copy() { + return new DamageControlCrew(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DampingEngine.java b/Mage.Sets/src/mage/cards/d/DampingEngine.java index d298afd4fd0..20d92803bfb 100644 --- a/Mage.Sets/src/mage/cards/d/DampingEngine.java +++ b/Mage.Sets/src/mage/cards/d/DampingEngine.java @@ -78,7 +78,7 @@ public class DampingEngine extends CardImpl { return "dampingEngine_" + playerId + "_" + source.getSourceId() + "_" - + source.getSourceObjectZoneChangeCounter() + "_" + + source.getStackMomentSourceZCC() + "_" + game.getTurnNum(); } diff --git a/Mage.Sets/src/mage/cards/d/DarkTriumph.java b/Mage.Sets/src/mage/cards/d/DarkTriumph.java index 9cc1f5f77f9..31d0e779f48 100644 --- a/Mage.Sets/src/mage/cards/d/DarkTriumph.java +++ b/Mage.Sets/src/mage/cards/d/DarkTriumph.java @@ -1,4 +1,3 @@ - package mage.cards.d; import mage.abilities.condition.Condition; @@ -13,7 +12,6 @@ import mage.constants.Duration; import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.target.common.TargetControlledCreaturePermanent; import java.util.UUID; @@ -23,7 +21,7 @@ import java.util.UUID; public final class DarkTriumph extends CardImpl { private static final Condition condition = new PermanentsOnTheBattlefieldCondition( - new FilterPermanent(SubType.SWAMP, "If you control a Swamp") + new FilterPermanent(SubType.SWAMP, "you control a Swamp") ); public DarkTriumph(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/d/DayOfTheDragons.java b/Mage.Sets/src/mage/cards/d/DayOfTheDragons.java index 660d41a4655..791ea5f5ba7 100644 --- a/Mage.Sets/src/mage/cards/d/DayOfTheDragons.java +++ b/Mage.Sets/src/mage/cards/d/DayOfTheDragons.java @@ -78,7 +78,7 @@ class DayOfTheDragonsEntersEffect extends OneShotEffect { Set toExile = new HashSet<>(); toExile.addAll(game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)); if (!toExile.isEmpty()) { - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); controller.moveCardsToExile(toExile, source, game, true, exileId, sourceObject.getIdName()); DragonToken2 token = new DragonToken2(); token.putOntoBattlefield(toExile.size(), game, source, source.getControllerId()); @@ -110,7 +110,7 @@ class DayOfTheDragonsLeavesEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = source.getSourceObject(game); if (controller != null) { - int zoneChangeCounter = source.getSourceObjectZoneChangeCounter(); + int zoneChangeCounter = source.getStackMomentSourceZCC(); if (zoneChangeCounter > 0 && !(sourceObject instanceof PermanentToken)) { zoneChangeCounter--; } diff --git a/Mage.Sets/src/mage/cards/d/DayOfTheMoon.java b/Mage.Sets/src/mage/cards/d/DayOfTheMoon.java index a5d100572d2..f5e39a65e3e 100644 --- a/Mage.Sets/src/mage/cards/d/DayOfTheMoon.java +++ b/Mage.Sets/src/mage/cards/d/DayOfTheMoon.java @@ -101,7 +101,7 @@ class DayOfTheMoonEffect extends OneShotEffect { } private static List getOrSetValue(Game game, Ability source) { - String key = "DayOfTheMoon_" + source.getControllerId() + '_' + source.getSourceObjectZoneChangeCounter(); + String key = "DayOfTheMoon_" + source.getControllerId() + '_' + source.getStackMomentSourceZCC(); List list = (List) game.getState().getValue(key); if (list != null) { return list; diff --git a/Mage.Sets/src/mage/cards/d/DeepwoodLegate.java b/Mage.Sets/src/mage/cards/d/DeepwoodLegate.java index f040237f1be..ce988c8efbd 100644 --- a/Mage.Sets/src/mage/cards/d/DeepwoodLegate.java +++ b/Mage.Sets/src/mage/cards/d/DeepwoodLegate.java @@ -40,7 +40,7 @@ public final class DeepwoodLegate extends CardImpl { this.toughness = new MageInt(1); // If an opponent controls a Forest and you control a Swamp, you may cast this spell without paying its mana cost. - Condition condition = new CompoundCondition("If an opponent controls a Forest and you control a Swamp", + Condition condition = new CompoundCondition("an opponent controls a Forest and you control a Swamp", new OpponentControlsPermanentCondition(filterForest), new PermanentsOnTheBattlefieldCondition(filterSwamp)); this.addAbility(new AlternativeCostSourceAbility(null, condition)); diff --git a/Mage.Sets/src/mage/cards/d/DesperateGambit.java b/Mage.Sets/src/mage/cards/d/DesperateGambit.java index 719077d465c..d51e7d978d6 100644 --- a/Mage.Sets/src/mage/cards/d/DesperateGambit.java +++ b/Mage.Sets/src/mage/cards/d/DesperateGambit.java @@ -161,7 +161,7 @@ class TargetControlledSource extends TargetSource { possibleTargets.add(card.getId()); } // 108.4a If anything asks for the controller of a card that doesn't have one (because it's not a permanent or spell), use its owner instead. - for (Card card : game.getExile().getAllCards(game)) { + for (Card card : game.getExile().getCardsInRange(game, sourceControllerId)) { if (Objects.equals(card.getOwnerId(), sourceControllerId)) { possibleTargets.add(card.getId()); } diff --git a/Mage.Sets/src/mage/cards/d/DetentionSphere.java b/Mage.Sets/src/mage/cards/d/DetentionSphere.java index c16dc102058..4238b79c43e 100644 --- a/Mage.Sets/src/mage/cards/d/DetentionSphere.java +++ b/Mage.Sets/src/mage/cards/d/DetentionSphere.java @@ -74,7 +74,7 @@ class DetentionSphereEntersEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); Permanent targetPermanent = game.getPermanent(getTargetPointer().getFirst(game, source)); Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = game.getObject(source); diff --git a/Mage.Sets/src/mage/cards/d/DiscipleOfPerdition.java b/Mage.Sets/src/mage/cards/d/DiscipleOfPerdition.java index d4bad5377df..0358fb12d3b 100644 --- a/Mage.Sets/src/mage/cards/d/DiscipleOfPerdition.java +++ b/Mage.Sets/src/mage/cards/d/DiscipleOfPerdition.java @@ -35,13 +35,12 @@ public final class DiscipleOfPerdition extends CardImpl { // When Disciple of Perdition dies, choose one. If you have exactly 13 life, you may choose both. // * You draw a card and you lose 1 life. Ability ability = new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1, true), false); - ability.getModes().setChooseText("choose one. If you have exactly 13 life, you may choose both."); + ability.getModes().setChooseText("choose one. If you have exactly 13 life, you may choose both instead."); ability.getModes().setMoreCondition(2, new LifeCompareCondition(TargetController.YOU, ComparisonType.EQUAL_TO, 13)); ability.addEffect(new LoseLifeSourceControllerEffect(1).concatBy("and")); // * Exile target opponent's graveyard. That player loses 1 life. - ability.addMode(new Mode(new ExileGraveyardAllTargetPlayerEffect() - .setText("Exile target opponent's graveyard")) + ability.addMode(new Mode(new ExileGraveyardAllTargetPlayerEffect()) .addEffect(new LoseLifeTargetEffect(1).setText("that player loses 1 life")) .addTarget(new TargetOpponent())); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/d/DocOcksTentacles.java b/Mage.Sets/src/mage/cards/d/DocOcksTentacles.java new file mode 100644 index 00000000000..33d12582124 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DocOcksTentacles.java @@ -0,0 +1,57 @@ +package mage.cards.d; + +import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.mageobject.ManaValuePredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DocOcksTentacles extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledCreaturePermanent("a creature you control with mana value 5 or greater"); + + static { + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 4)); + } + + public DocOcksTentacles(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Whenever a creature you control with mana value 5 or greater enters, you may attach this Equipment to it. + this.addAbility(new EntersBattlefieldAllTriggeredAbility( + new AttachEffect(Outcome.BoostCreature, "attach {this} to it"), filter, true + )); + + // Equipped creature gets +4/+4. + this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(4, 4))); + + // Equip {5} + this.addAbility(new EquipAbility(5)); + } + + private DocOcksTentacles(final DocOcksTentacles card) { + super(card); + } + + @Override + public DocOcksTentacles copy() { + return new DocOcksTentacles(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DragonKamisEgg.java b/Mage.Sets/src/mage/cards/d/DragonKamisEgg.java index 6e6b8c2d588..3def1f2d789 100644 --- a/Mage.Sets/src/mage/cards/d/DragonKamisEgg.java +++ b/Mage.Sets/src/mage/cards/d/DragonKamisEgg.java @@ -79,7 +79,7 @@ class DragonKamisEggEffect extends OneShotEffect { } Cards cards = new CardsImpl(); game.getExile() - .getAllCards(game, player.getId()) + .getCardsOwned(game, player.getId()) .stream() .filter(Objects::nonNull) .filter(card -> card.getCounters(game).containsKey(CounterType.HATCHLING)) diff --git a/Mage.Sets/src/mage/cards/d/DragonWhelp.java b/Mage.Sets/src/mage/cards/d/DragonWhelp.java index 3d721f6e9c9..f30efb7ff6a 100644 --- a/Mage.Sets/src/mage/cards/d/DragonWhelp.java +++ b/Mage.Sets/src/mage/cards/d/DragonWhelp.java @@ -19,7 +19,6 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; /** @@ -74,7 +73,7 @@ class DragonWhelpEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - ActivationInfo activationInfo = ActivationInfo.getInstance(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + ActivationInfo activationInfo = ActivationInfo.getInstance(game, source.getSourceId(), source.getStackMomentSourceZCC()); activationInfo.addActivation(game); if (activationInfo.getActivationCounter() >= 4) { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new SacrificeSourceEffect()); diff --git a/Mage.Sets/src/mage/cards/d/DragonsFire.java b/Mage.Sets/src/mage/cards/d/DragonsFire.java index 3492742a074..acb2a20cc50 100644 --- a/Mage.Sets/src/mage/cards/d/DragonsFire.java +++ b/Mage.Sets/src/mage/cards/d/DragonsFire.java @@ -18,8 +18,8 @@ import mage.filter.common.FilterControlledPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import mage.target.TargetPermanent; import mage.target.common.TargetCardInHand; -import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetCreatureOrPlaneswalker; /** @@ -54,11 +54,10 @@ class DragonsFireCost extends CostImpl { public enum DragonZone { HAND, - BATTLEFIELD, - NONE + BATTLEFIELD } - private DragonZone dragonZone = DragonZone.NONE; + private DragonZone dragonZone = null; private UUID selectedCardId = null; private static final FilterCard handFilter = new FilterCard("Dragon card from your hand"); @@ -90,14 +89,13 @@ class DragonsFireCost extends CostImpl { @Override public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - this.getTargets().clear(); - dragonZone = DragonZone.NONE; + dragonZone = null; selectedCardId = null; Player controller = game.getPlayer(controllerId); if (controller != null) { boolean dragonInHand = false; boolean dragonOnBattlefield = false; - DragonZone chosenZone = DragonZone.NONE; + DragonZone chosenZone = null; for (UUID cardId : controller.getHand()) { Card card = game.getCard(cardId); if (card != null && card.hasSubtype(SubType.DRAGON, game)) { @@ -132,23 +130,25 @@ class DragonsFireCost extends CostImpl { } switch (chosenZone) { case HAND: - this.getTargets().add(new TargetCardInHand(handFilter)); - if (this.getTargets().choose(Outcome.Benefit, controllerId, source.getSourceId(), source, game)) { - Card card = game.getCard(this.getTargets().getFirstTarget()); + TargetCardInHand handTarget = new TargetCardInHand(handFilter); + handTarget.withNotTarget(true); + if (controller.choose(Outcome.Benefit, handTarget, source, game)) { + Card card = game.getCard(handTarget.getFirstTarget()); if (card != null) { dragonZone = DragonZone.HAND; - selectedCardId = this.getTargets().getFirstTarget(); + selectedCardId = handTarget.getFirstTarget(); controller.revealCards(source, new CardsImpl(card), game); } } break; case BATTLEFIELD: - this.getTargets().add(new TargetControlledPermanent(battlefieldFilter)); - if (this.getTargets().choose(Outcome.Benefit, controllerId, source.getSourceId(), source, game)) { - Permanent permanent = game.getPermanent(this.getTargets().getFirstTarget()); + TargetPermanent battlefieldTarget = new TargetPermanent(battlefieldFilter); + battlefieldTarget.withNotTarget(true); + if (controller.choose(Outcome.Benefit, battlefieldTarget, source, game)) { + Permanent permanent = game.getPermanent(battlefieldTarget.getFirstTarget()); if (permanent != null) { dragonZone = DragonZone.BATTLEFIELD; - selectedCardId = this.getTargets().getFirstTarget(); + selectedCardId = battlefieldTarget.getFirstTarget(); game.informPlayers(controller.getLogName() + " chooses " + permanent.getLogName()); } } @@ -190,7 +190,7 @@ class DragonsFireEffect extends OneShotEffect { if (targetedPermanent == null) { return false; } - DragonsFireCost.DragonZone dragonZone = DragonsFireCost.DragonZone.NONE; + DragonsFireCost.DragonZone dragonZone = null; UUID selectedCardId = null; int damage = 3; for (Cost cost : source.getCosts()) { @@ -201,21 +201,23 @@ class DragonsFireEffect extends OneShotEffect { break; } } - switch (dragonZone) { - case HAND: - Card card = game.getCard(selectedCardId); - if (card != null) { - damage = card.getPower().getValue(); - } - break; - case BATTLEFIELD: - Permanent dragon = game.getPermanentOrLKIBattlefield(selectedCardId); - if (dragon != null) { - damage = dragon.getPower().getValue(); - } - break; + if (dragonZone != null) { + switch (dragonZone) { + case HAND: + Card card = game.getCard(selectedCardId); + if (card != null) { + damage = card.getPower().getValue(); + } + break; + case BATTLEFIELD: + Permanent dragon = game.getPermanentOrLKIBattlefield(selectedCardId); + if (dragon != null) { + damage = dragon.getPower().getValue(); + } + break; + } } targetedPermanent.damage(damage, source.getSourceId(), source, game); return true; } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/d/DralnusPet.java b/Mage.Sets/src/mage/cards/d/DralnusPet.java index 20979c4cffa..d3daea9aa08 100644 --- a/Mage.Sets/src/mage/cards/d/DralnusPet.java +++ b/Mage.Sets/src/mage/cards/d/DralnusPet.java @@ -92,7 +92,7 @@ class DralnusPetEffect extends OneShotEffect { SpellAbility spellAbility = (SpellAbility) getValue(EntersBattlefieldEffect.SOURCE_CAST_SPELL_ABILITY); if (spellAbility != null && spellAbility.getSourceId().equals(source.getSourceId()) - && permanent.getZoneChangeCounter(game) == spellAbility.getSourceObjectZoneChangeCounter()) { + && permanent.getZoneChangeCounter(game) == spellAbility.getStackMomentSourceZCC()) { int cmc = 0; for (Cost cost : spellAbility.getCosts()) { if (cost instanceof DiscardCardCost && !((DiscardCardCost) cost).getCards().isEmpty()) { diff --git a/Mage.Sets/src/mage/cards/d/DredgingClaw.java b/Mage.Sets/src/mage/cards/d/DredgingClaw.java index 10daded1f15..ffe7a8fee63 100644 --- a/Mage.Sets/src/mage/cards/d/DredgingClaw.java +++ b/Mage.Sets/src/mage/cards/d/DredgingClaw.java @@ -82,6 +82,6 @@ class DredgingClawTriggeredAbility extends TriggeredAbilityImpl { @Override public String getRule() { - return "Whenever a creature enters the battlefield from your graveyard, you may attach {this} to it."; + return "Whenever a creature enters from your graveyard, you may attach {this} to it."; } } diff --git a/Mage.Sets/src/mage/cards/d/DuneChanter.java b/Mage.Sets/src/mage/cards/d/DuneChanter.java index 23a1c23af93..bbc688657cf 100644 --- a/Mage.Sets/src/mage/cards/d/DuneChanter.java +++ b/Mage.Sets/src/mage/cards/d/DuneChanter.java @@ -5,6 +5,7 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.GainLifeEffect; @@ -45,10 +46,12 @@ public final class DuneChanter extends CardImpl { this.addAbility(new SimpleStaticAbility(new DuneChanterContinuousEffect())); // Lands you control have "{T}: Add one mana of any color." - this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect( + ContinuousEffect effect = new GainAbilityControlledEffect( new AnyColorManaAbility(), Duration.WhileOnBattlefield, StaticFilters.FILTER_LANDS, false - ))); + ); + effect.getDependedToTypes().add(DependencyType.BecomeNonbasicLand); + this.addAbility(new SimpleStaticAbility(effect)); // {T}: Mill two cards. You gain 1 life for each land card milled this way. this.addAbility(new SimpleActivatedAbility(new DuneChanterEffect(), new TapSourceCost())); @@ -76,6 +79,7 @@ class DuneChanterContinuousEffect extends ContinuousEffectImpl { public DuneChanterContinuousEffect() { super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit); staticText = "Lands you control and land cards you own that aren't on the battlefield are Deserts in addition to their other types"; + dependendToTypes.add(DependencyType.BecomeNonbasicLand); } private DuneChanterContinuousEffect(final DuneChanterContinuousEffect effect) { @@ -111,7 +115,7 @@ class DuneChanterContinuousEffect extends ContinuousEffectImpl { } } // in exile - for (Card card : game.getState().getExile().getAllCards(game, controllerId)) { + for (Card card : game.getState().getExile().getCardsOwned(game, controllerId)) { if (filterCard.match(card, controllerId, source, game) && !card.hasSubtype(subType, game)) { game.getState().getCreateMageObjectAttribute(card, game).getSubtype().add(subType); } @@ -170,5 +174,3 @@ class DuneChanterEffect extends OneShotEffect { return true; } } - - diff --git a/Mage.Sets/src/mage/cards/d/DurkwoodTracker.java b/Mage.Sets/src/mage/cards/d/DurkwoodTracker.java index 83f1f8de7db..1fdac9d4f5c 100644 --- a/Mage.Sets/src/mage/cards/d/DurkwoodTracker.java +++ b/Mage.Sets/src/mage/cards/d/DurkwoodTracker.java @@ -70,7 +70,7 @@ class DurkwoodTrackerEffect extends OneShotEffect { Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent == null || permanent.getZoneChangeCounter(game) - != source.getSourceObjectZoneChangeCounter()) { + != source.getStackMomentSourceZCC()) { return false; } Permanent targeted = game.getPermanent(source.getFirstTarget()); diff --git a/Mage.Sets/src/mage/cards/d/DustOfMoments.java b/Mage.Sets/src/mage/cards/d/DustOfMoments.java index d9d6231ee85..86f5c33ac31 100644 --- a/Mage.Sets/src/mage/cards/d/DustOfMoments.java +++ b/Mage.Sets/src/mage/cards/d/DustOfMoments.java @@ -83,7 +83,7 @@ class DustOfMomentsEffect extends OneShotEffect { permanent.addCounters(CounterType.TIME.createInstance(2), source, game); } } - for (Card card : game.getExile().getCards(filter.getCardFilter(), game)) { + for (Card card : game.getExile().getCardsInRange(filter.getCardFilter(), source.getControllerId(), source, game)) { if (remove) { card.removeCounters(CounterType.TIME.createInstance(2), source, game); } else { diff --git a/Mage.Sets/src/mage/cards/e/EddieBrock.java b/Mage.Sets/src/mage/cards/e/EddieBrock.java new file mode 100644 index 00000000000..cc84afc2c29 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EddieBrock.java @@ -0,0 +1,130 @@ +package mage.cards.e; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.PutCardFromHandOntoBattlefieldEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.MenaceAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacedCard; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreatureCard; +import mage.filter.common.FilterPermanentCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EddieBrock extends ModalDoubleFacedCard { + + private static final FilterCard filter = new FilterCreatureCard("creature card with mana value 1 or less"); + + static { + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 2)); + } + + public EddieBrock(UUID ownerId, CardSetInfo setInfo) { + super( + ownerId, setInfo, + new SuperType[]{SuperType.LEGENDARY}, new CardType[]{CardType.CREATURE}, new SubType[]{SubType.HUMAN, SubType.HERO, SubType.VILLAIN}, "{2}{B}", + "Venom, Lethal Protector", + new SuperType[]{SuperType.LEGENDARY}, new CardType[]{CardType.CREATURE}, new SubType[]{SubType.SYMBIOTE, SubType.HERO, SubType.VILLAIN}, "{3}{B}{R}{G}" + ); + this.getLeftHalfCard().setPT(5, 5); + this.getRightHalfCard().setPT(5, 5); + + // When Eddie Brock enters, return target creature card with mana value 1 or less from your graveyard to the battlefield. + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect()); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.getLeftHalfCard().addAbility(ability); + + // {3}{B}{R}{G}: Transform Eddie Brock. Activate only as a sorcery. + this.getLeftHalfCard().addAbility(new ActivateAsSorceryActivatedAbility( + new TransformSourceEffect(), new ManaCostsImpl<>("{3}{B}{R}{G}") + )); + + // Venom, Lethal Protector + // Menace + this.getRightHalfCard().addAbility(new MenaceAbility()); + + // Trample + this.getRightHalfCard().addAbility(TrampleAbility.getInstance()); + + // Haste + this.getRightHalfCard().addAbility(HasteAbility.getInstance()); + + // Whenever Venom attacks, you may sacrifice another creature. If you do, draw X cards, then you may put a permanent card with mana value X or less from your hand onto the battlefield, where X is the sacrificed creature's mana value. + this.getRightHalfCard().addAbility(new AttacksTriggeredAbility(new VenomLethalProtectorEffect())); + } + + private EddieBrock(final EddieBrock card) { + super(card); + } + + @Override + public EddieBrock copy() { + return new EddieBrock(this); + } +} + +class VenomLethalProtectorEffect extends OneShotEffect { + + VenomLethalProtectorEffect() { + super(Outcome.Benefit); + staticText = "you may sacrifice another creature. If you do, draw X cards, " + + "then you may put a permanent card with mana value X or less from your hand onto the battlefield, " + + "where X is the sacrificed creature's mana value"; + } + + private VenomLethalProtectorEffect(final VenomLethalProtectorEffect effect) { + super(effect); + } + + @Override + public VenomLethalProtectorEffect copy() { + return new VenomLethalProtectorEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + SacrificeTargetCost cost = new SacrificeTargetCost(StaticFilters.FILTER_ANOTHER_CREATURE); + if (!cost.canPay(source, source, source.getControllerId(), game) + || !player.chooseUse(Outcome.Sacrifice, "Sacrifice another creature?", source, game) + || !cost.pay(source, game, source, source.getControllerId(), true)) { + return false; + } + int amount = cost + .getPermanents() + .stream() + .filter(Objects::nonNull) + .mapToInt(MageObject::getManaValue) + .sum(); + player.drawCards(amount, source, game); + game.processAction(); + FilterCard filter = new FilterPermanentCard("permanent card with mana value " + amount + " or less"); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, amount + 1)); + new PutCardFromHandOntoBattlefieldEffect(filter).apply(game, source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/e/ElectroAssaultingBattery.java b/Mage.Sets/src/mage/cards/e/ElectroAssaultingBattery.java new file mode 100644 index 00000000000..34b05ea2ee8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ElectroAssaultingBattery.java @@ -0,0 +1,93 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.continuous.YouDontLoseManaEffect; +import mage.abilities.effects.mana.AddManaToManaPoolSourceControllerEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetPlayer; +import mage.util.ManaUtil; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class ElectroAssaultingBattery extends CardImpl { + + public ElectroAssaultingBattery(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // You don't lose unspent red mana as steps and phases end. + this.addAbility(new SimpleStaticAbility(new YouDontLoseManaEffect(ManaType.RED))); + + // Whenever you cast an instant or sorcery spell, add {R}. + this.addAbility(new SpellCastControllerTriggeredAbility(new AddManaToManaPoolSourceControllerEffect(Mana.RedMana(1)), false)); + + // When Electro leaves the battlefield, you may pay x. When you do, he deals X damage to target player. + this.addAbility(new LeavesBattlefieldTriggeredAbility(new ElectroAssaultingBatteryEffect())); + } + + private ElectroAssaultingBattery(final ElectroAssaultingBattery card) { + super(card); + } + + @Override + public ElectroAssaultingBattery copy() { + return new ElectroAssaultingBattery(this); + } +} +class ElectroAssaultingBatteryEffect extends OneShotEffect { + + ElectroAssaultingBatteryEffect() { + super(Outcome.Damage); + staticText = "you may pay x. When you do, he deals X damage to target player"; + } + + private ElectroAssaultingBatteryEffect(final ElectroAssaultingBatteryEffect effect) { + super(effect); + } + + @Override + public ElectroAssaultingBatteryEffect copy() { + return new ElectroAssaultingBatteryEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + if (controller.chooseUse(outcome, "Pay x mana to deal x damage to target player?", source, game)) { + TargetPlayer target = new TargetPlayer(); + target.chooseTarget(outcome, controller.getId(), source, game); + int amount = ManaUtil.playerPaysXGenericMana(false, "Electro, Assaulting Battery", controller, source, game); + Player targetOpponent = game.getPlayer(target.getFirstTarget()); + if (targetOpponent != null) { + targetOpponent.damage(amount, source, game); + } + } + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/e/ElspethsNightmare.java b/Mage.Sets/src/mage/cards/e/ElspethsNightmare.java index 96071aa6af5..484d2dc9c64 100644 --- a/Mage.Sets/src/mage/cards/e/ElspethsNightmare.java +++ b/Mage.Sets/src/mage/cards/e/ElspethsNightmare.java @@ -58,8 +58,7 @@ public final class ElspethsNightmare extends CardImpl { // III - Exile target opponent's graveyard. sagaAbility.addChapterEffect( this, SagaChapter.CHAPTER_III, SagaChapter.CHAPTER_III, - new ExileGraveyardAllTargetPlayerEffect() - .setText("exile target opponent's graveyard"), + new ExileGraveyardAllTargetPlayerEffect(), new TargetOpponent() ); this.addAbility(sagaAbility); diff --git a/Mage.Sets/src/mage/cards/e/EmberwildeDjinn.java b/Mage.Sets/src/mage/cards/e/EmberwildeDjinn.java index 027f7e47f62..981cd9ed20b 100644 --- a/Mage.Sets/src/mage/cards/e/EmberwildeDjinn.java +++ b/Mage.Sets/src/mage/cards/e/EmberwildeDjinn.java @@ -82,7 +82,7 @@ class EmberwildeDjinnEffect extends OneShotEffect { if (player.chooseUse(Outcome.GainControl, "Gain control of " + sourceObject.getLogName() + "?", source, game)) { if (cost.pay(source, game, source, player.getId(), false)) { ContinuousEffect effect = new GainControlTargetEffect(Duration.Custom, false, player.getId()); - effect.setTargetPointer(new FixedTarget(source.getSourceId(), source.getSourceObjectZoneChangeCounter())); + effect.setTargetPointer(new FixedTarget(source.getSourceId(), source.getStackMomentSourceZCC())); game.addEffect(effect, source); player.resetStoredBookmark(game); } diff --git a/Mage.Sets/src/mage/cards/e/EncroachingMycosynth.java b/Mage.Sets/src/mage/cards/e/EncroachingMycosynth.java index 5270c509724..ec5af61315b 100644 --- a/Mage.Sets/src/mage/cards/e/EncroachingMycosynth.java +++ b/Mage.Sets/src/mage/cards/e/EncroachingMycosynth.java @@ -45,6 +45,7 @@ class EncroachingMycosynthEffect extends ContinuousEffectImpl { super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit); staticText = "Nonland permanents you control are artifacts in addition to their other types. " + "The same is true for permanent spells you control and nonland permanent cards you own that aren't on the battlefield"; + this.dependendToTypes.add(DependencyType.BecomeNonbasicLand); this.dependencyTypes.add(DependencyType.ArtifactAddingRemoving); // March of the Machines } @@ -79,7 +80,7 @@ class EncroachingMycosynthEffect extends ContinuousEffectImpl { } } // in Exile - for (Card card : game.getState().getExile().getAllCards(game, source.getControllerId())) { + for (Card card : game.getState().getExile().getCardsOwned(game, source.getControllerId())) { if (card.isPermanent(game) && !card.isLand(game) && !card.isArtifact(game)) { card.addCardType(game, CardType.ARTIFACT); } diff --git a/Mage.Sets/src/mage/cards/e/EsperTerra.java b/Mage.Sets/src/mage/cards/e/EsperTerra.java index 884d7dfb4c4..fd4cde6c0f4 100644 --- a/Mage.Sets/src/mage/cards/e/EsperTerra.java +++ b/Mage.Sets/src/mage/cards/e/EsperTerra.java @@ -119,7 +119,7 @@ class EsperTerraEffect extends OneShotEffect { .filter(amount -> amount > 0) .ifPresent(amount -> token.addCounters(CounterType.LORE.createInstance(amount), source, game)); } - effect.sacrificeTokensCreatedAtNextEndStep(game, source); + effect.removeTokensCreatedAt(game, source, false, PhaseStep.END_TURN, TargetController.YOU); return true; } } diff --git a/Mage.Sets/src/mage/cards/e/EternalScourge.java b/Mage.Sets/src/mage/cards/e/EternalScourge.java index 74a9178677f..2396de67a6b 100644 --- a/Mage.Sets/src/mage/cards/e/EternalScourge.java +++ b/Mage.Sets/src/mage/cards/e/EternalScourge.java @@ -32,7 +32,7 @@ public final class EternalScourge extends CardImpl { // When Eternal Scourge becomes the target of a spell or ability an opponent controls, exile Eternal Scourge. this.addAbility(new BecomesTargetSourceTriggeredAbility(new ExileSourceEffect(), - StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS)); + StaticFilters.FILTER_SPELL_OR_ABILITY_OPPONENTS).withRuleTextReplacement(false)); } private EternalScourge(final EternalScourge card) { diff --git a/Mage.Sets/src/mage/cards/e/EtrataTheSilencer.java b/Mage.Sets/src/mage/cards/e/EtrataTheSilencer.java index 58f5cad072c..310065dfa11 100644 --- a/Mage.Sets/src/mage/cards/e/EtrataTheSilencer.java +++ b/Mage.Sets/src/mage/cards/e/EtrataTheSilencer.java @@ -96,8 +96,8 @@ class EtrataTheSilencerEffect extends OneShotEffect { if (card != null) { card.addCounters(CounterType.HIT.createInstance(), source.getControllerId(), source, game); } - int cardsFound = game.getExile().getAllCards(game).stream() - .filter(c -> c.getOwnerId().equals(player.getId())) + int cardsFound = game.getExile().getCardsOwned(game, player.getId()) + .stream() .filter(c -> c.getCounters(game).getCount(CounterType.HIT) > 0) .mapToInt(x -> 1) .sum(); diff --git a/Mage.Sets/src/mage/cards/e/EzekielSimsSpiderTotem.java b/Mage.Sets/src/mage/cards/e/EzekielSimsSpiderTotem.java new file mode 100644 index 00000000000..4c579649394 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EzekielSimsSpiderTotem.java @@ -0,0 +1,53 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.keyword.ReachAbility; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EzekielSimsSpiderTotem extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.SPIDER); + + public EzekielSimsSpiderTotem(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIDER); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ADVISOR); + this.power = new MageInt(3); + this.toughness = new MageInt(5); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // At the beginning of combat on your turn, target Spider you control gets +2/+2 until end of turn. + Ability ability = new BeginningOfCombatTriggeredAbility(new BoostTargetEffect(2, 2)); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private EzekielSimsSpiderTotem(final EzekielSimsSpiderTotem card) { + super(card); + } + + @Override + public EzekielSimsSpiderTotem copy() { + return new EzekielSimsSpiderTotem(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FaerieArtisans.java b/Mage.Sets/src/mage/cards/f/FaerieArtisans.java index f5038d7fb81..437a27bfa0c 100644 --- a/Mage.Sets/src/mage/cards/f/FaerieArtisans.java +++ b/Mage.Sets/src/mage/cards/f/FaerieArtisans.java @@ -77,7 +77,7 @@ class FaerieArtisansEffect extends OneShotEffect { CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(null, CardType.ARTIFACT, false); effect.setTargetPointer(new FixedTarget(permanentToCopy, game)); if (effect.apply(game, source)) { - String oldTokens = (String) game.getState().getValue(source.getSourceId().toString() + source.getSourceObjectZoneChangeCounter()); + String oldTokens = (String) game.getState().getValue(source.getSourceId().toString() + source.getStackMomentSourceZCC()); StringBuilder sb = new StringBuilder(); for (Permanent permanent : effect.getAddedPermanents()) { if (sb.length() > 0) { @@ -85,7 +85,7 @@ class FaerieArtisansEffect extends OneShotEffect { } sb.append(permanent.getId()); } - game.getState().setValue(source.getSourceId().toString() + source.getSourceObjectZoneChangeCounter(), sb.toString()); + game.getState().setValue(source.getSourceId().toString() + source.getStackMomentSourceZCC(), sb.toString()); if (oldTokens != null) { Cards cards = new CardsImpl(); diff --git a/Mage.Sets/src/mage/cards/f/FalkenrathGorger.java b/Mage.Sets/src/mage/cards/f/FalkenrathGorger.java index e4156ea35e1..e01d796163e 100644 --- a/Mage.Sets/src/mage/cards/f/FalkenrathGorger.java +++ b/Mage.Sets/src/mage/cards/f/FalkenrathGorger.java @@ -85,30 +85,26 @@ class FalkenrathGorgerEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Map usedMadnessAbilities = new HashMap<>(); - // hand - for (Card card : controller.getHand().getCards(filter, game)) { - addMadnessToCard(game, card, usedMadnessAbilities); - } - // graveyard - for (Card card : controller.getGraveyard().getCards(filter, game)) { - addMadnessToCard(game, card, usedMadnessAbilities); - } - // Exile - for (Card card : game.getExile().getAllCards(game)) { - if (filter.match(card, controller.getId(), source, game)) { - if (card.isOwnedBy(controller.getId())) { - addMadnessToCard(game, card, usedMadnessAbilities); - } - } - } - madnessAbilities.clear(); - madnessAbilities.putAll(usedMadnessAbilities); - return true; + if (controller == null) { + return false; } + Map usedMadnessAbilities = new HashMap<>(); + // hand + for (Card card : controller.getHand().getCards(filter, game)) { + addMadnessToCard(game, card, usedMadnessAbilities); + } + // graveyard + for (Card card : controller.getGraveyard().getCards(filter, game)) { + addMadnessToCard(game, card, usedMadnessAbilities); + } + // Exile + for (Card card : game.getExile().getCardsOwned(filter, controller.getId(), source, game)) { + addMadnessToCard(game, card, usedMadnessAbilities); + } + madnessAbilities.clear(); + madnessAbilities.putAll(usedMadnessAbilities); + return true; - return false; } private void addMadnessToCard(Game game, Card card, Map usedMadnessAbilities) { diff --git a/Mage.Sets/src/mage/cards/f/FarrelitePriest.java b/Mage.Sets/src/mage/cards/f/FarrelitePriest.java index e7fb0bd4d6a..3c234f51959 100644 --- a/Mage.Sets/src/mage/cards/f/FarrelitePriest.java +++ b/Mage.Sets/src/mage/cards/f/FarrelitePriest.java @@ -67,7 +67,7 @@ class FarrelitePriestEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - ActivationInfo activationInfo = ActivationInfo.getInstance(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + ActivationInfo activationInfo = ActivationInfo.getInstance(game, source.getSourceId(), source.getStackMomentSourceZCC()); activationInfo.addActivation(game); if (activationInfo.getActivationCounter() == 4) { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new SacrificeSourceEffect()); diff --git a/Mage.Sets/src/mage/cards/f/FavorOfTheMighty.java b/Mage.Sets/src/mage/cards/f/FavorOfTheMighty.java index 7e434ef6d0c..fb8723905e7 100644 --- a/Mage.Sets/src/mage/cards/f/FavorOfTheMighty.java +++ b/Mage.Sets/src/mage/cards/f/FavorOfTheMighty.java @@ -60,7 +60,7 @@ class FavorOfTheMightyEffect extends ContinuousEffectImpl { FavorOfTheMightyEffect() { super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); - this.staticText = "Each creature with the highest mana value has protection from all colors."; + this.staticText = "Each creature with the greatest mana value has protection from each color."; } private FavorOfTheMightyEffect(final FavorOfTheMightyEffect effect) { diff --git a/Mage.Sets/src/mage/cards/f/FlamewarBrashVeteran.java b/Mage.Sets/src/mage/cards/f/FlamewarBrashVeteran.java index c948082d996..167a48fc486 100644 --- a/Mage.Sets/src/mage/cards/f/FlamewarBrashVeteran.java +++ b/Mage.Sets/src/mage/cards/f/FlamewarBrashVeteran.java @@ -97,7 +97,7 @@ class FlamewarBrashVeteranEffect extends OneShotEffect { } Set cards = game .getExile() - .getAllCards(game, source.getControllerId()) + .getCardsOwned(game, source.getControllerId()) .stream() .filter(card -> card.getCounters(game).containsKey(CounterType.INTEL)) .collect(Collectors.toSet()); diff --git a/Mage.Sets/src/mage/cards/f/FlashThompsonSpiderFan.java b/Mage.Sets/src/mage/cards/f/FlashThompsonSpiderFan.java new file mode 100644 index 00000000000..a4124a17a35 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FlashThompsonSpiderFan.java @@ -0,0 +1,59 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FlashThompsonSpiderFan extends CardImpl { + + public FlashThompsonSpiderFan(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CITIZEN); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // When Flash Thompson enters, choose one or both-- + // * Heckle -- Tap target creature. + Ability ability = new EntersBattlefieldTriggeredAbility(new TapTargetEffect()); + ability.addTarget(new TargetCreaturePermanent()); + ability.getModes().setMinModes(1); + ability.getModes().setMaxModes(2); + ability.withFirstModeFlavorWord("Heckle"); + + // * Hero Worship -- Untap target creature. + ability.addMode(new Mode(new UntapTargetEffect()) + .addTarget(new TargetCreaturePermanent()) + .withFlavorWord("Hero Worship")); + this.addAbility(ability); + } + + private FlashThompsonSpiderFan(final FlashThompsonSpiderFan card) { + super(card); + } + + @Override + public FlashThompsonSpiderFan copy() { + return new FlashThompsonSpiderFan(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/ForgersFoundry.java b/Mage.Sets/src/mage/cards/f/ForgersFoundry.java new file mode 100644 index 00000000000..c228dfd1a0a --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/ForgersFoundry.java @@ -0,0 +1,206 @@ +package mage.cards.f; + +import java.util.UUID; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.mana.BlueManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.filter.common.FilterInstantOrSorcerySpell; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.ExileZone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.util.CardUtil; + +/** + * + * @author Grath + */ +public final class ForgersFoundry extends CardImpl { + + private static final FilterInstantOrSorcerySpell filter = new FilterInstantOrSorcerySpell("an instant or sorcery spell with mana value 3 or less"); + static { + filter.add(new ManaValuePredicate(ComparisonType.OR_LESS, 3)); + } + + public ForgersFoundry(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{U}"); + + // {T}: Add {U}. When you spend this mana to cast an instant or sorcery spell with mana value 3 or less, you may exile that spell instead of putting it into its owner's graveyard as it resolves. + Ability ability = new BlueManaAbility(); + this.addAbility(ability); + this.addAbility(new ForgersFoundryTriggeredAbility(ability.getOriginalId())); + + // {3}{U}{U}, {T}: You may cast any number of spells from among cards exiled with Forger's Foundry without paying their mana costs. Activate only as a sorcery. + ability = new SimpleActivatedAbility(new ForgersFoundryCastEffect(), new ManaCostsImpl<>("{3}{U}{U}")); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private ForgersFoundry(final ForgersFoundry card) { + super(card); + } + + @Override + public ForgersFoundry copy() { + return new ForgersFoundry(this); + } +} + +class ForgersFoundryTriggeredAbility extends TriggeredAbilityImpl { + + private static final FilterInstantOrSorcerySpell filter = new FilterInstantOrSorcerySpell("an instant or sorcery spell with mana value 3 or less"); + static { + filter.add(new ManaValuePredicate(ComparisonType.OR_LESS, 3)); + } + + String abilityOriginalId; + + public ForgersFoundryTriggeredAbility(UUID abilityOriginalId) { + super(Zone.ALL, null, false); + this.abilityOriginalId = abilityOriginalId.toString(); + setTriggerPhrase("When that mana is used to cast an instant or sorcery spell with mana value 3 or less, "); + } + + private ForgersFoundryTriggeredAbility(final ForgersFoundryTriggeredAbility ability) { + super(ability); + this.abilityOriginalId = ability.abilityOriginalId; + } + + @Override + public ForgersFoundryTriggeredAbility copy() { + return new ForgersFoundryTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.MANA_PAID; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getData().equals(abilityOriginalId)) { + Spell spell = game.getStack().getSpell(event.getTargetId()); + if (spell != null && filter.match(spell, getControllerId(), this, game)) { + this.getEffects().clear(); + this.addEffect(new ForgersFoundryExileEffect(spell, game)); + return true; + } + } + return false; + } +} + +class ForgersFoundryExileEffect extends ReplacementEffectImpl { + + // we store both Spell and Card to work properly on split cards. + private final MageObjectReference morSpell; + private final MageObjectReference morCard; + + ForgersFoundryExileEffect(Spell spell, Game game) { + super(Duration.OneUse, Outcome.Benefit); + this.morSpell = new MageObjectReference(spell.getCard(), game); + this.morCard = new MageObjectReference(spell.getMainCard(), game); + } + + private ForgersFoundryExileEffect(final ForgersFoundryExileEffect effect) { + super(effect); + this.morSpell = effect.morSpell; + this.morCard = effect.morCard; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Spell sourceSpell = game.getStack().getSpell(morSpell.getSourceId()); + if (sourceSpell == null || sourceSpell.isCopy()) { + return false; + } + Player player = game.getPlayer(sourceSpell.getOwnerId()); + if (player == null) { + return false; + } + player.moveCardsToExile( + sourceSpell, source, game, false, + CardUtil.getExileZoneId(game, source), + CardUtil.getSourceName(game, source) + ); + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Spell sourceSpell = morSpell.getSpell(game); + if (sourceSpell == null || sourceSpell.isCopy()) { + return false; + } + Player player = game.getPlayer(sourceSpell.getOwnerId()); + if (player == null) { + return false; + } + ZoneChangeEvent zEvent = ((ZoneChangeEvent) event); + return Zone.STACK.equals(zEvent.getFromZone()) + && Zone.GRAVEYARD.equals(zEvent.getToZone()) + && morSpell.refersTo(event.getSourceId(), game) // this is how we check that the spell resolved properly (and was not countered or the like) + && morCard.refersTo(event.getTargetId(), game); // this is how we check that the card being moved is the one we want. + } + + @Override + public ForgersFoundryExileEffect copy() { + return new ForgersFoundryExileEffect(this); + } +} + +class ForgersFoundryCastEffect extends OneShotEffect { + + ForgersFoundryCastEffect() { + super(Outcome.Benefit); + staticText = "you may cast any number of spells from among cards exiled with " + + "{this} with total mana value X or less without paying their mana costs"; + } + + private ForgersFoundryCastEffect(final ForgersFoundryCastEffect effect) { + super(effect); + } + + @Override + public ForgersFoundryCastEffect copy() { + return new ForgersFoundryCastEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + if (exileZone == null || exileZone.isEmpty()) { + return false; + } + + Cards cards = new CardsImpl(exileZone); + if (player == null || cards.isEmpty()) { + return false; + } + + CardUtil.castMultipleWithAttributeForFree(player, source, game, cards, StaticFilters.FILTER_CARD); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/f/FriendlyNeighborhood.java b/Mage.Sets/src/mage/cards/f/FriendlyNeighborhood.java new file mode 100644 index 00000000000..d6a1c2b4e4f --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FriendlyNeighborhood.java @@ -0,0 +1,65 @@ +package mage.cards.f; + +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.CreaturesYouControlCount; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.permanent.token.HumanCitizenToken; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetLandPermanent; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class FriendlyNeighborhood extends CardImpl { + + public FriendlyNeighborhood(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}"); + + this.subtype.add(SubType.AURA); + + // Enchant land + TargetPermanent auraTarget = new TargetLandPermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.Benefit)); + this.addAbility(new EnchantAbility(auraTarget)); + + // When this Aura enters, create three 1/1 green and white Human Citizen creature tokens. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new HumanCitizenToken(), 3))); + + // Enchanted land has "{1}, {T}: Target creature gets +1/+1 until end of turn for each creature you control. Activate only as a sorcery." + Effect boostEffect = new BoostTargetEffect(CreaturesYouControlCount.SINGULAR, CreaturesYouControlCount.SINGULAR) + .setText("Target creature gets +1/+1 until end of turn for each creature you control"); + Ability ability = new ActivateAsSorceryActivatedAbility(boostEffect, + new ManaCostsImpl<>("{1}")); + ability.addCost(new TapSourceCost()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(new SimpleStaticAbility(new GainAbilityAttachedEffect(ability, AttachmentType.AURA, Duration.WhileOnBattlefield, null, "land") + .withQuotes(true))); + } + + private FriendlyNeighborhood(final FriendlyNeighborhood card) { + super(card); + } + + @Override + public FriendlyNeighborhood copy() { + return new FriendlyNeighborhood(this); + } +} diff --git a/Mage.Sets/src/mage/cards/f/FriendlyRivalry.java b/Mage.Sets/src/mage/cards/f/FriendlyRivalry.java index c9748da9aaf..d01788246fa 100644 --- a/Mage.Sets/src/mage/cards/f/FriendlyRivalry.java +++ b/Mage.Sets/src/mage/cards/f/FriendlyRivalry.java @@ -1,24 +1,17 @@ package mage.cards.f; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.TargetsDamageTargetsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SuperType; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.other.AnotherTargetPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; import java.util.UUID; /** @@ -37,7 +30,7 @@ public final class FriendlyRivalry extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}{G}"); // Target creature you control and up to one other target legendary creature you control each deal damage equal to their power to target creature you don't control. - this.getSpellAbility().addEffect(new FriendlyRivalryEffect()); + this.getSpellAbility().addEffect(new TargetsDamageTargetsEffect(true)); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent().setTargetTag(1).withChooseHint("to deal damage")); this.getSpellAbility().addTarget(new TargetPermanent(0, 1, filter2).setTargetTag(2).withChooseHint("to deal damage")); this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL).setTargetTag(3).withChooseHint("to take damage")); @@ -52,52 +45,3 @@ public final class FriendlyRivalry extends CardImpl { return new FriendlyRivalry(this); } } - -class FriendlyRivalryEffect extends OneShotEffect { - - FriendlyRivalryEffect() { - super(Outcome.Benefit); - staticText = "Target creature you control and up to one other target legendary " + - "creature you control each deal damage equal to their power to target creature you don't control."; - } - - private FriendlyRivalryEffect(final FriendlyRivalryEffect effect) { - super(effect); - } - - @Override - public FriendlyRivalryEffect copy() { - return new FriendlyRivalryEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - int size = source.getTargets().size(); - if (size < 3) { - throw new IllegalArgumentException("Wrong code usage. Lost targets list, must has 3, but found: " + source.getTargets()); - } - - List toDealDamage = new ArrayList<>(); - source.getTargets().getTargetsByTag(1).stream() - .map(game::getPermanent) - .filter(Objects::nonNull) - .forEach(toDealDamage::add); - source.getTargets().getTargetsByTag(2).stream() - .map(game::getPermanent) - .filter(Objects::nonNull) - .forEach(toDealDamage::add); - Permanent toTakeDamage = source.getTargets().getTargetsByTag(3).stream() - .map(game::getPermanent) - .filter(Objects::nonNull) - .findFirst().orElse(null); - if (toDealDamage.isEmpty() || toTakeDamage == null) { - return false; - } - - toDealDamage.forEach(permanent -> { - toTakeDamage.damage(permanent.getPower().getValue(), permanent.getId(), source, game, false, true); - }); - - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/f/FuriousRise.java b/Mage.Sets/src/mage/cards/f/FuriousRise.java index 76265389d70..baea853ba69 100644 --- a/Mage.Sets/src/mage/cards/f/FuriousRise.java +++ b/Mage.Sets/src/mage/cards/f/FuriousRise.java @@ -1,27 +1,13 @@ package mage.cards.f; -import mage.MageObject; -import mage.abilities.Ability; import mage.abilities.condition.common.FerociousCondition; -import mage.abilities.effects.AsThoughEffect; -import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileTopCardPlayUntilExileAnotherEffect; import mage.abilities.hint.common.FerociousHint; import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AsThoughEffectType; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.game.Game; -import mage.players.Player; -import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; -import java.util.List; import java.util.UUID; /** @@ -34,7 +20,7 @@ public final class FuriousRise extends CardImpl { // At the beginning of your end step, if you control a creature with power 4 or greater, exile the top card of your library. // You may play that card until you exile another card with Furious Rise. - this.addAbility(new BeginningOfEndStepTriggeredAbility(new FuriousRiseEffect()) + this.addAbility(new BeginningOfEndStepTriggeredAbility(new ExileTopCardPlayUntilExileAnotherEffect()) .withInterveningIf(FerociousCondition.instance).addHint(FerociousHint.instance)); } @@ -47,88 +33,3 @@ public final class FuriousRise extends CardImpl { return new FuriousRise(this); } } - -class FuriousRiseEffect extends OneShotEffect { - - FuriousRiseEffect() { - super(Outcome.Benefit); - this.staticText = "exile the top card of your library. You may play that card until you exile another card with {this}"; - } - - private FuriousRiseEffect(final FuriousRiseEffect effect) { - super(effect); - } - - @Override - public FuriousRiseEffect copy() { - return new FuriousRiseEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject mageObject = source.getSourceObject(game); - if (controller != null && mageObject != null) { - Card cardToExile = controller.getLibrary().getFromTop(game); - - UUID exileId = CardUtil.getCardExileZoneId(game, source); - controller.moveCardsToExile(cardToExile, source, game, true, exileId, mageObject.getIdName() + " (" + source.getSourceObjectZoneChangeCounter() + ")"); - Card cardToPlay = game.getCard(cardToExile.getId()); - - endPreviousEffect(game, source); // workaround for Furious Rise - - ContinuousEffect effect = new FuriousRisePlayEffect(); - effect.setTargetPointer(new FixedTarget(cardToPlay, game)); - game.addEffect(effect, source); - return true; - } - return false; - } - - private static boolean endPreviousEffect(Game game, Ability source) { - for (AsThoughEffect effect : game.getContinuousEffects().getApplicableAsThoughEffects(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, game)) { - if (effect instanceof FuriousRisePlayEffect) { - for (Ability ability : game.getContinuousEffects().getAsThoughEffectsAbility(effect)) { - if (ability.getSourceId().equals(source.getSourceId()) - && source.getSourceObjectZoneChangeCounter() == ability.getSourceObjectZoneChangeCounter()) { - effect.discard(); - return true; - } - } - } - } - return false; - } -} - -class FuriousRisePlayEffect extends AsThoughEffectImpl { - - FuriousRisePlayEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit); - } - - private FuriousRisePlayEffect(final FuriousRisePlayEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public FuriousRisePlayEffect copy() { - return new FuriousRisePlayEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - List targets = getTargetPointer().getTargets(game, source); - if (targets.isEmpty()) { - this.discard(); - return false; - } - return targets.contains(objectId) - && affectedControllerId.equals(source.getControllerId()); - } -} diff --git a/Mage.Sets/src/mage/cards/g/GOTOJAIL.java b/Mage.Sets/src/mage/cards/g/GOTOJAIL.java index 730b6accdfe..78ad6c1b278 100644 --- a/Mage.Sets/src/mage/cards/g/GOTOJAIL.java +++ b/Mage.Sets/src/mage/cards/g/GOTOJAIL.java @@ -13,13 +13,11 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; -import mage.target.common.TargetCreaturePermanent; import mage.util.CardUtil; import java.util.List; @@ -82,7 +80,7 @@ class GoToJailExileEffect extends OneShotEffect { if (controller != null) { game.getState().setValue(permanent.getId() + ChooseOpponentEffect.VALUE_KEY, controller.getId()); new ExileTargetEffect( - CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()), permanent.getIdName() + CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()), permanent.getIdName() ).apply(game, source); game.addDelayedTriggeredAbility(new OnLeaveReturnExiledAbility(), source); return true; diff --git a/Mage.Sets/src/mage/cards/g/GallantCitizen.java b/Mage.Sets/src/mage/cards/g/GallantCitizen.java new file mode 100644 index 00000000000..9130c4c2a26 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GallantCitizen.java @@ -0,0 +1,38 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GallantCitizen extends CardImpl { + + public GallantCitizen(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G/W}{G/W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CITIZEN); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // When this creature enters, draw a card. + this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1))); + } + + private GallantCitizen(final GallantCitizen card) { + super(card); + } + + @Override + public GallantCitizen copy() { + return new GallantCitizen(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GarthOneEye.java b/Mage.Sets/src/mage/cards/g/GarthOneEye.java index 4a2b385deeb..77fc0c215a2 100644 --- a/Mage.Sets/src/mage/cards/g/GarthOneEye.java +++ b/Mage.Sets/src/mage/cards/g/GarthOneEye.java @@ -140,7 +140,7 @@ class GarthOneEyeEffect extends OneShotEffect { static final String getKey(Ability source) { return source.getSourceId() + "_" - + source.getSourceObjectZoneChangeCounter() + "_" + + source.getStackMomentSourceZCC() + "_" + source.getOriginalId() + "_garth"; } } diff --git a/Mage.Sets/src/mage/cards/g/GeistOfSaintTraft.java b/Mage.Sets/src/mage/cards/g/GeistOfSaintTraft.java index a372e6f28d8..958c5c9ef1e 100644 --- a/Mage.Sets/src/mage/cards/g/GeistOfSaintTraft.java +++ b/Mage.Sets/src/mage/cards/g/GeistOfSaintTraft.java @@ -9,10 +9,7 @@ import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.HexproofAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.SuperType; +import mage.constants.*; import mage.game.Game; import mage.game.permanent.token.AngelToken; import mage.players.Player; @@ -65,7 +62,7 @@ class GeistOfSaintTraftEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null && effect.apply(game, source)) { - effect.exileTokensCreatedAtEndOfCombat(game, source); + effect.removeTokensCreatedAt(game, source, true, PhaseStep.END_COMBAT, TargetController.ANY); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/g/Ghostway.java b/Mage.Sets/src/mage/cards/g/Ghostway.java index cd3e87eed23..f26f40f3469 100644 --- a/Mage.Sets/src/mage/cards/g/Ghostway.java +++ b/Mage.Sets/src/mage/cards/g/Ghostway.java @@ -67,7 +67,7 @@ class GhostwayEffect extends OneShotEffect { if (sourceObject != null && controller != null) { Set toExile = new HashSet<>(); toExile.addAll(game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)); - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); controller.moveCardsToExile(toExile, source, game, true, exileId, sourceObject.getIdName()); Cards cardsToReturn = new CardsImpl(); diff --git a/Mage.Sets/src/mage/cards/g/Glaciers.java b/Mage.Sets/src/mage/cards/g/Glaciers.java index d13f9efb471..71d51987e41 100644 --- a/Mage.Sets/src/mage/cards/g/Glaciers.java +++ b/Mage.Sets/src/mage/cards/g/Glaciers.java @@ -51,6 +51,12 @@ public final class Glaciers extends CardImpl { GlaciersEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); this.staticText = "All Mountains are Plains"; + this.dependendToTypes.add(DependencyType.BecomeForest); + this.dependendToTypes.add(DependencyType.BecomeIsland); + this.dependendToTypes.add(DependencyType.BecomeMountain); + this.dependendToTypes.add(DependencyType.BecomePlains); + this.dependendToTypes.add(DependencyType.BecomeSwamp); + this.dependencyTypes.add(DependencyType.BecomePlains); } private GlaciersEffect(final GlaciersEffect effect) { diff --git a/Mage.Sets/src/mage/cards/g/GlissaSunslayer.java b/Mage.Sets/src/mage/cards/g/GlissaSunslayer.java index 13b5de6d9c4..91ee16b5ae6 100644 --- a/Mage.Sets/src/mage/cards/g/GlissaSunslayer.java +++ b/Mage.Sets/src/mage/cards/g/GlissaSunslayer.java @@ -4,19 +4,15 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.LoseLifeSourceControllerEffect; -import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.RemoveUpToAmountCountersEffect; import mage.abilities.keyword.DeathtouchAbility; import mage.abilities.keyword.FirstStrikeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetEnchantmentPermanent; @@ -54,7 +50,7 @@ public final class GlissaSunslayer extends CardImpl { ability.addMode(mode); // • Remove up to three counters from target permanent. - mode = new Mode(new GlissaSunslayerEffect()); + mode = new Mode(new RemoveUpToAmountCountersEffect(3)); mode.addTarget(new TargetPermanent()); ability.addMode(mode); @@ -69,55 +65,4 @@ public final class GlissaSunslayer extends CardImpl { public GlissaSunslayer copy() { return new GlissaSunslayer(this); } -} - -class GlissaSunslayerEffect extends OneShotEffect { - - GlissaSunslayerEffect() { - super(Outcome.AIDontUseIt); - staticText = "Remove up to three counters from target permanent"; - } - - private GlissaSunslayerEffect(final GlissaSunslayerEffect effect) { - super(effect); - } - - @Override - public GlissaSunslayerEffect copy() { - return new GlissaSunslayerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { - return false; - } - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent != null) { - int toRemove = 3; - int removed = 0; - String[] counterNames = permanent.getCounters(game).keySet().toArray(new String[0]); - for (String counterName : counterNames) { - if (controller.chooseUse(Outcome.Neutral, "Remove " + counterName + " counters?", source, game)) { - if (permanent.getCounters(game).get(counterName).getCount() == 1 || (toRemove - removed == 1)) { - permanent.removeCounters(counterName, 1, source, game); - removed++; - } else { - int amount = controller.getAmount(1, Math.min(permanent.getCounters(game).get(counterName).getCount(), toRemove - removed), "How many?", source, game); - if (amount > 0) { - removed += amount; - permanent.removeCounters(counterName, amount, source, game); - } - } - } - if (removed >= toRemove) { - break; - } - } - game.addEffect(new BoostSourceEffect(removed, 0, Duration.EndOfTurn), source); - return true; - } - return true; - } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/g/GloriousProtector.java b/Mage.Sets/src/mage/cards/g/GloriousProtector.java index f667a959a5e..77eadff6410 100644 --- a/Mage.Sets/src/mage/cards/g/GloriousProtector.java +++ b/Mage.Sets/src/mage/cards/g/GloriousProtector.java @@ -100,7 +100,7 @@ class GloriousProtectorEffect extends OneShotEffect { player.moveCardsToExile( new CardsImpl(target.getTargets()).getCards(game), source, game, true, CardUtil.getExileZoneId( - game, source.getSourceId(), source.getSourceObjectZoneChangeCounter() + game, source.getSourceId(), source.getStackMomentSourceZCC() ), sourceObject.getIdName() ); game.addDelayedTriggeredAbility(new OnLeaveReturnExiledAbility(), source); diff --git a/Mage.Sets/src/mage/cards/g/GolbezCrystalCollector.java b/Mage.Sets/src/mage/cards/g/GolbezCrystalCollector.java index d4e79a069d1..9d70650028f 100644 --- a/Mage.Sets/src/mage/cards/g/GolbezCrystalCollector.java +++ b/Mage.Sets/src/mage/cards/g/GolbezCrystalCollector.java @@ -81,7 +81,7 @@ enum GolbezCrystalCollectorValue implements DynamicValue { public int calculate(Game game, Ability sourceAbility, Effect effect) { return Optional .ofNullable(effect.getTargetPointer().getFirst(game, sourceAbility)) - .map(game::getPermanent) + .map(game::getCard) .map(MageObject::getPower) .map(MageInt::getValue) .orElse(0); diff --git a/Mage.Sets/src/mage/cards/g/GolemSkinGauntlets.java b/Mage.Sets/src/mage/cards/g/GolemSkinGauntlets.java index a61526367c8..2ca99ec2e71 100644 --- a/Mage.Sets/src/mage/cards/g/GolemSkinGauntlets.java +++ b/Mage.Sets/src/mage/cards/g/GolemSkinGauntlets.java @@ -1,36 +1,44 @@ package mage.cards.g; -import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.BoostEquippedEffect; import mage.abilities.keyword.EquipAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.permanent.AttachedToAttachedPredicate; -import java.util.List; import java.util.UUID; /** - * * @author North */ public final class GolemSkinGauntlets extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent("Equipment attached to it"); + + static { + filter.add(SubType.EQUIPMENT.getPredicate()); + filter.add(AttachedToAttachedPredicate.instance); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); public GolemSkinGauntlets(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{1}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); this.subtype.add(SubType.EQUIPMENT); // Equipped creature gets +1/+0 for each Equipment attached to it. + this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(xValue, StaticValue.get(0)))); + // Equip 2 (2: Attach to target creature you control. Equip only as a sorcery. This card enters the battlefield unattached and stays on the battlefield if the creature leaves.) - this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(new GolemSkinGauntletsAttachedCount(), StaticValue.get(0), Duration.WhileOnBattlefield))); this.addAbility(new EquipAbility(Outcome.BoostCreature, new GenericManaCost(2))); } @@ -43,49 +51,3 @@ public final class GolemSkinGauntlets extends CardImpl { return new GolemSkinGauntlets(this); } } - -// we can't use GolemSkinGauntletsAttachedCount -// compare to Goblin Gaveleer -class GolemSkinGauntletsAttachedCount implements DynamicValue { - - GolemSkinGauntletsAttachedCount() { - } - - private GolemSkinGauntletsAttachedCount(final GolemSkinGauntletsAttachedCount dynamicValue) { - } - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - int count = 0; - Permanent equipment = game.getPermanent(sourceAbility.getSourceId()); - if (equipment != null) { - Permanent permanent = game.getPermanent(equipment.getAttachedTo()); - if (permanent != null) { - List attachments = permanent.getAttachments(); - for (UUID attachmentId : attachments) { - Permanent attached = game.getPermanent(attachmentId); - if (attached != null && attached.hasSubtype(SubType.EQUIPMENT, game)) { - count++; - } - } - } - - } - return count; - } - - @Override - public GolemSkinGauntletsAttachedCount copy() { - return new GolemSkinGauntletsAttachedCount(this); - } - - @Override - public String toString() { - return "1"; - } - - @Override - public String getMessage() { - return "Equipment attached to it"; - } -} diff --git a/Mage.Sets/src/mage/cards/g/GontiNightMinister.java b/Mage.Sets/src/mage/cards/g/GontiNightMinister.java index 365c57648ff..af7783794e0 100644 --- a/Mage.Sets/src/mage/cards/g/GontiNightMinister.java +++ b/Mage.Sets/src/mage/cards/g/GontiNightMinister.java @@ -12,7 +12,6 @@ import mage.cards.Card; import mage.constants.*; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.filter.FilterSpell; import mage.game.Game; import mage.game.events.DamagedEvent; import mage.game.events.GameEvent; @@ -152,7 +151,7 @@ class GontiExileEffect extends OneShotEffect { if (card == null) { return false; } - UUID exileZoneId = CardUtil.getExileZoneId(game, controller.getId(), source.getSourceObjectZoneChangeCounter()); + UUID exileZoneId = CardUtil.getExileZoneId(game, controller.getId(), source.getStackMomentSourceZCC()); String exileName = CardUtil.getSourceName(game, source) + " - " + controller.getName(); if (controller.moveCardsToExile(card, source, game, false, exileZoneId, exileName)) { card.setFaceDown(true, game); diff --git a/Mage.Sets/src/mage/cards/g/GorexTheTombshell.java b/Mage.Sets/src/mage/cards/g/GorexTheTombshell.java index 691fac1f249..5f3ccc7a9c1 100644 --- a/Mage.Sets/src/mage/cards/g/GorexTheTombshell.java +++ b/Mage.Sets/src/mage/cards/g/GorexTheTombshell.java @@ -135,11 +135,11 @@ class GorexTheTombshellReturnEffect extends OneShotEffect { // relative zcc depends on object zone (battlefield for attacks trigger, graveyard for dies) // so try both zcc offsets to find zone ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId( - game, source.getSourceId(), source.getSourceObjectZoneChangeCounter() - 1 + game, source.getSourceId(), source.getStackMomentSourceZCC() - 1 )); if (exileZone == null || exileZone.isEmpty()) { exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId( - game, source.getSourceId(), source.getSourceObjectZoneChangeCounter() - 2 + game, source.getSourceId(), source.getStackMomentSourceZCC() - 2 )); if (exileZone == null || exileZone.isEmpty()) { return false; diff --git a/Mage.Sets/src/mage/cards/g/GracefulTakedown.java b/Mage.Sets/src/mage/cards/g/GracefulTakedown.java index 42225c2acb6..b8298e3b973 100644 --- a/Mage.Sets/src/mage/cards/g/GracefulTakedown.java +++ b/Mage.Sets/src/mage/cards/g/GracefulTakedown.java @@ -1,36 +1,38 @@ package mage.cards.g; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.TargetsDamageTargetsEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.other.AnotherTargetPredicate; import mage.filter.predicate.permanent.EnchantedPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.TargetPermanent; -import java.util.List; -import java.util.Objects; import java.util.UUID; -import java.util.stream.Collectors; /** * @author TheElk801 */ public final class GracefulTakedown extends CardImpl { + private static final FilterPermanent filter = new FilterControlledCreaturePermanent("enchanted creatures you control"); + private static final FilterControlledCreaturePermanent otherCreatureFilter = new FilterControlledCreaturePermanent("other target creature you control"); + static { + filter.add(EnchantedPredicate.instance); + otherCreatureFilter.add(new AnotherTargetPredicate(2)); + } + public GracefulTakedown(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{G}"); // Any number of target enchanted creatures you control and up to one other target creature you control each deal damage equal to their power to target creature you don't control. - this.getSpellAbility().addEffect(new GracefulTakedownEffect()); - this.getSpellAbility().addTarget(new GracefulTakedownTarget()); - this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL)); + this.getSpellAbility().addEffect(new TargetsDamageTargetsEffect(true)); + this.getSpellAbility().addTarget(new TargetPermanent(0, Integer.MAX_VALUE, filter).setTargetTag(1).withChooseHint("enchanted")); + this.getSpellAbility().addTarget(new TargetPermanent(0, 1, otherCreatureFilter).setTargetTag(2)); + this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_CREATURE_YOU_DONT_CONTROL).setTargetTag(3)); } private GracefulTakedown(final GracefulTakedown card) { @@ -42,78 +44,3 @@ public final class GracefulTakedown extends CardImpl { return new GracefulTakedown(this); } } - -class GracefulTakedownTarget extends TargetPermanent { - - private static final FilterPermanent filter = new FilterControlledCreaturePermanent("enchanted creatures you control and up to one other creature you control"); - - GracefulTakedownTarget() { - super(0, Integer.MAX_VALUE, filter); - } - - private GracefulTakedownTarget(final GracefulTakedownTarget target) { - super(target); - } - - @Override - public GracefulTakedownTarget copy() { - return new GracefulTakedownTarget(this); - } - - @Override - public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) { - if (!super.canTarget(playerId, id, source, game)) { - return false; - } - Permanent permanent = game.getPermanent(id); - return permanent != null - && (EnchantedPredicate.instance.apply(permanent, game) - || this - .getTargets() - .stream() - .map(game::getPermanent) - .filter(Objects::nonNull) - .allMatch(p -> p.getId().equals(id) || EnchantedPredicate.instance.apply(p, game))); - } -} - -class GracefulTakedownEffect extends OneShotEffect { - - GracefulTakedownEffect() { - super(Outcome.Benefit); - staticText = "any number of target enchanted creatures you control and up to one other target creature " + - "you control each deal damage equal to their power to target creature you don't control"; - } - - private GracefulTakedownEffect(final GracefulTakedownEffect effect) { - super(effect); - } - - @Override - public GracefulTakedownEffect copy() { - return new GracefulTakedownEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - List permanents = source - .getTargets() - .get(0) - .getTargets() - .stream() - .map(game::getPermanent) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - if (permanents.isEmpty()) { - return false; - } - Permanent creature = game.getPermanent(source.getTargets().get(1).getFirstTarget()); - if (creature == null) { - return false; - } - for (Permanent permanent : permanents) { - creature.damage(permanent.getPower().getValue(), permanent.getId(), source, game); - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/g/GrimoireThief.java b/Mage.Sets/src/mage/cards/g/GrimoireThief.java index e521250b5e0..18e81ea55b2 100644 --- a/Mage.Sets/src/mage/cards/g/GrimoireThief.java +++ b/Mage.Sets/src/mage/cards/g/GrimoireThief.java @@ -90,7 +90,7 @@ class GrimoireThiefExileEffect extends OneShotEffect { card.setFaceDown(true, game); } UUID exileZoneId = CardUtil.getExileZoneId(game, - source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + source.getSourceId(), source.getStackMomentSourceZCC()); targetOpponent.moveCardsToExile(cards, source, game, false, exileZoneId, sourceObject.getIdName()); for (Card card : cards) { diff --git a/Mage.Sets/src/mage/cards/g/GrizzledAngler.java b/Mage.Sets/src/mage/cards/g/GrizzledAngler.java index dbe16de1fbc..ac856108974 100644 --- a/Mage.Sets/src/mage/cards/g/GrizzledAngler.java +++ b/Mage.Sets/src/mage/cards/g/GrizzledAngler.java @@ -48,6 +48,7 @@ public final class GrizzledAngler extends CardImpl { new TransformSourceEffect(), condition, "Then if there is a colorless creature card in your graveyard, transform {this}" )); + this.addAbility(ability); } private GrizzledAngler(final GrizzledAngler card) { diff --git a/Mage.Sets/src/mage/cards/g/GrothamaAllDevouring.java b/Mage.Sets/src/mage/cards/g/GrothamaAllDevouring.java index 76598f8f73e..7830b4f8fe0 100644 --- a/Mage.Sets/src/mage/cards/g/GrothamaAllDevouring.java +++ b/Mage.Sets/src/mage/cards/g/GrothamaAllDevouring.java @@ -19,7 +19,6 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.WatcherScope; -import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; @@ -152,7 +151,7 @@ class GrothamaAllDevouringDrawCardsEffect extends OneShotEffect { if (watcher == null) { return false; } - Map damageMap = watcher.getDamageMap(new MageObjectReference(source.getSourceId(), source.getSourceObjectZoneChangeCounter() - 1, game)); + Map damageMap = watcher.getDamageMap(new MageObjectReference(source.getSourceId(), source.getStackMomentSourceZCC() - 1, game)); for (UUID playerId : game.getPlayerList()) { Player player = game.getPlayer(playerId); if (player != null) { diff --git a/Mage.Sets/src/mage/cards/g/GwenStacy.java b/Mage.Sets/src/mage/cards/g/GwenStacy.java new file mode 100644 index 00000000000..cfb9cbbfc5e --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GwenStacy.java @@ -0,0 +1,67 @@ +package mage.cards.g; + +import mage.abilities.common.*; +import mage.abilities.costs.common.RemoveCountersSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.ExileTopXMayPlayUntilEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacedCard; +import mage.constants.*; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class GwenStacy extends ModalDoubleFacedCard { + + public GwenStacy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, + new SuperType[]{SuperType.LEGENDARY}, new CardType[]{CardType.CREATURE}, new SubType[]{SubType.HUMAN, SubType.PERFORMER, SubType.HERO}, "{1}{R}", + "Ghost-Spider", + new SuperType[]{SuperType.LEGENDARY}, new CardType[]{CardType.CREATURE}, new SubType[]{SubType.SPIDER, SubType.HUMAN, SubType.HERO}, "{2}{U}{R}{W}"); + + this.getLeftHalfCard().setPT(2, 1); + this.getRightHalfCard().setPT(4, 4); + + // When Gwen Stacy enters, exile the top card of your library. You may play that card for as long as you control this creature. + this.getLeftHalfCard().addAbility(new EntersBattlefieldTriggeredAbility(new ExileTopXMayPlayUntilEffect(1, Duration.WhileControlled) + .withTextOptions("that card", true))); + // {2}{U}{R}{W}: Transform Gwen Stacy. Activate only as a sorcery. + this.getLeftHalfCard().addAbility(new ActivateAsSorceryActivatedAbility(new TransformSourceEffect(), new ManaCostsImpl<>("{2}{U}{R}{W}"))); + + // Ghost-Spider + // Flying + this.getRightHalfCard().addAbility(FlyingAbility.getInstance()); + + // Vigilance + this.getRightHalfCard().addAbility(VigilanceAbility.getInstance()); + + // Haste + this.getRightHalfCard().addAbility(HasteAbility.getInstance()); + + // Whenever you play a land from exile or cast a spell from exile, put a +1/+1 counter on Ghost-Spider. + this.getRightHalfCard().addAbility(new PlayLandOrCastSpellTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), true, false)); + // Remove two counters from Ghost-Spider: Exile the top card of your library. You may play that card this turn. + this.getRightHalfCard().addAbility(new SimpleActivatedAbility( + new ExileTopXMayPlayUntilEffect(1, Duration.EndOfTurn), + new RemoveCountersSourceCost(2))); + } + + private GwenStacy(final GwenStacy card) { + super(card); + } + + @Override + public GwenStacy copy() { + return new GwenStacy(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GwenomRemorseless.java b/Mage.Sets/src/mage/cards/g/GwenomRemorseless.java new file mode 100644 index 00000000000..dc637d370e3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GwenomRemorseless.java @@ -0,0 +1,124 @@ +package mage.cards.g; + +import mage.MageIdentifier; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.costs.Costs; +import mage.abilities.costs.CostsImpl; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.common.AddContinuousEffectToGame; +import mage.abilities.effects.common.continuous.LookAtTopCardOfLibraryAnyTimeEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class GwenomRemorseless extends CardImpl { + + public GwenomRemorseless(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SYMBIOTE); + this.subtype.add(SubType.SPIDER); + this.subtype.add(SubType.HERO); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Whenever Gwenom attacks, until end of turn you may look at the top card of your library any time and you may play cards from the top of your library. If you cast a spell this way, pay life equal to its mana value rather than pay its mana cost. + ContinuousEffect libraryAnyTimeEffect = new LookAtTopCardOfLibraryAnyTimeEffect(Duration.EndOfTurn); + libraryAnyTimeEffect.setText("until end of turn you may look at the top card of your library any time"); + libraryAnyTimeEffect.concatBy(" "); + AsThoughEffectImpl playCardEffect = new GwenomRemorselessPlayTopCardEffect(); + playCardEffect.concatBy("and"); + this.addAbility(new AttacksTriggeredAbility(new AddContinuousEffectToGame(libraryAnyTimeEffect, playCardEffect)) + .setIdentifier(MageIdentifier.GwenomRemorselessAlternateCast)); + } + + private GwenomRemorseless(final GwenomRemorseless card) { + super(card); + } + + @Override + public GwenomRemorseless copy() { + return new GwenomRemorseless(this); + } +} + +class GwenomRemorselessPlayTopCardEffect extends AsThoughEffectImpl { + + GwenomRemorselessPlayTopCardEffect() { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, + Duration.EndOfTurn, Outcome.AIDontUseIt); // AI will need help with this + staticText = "you may play cards from the top of your library. If you cast a spell this way, " + + "pay life equal to its mana value rather than pay its mana cost."; + } + + private GwenomRemorselessPlayTopCardEffect(final GwenomRemorselessPlayTopCardEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public GwenomRemorselessPlayTopCardEffect copy() { + return new GwenomRemorselessPlayTopCardEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + // current card's part + Card cardToCheck = game.getCard(objectId); + if (cardToCheck == null) { + return false; + } + + // must be you + if (!affectedControllerId.equals(source.getControllerId())) { + return false; + } + + // must be your card + Player player = game.getPlayer(cardToCheck.getOwnerId()); + if (player == null || !player.getId().equals(affectedControllerId)) { + return false; + } + + // must be from your library + Card topCard = player.getLibrary().getFromTop(game); + if (topCard == null || !topCard.getId().equals(cardToCheck.getMainCard().getId())) { + return false; + } + // allows to play/cast with alternative life cost + if (!cardToCheck.isLand(game)) { + PayLifeCost lifeCost = new PayLifeCost(cardToCheck.getSpellAbility().getManaCosts().manaValue()); + Costs newCosts = new CostsImpl(); + newCosts.add(lifeCost); + newCosts.addAll(cardToCheck.getSpellAbility().getCosts()); + player.setCastSourceIdWithAlternateMana(cardToCheck.getId(), null, newCosts, MageIdentifier.GwenomRemorselessAlternateCast); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/h/HakodaSelflessCommander.java b/Mage.Sets/src/mage/cards/h/HakodaSelflessCommander.java index 09523f531aa..1f16dd1a827 100644 --- a/Mage.Sets/src/mage/cards/h/HakodaSelflessCommander.java +++ b/Mage.Sets/src/mage/cards/h/HakodaSelflessCommander.java @@ -27,7 +27,7 @@ import java.util.UUID; */ public final class HakodaSelflessCommander extends CardImpl { - private static final FilterCard filter = new FilterCard(SubType.ALLY, "Ally spells"); + private static final FilterCard filter = new FilterCard(SubType.ALLY, "cast Ally spells"); public HakodaSelflessCommander(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); diff --git a/Mage.Sets/src/mage/cards/h/HaktosTheUnscarred.java b/Mage.Sets/src/mage/cards/h/HaktosTheUnscarred.java index 170498ae371..dc4c3aebd73 100644 --- a/Mage.Sets/src/mage/cards/h/HaktosTheUnscarred.java +++ b/Mage.Sets/src/mage/cards/h/HaktosTheUnscarred.java @@ -95,7 +95,7 @@ class HaktosTheUnscarredChooseEffect extends OneShotEffect { } int number = 2 + RandomUtil.nextInt(3); game.informPlayers(permanent.getLogName() + ": " + controller.getLogName() + " has chosen " + number + " at random"); - game.getState().setValue(permanent.getId() + "" + source.getSourceObjectZoneChangeCounter() + "_haktos_number", number); + game.getState().setValue(permanent.getId() + "" + source.getStackMomentSourceZCC() + "_haktos_number", number); permanent.addInfo("chosen number", CardUtil.addToolTipMarkTags("Chosen number: " + number), game); return true; } diff --git a/Mage.Sets/src/mage/cards/h/HarbingerOfTheSeas.java b/Mage.Sets/src/mage/cards/h/HarbingerOfTheSeas.java index f7856b72692..8a7f1d34cc2 100644 --- a/Mage.Sets/src/mage/cards/h/HarbingerOfTheSeas.java +++ b/Mage.Sets/src/mage/cards/h/HarbingerOfTheSeas.java @@ -54,6 +54,7 @@ class HarbingerOfTheSeasEffect extends ContinuousEffectImpl { HarbingerOfTheSeasEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); this.staticText = "Nonbasic lands are Islands"; + dependendToTypes.add(DependencyType.BecomeNonbasicLand); dependencyTypes.add(DependencyType.BecomeIsland); } diff --git a/Mage.Sets/src/mage/cards/h/HaukensInsight.java b/Mage.Sets/src/mage/cards/h/HaukensInsight.java index 19b896ecdd0..d2b944a5f9e 100644 --- a/Mage.Sets/src/mage/cards/h/HaukensInsight.java +++ b/Mage.Sets/src/mage/cards/h/HaukensInsight.java @@ -80,7 +80,7 @@ class HaukensInsightExileEffect extends OneShotEffect { if (controller != null) { Card card = controller.getLibrary().getFromTop(game); if (card != null) { - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); MageObject sourceObject = source.getSourceObject(game); String exileName = sourceObject == null ? null : sourceObject.getIdName(); card.setFaceDown(true, game); diff --git a/Mage.Sets/src/mage/cards/h/HeartlessAct.java b/Mage.Sets/src/mage/cards/h/HeartlessAct.java index 7451941044b..3a2e701c8ac 100644 --- a/Mage.Sets/src/mage/cards/h/HeartlessAct.java +++ b/Mage.Sets/src/mage/cards/h/HeartlessAct.java @@ -1,21 +1,14 @@ package mage.cards.h; -import mage.abilities.Ability; import mage.abilities.Mode; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect; -import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.RemoveUpToAmountCountersEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.CounterAnyPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; @@ -42,7 +35,7 @@ public final class HeartlessAct extends CardImpl { this.getSpellAbility().addTarget(new TargetPermanent(filterWithoutCounters)); // • Remove up to three counters from target creature. - Mode mode = new Mode(new HeartlessActEffect()); + Mode mode = new Mode(new RemoveUpToAmountCountersEffect(3)); mode.addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addMode(mode); } @@ -55,55 +48,4 @@ public final class HeartlessAct extends CardImpl { public HeartlessAct copy() { return new HeartlessAct(this); } -} - -class HeartlessActEffect extends OneShotEffect { - - HeartlessActEffect() { - super(Outcome.AIDontUseIt); - staticText = "Remove up to three counters from target creature"; - } - - private HeartlessActEffect(final HeartlessActEffect effect) { - super(effect); - } - - @Override - public HeartlessActEffect copy() { - return new HeartlessActEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { - return false; - } - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent != null) { - int toRemove = 3; - int removed = 0; - String[] counterNames = permanent.getCounters(game).keySet().toArray(new String[0]); - for (String counterName : counterNames) { - if (controller.chooseUse(Outcome.Neutral, "Remove " + counterName + " counters?", source, game)) { - if (permanent.getCounters(game).get(counterName).getCount() == 1 || (toRemove - removed == 1)) { - permanent.removeCounters(counterName, 1, source, game); - removed++; - } else { - int amount = controller.getAmount(1, Math.min(permanent.getCounters(game).get(counterName).getCount(), toRemove - removed), "How many?", source, game); - if (amount > 0) { - removed += amount; - permanent.removeCounters(counterName, amount, source, game); - } - } - } - if (removed >= toRemove) { - break; - } - } - game.addEffect(new BoostSourceEffect(removed, 0, Duration.EndOfTurn), source); - return true; - } - return true; - } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/h/HedronAlignment.java b/Mage.Sets/src/mage/cards/h/HedronAlignment.java index bb0339d9e31..b84cd9b539e 100644 --- a/Mage.Sets/src/mage/cards/h/HedronAlignment.java +++ b/Mage.Sets/src/mage/cards/h/HedronAlignment.java @@ -2,12 +2,12 @@ package mage.cards.h; import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.keyword.ScryEffect; import mage.abilities.keyword.HexproofAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.cards.Cards; @@ -15,7 +15,6 @@ import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.TargetController; -import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.FilterPermanent; import mage.filter.predicate.mageobject.NamePredicate; @@ -93,9 +92,7 @@ class HedronAlignmentEffect extends OneShotEffect { if (controller.getGraveyard().getCards(filterCard, controller.getId(), source, game).isEmpty()) { return true; } - Cards cardsToCheck = new CardsImpl(); - cardsToCheck.addAllCards(game.getExile().getAllCards(game)); - if (cardsToCheck.count(filterCard, controller.getId(), source, game) == 0) { + if (game.getExile().getCardsOwned(filterCard, controller.getId(), source, game).isEmpty()) { return true; } controller.won(game); diff --git a/Mage.Sets/src/mage/cards/h/HenzieToolboxTorre.java b/Mage.Sets/src/mage/cards/h/HenzieToolboxTorre.java index fa81d19c614..6b7e136be49 100644 --- a/Mage.Sets/src/mage/cards/h/HenzieToolboxTorre.java +++ b/Mage.Sets/src/mage/cards/h/HenzieToolboxTorre.java @@ -87,7 +87,7 @@ class HenzieToolboxTorreGainBlitzEffect extends ContinuousEffectImpl { controller.getLibrary().getCards(game).stream() .filter(c -> filter.match(c, game)) .forEach(cardsToGainBlitz::add); - game.getExile().getAllCardsByRange(game, controller.getId()).stream() + game.getExile().getCardsInRange(game, controller.getId()).stream() .filter(c -> filter.match(c, game)) .forEach(cardsToGainBlitz::add); game.getCommanderCardsFromCommandZone(controller, CommanderCardType.ANY) diff --git a/Mage.Sets/src/mage/cards/h/HerigastEruptingNullkite.java b/Mage.Sets/src/mage/cards/h/HerigastEruptingNullkite.java index 4fdba0df37c..59b0c78edb0 100644 --- a/Mage.Sets/src/mage/cards/h/HerigastEruptingNullkite.java +++ b/Mage.Sets/src/mage/cards/h/HerigastEruptingNullkite.java @@ -87,7 +87,7 @@ class HerigastEruptingNullkiteEffect extends ContinuousEffectImpl { controller.getLibrary().getCards(game).stream() .filter(c -> StaticFilters.FILTER_CARD_CREATURE.match(c, game)) .forEach(cardsToGainEmerge::add); - game.getExile().getAllCardsByRange(game, controller.getId()).stream() + game.getExile().getCardsInRange(game, controller.getId()).stream() .filter(c -> StaticFilters.FILTER_CARD_CREATURE.match(c, game)) .forEach(cardsToGainEmerge::add); game.getCommanderCardsFromCommandZone(controller, CommanderCardType.ANY) diff --git a/Mage.Sets/src/mage/cards/h/HeroesHangout.java b/Mage.Sets/src/mage/cards/h/HeroesHangout.java new file mode 100644 index 00000000000..64bca996bad --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HeroesHangout.java @@ -0,0 +1,41 @@ +package mage.cards.h; + +import mage.abilities.Mode; +import mage.abilities.effects.common.ExileTopXMayPlayUntilEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class HeroesHangout extends CardImpl { + + public HeroesHangout(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}"); + + + // Choose one -- + // * Date Night -- Exile the top two cards of your library. Choose one of them. Until the end of your next turn, you may play that card. + this.getSpellAbility().addEffect(new ExileTopXMayPlayUntilEffect(2, true, Duration.UntilEndOfYourNextTurn)); + + // * Patrol Night -- One or two target creatures each get +1/+0 and gain first strike until end of turn. + this.getSpellAbility().addMode(new Mode(new BoostTargetEffect(1, 0)).addTarget(new TargetCreaturePermanent(1, 2))); + + } + + private HeroesHangout(final HeroesHangout card) { + super(card); + } + + @Override + public HeroesHangout copy() { + return new HeroesHangout(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HideOnTheCeiling.java b/Mage.Sets/src/mage/cards/h/HideOnTheCeiling.java new file mode 100644 index 00000000000..8c6dd4b8ca4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HideOnTheCeiling.java @@ -0,0 +1,51 @@ +package mage.cards.h; + +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.ExileReturnBattlefieldNextEndStepTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.target.TargetPermanent; +import mage.target.targetadjustment.XTargetsCountAdjuster; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class HideOnTheCeiling extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("artifacts and/or creatures"); + + static { + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.CREATURE.getPredicate() + )); + } + + public HideOnTheCeiling(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{U}"); + + + // Exile X target artifacts and/or creatures. Return the exiled cards to the battlefield under their owners' control at the beginning of the next end step. + Effect effect = new ExileReturnBattlefieldNextEndStepTargetEffect() + .returnExiledOnly(true); + effect.setText("Exile X target artifacts and/or creatures. Return the exiled cards to the battlefield under their owners' control at the beginning of the next end step"); + this.getSpellAbility().addEffect(effect); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().setTargetAdjuster(new XTargetsCountAdjuster()); + } + + private HideOnTheCeiling(final HideOnTheCeiling card) { + super(card); + } + + @Override + public HideOnTheCeiling copy() { + return new HideOnTheCeiling(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HobgoblinMantledMarauder.java b/Mage.Sets/src/mage/cards/h/HobgoblinMantledMarauder.java new file mode 100644 index 00000000000..74d5882600e --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HobgoblinMantledMarauder.java @@ -0,0 +1,50 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.abilities.effects.common.DiscardCardControllerTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HobgoblinMantledMarauder extends CardImpl { + + public HobgoblinMantledMarauder(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.GOBLIN); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Whenever you discard a card, Hobgoblin gets +2/+0 until end of turn. + this.addAbility(new DiscardCardControllerTriggeredAbility(new BoostSourceEffect(2, 0, Duration.EndOfTurn), false)); + } + + private HobgoblinMantledMarauder(final HobgoblinMantledMarauder card) { + super(card); + } + + @Override + public HobgoblinMantledMarauder copy() { + return new HobgoblinMantledMarauder(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HopeOfGhirapur.java b/Mage.Sets/src/mage/cards/h/HopeOfGhirapur.java index 34598fa9a96..8daafe3625b 100644 --- a/Mage.Sets/src/mage/cards/h/HopeOfGhirapur.java +++ b/Mage.Sets/src/mage/cards/h/HopeOfGhirapur.java @@ -164,7 +164,7 @@ class HopeOfGhirapurCombatDamageWatcher extends Watcher { MageObjectReference mor; if (stackObject instanceof StackAbility) { // This is neccessary because the source object was sacrificed as cost and the correct zone change counter for target calid check can only be get from stack - mor = new MageObjectReference(objectId, ((StackAbility) stackObject).getSourceObjectZoneChangeCounter(), game); + mor = new MageObjectReference(objectId, ((StackAbility) stackObject).getStackMomentSourceZCC(), game); } else { mor = new MageObjectReference(objectId, game); } diff --git a/Mage.Sets/src/mage/cards/h/HotDogCart.java b/Mage.Sets/src/mage/cards/h/HotDogCart.java new file mode 100644 index 00000000000..89f69f2f668 --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HotDogCart.java @@ -0,0 +1,36 @@ +package mage.cards.h; + +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.mana.AnyColorManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.game.permanent.token.FoodToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class HotDogCart extends CardImpl { + + public HotDogCart(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + // When this artifact enters, create a Food token. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new FoodToken()))); + + // {T}: Add one mana of any color. + this.addAbility(new AnyColorManaAbility()); + } + + private HotDogCart(final HotDogCart card) { + super(card); + } + + @Override + public HotDogCart copy() { + return new HotDogCart(this); + } +} diff --git a/Mage.Sets/src/mage/cards/h/HowlingGalefang.java b/Mage.Sets/src/mage/cards/h/HowlingGalefang.java index 6a4634a1715..57b49fe5903 100644 --- a/Mage.Sets/src/mage/cards/h/HowlingGalefang.java +++ b/Mage.Sets/src/mage/cards/h/HowlingGalefang.java @@ -59,7 +59,7 @@ enum HowlingGalefangCondition implements Condition { public boolean apply(Game game, Ability source) { return game .getExile() - .getAllCards(game, source.getControllerId()) + .getCardsOwned(game, source.getControllerId()) .stream() .anyMatch(AdventureCard.class::isInstance); } @@ -67,4 +67,4 @@ enum HowlingGalefangCondition implements Condition { public static Hint getHint() { return hint; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/h/HuskbursterSwarm.java b/Mage.Sets/src/mage/cards/h/HuskbursterSwarm.java index 494ed2bf251..6fa31d69b4b 100644 --- a/Mage.Sets/src/mage/cards/h/HuskbursterSwarm.java +++ b/Mage.Sets/src/mage/cards/h/HuskbursterSwarm.java @@ -77,13 +77,8 @@ enum HuskbursterSwarmValue implements DynamicValue { } return game .getExile() - .getAllCards(game) - .stream() - .filter(card -> card.isCreature(game)) - .map(Ownerable::getOwnerId) - .filter(sourceAbility::isControlledBy) - .mapToInt(x -> 1) - .sum() + .getCardsOwned(StaticFilters.FILTER_CARD_CREATURE, player.getId(), sourceAbility, game) + .size() + player .getGraveyard() .count(StaticFilters.FILTER_CARD_CREATURE, game); diff --git a/Mage.Sets/src/mage/cards/h/HydroManFluidFelon.java b/Mage.Sets/src/mage/cards/h/HydroManFluidFelon.java new file mode 100644 index 00000000000..f8befbd42cf --- /dev/null +++ b/Mage.Sets/src/mage/cards/h/HydroManFluidFelon.java @@ -0,0 +1,116 @@ +package mage.cards.h; + +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.SourceMatchesFilterCondition; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.UntapSourceEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterSpell; +import mage.filter.StaticFilters; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.awt.*; +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class HydroManFluidFelon extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("a blue spell"); + + private static final Condition condition = new SourceMatchesFilterCondition( + "this {this} is a creature", StaticFilters.FILTER_PERMANENT_CREATURE + ); + + static { + filter.add(new ColorPredicate(ObjectColor.BLUE)); + } + + public HydroManFluidFelon(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever you cast a blue spell, if Hydro-Man is a creature, he gets +1/+1 until end of turn. + this.addAbility(new SpellCastControllerTriggeredAbility( + new BoostSourceEffect(1, 1, Duration.EndOfTurn), filter,false) + .withInterveningIf(condition).withRuleTextReplacement(true)); + + // At the beginning of your end step, untap Hydro-Man. Until your next turn, he becomes a land and gains "{T}: Add {U}." + Ability ability = new BeginningOfEndStepTriggeredAbility(new UntapSourceEffect()); + ability.addEffect(new HydroManFluidFelonEffect()); + this.addAbility(ability); + } + + private HydroManFluidFelon(final HydroManFluidFelon card) { + super(card); + } + + @Override + public HydroManFluidFelon copy() { + return new HydroManFluidFelon(this); + } +} +class HydroManFluidFelonEffect extends ContinuousEffectImpl { + + HydroManFluidFelonEffect() { + super(Duration.UntilYourNextTurn, Outcome.Neutral); + this.staticText = "Until your next turn, he becomes a land and gains \"{T}: Add {U}.\""; + this.addDependencyType(DependencyType.BecomeNonbasicLand); + } + + protected HydroManFluidFelonEffect(final HydroManFluidFelonEffect effect) { + super(effect); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent == null) { + return false; + } + switch (layer) { + case TypeChangingEffects_4: + permanent.removeAllCardTypes(game); + permanent.removeAllCreatureTypes(game); + permanent.addCardType(game, CardType.LAND); + break; + case AbilityAddingRemovingEffects_6: + permanent.addAbility(new BlueManaAbility(), source.getSourceId(), game); + break; + } + return true; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.TypeChangingEffects_4 || layer == Layer.AbilityAddingRemovingEffects_6; + } + + @Override + public HydroManFluidFelonEffect copy() { + return new HydroManFluidFelonEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/IanTheReckless.java b/Mage.Sets/src/mage/cards/i/IanTheReckless.java index f809522becf..659edde93e2 100644 --- a/Mage.Sets/src/mage/cards/i/IanTheReckless.java +++ b/Mage.Sets/src/mage/cards/i/IanTheReckless.java @@ -3,8 +3,7 @@ package mage.cards.i; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; -import mage.abilities.condition.Condition; -import mage.abilities.condition.common.SourceMatchesFilterCondition; +import mage.abilities.condition.common.SourceModifiedCondition; import mage.abilities.dynamicvalue.common.SourcePermanentPowerValue; import mage.abilities.effects.common.DamageControllerEffect; import mage.abilities.effects.common.DamageTargetEffect; @@ -13,8 +12,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.ModifiedPredicate; import mage.target.common.TargetAnyTarget; import java.util.UUID; @@ -24,14 +21,6 @@ import java.util.UUID; */ public final class IanTheReckless extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("it's modified"); - - static { - filter.add(ModifiedPredicate.instance); - } - - private static final Condition condition = new SourceMatchesFilterCondition(filter); - public IanTheReckless(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); @@ -42,8 +31,10 @@ public final class IanTheReckless extends CardImpl { this.toughness = new MageInt(1); // Whenever Ian the Reckless attacks, if it's modified, you may have it deal damage equal to its power to you and any target. - Ability ability = new AttacksTriggeredAbility(new DamageControllerEffect(SourcePermanentPowerValue.NOT_NEGATIVE) - .setText("have it deal damage equal to its power to you"), true).withInterveningIf(condition); + Ability ability = new AttacksTriggeredAbility( + new DamageControllerEffect(SourcePermanentPowerValue.NOT_NEGATIVE) + .setText("have it deal damage equal to its power to you"), true + ).withInterveningIf(SourceModifiedCondition.instance); ability.addEffect(new DamageTargetEffect(SourcePermanentPowerValue.NOT_NEGATIVE).setText("and any target")); ability.addTarget(new TargetAnyTarget()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/i/ImpostorSyndrome.java b/Mage.Sets/src/mage/cards/i/ImpostorSyndrome.java new file mode 100644 index 00000000000..f6ea5272b05 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/ImpostorSyndrome.java @@ -0,0 +1,39 @@ +package mage.cards.i; + +import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility; +import mage.abilities.effects.common.CreateTokenCopyTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SetTargetPointer; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ImpostorSyndrome extends CardImpl { + + public ImpostorSyndrome(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{U}{U}"); + + // Whenever a nontoken creature you control deals combat damage to a player, create a token that's a copy of it, except it isn't legendary. + this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility( + new CreateTokenCopyTargetEffect() + .setIsntLegendary(true) + .setText("create a token that's a copy of it, except it isn't legendary"), + StaticFilters.FILTER_CONTROLLED_CREATURE_NON_TOKEN, false, + SetTargetPointer.PERMANENT, true + )); + } + + private ImpostorSyndrome(final ImpostorSyndrome card) { + super(card); + } + + @Override + public ImpostorSyndrome copy() { + return new ImpostorSyndrome(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/InfectiousRage.java b/Mage.Sets/src/mage/cards/i/InfectiousRage.java index ecf4aba4374..b341977041e 100644 --- a/Mage.Sets/src/mage/cards/i/InfectiousRage.java +++ b/Mage.Sets/src/mage/cards/i/InfectiousRage.java @@ -83,7 +83,7 @@ class InfectiousRageReattachEffect extends OneShotEffect { if (controller == null || auraCard == null) { return false; } - if (source.getSourceObjectZoneChangeCounter() != auraCard.getZoneChangeCounter(game)) { + if (source.getStackMomentSourceZCC() != auraCard.getZoneChangeCounter(game)) { return false; } diff --git a/Mage.Sets/src/mage/cards/i/InfernoTrap.java b/Mage.Sets/src/mage/cards/i/InfernoTrap.java index 4a57ffd396f..3b0f6426fb3 100644 --- a/Mage.Sets/src/mage/cards/i/InfernoTrap.java +++ b/Mage.Sets/src/mage/cards/i/InfernoTrap.java @@ -63,7 +63,7 @@ enum InfernoTrapCondition implements Condition { @Override public String toString() { - return "If you've been dealt damage by two or more creatures this turn"; + return "you've been dealt damage by two or more creatures this turn"; } } diff --git a/Mage.Sets/src/mage/cards/i/InitiatesOfTheEbonHand.java b/Mage.Sets/src/mage/cards/i/InitiatesOfTheEbonHand.java index 408079e1561..3136d4bfa93 100644 --- a/Mage.Sets/src/mage/cards/i/InitiatesOfTheEbonHand.java +++ b/Mage.Sets/src/mage/cards/i/InitiatesOfTheEbonHand.java @@ -66,7 +66,7 @@ class InitiatesOfTheEbonHandEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - ActivationInfo activationInfo = ActivationInfo.getInstance(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + ActivationInfo activationInfo = ActivationInfo.getInstance(game, source.getSourceId(), source.getStackMomentSourceZCC()); activationInfo.addActivation(game); if (activationInfo.getActivationCounter() == 4) { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new SacrificeSourceEffect()); diff --git a/Mage.Sets/src/mage/cards/i/InkTreaderNephilim.java b/Mage.Sets/src/mage/cards/i/InkTreaderNephilim.java index 9f584c8820d..e6122255b1d 100644 --- a/Mage.Sets/src/mage/cards/i/InkTreaderNephilim.java +++ b/Mage.Sets/src/mage/cards/i/InkTreaderNephilim.java @@ -99,8 +99,8 @@ class InkTreaderNephilimTriggeredAbility extends TriggeredAbilityImpl { if (permanent == null || !permanent.getId().equals(getSourceId())) { return false; } - if (getSourceObjectZoneChangeCounter() != 0 - && getSourceObjectZoneChangeCounter() != permanent.getZoneChangeCounter(game)) { + if (getStackMomentSourceZCC() != 0 + && getStackMomentSourceZCC() != permanent.getZoneChangeCounter(game)) { return false; } flag = true; diff --git a/Mage.Sets/src/mage/cards/i/InnerDemonsGangsters.java b/Mage.Sets/src/mage/cards/i/InnerDemonsGangsters.java new file mode 100644 index 00000000000..4e46530dd65 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InnerDemonsGangsters.java @@ -0,0 +1,51 @@ +package mage.cards.i; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class InnerDemonsGangsters extends CardImpl { + + public InnerDemonsGangsters(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ROGUE); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Discard a card: This creature gets +1/+0 and gains menace until end of turn. Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility( + new BoostSourceEffect(1, 0 , Duration.EndOfTurn).setText("{this} gets +1/+0"), + new DiscardCardCost() + ); + ability.addEffect(new GainAbilitySourceEffect(new MenaceAbility(), Duration.EndOfTurn) + .setText("and gains menace until end of turn")); + this.addAbility(ability); + } + + private InnerDemonsGangsters(final InnerDemonsGangsters card) { + super(card); + } + + @Override + public InnerDemonsGangsters copy() { + return new InnerDemonsGangsters(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/IntellectDevourer.java b/Mage.Sets/src/mage/cards/i/IntellectDevourer.java index b6bd578112e..1e84ac9092f 100644 --- a/Mage.Sets/src/mage/cards/i/IntellectDevourer.java +++ b/Mage.Sets/src/mage/cards/i/IntellectDevourer.java @@ -187,7 +187,7 @@ class IntellectDevourerReturnExiledCardEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = source.getSourceObject(game); if (sourceObject != null && controller != null) { - ExileZone exile = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter())); + ExileZone exile = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC())); Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (exile != null && sourcePermanent != null) { controller.moveCards(exile, Zone.HAND, source, game); diff --git a/Mage.Sets/src/mage/cards/i/InterdimensionalWebWatch.java b/Mage.Sets/src/mage/cards/i/InterdimensionalWebWatch.java new file mode 100644 index 00000000000..21fc4f09a57 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InterdimensionalWebWatch.java @@ -0,0 +1,82 @@ +package mage.cards.i; + +import mage.ConditionalMana; +import mage.MageObject; +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.effects.common.ExileTopXMayPlayUntilEffect; +import mage.abilities.mana.ConditionalAnyColorManaAbility; +import mage.abilities.mana.builder.ConditionalManaBuilder; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.stack.Spell; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class InterdimensionalWebWatch extends CardImpl { + + public InterdimensionalWebWatch(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); + + + // When this artifact enters, exile the top two cards of your library. Until the end of your next turn, you may play those cards. + this.addAbility(new EntersBattlefieldTriggeredAbility(new ExileTopXMayPlayUntilEffect(2, Duration.UntilEndOfYourNextTurn))); + + // {T}: Add two mana in any combination of colors. Spend this mana only to cast spells from exile. + this.addAbility(new ConditionalAnyColorManaAbility(2, new InterdimensionalWebWatchManaBuilder())); + } + + private InterdimensionalWebWatch(final InterdimensionalWebWatch card) { + super(card); + } + + @Override + public InterdimensionalWebWatch copy() { + return new InterdimensionalWebWatch(this); + } +} + +class InterdimensionalWebWatchManaBuilder extends ConditionalManaBuilder { + + @Override + public ConditionalMana build(Object... options) { + return new InterdimensionalWebWatchConditionalMana(this.mana); + } + + @Override + public String getRule() { + return "Spend this mana only to cast spells from exile"; + } +} + +class InterdimensionalWebWatchConditionalMana extends ConditionalMana { + + public InterdimensionalWebWatchConditionalMana(Mana mana) { + super(mana); + staticText = "Spend this mana only to cast spells from exile"; + addCondition(new InterdimensionalWebWatchCondition()); + } +} + +class InterdimensionalWebWatchCondition implements Condition { + + @Override + public boolean apply(Game game, Ability source) { + MageObject object = game.getObject(source.getSourceId()); + if (game.inCheckPlayableState()) { + return object instanceof Card && game.getState().getZone(source.getSourceId()) == Zone.EXILED; + } + return object instanceof Spell && ((Spell) object).getFromZone() == Zone.EXILED; + } +} diff --git a/Mage.Sets/src/mage/cards/i/InvocationOfSaintTraft.java b/Mage.Sets/src/mage/cards/i/InvocationOfSaintTraft.java index d5742a4bff4..fb67d5b61eb 100644 --- a/Mage.Sets/src/mage/cards/i/InvocationOfSaintTraft.java +++ b/Mage.Sets/src/mage/cards/i/InvocationOfSaintTraft.java @@ -12,11 +12,7 @@ import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AttachmentType; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.game.Game; import mage.game.permanent.token.AngelToken; import mage.players.Player; @@ -72,7 +68,7 @@ class InvocationOfSaintTraftEffect extends OneShotEffect { CreateTokenEffect effect = new CreateTokenEffect(new AngelToken(), 1, true, true); Player controller = game.getPlayer(source.getControllerId()); if (controller != null && (effect.apply(game, source))) { - effect.exileTokensCreatedAtEndOfCombat(game, source); + effect.removeTokensCreatedAt(game, source, true, PhaseStep.END_COMBAT, TargetController.ANY); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/i/IxalansBinding.java b/Mage.Sets/src/mage/cards/i/IxalansBinding.java index 76da782b496..3f80cd14da5 100644 --- a/Mage.Sets/src/mage/cards/i/IxalansBinding.java +++ b/Mage.Sets/src/mage/cards/i/IxalansBinding.java @@ -12,7 +12,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.ExileZone; import mage.game.Game; @@ -78,7 +77,7 @@ class IxalansBindingReplacementEffect extends ContinuousRuleModifyingEffectImpl } Card card = spellAbility.getCharacteristics(game); if (sourcePermanent != null && card != null) { - UUID exileZone = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileZone = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); if (exileZone != null) { ExileZone exile = game.getExile().getExileZone(exileZone); if (exile == null) { diff --git a/Mage.Sets/src/mage/cards/j/JJonahJameson.java b/Mage.Sets/src/mage/cards/j/JJonahJameson.java new file mode 100644 index 00000000000..5db86ffcf5c --- /dev/null +++ b/Mage.Sets/src/mage/cards/j/JJonahJameson.java @@ -0,0 +1,61 @@ +package mage.cards.j; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksAllTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.SuspectTargetEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.game.permanent.token.TreasureToken; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class JJonahJameson extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature you control with menace"); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + filter.add(new AbilityPredicate(MenaceAbility.class)); + } + + public JJonahJameson(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CITIZEN); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // When J. Jonah Jameson enters, suspect up to one target creature. + Ability ability = new EntersBattlefieldTriggeredAbility(new SuspectTargetEffect()); + ability.addTarget(new TargetCreaturePermanent(0, 1)); + this.addAbility(ability); + + // Whenever a creature you control with menace attacks, create a Treasure token. + this.addAbility(new AttacksAllTriggeredAbility( + new CreateTokenEffect(new TreasureToken()), false, + filter, SetTargetPointer.NONE, false + )); + } + + private JJonahJameson(final JJonahJameson card) { + super(card); + } + + @Override + public JJonahJameson copy() { + return new JJonahJameson(this); + } +} diff --git a/Mage.Sets/src/mage/cards/j/JackalGeniusGeneticist.java b/Mage.Sets/src/mage/cards/j/JackalGeniusGeneticist.java new file mode 100644 index 00000000000..a149dd7f5e2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/j/JackalGeniusGeneticist.java @@ -0,0 +1,88 @@ +package mage.cards.j; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.CopyTargetStackObjectEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SetTargetPointer; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.common.FilterCreatureSpell; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.util.functions.RemoveTypeCopyApplier; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class JackalGeniusGeneticist extends CardImpl { + + private static final FilterCreatureSpell filter = new FilterCreatureSpell("a creature spell with mana value equal to {this}'s power"); + + static { + filter.add(JackalGeniusGeneticistPredicate.instance); + } + + public JackalGeniusGeneticist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SCIENTIST); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Whenever you cast a creature spell with mana value equal to Jackal's power, copy that spell, except the copy isn't legendary. Then put a +1/+1 counter on Jackal. + Ability ability = new SpellCastControllerTriggeredAbility( + new CopyTargetStackObjectEffect(false, false, false, 1, new RemoveTypeCopyApplier(SuperType.LEGENDARY)) + .setText("copy that spell, except the copy isn't legendary."), + filter, + false, + SetTargetPointer.SPELL + ); + ability.addEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance()) + .concatBy("Then")); + this.addAbility(ability); + } + + private JackalGeniusGeneticist(final JackalGeniusGeneticist card) { + super(card); + } + + @Override + public JackalGeniusGeneticist copy() { + return new JackalGeniusGeneticist(this); + } +} + +enum JackalGeniusGeneticistPredicate implements ObjectSourcePlayerPredicate { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + Permanent sourcePermanent = input.getSource().getSourcePermanentOrLKI(game); + return sourcePermanent != null && input.getObject().getManaValue() == sourcePermanent.getPower().getValue(); + } + + @Override + public String toString() { + return "mana value equal to {this}'s power"; + } +} + diff --git a/Mage.Sets/src/mage/cards/j/JacobHaukenInspector.java b/Mage.Sets/src/mage/cards/j/JacobHaukenInspector.java index 38379f9ed13..c71aedd5f2c 100644 --- a/Mage.Sets/src/mage/cards/j/JacobHaukenInspector.java +++ b/Mage.Sets/src/mage/cards/j/JacobHaukenInspector.java @@ -84,7 +84,7 @@ class JacobHaukenInspectorExileEffect extends OneShotEffect { controller.chooseTarget(outcome, controller.getHand(), target, source, game); Card card = game.getCard(target.getFirstTarget()); if (card != null) { - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); MageObject sourceObject = source.getSourceObject(game); String exileName = sourceObject == null ? null : sourceObject.getIdName(); card.setFaceDown(true, game); diff --git a/Mage.Sets/src/mage/cards/j/JodahsAvenger.java b/Mage.Sets/src/mage/cards/j/JodahsAvenger.java index a164f959460..e41a9146461 100644 --- a/Mage.Sets/src/mage/cards/j/JodahsAvenger.java +++ b/Mage.Sets/src/mage/cards/j/JodahsAvenger.java @@ -7,7 +7,7 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.GainsChoiceOfAbilitiesEffect; -import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; import mage.abilities.keyword.DoubleStrikeAbility; import mage.abilities.keyword.ProtectionAbility; import mage.abilities.keyword.ShadowAbility; @@ -15,6 +15,7 @@ import mage.abilities.keyword.VigilanceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Duration; import mage.constants.SubType; import java.util.UUID; @@ -33,7 +34,7 @@ public final class JodahsAvenger extends CardImpl { this.toughness = new MageInt(4); // {0}: Until end of turn, Jodah's Avenger gets -1/-1 and gains your choice of double strike, protection from red, vigilance, or shadow. - Ability ability = new SimpleActivatedAbility(new BoostTargetEffect(-1, -1) + Ability ability = new SimpleActivatedAbility(new BoostSourceEffect(-1, -1, Duration.EndOfTurn) .setText("Until end of turn, {this} gets -1/-1"), new ManaCostsImpl<>("{0}")); ability.addEffect(new GainsChoiceOfAbilitiesEffect(GainsChoiceOfAbilitiesEffect.TargetType.Source, "", false, DoubleStrikeAbility.getInstance(), ProtectionAbility.from(ObjectColor.RED), VigilanceAbility.getInstance(), ShadowAbility.getInstance()) diff --git a/Mage.Sets/src/mage/cards/k/KariZevSkyshipRaider.java b/Mage.Sets/src/mage/cards/k/KariZevSkyshipRaider.java index 93d407ce393..b864a33b8ac 100644 --- a/Mage.Sets/src/mage/cards/k/KariZevSkyshipRaider.java +++ b/Mage.Sets/src/mage/cards/k/KariZevSkyshipRaider.java @@ -11,10 +11,7 @@ import mage.abilities.keyword.FirstStrikeAbility; import mage.abilities.keyword.MenaceAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.SuperType; +import mage.constants.*; import mage.game.Game; import mage.game.permanent.token.RagavanToken; import mage.players.Player; @@ -70,7 +67,7 @@ class KariZevSkyshipRaiderEffect extends OneShotEffect { CreateTokenEffect effect = new CreateTokenEffect(new RagavanToken(), 1, true, true); Player controller = game.getPlayer(source.getControllerId()); if (controller != null && effect.apply(game, source)) { - effect.exileTokensCreatedAtEndOfCombat(game, source); + effect.removeTokensCreatedAt(game, source, true, PhaseStep.END_COMBAT, TargetController.ANY); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/k/KarnLiberated.java b/Mage.Sets/src/mage/cards/k/KarnLiberated.java index 8d77814e4c7..c38cd26e79b 100644 --- a/Mage.Sets/src/mage/cards/k/KarnLiberated.java +++ b/Mage.Sets/src/mage/cards/k/KarnLiberated.java @@ -81,7 +81,7 @@ class KarnLiberatedEffect extends OneShotEffect { } List keepExiled = new ArrayList<>(); for (ExileZone zone : game.getExile().getExileZones()) { - exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); if (zone.getId().equals(exileId)) { for (Card card : zone.getCards(game)) { if (!card.hasSubtype(SubType.AURA, game) @@ -243,7 +243,7 @@ class KarnPlayerExileEffect extends OneShotEffect { TargetCardInHand target = new TargetCardInHand(); if (target.canChoose(player.getId(), source, game) && target.chooseTarget(Outcome.Exile, player.getId(), source, game)) { - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); return player.moveCardsToExile(new CardsImpl(target.getTargets()).getCards(game), source, game, true, exileId, sourceObject.getIdName()); } return false; diff --git a/Mage.Sets/src/mage/cards/k/KarnScionOfUrza.java b/Mage.Sets/src/mage/cards/k/KarnScionOfUrza.java index e825da2fab5..0e3fd6882af 100644 --- a/Mage.Sets/src/mage/cards/k/KarnScionOfUrza.java +++ b/Mage.Sets/src/mage/cards/k/KarnScionOfUrza.java @@ -18,10 +18,8 @@ import mage.target.TargetCard; import mage.target.common.TargetCardInExile; import mage.target.common.TargetOpponent; -import java.util.Objects; import java.util.Set; import java.util.UUID; -import java.util.stream.Collectors; /** * @author spjspj @@ -150,13 +148,7 @@ class KarnMinus1Effect extends OneShotEffect { if (controller == null) { return false; } - Cards cards = new CardsImpl(game - .getExile() - .getCards(filter, game) - .stream() - .filter(Objects::nonNull) - .filter(card -> card.isOwnedBy(source.getControllerId())) - .collect(Collectors.toList())); + Cards cards = new CardsImpl(game.getExile().getCardsOwned(filter, controller.getId(), source, game)); Card card; switch (cards.size()) { case 0: @@ -173,6 +165,6 @@ class KarnMinus1Effect extends OneShotEffect { if (card == null) { return false; } - return card != null && controller.moveCards(card, Zone.HAND, source, game); + return controller.moveCards(card, Zone.HAND, source, game); } } diff --git a/Mage.Sets/src/mage/cards/k/KataraWaterbendingMaster.java b/Mage.Sets/src/mage/cards/k/KataraWaterbendingMaster.java index a6123b98f0b..33404be941a 100644 --- a/Mage.Sets/src/mage/cards/k/KataraWaterbendingMaster.java +++ b/Mage.Sets/src/mage/cards/k/KataraWaterbendingMaster.java @@ -44,7 +44,7 @@ public final class KataraWaterbendingMaster extends CardImpl { // Whenever Katara attacks, you may draw a card for each experience counter you have. If you do, discard a card. Ability ability = new AttacksTriggeredAbility(new DrawCardSourceControllerEffect(xValue) - .setText("draw a card for each experience counter you have")); + .setText("draw a card for each experience counter you have"), true); ability.addEffect(new DiscardControllerEffect(1).concatBy("If you do,")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/k/KayaOrzhovUsurper.java b/Mage.Sets/src/mage/cards/k/KayaOrzhovUsurper.java index a5fd8a8d4a0..ce6f917024a 100644 --- a/Mage.Sets/src/mage/cards/k/KayaOrzhovUsurper.java +++ b/Mage.Sets/src/mage/cards/k/KayaOrzhovUsurper.java @@ -131,14 +131,9 @@ class KayaOrzhovUsurperDamageEffect extends OneShotEffect { if (controller == null || player == null) { return false; } - int count = 0; - for (Card card : game.getExile().getAllCards(game)) { - if (card != null && card.getOwnerId().equals(player.getId())) { - count += 1; - } - } + int count = game.getExile().getCardsOwned(game, player.getId()).size(); player.damage(count, source.getSourceId(), source, game); controller.gainLife(count, game, source); return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/k/KayaSpiritsJustice.java b/Mage.Sets/src/mage/cards/k/KayaSpiritsJustice.java index f2d8dc1aa49..37b4fb74752 100644 --- a/Mage.Sets/src/mage/cards/k/KayaSpiritsJustice.java +++ b/Mage.Sets/src/mage/cards/k/KayaSpiritsJustice.java @@ -219,7 +219,7 @@ class KayaSpiritsJusticeExileEffect extends OneShotEffect { controller.choose(outcome, target, source, game); Card card = game.getCard(target.getFirstTarget()); if (card != null) { - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); MageObject sourceObject = source.getSourceObject(game); String exileName = sourceObject == null ? null : sourceObject.getIdName(); return controller.moveCardsToExile(card, source, game, true, exileId, exileName); diff --git a/Mage.Sets/src/mage/cards/k/KianneDeanOfSubstance.java b/Mage.Sets/src/mage/cards/k/KianneDeanOfSubstance.java index 9989529f7f5..d8cca3683de 100644 --- a/Mage.Sets/src/mage/cards/k/KianneDeanOfSubstance.java +++ b/Mage.Sets/src/mage/cards/k/KianneDeanOfSubstance.java @@ -127,10 +127,8 @@ enum KianneDeanOfSubstanceValue implements DynamicValue { public int calculate(Game game, Ability sourceAbility, Effect effect) { return game .getExile() - .getAllCards(game) + .getCardsOwned(game, sourceAbility.getControllerId()) .stream() - .filter(Objects::nonNull) - .filter(card -> card.isOwnedBy(sourceAbility.getControllerId())) .filter(card -> card.getCounters(game).containsKey(CounterType.STUDY)) .map(MageObject::getManaValue) .distinct() @@ -155,10 +153,8 @@ enum KianneDeanOfSubstanceHint implements Hint { @Override public String getText(Game game, Ability ability) { List values = game.getExile() - .getAllCards(game) + .getCardsOwned(game, ability.getControllerId()) .stream() - .filter(Objects::nonNull) - .filter(card -> card.isOwnedBy(ability.getControllerId())) .filter(card -> card.getCounters(game).containsKey(CounterType.STUDY)) .mapToInt(MageObject::getManaValue) .distinct() @@ -166,7 +162,7 @@ enum KianneDeanOfSubstanceHint implements Hint { .mapToObj(String::valueOf) .collect(Collectors.toList()); return "Mana values of cards exiled with study counters: " + values.size() - + (values.size() > 0 ? " (" + String.join(", ", values) + ')' : ""); + + (values.isEmpty() ? "" : " (" + String.join(", ", values) + ')'); } @Override diff --git a/Mage.Sets/src/mage/cards/k/KinjallisSunwing.java b/Mage.Sets/src/mage/cards/k/KinjallisSunwing.java index 597a57865f0..91a74916cdd 100644 --- a/Mage.Sets/src/mage/cards/k/KinjallisSunwing.java +++ b/Mage.Sets/src/mage/cards/k/KinjallisSunwing.java @@ -29,8 +29,8 @@ public final class KinjallisSunwing extends CardImpl { // Creatures your opponents control enter the battlefield tapped. this.addAbility(new SimpleStaticAbility(new PermanentsEnterBattlefieldTappedEffect( - StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE - ).setText("creatures your opponents control enter the battlefield tapped"))); + StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURES + ))); } private KinjallisSunwing(final KinjallisSunwing card) { diff --git a/Mage.Sets/src/mage/cards/k/KnowledgePool.java b/Mage.Sets/src/mage/cards/k/KnowledgePool.java index 356eeb493c7..e553c7110c3 100644 --- a/Mage.Sets/src/mage/cards/k/KnowledgePool.java +++ b/Mage.Sets/src/mage/cards/k/KnowledgePool.java @@ -86,7 +86,7 @@ class KnowledgePoolExileThreeCardsEffect extends OneShotEffect { source, game, true, - CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()), + CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()), sourceObject.getIdName() + " (" + sourceObject.getZoneChangeCounter(game) + ')' ); } diff --git a/Mage.Sets/src/mage/cards/k/KormusBell.java b/Mage.Sets/src/mage/cards/k/KormusBell.java index 84bbcf7abdc..d186e67963b 100644 --- a/Mage.Sets/src/mage/cards/k/KormusBell.java +++ b/Mage.Sets/src/mage/cards/k/KormusBell.java @@ -27,11 +27,12 @@ public final class KormusBell extends CardImpl { new CreatureToken(1, 1, "1/1 black creatures").withColor("B"), "lands", filter, Duration.WhileOnBattlefield, true); - effect.addDependedToType(DependencyType.BecomeSwamp); // TODO: are these dependencies correct/complete? - effect.addDependedToType(DependencyType.BecomeIsland); + effect.addDependedToType(DependencyType.BecomeNonbasicLand); effect.addDependedToType(DependencyType.BecomeForest); + effect.addDependedToType(DependencyType.BecomeIsland); effect.addDependedToType(DependencyType.BecomeMountain); effect.addDependedToType(DependencyType.BecomePlains); + effect.addDependedToType(DependencyType.BecomeSwamp); this.addAbility(new SimpleStaticAbility(effect)); } diff --git a/Mage.Sets/src/mage/cards/k/KravenProudPredator.java b/Mage.Sets/src/mage/cards/k/KravenProudPredator.java new file mode 100644 index 00000000000..81aa267a90e --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KravenProudPredator.java @@ -0,0 +1,49 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue; +import mage.abilities.effects.common.continuous.SetBasePowerSourceEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.Zone; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class KravenProudPredator extends CardImpl { + + public KravenProudPredator(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{G}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(0); + this.toughness = new MageInt(4); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Top of the Food Chain -- Kraven's power is equal to the greatest mana value among permanents you control. + this.addAbility(new SimpleStaticAbility( + Zone.ALL, new SetBasePowerSourceEffect(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_PERMANENTS) + ).withFlavorWord("Top of the Food Chain")); + } + + private KravenProudPredator(final KravenProudPredator card) { + super(card); + } + + @Override + public KravenProudPredator copy() { + return new KravenProudPredator(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KravenTheHunter.java b/Mage.Sets/src/mage/cards/k/KravenTheHunter.java new file mode 100644 index 00000000000..41d5d2596e6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KravenTheHunter.java @@ -0,0 +1,59 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.GreatestPowerControlledPredicate; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class KravenTheHunter extends CardImpl { + private static final FilterPermanent filter = new FilterCreaturePermanent("creature an opponent controls with the greatest power among creatures that player controls"); + + static { + filter.add(GreatestPowerControlledPredicate.instance); + } + public KravenTheHunter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{G}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Whenever a creature an opponent controls with the greatest power among creatures that player controls dies, draw a card and put a +1/+1 counter on Kraven the Hunter. + Ability ability = new DiesCreatureTriggeredAbility(new DrawCardSourceControllerEffect(1), false, filter); + ability.addEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance()) + .concatBy("and")); + this.addAbility(ability); + } + + private KravenTheHunter(final KravenTheHunter card) { + super(card); + } + + @Override + public KravenTheHunter copy() { + return new KravenTheHunter(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KroxaTitanOfDeathsHunger.java b/Mage.Sets/src/mage/cards/k/KroxaTitanOfDeathsHunger.java index 3cc9434f5fa..44d3d508232 100644 --- a/Mage.Sets/src/mage/cards/k/KroxaTitanOfDeathsHunger.java +++ b/Mage.Sets/src/mage/cards/k/KroxaTitanOfDeathsHunger.java @@ -82,7 +82,7 @@ class KroxaTitanOfDeathsHungerEntersEffect extends OneShotEffect { if (permanent == null) { return false; } - if (EscapeAbility.wasCastedWithEscape(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter())) { + if (EscapeAbility.wasCastedWithEscape(game, source.getSourceId(), source.getStackMomentSourceZCC())) { return false; } return permanent.sacrifice(source, game); diff --git a/Mage.Sets/src/mage/cards/k/KyrenLegate.java b/Mage.Sets/src/mage/cards/k/KyrenLegate.java index 5a77619a080..c43520686cd 100644 --- a/Mage.Sets/src/mage/cards/k/KyrenLegate.java +++ b/Mage.Sets/src/mage/cards/k/KyrenLegate.java @@ -39,7 +39,7 @@ public final class KyrenLegate extends CardImpl { this.addAbility(HasteAbility.getInstance()); // If an opponent controls a Plains and you control a Mountain, you may cast this spell without paying its mana cost. - Condition condition = new CompoundCondition("If an opponent controls a Plains and you control a Mountain", + Condition condition = new CompoundCondition("an opponent controls a Plains and you control a Mountain", new OpponentControlsPermanentCondition(filterPlains), new PermanentsOnTheBattlefieldCondition(filterMountain)); this.addAbility(new AlternativeCostSourceAbility(null, condition)); diff --git a/Mage.Sets/src/mage/cards/l/LadyOctopusInspiredInventor.java b/Mage.Sets/src/mage/cards/l/LadyOctopusInspiredInventor.java new file mode 100644 index 00000000000..a3389f0457e --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LadyOctopusInspiredInventor.java @@ -0,0 +1,60 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.common.DrawNthOrNthCardTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.cost.CastFromHandForFreeEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.FilterCard; +import mage.filter.common.FilterArtifactCard; +import mage.filter.predicate.mageobject.ManaValueCompareToCountersSourceCountPredicate; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class LadyOctopusInspiredInventor extends CardImpl { + + private static final FilterCard filter = new FilterArtifactCard("an artifact spell from your hand with mana value less than or " + + "equal to the number of ingenuity counters on {this}"); + + static { + filter.add(new ManaValueCompareToCountersSourceCountPredicate(CounterType.INGENUITY, ComparisonType.OR_LESS)); + } + + public LadyOctopusInspiredInventor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SCIENTIST); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(0); + this.toughness = new MageInt(2); + + // Whenever you draw your first or second card each turn, put an ingenuity counter on Lady Octopus. + this.addAbility(new DrawNthOrNthCardTriggeredAbility(new AddCountersSourceEffect(CounterType.INGENUITY.createInstance()))); + + // {T}: You may cast an artifact spell from your hand with mana value less than or equal to the number of ingenuity counters on Lady Octopus without paying its mana cost. + this.addAbility(new SimpleActivatedAbility(new CastFromHandForFreeEffect(filter), new TapSourceCost())); + } + + private LadyOctopusInspiredInventor(final LadyOctopusInspiredInventor card) { + super(card); + } + + @Override + public LadyOctopusInspiredInventor copy() { + return new LadyOctopusInspiredInventor(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/l/Lashknife.java b/Mage.Sets/src/mage/cards/l/Lashknife.java index f00119b1896..9cec47d8460 100644 --- a/Mage.Sets/src/mage/cards/l/Lashknife.java +++ b/Mage.Sets/src/mage/cards/l/Lashknife.java @@ -31,7 +31,7 @@ import mage.target.common.TargetControlledPermanent; */ public final class Lashknife extends CardImpl { - private static final FilterControlledPermanent plainsFilter = new FilterControlledPermanent("If you control a Plains"); + private static final FilterControlledPermanent plainsFilter = new FilterControlledPermanent("you control a Plains"); private static final FilterControlledCreaturePermanent creatureFilter = new FilterControlledCreaturePermanent("an untapped creature you control"); static { diff --git a/Mage.Sets/src/mage/cards/l/LavaballTrap.java b/Mage.Sets/src/mage/cards/l/LavaballTrap.java index 2b92d719726..9774162d9c7 100644 --- a/Mage.Sets/src/mage/cards/l/LavaballTrap.java +++ b/Mage.Sets/src/mage/cards/l/LavaballTrap.java @@ -78,6 +78,6 @@ enum LavaballTrapCondition implements Condition { @Override public String toString() { - return "If an opponent had two or more lands enter the battlefield under their control this turn"; + return "an opponent had two or more lands enter the battlefield under their control this turn"; } } diff --git a/Mage.Sets/src/mage/cards/l/LazavWearerOfFaces.java b/Mage.Sets/src/mage/cards/l/LazavWearerOfFaces.java index b3152bf3bee..33bd4cd10e6 100644 --- a/Mage.Sets/src/mage/cards/l/LazavWearerOfFaces.java +++ b/Mage.Sets/src/mage/cards/l/LazavWearerOfFaces.java @@ -94,7 +94,7 @@ class LazavWearerOfFacesEffect extends OneShotEffect { return false; } - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); ExileZone exile = game.getExile().getExileZone(exileId); if (exile == null) { return false; diff --git a/Mage.Sets/src/mage/cards/l/LethargyTrap.java b/Mage.Sets/src/mage/cards/l/LethargyTrap.java index caec0fa8e63..95cf0693efd 100644 --- a/Mage.Sets/src/mage/cards/l/LethargyTrap.java +++ b/Mage.Sets/src/mage/cards/l/LethargyTrap.java @@ -53,6 +53,6 @@ enum LethargyTrapCondition implements Condition { @Override public String toString() { - return "If three or more creatures are attacking"; + return "three or more creatures are attacking"; } } diff --git a/Mage.Sets/src/mage/cards/l/LeylineOfSingularity.java b/Mage.Sets/src/mage/cards/l/LeylineOfSingularity.java index e2fd00ab094..c83a5a500e3 100644 --- a/Mage.Sets/src/mage/cards/l/LeylineOfSingularity.java +++ b/Mage.Sets/src/mage/cards/l/LeylineOfSingularity.java @@ -3,7 +3,6 @@ package mage.cards.l; import java.util.UUID; import mage.abilities.Ability; -import mage.abilities.Mode; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.keyword.LeylineAbility; @@ -47,6 +46,7 @@ class SetSupertypeAllEffect extends ContinuousEffectImpl { public SetSupertypeAllEffect() { super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Detriment); this.staticText = "All nonland permanents are legendary"; + this.dependendToTypes.add(DependencyType.BecomeNonbasicLand); } private SetSupertypeAllEffect(final SetSupertypeAllEffect effect) { diff --git a/Mage.Sets/src/mage/cards/l/LeylineOfTheGuildpact.java b/Mage.Sets/src/mage/cards/l/LeylineOfTheGuildpact.java index df4416bb366..a3de4e672b2 100644 --- a/Mage.Sets/src/mage/cards/l/LeylineOfTheGuildpact.java +++ b/Mage.Sets/src/mage/cards/l/LeylineOfTheGuildpact.java @@ -50,6 +50,7 @@ class LeylineOfTheGuildpactEffect extends ContinuousEffectImpl { LeylineOfTheGuildpactEffect() { super(Duration.WhileOnBattlefield, Layer.ColorChangingEffects_5, SubLayer.NA, Outcome.Benefit); staticText = "each nonland permanent you control is all colors"; + dependendToTypes.add(DependencyType.BecomeNonbasicLand); } private LeylineOfTheGuildpactEffect(final LeylineOfTheGuildpactEffect effect) { diff --git a/Mage.Sets/src/mage/cards/l/LidlessGaze.java b/Mage.Sets/src/mage/cards/l/LidlessGaze.java index 2f12678b403..2ecb73f2623 100644 --- a/Mage.Sets/src/mage/cards/l/LidlessGaze.java +++ b/Mage.Sets/src/mage/cards/l/LidlessGaze.java @@ -65,7 +65,7 @@ class LidlessGazeEffect extends OneShotEffect { return false; } - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); MageObject sourceObject = source.getSourceObject(game); String exileName = sourceObject == null ? null : sourceObject.getIdName(); diff --git a/Mage.Sets/src/mage/cards/l/LifeAndLimb.java b/Mage.Sets/src/mage/cards/l/LifeAndLimb.java index 28847dcff26..a6f00bb8bd0 100644 --- a/Mage.Sets/src/mage/cards/l/LifeAndLimb.java +++ b/Mage.Sets/src/mage/cards/l/LifeAndLimb.java @@ -50,7 +50,12 @@ class LifeAndLimbEffect extends ContinuousEffectImpl { super(Duration.WhileOnBattlefield, Outcome.Neutral); staticText = "All Forests and all Saprolings are 1/1 green Saproling creatures and Forest lands in addition to their other types"; + this.dependendToTypes.add(DependencyType.BecomeNonbasicLand); this.dependendToTypes.add(DependencyType.BecomeForest); + this.dependendToTypes.add(DependencyType.BecomeIsland); + this.dependendToTypes.add(DependencyType.BecomeMountain); + this.dependendToTypes.add(DependencyType.BecomePlains); + this.dependendToTypes.add(DependencyType.BecomeSwamp); this.dependendToTypes.add(DependencyType.BecomeCreature); } diff --git a/Mage.Sets/src/mage/cards/l/LifestreamsBlessing.java b/Mage.Sets/src/mage/cards/l/LifestreamsBlessing.java index 450b5f036bf..08e83d92d03 100644 --- a/Mage.Sets/src/mage/cards/l/LifestreamsBlessing.java +++ b/Mage.Sets/src/mage/cards/l/LifestreamsBlessing.java @@ -135,7 +135,7 @@ class LifestreamsBlessingWatcher extends Watcher { .getWatcher(LifestreamsBlessingWatcher.class) .map .getOrDefault(new MageObjectReference( - source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game + source.getSourceId(), source.getStackMomentSourceZCC(), game ), 0); } } diff --git a/Mage.Sets/src/mage/cards/l/LivingBrainMechanicalMarvel.java b/Mage.Sets/src/mage/cards/l/LivingBrainMechanicalMarvel.java new file mode 100644 index 00000000000..f1efe82bb29 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LivingBrainMechanicalMarvel.java @@ -0,0 +1,61 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.effects.common.UntapTargetEffect; +import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.common.FilterControlledArtifactPermanent; +import mage.filter.predicate.Predicates; +import mage.game.permanent.token.custom.CreatureToken; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class LivingBrainMechanicalMarvel extends CardImpl { + + private static final FilterControlledArtifactPermanent filter = new FilterControlledArtifactPermanent("non-Equipment artifact you control"); + + static { + filter.add(Predicates.not(SubType.EQUIPMENT.getPredicate())); + } + + public LivingBrainMechanicalMarvel(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.ROBOT); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // At the beginning of combat on your turn, target non-Equipment artifact you control becomes an artifact creature with base power and toughness 3/3 until end of turn. Untap it. + CreatureToken token = new CreatureToken(3, 3, "artifact creature with base power and toughness 3/3") + .withType(CardType.ARTIFACT); + Ability ability = new BeginningOfCombatTriggeredAbility( + new BecomesCreatureTargetEffect(token, false, false, Duration.EndOfTurn, false, true, false) + ); + ability.addEffect(new UntapTargetEffect("untap it")); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private LivingBrainMechanicalMarvel(final LivingBrainMechanicalMarvel card) { + super(card); + } + + @Override + public LivingBrainMechanicalMarvel copy() { + return new LivingBrainMechanicalMarvel(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LivingLands.java b/Mage.Sets/src/mage/cards/l/LivingLands.java index fee8a2eadcf..2b371781fd5 100644 --- a/Mage.Sets/src/mage/cards/l/LivingLands.java +++ b/Mage.Sets/src/mage/cards/l/LivingLands.java @@ -26,7 +26,12 @@ public final class LivingLands extends CardImpl { ContinuousEffect effect = new BecomesCreatureAllEffect( new CreatureToken(1, 1, "1/1 creatures"), "lands", filter, Duration.WhileOnBattlefield, false); - effect.getDependencyTypes().add(DependencyType.BecomeForest); // TODO: are these dependencies correct/complete? + effect.getDependedToTypes().add(DependencyType.BecomeNonbasicLand); + effect.addDependedToType(DependencyType.BecomeForest); + effect.addDependedToType(DependencyType.BecomeIsland); + effect.addDependedToType(DependencyType.BecomeMountain); + effect.addDependedToType(DependencyType.BecomePlains); + effect.addDependedToType(DependencyType.BecomeSwamp); this.addAbility(new SimpleStaticAbility(effect)); } diff --git a/Mage.Sets/src/mage/cards/l/LivingPlane.java b/Mage.Sets/src/mage/cards/l/LivingPlane.java index 3ad0b887dcd..a9ca5b7904a 100644 --- a/Mage.Sets/src/mage/cards/l/LivingPlane.java +++ b/Mage.Sets/src/mage/cards/l/LivingPlane.java @@ -2,13 +2,11 @@ package mage.cards.l; import java.util.UUID; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.continuous.BecomesCreatureAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SuperType; -import mage.constants.Zone; +import mage.constants.*; import mage.filter.common.FilterLandPermanent; import mage.game.permanent.token.custom.CreatureToken; @@ -25,9 +23,11 @@ public final class LivingPlane extends CardImpl { this.supertype.add(SuperType.WORLD); // All lands are 1/1 creatures that are still lands. - this.addAbility(new SimpleStaticAbility(new BecomesCreatureAllEffect( + ContinuousEffect effect = new BecomesCreatureAllEffect( new CreatureToken(1, 1, "1/1 creatures"), - "lands", filter, Duration.WhileOnBattlefield, false))); + "lands", filter, Duration.WhileOnBattlefield, false); + effect.getDependedToTypes().add(DependencyType.BecomeNonbasicLand); + this.addAbility(new SimpleStaticAbility(effect)); } private LivingPlane(final LivingPlane card) { diff --git a/Mage.Sets/src/mage/cards/l/LivioOathswornSentinel.java b/Mage.Sets/src/mage/cards/l/LivioOathswornSentinel.java index 3d0196f39fb..d32f957d3ac 100644 --- a/Mage.Sets/src/mage/cards/l/LivioOathswornSentinel.java +++ b/Mage.Sets/src/mage/cards/l/LivioOathswornSentinel.java @@ -125,7 +125,7 @@ class LivioOathswornSentinelReturnEffect extends OneShotEffect { } Set cards = game .getExile() - .getAllCards(game) + .getCardsInRange(game, player.getId()) .stream() .filter(Objects::nonNull) .filter(card -> card.getCounters(game).containsKey(CounterType.AEGIS)) diff --git a/Mage.Sets/src/mage/cards/l/LizardConnorssCurse.java b/Mage.Sets/src/mage/cards/l/LizardConnorssCurse.java new file mode 100644 index 00000000000..c67ae20fb01 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LizardConnorssCurse.java @@ -0,0 +1,64 @@ +package mage.cards.l; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.game.permanent.token.custom.CreatureToken; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class LizardConnorssCurse extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("other target creature"); + + static { + filter.add(AnotherPredicate.instance); + } + + public LizardConnorssCurse(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.LIZARD); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Lizard Formula -- When Lizard, Connors's Curse enters, up to one other target creature loses all abilities and becomes a green Lizard creature with base power and toughness 4/4. + CreatureToken token = new CreatureToken(4, 4, "green Lizard creature with base power and toughness 4/4", SubType.LIZARD) + .withColor("G"); + Ability ability = new EntersBattlefieldTriggeredAbility( + new BecomesCreatureTargetEffect(token, true, false, Duration.WhileOnBattlefield) + ); + ability.addTarget(new TargetPermanent(0, 1, filter)); + ability.withFlavorWord("Lizard Formula"); + this.addAbility(ability); + } + + private LizardConnorssCurse(final LizardConnorssCurse card) { + super(card); + } + + @Override + public LizardConnorssCurse copy() { + return new LizardConnorssCurse(this); + } +} diff --git a/Mage.Sets/src/mage/cards/l/LostInThought.java b/Mage.Sets/src/mage/cards/l/LostInThought.java index 703d907f8d2..42e130ef789 100644 --- a/Mage.Sets/src/mage/cards/l/LostInThought.java +++ b/Mage.Sets/src/mage/cards/l/LostInThought.java @@ -173,7 +173,7 @@ class LostInThoughtIgnoreEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - String key = source.getSourceId().toString() + source.getSourceObjectZoneChangeCounter() + LostInThought.keyString + game.getTurnNum() + ((ActivatedAbilityImpl) source).getActivatorId(); + String key = source.getSourceId().toString() + source.getStackMomentSourceZCC() + LostInThought.keyString + game.getTurnNum() + ((ActivatedAbilityImpl) source).getActivatorId(); game.getState().setValue(key, true); return true; } diff --git a/Mage.Sets/src/mage/cards/l/LumberingBattlement.java b/Mage.Sets/src/mage/cards/l/LumberingBattlement.java index 05320c9c291..4c27966baef 100644 --- a/Mage.Sets/src/mage/cards/l/LumberingBattlement.java +++ b/Mage.Sets/src/mage/cards/l/LumberingBattlement.java @@ -130,7 +130,7 @@ enum LumberingBattlementValue implements DynamicValue { } ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId( game, sourceAbility.getSourceId(), - sourceAbility.getSourceObjectZoneChangeCounter() + sourceAbility.getStackMomentSourceZCC() )); if (exileZone == null) { exileZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, sourceAbility)); diff --git a/Mage.Sets/src/mage/cards/m/MadameWebClairvoyant.java b/Mage.Sets/src/mage/cards/m/MadameWebClairvoyant.java new file mode 100644 index 00000000000..13f526d4a12 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MadameWebClairvoyant.java @@ -0,0 +1,63 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.MillCardsControllerEffect; +import mage.abilities.effects.common.continuous.LookAtTopCardOfLibraryAnyTimeEffect; +import mage.abilities.effects.common.continuous.PlayFromTopOfLibraryEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.predicate.Predicates; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class MadameWebClairvoyant extends CardImpl { + + private static final FilterCard filter = new FilterCard("cast Spider spells and noncreature spells"); + + static { + filter.add(Predicates.or( + Predicates.not(CardType.CREATURE.getPredicate()), + SubType.SPIDER.getPredicate() + )); + } + + public MadameWebClairvoyant(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.MUTANT); + this.subtype.add(SubType.ADVISOR); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // You may look at the top card of your library any time. + this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect())); + + // You may cast Spider spells and noncreature spells from the top of your library. + this.addAbility(new SimpleStaticAbility(new PlayFromTopOfLibraryEffect(filter))); + + // Whenever you attack, you may mill a card. + this.addAbility(new AttacksWithCreaturesTriggeredAbility( + Zone.BATTLEFIELD, new MillCardsControllerEffect(1), + 1, StaticFilters.FILTER_PERMANENT_CREATURES, false, true) + ); + } + + private MadameWebClairvoyant(final MadameWebClairvoyant card) { + super(card); + } + + @Override + public MadameWebClairvoyant copy() { + return new MadameWebClairvoyant(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MagusOfTheMoon.java b/Mage.Sets/src/mage/cards/m/MagusOfTheMoon.java index 4d1beec9ae5..e07c277a4e8 100644 --- a/Mage.Sets/src/mage/cards/m/MagusOfTheMoon.java +++ b/Mage.Sets/src/mage/cards/m/MagusOfTheMoon.java @@ -54,6 +54,7 @@ class MagusOfTheMoonEffect extends ContinuousEffectImpl { MagusOfTheMoonEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); this.staticText = "Nonbasic lands are Mountains"; + dependendToTypes.add(DependencyType.BecomeNonbasicLand); dependencyTypes.add(DependencyType.BecomeMountain); } diff --git a/Mage.Sets/src/mage/cards/m/MairsilThePretender.java b/Mage.Sets/src/mage/cards/m/MairsilThePretender.java index 1df506dee92..59853dba53b 100644 --- a/Mage.Sets/src/mage/cards/m/MairsilThePretender.java +++ b/Mage.Sets/src/mage/cards/m/MairsilThePretender.java @@ -135,14 +135,12 @@ class MairsilThePretenderGainAbilitiesEffect extends ContinuousEffectImpl { if (perm == null) { return false; } - for (Card card : game.getExile().getAllCards(game)) { - if (filter.match(card, game) && Objects.equals(card.getOwnerId(), perm.getControllerId())) { - for (Ability ability : card.getAbilities(game)) { - if (ability.isActivatedAbility()) { - ActivatedAbility copyAbility = (ActivatedAbility) ability.copy(); - copyAbility.setMaxActivationsPerTurn(1); - perm.addAbility(copyAbility, source.getSourceId(), game, true); - } + for (Card card : game.getExile().getCardsOwned(filter, perm.getControllerId(), source, game)) { + for (Ability ability : card.getAbilities(game)) { + if (ability.isActivatedAbility()) { + ActivatedAbility copyAbility = (ActivatedAbility) ability.copy(); + copyAbility.setMaxActivationsPerTurn(1); + perm.addAbility(copyAbility, source.getSourceId(), game, true); } } } diff --git a/Mage.Sets/src/mage/cards/m/MantleOfTheAncients.java b/Mage.Sets/src/mage/cards/m/MantleOfTheAncients.java index d01b21e9f6f..dca9f88ccd6 100644 --- a/Mage.Sets/src/mage/cards/m/MantleOfTheAncients.java +++ b/Mage.Sets/src/mage/cards/m/MantleOfTheAncients.java @@ -4,7 +4,7 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.continuous.BoostEnchantedEffect; @@ -15,8 +15,10 @@ import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterCard; +import mage.filter.FilterPermanent; import mage.filter.common.FilterPermanentCard; import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.AttachedToAttachedPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -34,14 +36,22 @@ import java.util.stream.Collectors; */ public final class MantleOfTheAncients extends CardImpl { private static final FilterCard filter = new FilterPermanentCard(); + private static final FilterPermanent filter2 = new FilterPermanent("Aura and Equipment attached to it"); static { filter.add(Predicates.or( SubType.AURA.getPredicate(), SubType.EQUIPMENT.getPredicate() )); + filter2.add(Predicates.or( + SubType.AURA.getPredicate(), + SubType.EQUIPMENT.getPredicate() + )); + filter2.add(AttachedToAttachedPredicate.instance); } + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter2); + public MantleOfTheAncients(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}{W}"); @@ -59,9 +69,7 @@ public final class MantleOfTheAncients extends CardImpl { this.addAbility(ability); // Enchanted creature gets +1/+1 for each Aura and Equipment attached to it. - this.addAbility(new SimpleStaticAbility(new BoostEnchantedEffect( - MantleOfTheAncientsValue.instance, MantleOfTheAncientsValue.instance - ))); + this.addAbility(new SimpleStaticAbility(new BoostEnchantedEffect(xValue, xValue))); } private MantleOfTheAncients(final MantleOfTheAncients card) { @@ -116,39 +124,3 @@ class MantleOfTheAncientsEffect extends OneShotEffect { return true; } } - -enum MantleOfTheAncientsValue implements DynamicValue { - instance; - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - Permanent sourcePermanent = sourceAbility.getSourcePermanentOrLKI(game); - if (sourcePermanent == null) { - return 0; - } - Permanent permanent = game.getPermanent(sourcePermanent.getAttachedTo()); - return permanent == null ? 0 : permanent - .getAttachments() - .stream() - .map(game::getPermanentOrLKIBattlefield) - .filter(Objects::nonNull) - .map(p -> p.hasSubtype(SubType.EQUIPMENT, game) || p.hasSubtype(SubType.AURA, game)) - .mapToInt(b -> b ? 1 : 0) - .sum(); - } - - @Override - public MantleOfTheAncientsValue copy() { - return instance; - } - - @Override - public String getMessage() { - return "Aura and Equipment attached to it"; - } - - @Override - public String toString() { - return "1"; - } -} diff --git a/Mage.Sets/src/mage/cards/m/MarduSiegebreaker.java b/Mage.Sets/src/mage/cards/m/MarduSiegebreaker.java index f101314369a..a7d0611a6ca 100644 --- a/Mage.Sets/src/mage/cards/m/MarduSiegebreaker.java +++ b/Mage.Sets/src/mage/cards/m/MarduSiegebreaker.java @@ -18,12 +18,12 @@ import mage.cards.Cards; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; +import mage.constants.TargetController; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; -import mage.game.GameState; import mage.game.permanent.token.Token; import mage.target.TargetCard; import mage.target.TargetPermanent; @@ -100,12 +100,7 @@ class MarduSiegebreakerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Cards cards = Optional - .ofNullable(game) - .map(Game::getState) - .map(GameState::getExile) - .map(exile -> exile.getExileZone(CardUtil.getExileZoneId(game, source))) - .orElse(null); + Cards cards = game.getState().getExile().getExileZone(CardUtil.getExileZoneId(game, source)); if (cards == null) { return false; } @@ -138,7 +133,7 @@ class MarduSiegebreakerEffect extends OneShotEffect { .forEach(addedTokens::add); } game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility( - new SacrificeTargetEffect().setTargetPointer(new FixedTargets(addedTokens)) + new SacrificeTargetEffect().setTargetPointer(new FixedTargets(addedTokens)), TargetController.YOU ), source); return true; } diff --git a/Mage.Sets/src/mage/cards/m/MaryJaneWatson.java b/Mage.Sets/src/mage/cards/m/MaryJaneWatson.java new file mode 100644 index 00000000000..ed03e55cd2d --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MaryJaneWatson.java @@ -0,0 +1,46 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MaryJaneWatson extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.SPIDER); + + public MaryJaneWatson(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G/W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.PERFORMER); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever a Spider you control enters, draw a card. This ability triggers only once each turn. + this.addAbility(new EntersBattlefieldAllTriggeredAbility( + new DrawCardSourceControllerEffect(1), filter + ).setTriggersLimitEachTurn(1)); + } + + private MaryJaneWatson(final MaryJaneWatson card) { + super(card); + } + + @Override + public MaryJaneWatson copy() { + return new MaryJaneWatson(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MaskwoodNexus.java b/Mage.Sets/src/mage/cards/m/MaskwoodNexus.java index eb4110d319d..d11db30b5ca 100644 --- a/Mage.Sets/src/mage/cards/m/MaskwoodNexus.java +++ b/Mage.Sets/src/mage/cards/m/MaskwoodNexus.java @@ -96,7 +96,7 @@ class MaskwoodNexusEffect extends ContinuousEffectImpl { .forEach(affectedCards::add); // in Exile - game.getState().getExile().getAllCards(game, controller.getId()).stream() + game.getState().getExile().getCardsOwned(game, controller.getId()).stream() .filter(card -> card.isOwnedBy(controller.getId())) .forEach(affectedCards::add); diff --git a/Mage.Sets/src/mage/cards/m/Massacre.java b/Mage.Sets/src/mage/cards/m/Massacre.java index ca140621504..863b7729a75 100644 --- a/Mage.Sets/src/mage/cards/m/Massacre.java +++ b/Mage.Sets/src/mage/cards/m/Massacre.java @@ -34,7 +34,7 @@ public final class Massacre extends CardImpl { // If an opponent controls a Plains and you control a Swamp, you may cast this spell without paying its mana cost. - Condition condition = new CompoundCondition("If an opponent controls a Plains and you control a Swamp", + Condition condition = new CompoundCondition("an opponent controls a Plains and you control a Swamp", new OpponentControlsPermanentCondition(filterPlains), new PermanentsOnTheBattlefieldCondition(filterSwamp)); this.addAbility(new AlternativeCostSourceAbility(null, condition)); diff --git a/Mage.Sets/src/mage/cards/m/MaximumCarnage.java b/Mage.Sets/src/mage/cards/m/MaximumCarnage.java new file mode 100644 index 00000000000..42aef22cfab --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MaximumCarnage.java @@ -0,0 +1,90 @@ +package mage.cards.m; + +import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.RestrictionEffect; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.effects.common.combat.AttacksIfAbleAllEffect; +import mage.abilities.effects.mana.AddManaToManaPoolSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.common.FilterOpponentsCreaturePermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * @author Jmlundeen + */ +public final class MaximumCarnage extends CardImpl { + + public MaximumCarnage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{R}"); + + this.subtype.add(SubType.SAGA); + + // (As this Saga enters step and after your draw step, add a lore counter. Sacrifice after III.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I -- Until your next turn, each creature attacks each combat if able and attacks a player other than you if able. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new AttacksIfAbleAllEffect( + new FilterOpponentsCreaturePermanent("each creature an opponent controls"), Duration.UntilYourNextTurn + ), new MaximumCarnageEffect()); + + // II -- Add {R}{R}{R}. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II, new AddManaToManaPoolSourceControllerEffect(new Mana(ManaType.RED, 3))); + + // III -- This Saga deals 5 damage to each opponent. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new DamagePlayersEffect(5, TargetController.OPPONENT)); + + this.addAbility(sagaAbility); + } + + private MaximumCarnage(final MaximumCarnage card) { + super(card); + } + + @Override + public MaximumCarnage copy() { + return new MaximumCarnage(this); + } +} + +class MaximumCarnageEffect extends RestrictionEffect { + + MaximumCarnageEffect() { + super(Duration.UntilYourNextTurn); + staticText = "and attacks a player other than you if able"; + } + + private MaximumCarnageEffect(final MaximumCarnageEffect effect) { + super(effect); + } + + @Override + public MaximumCarnageEffect copy() { + return new MaximumCarnageEffect(this); + } + + @Override + public boolean applies(Permanent permanent, Ability source, Game game) { + return game.getOpponents(permanent.getControllerId()).contains(source.getControllerId()); + } + + @Override + public boolean canAttack(Permanent attacker, UUID defenderId, Ability source, Game game, boolean canUseChooseDialogs) { + if (defenderId == null + || game.getState().getPlayersInRange(attacker.getControllerId(), game).size() == 2) { // just 2 players left, so it may attack you + return true; + } + // A planeswalker controlled by the controller is the defender + if (game.getPermanent(defenderId) != null) { + return !game.getPermanent(defenderId).getControllerId().equals(source.getControllerId()); + } + // The controller is the defender + return !defenderId.equals(source.getControllerId()); + } +} diff --git a/Mage.Sets/src/mage/cards/m/Melting.java b/Mage.Sets/src/mage/cards/m/Melting.java index d72ded8a51b..81371767199 100644 --- a/Mage.Sets/src/mage/cards/m/Melting.java +++ b/Mage.Sets/src/mage/cards/m/Melting.java @@ -39,6 +39,7 @@ class MeltingEffect extends ContinuousEffectImpl { MeltingEffect() { super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Detriment); this.staticText = "All lands are no longer snow"; + this.dependendToTypes.add(DependencyType.BecomeNonbasicLand); } private MeltingEffect(final MeltingEffect effect) { diff --git a/Mage.Sets/src/mage/cards/m/MeriekeRiBerit.java b/Mage.Sets/src/mage/cards/m/MeriekeRiBerit.java index 346391f88f9..4c963312879 100644 --- a/Mage.Sets/src/mage/cards/m/MeriekeRiBerit.java +++ b/Mage.Sets/src/mage/cards/m/MeriekeRiBerit.java @@ -104,7 +104,7 @@ class MeriekeRiBeritDelayedTriggeredAbility extends DelayedTriggeredAbility { @Override public boolean isInactive(Game game) { return getSourceObjectIfItStillExists(game) == null - && game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD, getSourceObjectZoneChangeCounter()) == null; + && game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD, getStackMomentSourceZCC()) == null; } @Override diff --git a/Mage.Sets/src/mage/cards/m/MesmericFiend.java b/Mage.Sets/src/mage/cards/m/MesmericFiend.java index d9c6b7ce5c8..127d16d0920 100644 --- a/Mage.Sets/src/mage/cards/m/MesmericFiend.java +++ b/Mage.Sets/src/mage/cards/m/MesmericFiend.java @@ -85,8 +85,8 @@ class MesmericFiendExileEffect extends OneShotEffect { if (controller.choose(Outcome.Exile, opponent.getHand(), target, source, game)) { Card card = opponent.getHand().get(target.getFirstTarget(), game); if (card != null) { - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); - game.getState().setValue(source.getSourceId().toString() + source.getSourceObjectZoneChangeCounter(), exileId); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); + game.getState().setValue(source.getSourceId().toString() + source.getStackMomentSourceZCC(), exileId); controller.moveCardsToExile(card, source, game, true, exileId, sourcePermanent.getName()); } } @@ -118,7 +118,7 @@ class MesmericFiendLeaveEffect extends OneShotEffect { MageObject sourceObject = source.getSourceObject(game); if (controller != null && sourceObject != null) { - int zoneChangeMinusOne = source.getSourceObjectZoneChangeCounter() - 1; + int zoneChangeMinusOne = source.getStackMomentSourceZCC() - 1; UUID exileId = (UUID) game.getState().getValue(source.getSourceId().toString() + zoneChangeMinusOne); if (exileId != null) { Cards cards = game.getExile().getExileZone(exileId); diff --git a/Mage.Sets/src/mage/cards/m/MilesMorales.java b/Mage.Sets/src/mage/cards/m/MilesMorales.java new file mode 100644 index 00000000000..6069cffefeb --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MilesMorales.java @@ -0,0 +1,103 @@ +package mage.cards.m; + +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.effects.common.continuous.BecomesColorSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.effects.common.counter.DoubleCounterOnEachPermanentEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.HexproofAbility; +import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacedCard; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MilesMorales extends ModalDoubleFacedCard { + + private static final FilterPermanent filter = new FilterControlledPermanent("Spider and legendary creature you control"); + + static { + filter.add(Predicates.or( + SubType.SPIDER.getPredicate(), + Predicates.and( + SuperType.LEGENDARY.getPredicate(), + CardType.CREATURE.getPredicate() + ))); + } + + public MilesMorales(UUID ownerId, CardSetInfo setInfo) { + super( + ownerId, setInfo, + new SuperType[]{SuperType.LEGENDARY}, new CardType[]{CardType.CREATURE}, new SubType[]{SubType.HUMAN, SubType.CITIZEN, SubType.HERO}, "{1}{G}", + "Ultimate Spider-Man", + new SuperType[]{SuperType.LEGENDARY}, new CardType[]{CardType.CREATURE}, new SubType[]{SubType.SPIDER, SubType.HUMAN, SubType.HERO}, "{3}{R}{G}{W}" + ); + this.getLeftHalfCard().setPT(1, 2); + this.getRightHalfCard().setPT(4, 3); + + // When Miles Morales enters, put a +1/+1 counter on each of up to two target creatures. + Ability ability = new EntersBattlefieldTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + ability.addTarget(new TargetCreaturePermanent(0, 2)); + this.addAbility(ability); + + // {3}{R}{G}{W}: Transform Miles Morales. Activate only as a sorcery. + this.getLeftHalfCard().addAbility(new ActivateAsSorceryActivatedAbility( + new TransformSourceEffect(), new ManaCostsImpl<>("{3}{R}{G}{W}") + )); + + // Ultimate Spider-Man + // First strike + this.getRightHalfCard().addAbility(FirstStrikeAbility.getInstance()); + + // Haste + this.getRightHalfCard().addAbility(HasteAbility.getInstance()); + + // Camouflage -- {2}: Put a +1/+1 counter on Ultimate Spider-Man. He gains hexproof and becomes colorless until end of turn. + ability = new SimpleActivatedAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), new GenericManaCost(2) + ); + ability.addEffect(new GainAbilitySourceEffect( + HexproofAbility.getInstance(), Duration.EndOfTurn + ).setText("He gains hexproof")); + ability.addEffect(new BecomesColorSourceEffect( + ObjectColor.COLORLESS, Duration.EndOfTurn + ).setText("and becomes colorless until end of turn")); + this.getRightHalfCard().addAbility(ability.withFlavorWord("Camouflage")); + + // Whenever you attack, double the number of each kind of counter on each Spider and legendary creature you control. + this.getRightHalfCard().addAbility(new AttacksWithCreaturesTriggeredAbility( + new DoubleCounterOnEachPermanentEffect(null, filter), 1 + )); + } + + private MilesMorales(final MilesMorales card) { + super(card); + } + + @Override + public MilesMorales copy() { + return new MilesMorales(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MindSwords.java b/Mage.Sets/src/mage/cards/m/MindSwords.java index 50a4d4a5eec..900aa942501 100644 --- a/Mage.Sets/src/mage/cards/m/MindSwords.java +++ b/Mage.Sets/src/mage/cards/m/MindSwords.java @@ -1,9 +1,5 @@ - package mage.cards.m; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.costs.AlternativeCostSourceAbility; @@ -24,7 +20,10 @@ import mage.game.Game; import mage.players.Player; import mage.target.Target; import mage.target.common.TargetCardInHand; -import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; /** * @@ -32,7 +31,7 @@ import mage.target.common.TargetControlledCreaturePermanent; */ public final class MindSwords extends CardImpl { - private static final FilterLandPermanent filterSwamp = new FilterLandPermanent("If you control a Swamp"); + private static final FilterLandPermanent filterSwamp = new FilterLandPermanent("you control a Swamp"); static { filterSwamp.add(SubType.SWAMP.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/m/MindbreakTrap.java b/Mage.Sets/src/mage/cards/m/MindbreakTrap.java index 04cd4fc4e1b..8b995edab89 100644 --- a/Mage.Sets/src/mage/cards/m/MindbreakTrap.java +++ b/Mage.Sets/src/mage/cards/m/MindbreakTrap.java @@ -65,7 +65,7 @@ enum MindbreakTrapCondition implements Condition { @Override public String toString() { - return "If an opponent cast three or more spells this turn"; + return "an opponent cast three or more spells this turn"; } } diff --git a/Mage.Sets/src/mage/cards/m/MiragePhalanx.java b/Mage.Sets/src/mage/cards/m/MiragePhalanx.java index aa74aa821be..6649a8e3a3d 100644 --- a/Mage.Sets/src/mage/cards/m/MiragePhalanx.java +++ b/Mage.Sets/src/mage/cards/m/MiragePhalanx.java @@ -82,7 +82,7 @@ class MiragePhalanxEffect extends OneShotEffect { // Create the token(s) tokenCopyEffect.apply(game, source); // Exile it at the end of combat - tokenCopyEffect.exileTokensCreatedAtEndOfCombat(game, source); + tokenCopyEffect.removeTokensCreatedAt(game, source, true, PhaseStep.END_COMBAT, TargetController.ANY); return !tokenCopyEffect.getAddedPermanents().isEmpty(); } diff --git a/Mage.Sets/src/mage/cards/m/MirrorOfLifeTrapping.java b/Mage.Sets/src/mage/cards/m/MirrorOfLifeTrapping.java index d4e15becb54..7a94684c154 100644 --- a/Mage.Sets/src/mage/cards/m/MirrorOfLifeTrapping.java +++ b/Mage.Sets/src/mage/cards/m/MirrorOfLifeTrapping.java @@ -88,7 +88,7 @@ class MirrorOfLifeTrappingEffect extends OneShotEffect { return false; } - UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); ExileZone exileZone = game.getExile().getExileZone(exileZoneId); Cards toBattlefield = null; diff --git a/Mage.Sets/src/mage/cards/m/MishrasWarMachine.java b/Mage.Sets/src/mage/cards/m/MishrasWarMachine.java index 63ecd3d24ee..289b061262b 100644 --- a/Mage.Sets/src/mage/cards/m/MishrasWarMachine.java +++ b/Mage.Sets/src/mage/cards/m/MishrasWarMachine.java @@ -51,7 +51,7 @@ class MishrasWarMachineEffect extends OneShotEffect { MishrasWarMachineEffect() { super(Outcome.Sacrifice); - staticText = "{this} deals 3 damage to you unless you discard a card. If {this} deals damage to you this way, tap it"; + staticText = "{this} deals 3 damage to you unless you discard a card. If it deals damage to you this way, tap it"; } private MishrasWarMachineEffect(final MishrasWarMachineEffect effect) { diff --git a/Mage.Sets/src/mage/cards/m/MisterNegative.java b/Mage.Sets/src/mage/cards/m/MisterNegative.java new file mode 100644 index 00000000000..47f035eccda --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MisterNegative.java @@ -0,0 +1,89 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author Jmlundeen + */ +public final class MisterNegative extends CardImpl { + + public MisterNegative(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Darkforce Inversion — When Mister Negative enters, you may exchange your life total with target opponent. If you lose life this way, draw that many cards. + Ability ability = new EntersBattlefieldTriggeredAbility(new MisterNegativeEffect(), true); + ability.addTarget(new TargetOpponent()); + ability.withFlavorWord("Darkforce Inversion"); + this.addAbility(ability); + } + + private MisterNegative(final MisterNegative card) { + super(card); + } + + @Override + public MisterNegative copy() { + return new MisterNegative(this); + } +} + +class MisterNegativeEffect extends OneShotEffect { + + MisterNegativeEffect() { + super(Outcome.Neutral); + staticText = "When {this} enters, you may exchange your life total with target opponent. If you lose life this way, draw that many cards."; + } + + protected MisterNegativeEffect(final MisterNegativeEffect effect) { + super(effect); + } + + @Override + public MisterNegativeEffect copy() { + return new MisterNegativeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player player = game.getPlayer(source.getFirstTarget()); + if (controller == null || player == null) { + return false; + } + int startingLife = controller.getLife(); + controller.exchangeLife(player, source, game); + int lifeChange = startingLife - controller.getLife(); + if (lifeChange > 0) { + controller.drawCards(lifeChange, source, game); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MisthollowGriffin.java b/Mage.Sets/src/mage/cards/m/MisthollowGriffin.java index e4829436d87..6f5d9e6012e 100644 --- a/Mage.Sets/src/mage/cards/m/MisthollowGriffin.java +++ b/Mage.Sets/src/mage/cards/m/MisthollowGriffin.java @@ -46,7 +46,7 @@ class MisthollowGriffinPlayEffect extends AsThoughEffectImpl { MisthollowGriffinPlayEffect() { super(AsThoughEffectType.CAST_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfGame, Outcome.Benefit); - staticText = "You may cast {this} from exile"; + staticText = "You may cast this card from exile"; } private MisthollowGriffinPlayEffect(final MisthollowGriffinPlayEffect effect) { diff --git a/Mage.Sets/src/mage/cards/m/MobLookout.java b/Mage.Sets/src/mage/cards/m/MobLookout.java new file mode 100644 index 00000000000..7c36df269c3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MobLookout.java @@ -0,0 +1,43 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.keyword.ConniveTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MobLookout extends CardImpl { + + public MobLookout(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U/B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ROGUE); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(0); + this.toughness = new MageInt(3); + + // When this creature enters, target creature you control connives. + Ability ability = new EntersBattlefieldTriggeredAbility(new ConniveTargetEffect()); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + private MobLookout(final MobLookout card) { + super(card); + } + + @Override + public MobLookout copy() { + return new MobLookout(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MoggSalvage.java b/Mage.Sets/src/mage/cards/m/MoggSalvage.java index f7ffec6f14d..6bd42bec347 100644 --- a/Mage.Sets/src/mage/cards/m/MoggSalvage.java +++ b/Mage.Sets/src/mage/cards/m/MoggSalvage.java @@ -33,7 +33,7 @@ public final class MoggSalvage extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{R}"); // If an opponent controls an Island and you control a Mountain, you may cast this spell without paying its mana cost. - Condition condition = new CompoundCondition("If an opponent controls an Island and you control a Mountain", + Condition condition = new CompoundCondition("an opponent controls an Island and you control a Mountain", new OpponentControlsPermanentCondition(filterIsland), new PermanentsOnTheBattlefieldCondition(filterMountain)); this.addAbility(new AlternativeCostSourceAbility(null, condition)); diff --git a/Mage.Sets/src/mage/cards/m/MoltenManInfernoIncarnate.java b/Mage.Sets/src/mage/cards/m/MoltenManInfernoIncarnate.java new file mode 100644 index 00000000000..d7a228a8abb --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MoltenManInfernoIncarnate.java @@ -0,0 +1,63 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.SacrificeEffect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.common.FilterBasicCard; +import mage.filter.common.FilterControlledLandPermanent; +import mage.filter.common.FilterLandPermanent; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class MoltenManInfernoIncarnate extends CardImpl { + + public MoltenManInfernoIncarnate(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // When this enters the battlefield, search your library for a basic Mountain card and put it onto the battlefield tapped. Then shuffle your library. + this.addAbility(new EntersBattlefieldTriggeredAbility( + new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(new FilterBasicCard(SubType.MOUNTAIN)), true) + )); + + // This creature gets +1/+1 for each Mountain you control. + DynamicValue value = new PermanentsOnBattlefieldCount(new FilterLandPermanent(SubType.MOUNTAIN, "Mountain you control")); + this.addAbility(new SimpleStaticAbility(new BoostSourceEffect(value, value, Duration.WhileOnBattlefield))); + + // When this leaves the battlefield, sacrifice a land. + this.addAbility(new LeavesBattlefieldTriggeredAbility( + new SacrificeEffect(new FilterControlledLandPermanent("a land"), 1, "")) + ); + } + + private MoltenManInfernoIncarnate(final MoltenManInfernoIncarnate card) { + super(card); + } + + @Override + public MoltenManInfernoIncarnate copy() { + return new MoltenManInfernoIncarnate(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MoonringMirror.java b/Mage.Sets/src/mage/cards/m/MoonringMirror.java index 64e79ec3c56..1bdcdb845f6 100644 --- a/Mage.Sets/src/mage/cards/m/MoonringMirror.java +++ b/Mage.Sets/src/mage/cards/m/MoonringMirror.java @@ -64,7 +64,7 @@ class MoonringMirrorExileEffect extends OneShotEffect { Card card = controller.getLibrary().getFromTop(game); MageObject sourceObject = source.getSourceObject(game); if (card != null && sourceObject != null) { - UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); card.setFaceDown(true, game); controller.moveCardsToExile(card, source, game, false, exileZoneId, sourceObject.getIdName()); card.setFaceDown(true, game); @@ -110,7 +110,7 @@ class MoonringMirrorEffect extends OneShotEffect { return false; } - UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); ExileZone exileZone = game.getExile().getExileZone(exileZoneId); Cards cardsToHand = null; diff --git a/Mage.Sets/src/mage/cards/m/MorbiusTheLivingVampire.java b/Mage.Sets/src/mage/cards/m/MorbiusTheLivingVampire.java new file mode 100644 index 00000000000..0f8f8c2baf5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MorbiusTheLivingVampire.java @@ -0,0 +1,58 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.ExileSourceFromGraveCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class MorbiusTheLivingVampire extends CardImpl { + + public MorbiusTheLivingVampire(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.SCIENTIST); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // {U}{B}, Exile this card from your graveyard: Look at the top three cards of your library. Put one of them into your hand and the rest on the bottom of your library in any order. + Ability ability = new SimpleActivatedAbility(Zone.GRAVEYARD, new LookLibraryAndPickControllerEffect(3, 1, PutCards.HAND, PutCards.BOTTOM_ANY), + new ManaCostsImpl<>("{U}{B}")); + ability.addCost(new ExileSourceFromGraveCost()); + this.addAbility(ability); + } + + private MorbiusTheLivingVampire(final MorbiusTheLivingVampire card) { + super(card); + } + + @Override + public MorbiusTheLivingVampire copy() { + return new MorbiusTheLivingVampire(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MorlunDevourerOfSpiders.java b/Mage.Sets/src/mage/cards/m/MorlunDevourerOfSpiders.java new file mode 100644 index 00000000000..eb681bdf44c --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MorlunDevourerOfSpiders.java @@ -0,0 +1,56 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.common.GetXValue; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.EntersBattlefieldWithXCountersEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class MorlunDevourerOfSpiders extends CardImpl { + + public MorlunDevourerOfSpiders(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{X}{B}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.VAMPIRE); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Morlun enters with X +1/+1 counters on him. + this.addAbility(new EntersBattlefieldAbility(new EntersBattlefieldWithXCountersEffect(CounterType.P1P1.createInstance()))); + + // When Morlun enters, he deals X damage to target opponent. + Ability ability = new EntersBattlefieldTriggeredAbility(new DamageTargetEffect(GetXValue.instance, "he")); + ability.addTarget(new TargetOpponent()); + this.addAbility(ability); + } + + private MorlunDevourerOfSpiders(final MorlunDevourerOfSpiders card) { + super(card); + } + + @Override + public MorlunDevourerOfSpiders copy() { + return new MorlunDevourerOfSpiders(this); + } +} diff --git a/Mage.Sets/src/mage/cards/m/MournersShield.java b/Mage.Sets/src/mage/cards/m/MournersShield.java index 3c190addd62..68bdd029189 100644 --- a/Mage.Sets/src/mage/cards/m/MournersShield.java +++ b/Mage.Sets/src/mage/cards/m/MournersShield.java @@ -83,7 +83,7 @@ class MournersShieldImprintEffect extends OneShotEffect { if (controller != null) { Card card = game.getCard(this.getTargetPointer().getFirst(game, source)); if (card != null) { - controller.moveCardsToExile(card, source, game, true, CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()), source.getSourceObject(game).getIdName()); + controller.moveCardsToExile(card, source, game, true, CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()), source.getSourceObject(game).getIdName()); Permanent sourcePermanent = game.getPermanent(source.getSourceId()); if (sourcePermanent != null) { sourcePermanent.imprint(this.getTargetPointer().getFirst(game, source), game); diff --git a/Mage.Sets/src/mage/cards/m/MoxDiamond.java b/Mage.Sets/src/mage/cards/m/MoxDiamond.java index 549f85f7e2b..b9a17dc1666 100644 --- a/Mage.Sets/src/mage/cards/m/MoxDiamond.java +++ b/Mage.Sets/src/mage/cards/m/MoxDiamond.java @@ -50,7 +50,7 @@ class MoxDiamondReplacementEffect extends ReplacementEffectImpl { MoxDiamondReplacementEffect() { super(Duration.WhileOnBattlefield, Outcome.Exile); - staticText = "If {this} would enter the battlefield, you may discard a land card instead. If you do, put {this} onto the battlefield. If you don't, put it into its owner's graveyard"; + staticText = "If {this} would enter, you may discard a land card instead. If you do, put {this} onto the battlefield. If you don't, put it into its owner's graveyard"; } private MoxDiamondReplacementEffect(final MoxDiamondReplacementEffect effect) { diff --git a/Mage.Sets/src/mage/cards/m/MultiversalPassage.java b/Mage.Sets/src/mage/cards/m/MultiversalPassage.java new file mode 100644 index 00000000000..e0fa229ff4d --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MultiversalPassage.java @@ -0,0 +1,125 @@ +package mage.cards.m; + +import mage.abilities.Ability; +import mage.abilities.common.AsEntersBattlefieldAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.ChooseBasicLandTypeEffect; +import mage.abilities.effects.common.TapSourceUnlessPaysEffect; +import mage.abilities.mana.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class MultiversalPassage extends CardImpl { + + public MultiversalPassage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + + // As this land enters, choose a basic land type. Then you may pay 2 life. If you don't, it enters tapped. + Ability ability = new AsEntersBattlefieldAbility(new ChooseBasicLandTypeEffect(Outcome.Benefit)); + ability.addEffect(new TapSourceUnlessPaysEffect(new PayLifeCost(2))); + this.addAbility(ability); + + // This land is the chosen type. + this.addAbility(new SimpleStaticAbility(new MultiversalPassagePassageEffect())); + } + + private MultiversalPassage(final MultiversalPassage card) { + super(card); + } + + @Override + public MultiversalPassage copy() { + return new MultiversalPassage(this); + } +} +class MultiversalPassagePassageEffect extends ContinuousEffectImpl { + + MultiversalPassagePassageEffect() { + super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Neutral); + staticText = "This land is the chosen type."; + } + + private MultiversalPassagePassageEffect(final MultiversalPassagePassageEffect effect) { + super(effect); + } + + @Override + public MultiversalPassagePassageEffect copy() { + return new MultiversalPassagePassageEffect(this); + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + SubType choice = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + ChooseBasicLandTypeEffect.VALUE_KEY)); + if (choice == null) { + discard(); + return; + } + + switch (choice) { + case PLAINS: + dependencyTypes.add(DependencyType.BecomePlains); + break; + case ISLAND: + dependencyTypes.add(DependencyType.BecomeIsland); + break; + case SWAMP: + dependencyTypes.add(DependencyType.BecomeSwamp); + break; + case MOUNTAIN: + dependencyTypes.add(DependencyType.BecomeMountain); + break; + case FOREST: + dependencyTypes.add(DependencyType.BecomeForest); + break; + } + } + + @Override + public boolean apply(Game game, Ability source) { + SubType choice = SubType.byDescription((String) game.getState().getValue(source.getSourceId().toString() + ChooseBasicLandTypeEffect.VALUE_KEY)); + if (choice == null) { + return false; + } + Ability ability; + switch (choice) { + case PLAINS: + ability = new WhiteManaAbility(); + break; + case ISLAND: + ability = new BlueManaAbility(); + break; + case SWAMP: + ability = new BlackManaAbility(); + break; + case MOUNTAIN: + ability = new RedManaAbility(); + break; + case FOREST: + ability = new GreenManaAbility(); + break; + default: + ability = null; + } + Permanent land = game.getPermanent(source.getSourceId()); + if (land == null || land.hasSubtype(choice, game)) { + return false; + } + land.addSubType(game, choice); + land.addAbility(ability, source.getSourceId(), game); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MycosynthLattice.java b/Mage.Sets/src/mage/cards/m/MycosynthLattice.java index f9980c60f77..cbefd32b853 100644 --- a/Mage.Sets/src/mage/cards/m/MycosynthLattice.java +++ b/Mage.Sets/src/mage/cards/m/MycosynthLattice.java @@ -107,7 +107,7 @@ class EverythingIsColorlessEffect extends ContinuousEffectImpl { } // exile - affectedCards.addAll(game.getExile().getAllCardsByRange(game, controller.getId())); + affectedCards.addAll(game.getExile().getCardsInRange(game, controller.getId())); for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { diff --git a/Mage.Sets/src/mage/cards/m/MysterioMasterOfIllusion.java b/Mage.Sets/src/mage/cards/m/MysterioMasterOfIllusion.java new file mode 100644 index 00000000000..865ae79068b --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MysterioMasterOfIllusion.java @@ -0,0 +1,119 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.permanent.TokenPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.IllusionVillainToken; +import mage.game.permanent.token.Token; +import mage.target.targetpointer.FixedTargets; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MysterioMasterOfIllusion extends CardImpl { + + public MysterioMasterOfIllusion(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When Mysterio enters, create a 3/3 blue Illusion Villain creature token for each nontoken Villain you control. Exile those tokens when Mysterio leaves the battlefield. + this.addAbility(new EntersBattlefieldTriggeredAbility(new MysterioMasterOfIllusionEffect())); + } + + private MysterioMasterOfIllusion(final MysterioMasterOfIllusion card) { + super(card); + } + + @Override + public MysterioMasterOfIllusion copy() { + return new MysterioMasterOfIllusion(this); + } +} + +class MysterioMasterOfIllusionEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.VILLAIN); + + static { + filter.add(TokenPredicate.FALSE); + } + + MysterioMasterOfIllusionEffect() { + super(Outcome.Benefit); + staticText = "create a 3/3 blue Illusion Villain creature token for each nontoken Villain you control. " + + "Exile those tokens when {this} leaves the battlefield"; + } + + private MysterioMasterOfIllusionEffect(final MysterioMasterOfIllusionEffect effect) { + super(effect); + } + + @Override + public MysterioMasterOfIllusionEffect copy() { + return new MysterioMasterOfIllusionEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Token token = new IllusionVillainToken(); + token.putOntoBattlefield(game.getBattlefield().count(filter, source.getControllerId(), source, game), game, source); + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent != null) { + game.addDelayedTriggeredAbility(new MysterioMasterOfIllusionTriggeredAbility(token, game), source); + } + return true; + } +} + +class MysterioMasterOfIllusionTriggeredAbility extends DelayedTriggeredAbility { + + MysterioMasterOfIllusionTriggeredAbility(Token token, Game game) { + super(new ExileTargetEffect().setTargetPointer(new FixedTargets(token, game)), Duration.Custom, true, false); + this.setLeavesTheBattlefieldTrigger(true); + } + + private MysterioMasterOfIllusionTriggeredAbility(final MysterioMasterOfIllusionTriggeredAbility ability) { + super(ability); + } + + @Override + public MysterioMasterOfIllusionTriggeredAbility copy() { + return new MysterioMasterOfIllusionTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return Zone.BATTLEFIELD.match(((ZoneChangeEvent) event).getFromZone()) + && event.getSourceId().equals(getSourceId()); + } + + @Override + public String getRule() { + return "Exile those tokens when {this} leaves the battlefield."; + } +} diff --git a/Mage.Sets/src/mage/cards/m/MysteriosPhantasm.java b/Mage.Sets/src/mage/cards/m/MysteriosPhantasm.java new file mode 100644 index 00000000000..508a4070cfd --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MysteriosPhantasm.java @@ -0,0 +1,46 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.MillCardsControllerEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MysteriosPhantasm extends CardImpl { + + public MysteriosPhantasm(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + + this.subtype.add(SubType.ILLUSION); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Whenever this creature attacks, mill a card. + this.addAbility(new AttacksTriggeredAbility(new MillCardsControllerEffect(1))); + } + + private MysteriosPhantasm(final MysteriosPhantasm card) { + super(card); + } + + @Override + public MysteriosPhantasm copy() { + return new MysteriosPhantasm(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/NahirisSacrifice.java b/Mage.Sets/src/mage/cards/n/NahirisSacrifice.java index 9bb7df08c61..79ee6ad1e61 100644 --- a/Mage.Sets/src/mage/cards/n/NahirisSacrifice.java +++ b/Mage.Sets/src/mage/cards/n/NahirisSacrifice.java @@ -1,5 +1,6 @@ package mage.cards.n; +import mage.abilities.Ability; import mage.abilities.costs.Cost; import mage.abilities.costs.SacrificeCost; import mage.abilities.costs.VariableCostImpl; @@ -15,6 +16,9 @@ import mage.constants.ComparisonType; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.common.TargetCreaturePermanentAmount; import java.util.UUID; @@ -90,4 +94,33 @@ class SacrificeXManaValueCost extends VariableCostImpl implements SacrificeCost return new SacrificeTargetCost(manavaluefilter); } + @Override + public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { + Player controller = game.getPlayer(controllerId); + if (controller == null) { + return false; + } + int validTargets = 0; + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, controllerId, game)) { + if (controller.canPaySacrificeCost(permanent, source, controllerId, game)) { + validTargets++; + } + } + return validTargets > 0; + } + + @Override + public int getMaxValue(Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return super.getMaxValue(source, game); + } + int maxValue = 0; + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, controller.getId(), game)) { + if (controller.canPaySacrificeCost(permanent, source, controller.getId(), game)) { + maxValue = Math.max(maxValue, permanent.getManaValue()); + } + } + return maxValue; + } } diff --git a/Mage.Sets/src/mage/cards/n/NalathniDragon.java b/Mage.Sets/src/mage/cards/n/NalathniDragon.java index 1ef194771d7..d82de639621 100644 --- a/Mage.Sets/src/mage/cards/n/NalathniDragon.java +++ b/Mage.Sets/src/mage/cards/n/NalathniDragon.java @@ -20,7 +20,6 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; import mage.game.Game; /** @@ -78,7 +77,7 @@ class NalathniDragonEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - ActivationInfo activationInfo = ActivationInfo.getInstance(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + ActivationInfo activationInfo = ActivationInfo.getInstance(game, source.getSourceId(), source.getStackMomentSourceZCC()); activationInfo.addActivation(game); if (activationInfo.getActivationCounter() >= 4) { DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new SacrificeSourceEffect()); diff --git a/Mage.Sets/src/mage/cards/n/NaturalEmergence.java b/Mage.Sets/src/mage/cards/n/NaturalEmergence.java index c847a1f6252..2b98c547b0c 100644 --- a/Mage.Sets/src/mage/cards/n/NaturalEmergence.java +++ b/Mage.Sets/src/mage/cards/n/NaturalEmergence.java @@ -3,12 +3,14 @@ package mage.cards.n; import mage.ObjectColor; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.ReturnToHandChosenControlledPermanentEffect; import mage.abilities.effects.common.continuous.BecomesCreatureAllEffect; import mage.abilities.keyword.FirstStrikeAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.DependencyType; import mage.constants.Duration; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledEnchantmentPermanent; @@ -37,12 +39,14 @@ public final class NaturalEmergence extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new ReturnToHandChosenControlledPermanentEffect(filter), false)); // Lands you control are 2/2 creatures with first strike. They're still lands. - this.addAbility(new SimpleStaticAbility(new BecomesCreatureAllEffect( + ContinuousEffect effect = new BecomesCreatureAllEffect( new CreatureToken( 2, 2, "2/2 creatures with first strike" ).withAbility(FirstStrikeAbility.getInstance()), "lands", StaticFilters.FILTER_CONTROLLED_PERMANENT_LANDS, Duration.WhileOnBattlefield, false - ))); + ); + effect.getDependedToTypes().add(DependencyType.BecomeNonbasicLand); + this.addAbility(new SimpleStaticAbility(effect)); } private NaturalEmergence(final NaturalEmergence card) { diff --git a/Mage.Sets/src/mage/cards/n/NaturesRevolt.java b/Mage.Sets/src/mage/cards/n/NaturesRevolt.java index b4f8b3d9df3..ea5ae34c91a 100644 --- a/Mage.Sets/src/mage/cards/n/NaturesRevolt.java +++ b/Mage.Sets/src/mage/cards/n/NaturesRevolt.java @@ -2,12 +2,13 @@ package mage.cards.n; import java.util.UUID; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.continuous.BecomesCreatureAllEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.DependencyType; import mage.constants.Duration; -import mage.constants.Zone; import mage.filter.common.FilterLandPermanent; import mage.game.permanent.token.custom.CreatureToken; @@ -24,9 +25,11 @@ public final class NaturesRevolt extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}{G}"); // All lands are 2/2 creatures that are still lands. - this.addAbility(new SimpleStaticAbility(new BecomesCreatureAllEffect( + ContinuousEffect effect = new BecomesCreatureAllEffect( new CreatureToken(2, 2, "2/2 creatures"), - "lands", filter, Duration.WhileOnBattlefield, false))); + "lands", filter, Duration.WhileOnBattlefield, false); + effect.getDependedToTypes().add(DependencyType.BecomeNonbasicLand); + this.addAbility(new SimpleStaticAbility(effect)); } private NaturesRevolt(final NaturesRevolt card) { diff --git a/Mage.Sets/src/mage/cards/n/NeedlebiteTrap.java b/Mage.Sets/src/mage/cards/n/NeedlebiteTrap.java index 990db5e6f6b..01f83e596cf 100644 --- a/Mage.Sets/src/mage/cards/n/NeedlebiteTrap.java +++ b/Mage.Sets/src/mage/cards/n/NeedlebiteTrap.java @@ -64,6 +64,6 @@ enum NeedlebiteTrapCondition implements Condition { @Override public String toString() { - return "If an opponent gained life this turn"; + return "an opponent gained life this turn"; } } diff --git a/Mage.Sets/src/mage/cards/n/NemesisTrap.java b/Mage.Sets/src/mage/cards/n/NemesisTrap.java index 2f45977dbf5..f40ec89425b 100644 --- a/Mage.Sets/src/mage/cards/n/NemesisTrap.java +++ b/Mage.Sets/src/mage/cards/n/NemesisTrap.java @@ -32,7 +32,7 @@ import java.util.UUID; */ public final class NemesisTrap extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("If a white creature is attacking"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a white creature is attacking"); static { filter.add(new ColorPredicate(ObjectColor.WHITE)); diff --git a/Mage.Sets/src/mage/cards/n/NewsHelicopter.java b/Mage.Sets/src/mage/cards/n/NewsHelicopter.java new file mode 100644 index 00000000000..c35bd9937e7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NewsHelicopter.java @@ -0,0 +1,42 @@ +package mage.cards.n; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.HumanCitizenToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class NewsHelicopter extends CardImpl { + + public NewsHelicopter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); + + this.subtype.add(SubType.CONSTRUCT); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When this creature enters, create a 1/1 green and white Human Citizen creature token. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new HumanCitizenToken()))); + } + + private NewsHelicopter(final NewsHelicopter card) { + super(card); + } + + @Override + public NewsHelicopter copy() { + return new NewsHelicopter(this); + } +} diff --git a/Mage.Sets/src/mage/cards/n/NormanOsborn.java b/Mage.Sets/src/mage/cards/n/NormanOsborn.java new file mode 100644 index 00000000000..96b5f24f0d9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/n/NormanOsborn.java @@ -0,0 +1,117 @@ +package mage.cards.n; + +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; +import mage.abilities.effects.keyword.ConniveSourceEffect; +import mage.abilities.keyword.*; +import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacedCard; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.predicate.card.CastFromZonePredicate; +import mage.game.Game; +import mage.players.Player; + +import java.util.Objects; +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class NormanOsborn extends ModalDoubleFacedCard { + + private static final FilterCard filter = new FilterCard("spells you cast from your graveyard"); + + static { + filter.add(new CastFromZonePredicate(Zone.GRAVEYARD)); + } + + public NormanOsborn(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, + new SuperType[]{SuperType.LEGENDARY}, new CardType[]{CardType.CREATURE}, new SubType[]{SubType.HUMAN, SubType.SCIENTIST, SubType.VILLAIN}, "{1}{U}", + "Green Goblin", + new SuperType[]{SuperType.LEGENDARY}, new CardType[]{CardType.CREATURE}, new SubType[]{SubType.GOBLIN, SubType.HUMAN, SubType.VILLAIN}, "{1}{U}{B}{R}"); + + this.getLeftHalfCard().setPT(1, 1); + this.getRightHalfCard().setPT(3, 3); + + // Norman Osborn can't be blocked. + this.getLeftHalfCard().addAbility(new CantBeBlockedSourceAbility()); + + // Whenever Norman Osborn deals combat damage to a player, he connives. + this.getLeftHalfCard().addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new ConniveSourceEffect("he"))); + + // {1}{U}{B}{R}: Transform Norman Osborn. Activate only as a sorcery. + this.getLeftHalfCard().addAbility(new ActivateAsSorceryActivatedAbility( + new TransformSourceEffect(), new ManaCostsImpl<>("{1}{U}{B}{R}") + )); + + // Green Goblin + // Flying + this.getRightHalfCard().addAbility(FlyingAbility.getInstance()); + + // Menace + this.getRightHalfCard().addAbility(new MenaceAbility()); + + // Spells you cast from your graveyard cost {2} less to cast. + this.getRightHalfCard().addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 2))); + + // Goblin Formula -- Each nonland card in your graveyard has mayhem. The mayhem cost is equal to its mana cost. + this.getRightHalfCard().addAbility(new SimpleStaticAbility(new GreenGoblinEffect())); + } + + private NormanOsborn(final NormanOsborn card) { + super(card); + } + + @Override + public NormanOsborn copy() { + return new NormanOsborn(this); + } +} +class GreenGoblinEffect extends ContinuousEffectImpl { + + GreenGoblinEffect() { + super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + staticText = "Each nonland card in your graveyard has mayhem. " + + "The mayhem cost is equal to the card's mana cost."; + } + + private GreenGoblinEffect(final GreenGoblinEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + controller + .getGraveyard() + .getCards(game) + .stream() + .filter(Objects::nonNull) + .filter(card -> !card.getManaCost().getText().isEmpty()) // card must have a mana cost + .filter(card -> !card.isLand(game)) + .forEach(card -> { + Ability ability = new MayhemAbility(card, card.getManaCost().getText()); + ability.setSourceId(card.getId()); + ability.setControllerId(card.getOwnerId()); + game.getState().addOtherAbility(card, ability); + }); + return true; + } + + @Override + public GreenGoblinEffect copy() { + return new GreenGoblinEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/OblivionSower.java b/Mage.Sets/src/mage/cards/o/OblivionSower.java index 6cdeecf3533..ac10cdf7b5d 100644 --- a/Mage.Sets/src/mage/cards/o/OblivionSower.java +++ b/Mage.Sets/src/mage/cards/o/OblivionSower.java @@ -1,7 +1,5 @@ - package mage.cards.o; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; @@ -12,19 +10,20 @@ import mage.cards.CardSetInfo; import mage.cards.Cards; import mage.cards.CardsImpl; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.common.FilterLandCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.card.FaceDownPredicate; -import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetOpponent; +import java.util.UUID; + /** * * @author LevelX2 @@ -56,6 +55,11 @@ public final class OblivionSower extends CardImpl { class OblivionSowerEffect extends OneShotEffect { + private static final FilterCard filter = new FilterLandCard(); + static { + filter.add(Predicates.not(FaceDownPredicate.instance)); + } + OblivionSowerEffect() { super(Outcome.PutLandInPlay); this.staticText = ", then you may put any number of land cards that player owns from exile onto the battlefield under your control"; @@ -78,25 +82,21 @@ class OblivionSowerEffect extends OneShotEffect { */ Player controller = game.getPlayer(source.getControllerId()); Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source)); - if (controller != null && targetPlayer != null) { - FilterLandCard filter = new FilterLandCard(); - filter.add(new OwnerIdPredicate(targetPlayer.getId())); - filter.add(Predicates.not(FaceDownPredicate.instance)); - Cards exiledCards = new CardsImpl(); - exiledCards.addAllCards(game.getExile().getAllCards(game)); - Cards exiledLands = new CardsImpl(); - exiledLands.addAllCards(exiledCards.getCards(filter, controller.getId(), source, game)); - if (!exiledLands.isEmpty() && controller.chooseUse(outcome, "Put lands into play?", source, game)) { - FilterCard filterToPlay = new FilterCard("land" - + (exiledLands.size() > 1 ? "s" : "") + " from exile owned by " - + targetPlayer.getName() + " to put into play under your control"); - TargetCard targetCards = new TargetCard(0, exiledLands.size(), Zone.EXILED, filterToPlay); - if (controller.chooseTarget(outcome, exiledLands, targetCards, source, game)) { - controller.moveCards(new CardsImpl(targetCards.getTargets()), Zone.BATTLEFIELD, source, game); - } - } - return true; + if (controller == null || targetPlayer == null) { + return false; } - return false; + Cards exiledLands = new CardsImpl(game.getExile() + .getCardsOwned(filter, targetPlayer.getId(), source, game) + ); + if (!exiledLands.isEmpty() && controller.chooseUse(outcome, "Put lands into play?", source, game)) { + FilterCard filterToPlay = new FilterCard("land" + + (exiledLands.size() > 1 ? "s" : "") + " from exile owned by " + + targetPlayer.getName() + " to put into play under your control"); + TargetCard targetCards = new TargetCard(0, exiledLands.size(), Zone.EXILED, filterToPlay); + if (controller.chooseTarget(outcome, exiledLands, targetCards, source, game)) { + controller.moveCards(new CardsImpl(targetCards.getTargets()), Zone.BATTLEFIELD, source, game); + } + } + return true; } } diff --git a/Mage.Sets/src/mage/cards/o/OminousAsylum.java b/Mage.Sets/src/mage/cards/o/OminousAsylum.java new file mode 100644 index 00000000000..8c6d06632a6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OminousAsylum.java @@ -0,0 +1,46 @@ +package mage.cards.o; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.keyword.SurveilEffect; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.RedManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class OminousAsylum extends CardImpl { + + public OminousAsylum(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // This land enters tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {B} or {R}. + this.addAbility(new BlackManaAbility()); + this.addAbility(new RedManaAbility()); + + // {4}, {T}: Surveil 1. + Ability ability = new SimpleActivatedAbility(new SurveilEffect(1), new GenericManaCost(4)); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private OminousAsylum(final OminousAsylum card) { + super(card); + } + + @Override + public OminousAsylum copy() { + return new OminousAsylum(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/OrimsCure.java b/Mage.Sets/src/mage/cards/o/OrimsCure.java index c35d0b03b70..894ea3d0cd6 100644 --- a/Mage.Sets/src/mage/cards/o/OrimsCure.java +++ b/Mage.Sets/src/mage/cards/o/OrimsCure.java @@ -24,7 +24,7 @@ import mage.target.common.TargetAnyTarget; */ public final class OrimsCure extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("If you control a Plains"); + private static final FilterPermanent filter = new FilterPermanent("you control a Plains"); private static final FilterControlledCreaturePermanent filterCreature = new FilterControlledCreaturePermanent("untapped creature you control"); static { diff --git a/Mage.Sets/src/mage/cards/o/OrochiMergeKeeper.java b/Mage.Sets/src/mage/cards/o/OrochiMergeKeeper.java index 39b4429664f..b9af61a8062 100644 --- a/Mage.Sets/src/mage/cards/o/OrochiMergeKeeper.java +++ b/Mage.Sets/src/mage/cards/o/OrochiMergeKeeper.java @@ -3,8 +3,7 @@ package mage.cards.o; import mage.MageInt; import mage.Mana; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.condition.Condition; -import mage.abilities.condition.common.SourceMatchesFilterCondition; +import mage.abilities.condition.common.SourceModifiedCondition; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; @@ -15,8 +14,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.predicate.permanent.ModifiedPredicate; import java.util.UUID; @@ -25,14 +22,6 @@ import java.util.UUID; */ public final class OrochiMergeKeeper extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent(); - - static { - filter.add(ModifiedPredicate.instance); - } - - private static final Condition condition = new SourceMatchesFilterCondition(filter); - public OrochiMergeKeeper(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); @@ -48,7 +37,7 @@ public final class OrochiMergeKeeper extends CardImpl { this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( new GainAbilitySourceEffect(new SimpleManaAbility( Zone.BATTLEFIELD, Mana.GreenMana(2), new TapSourceCost() - )), condition, "as long as {this} is modified, it has \"{T}: Add {G}{G}.\"" + )), SourceModifiedCondition.instance, "as long as {this} is modified, it has \"{T}: Add {G}{G}.\"" ))); } diff --git a/Mage.Sets/src/mage/cards/o/OrvarTheAllForm.java b/Mage.Sets/src/mage/cards/o/OrvarTheAllForm.java index 160f051cb85..557f25730b0 100644 --- a/Mage.Sets/src/mage/cards/o/OrvarTheAllForm.java +++ b/Mage.Sets/src/mage/cards/o/OrvarTheAllForm.java @@ -82,7 +82,7 @@ enum OrvarTheAllFormCondition implements Condition { public boolean apply(Game game, Ability source) { Spell spell = (Spell) source.getEffects().get(0).getValue("spellCast"); MageObjectReference mor; - if (source.getSourceObjectZoneChangeCounter() == 0) { + if (source.getStackMomentSourceZCC() == 0) { mor = new MageObjectReference(source.getSourceId(), game); } else { mor = new MageObjectReference(source); diff --git a/Mage.Sets/src/mage/cards/o/OscorpIndustries.java b/Mage.Sets/src/mage/cards/o/OscorpIndustries.java new file mode 100644 index 00000000000..f694701108b --- /dev/null +++ b/Mage.Sets/src/mage/cards/o/OscorpIndustries.java @@ -0,0 +1,50 @@ +package mage.cards.o; + +import mage.abilities.common.EntersBattlefieldFromGraveyardTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.effects.common.LoseLifeSourceControllerEffect; +import mage.abilities.keyword.MayhemLandAbility; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.RedManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class OscorpIndustries extends CardImpl { + + public OscorpIndustries(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + + // This land enters tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // When this land enters from a graveyard, you lose 2 life. + this.addAbility(new EntersBattlefieldFromGraveyardTriggeredAbility(new LoseLifeSourceControllerEffect(2))); + + // {T}: Add {U}, {B}, or {R}. + this.addAbility(new BlueManaAbility()); + this.addAbility(new BlackManaAbility()); + this.addAbility(new RedManaAbility()); + + // Mayhem + this.addAbility(new MayhemLandAbility(this)); + + } + + private OscorpIndustries(final OscorpIndustries card) { + super(card); + } + + @Override + public OscorpIndustries copy() { + return new OscorpIndustries(this); + } +} diff --git a/Mage.Sets/src/mage/cards/o/OutOfTime.java b/Mage.Sets/src/mage/cards/o/OutOfTime.java index 0bdb44d4440..78a6ccb5d7d 100644 --- a/Mage.Sets/src/mage/cards/o/OutOfTime.java +++ b/Mage.Sets/src/mage/cards/o/OutOfTime.java @@ -185,7 +185,7 @@ class OutOfTimeReplacementEffect extends ContinuousRuleModifyingEffectImpl { public boolean applies(GameEvent event, Ability source, Game game) { Set creatureIds = (Set) game.getState().getValue("phasedOutCreatures" + source.getId().toString()); - return source.getSourceObjectZoneChangeCounter() == game.getState().getZoneChangeCounter(source.getSourceId()) // blinked + return source.getStackMomentSourceZCC() == game.getState().getZoneChangeCounter(source.getSourceId()) // blinked && creatureIds != null && creatureIds.contains(event.getTargetId()); } diff --git a/Mage.Sets/src/mage/cards/p/PaintersServant.java b/Mage.Sets/src/mage/cards/p/PaintersServant.java index b61327c63c6..bdffc476a83 100644 --- a/Mage.Sets/src/mage/cards/p/PaintersServant.java +++ b/Mage.Sets/src/mage/cards/p/PaintersServant.java @@ -82,7 +82,7 @@ class PaintersServantEffect extends ContinuousEffectImpl { } // exile - affectedCards.addAll(game.getExile().getAllCardsByRange(game, controller.getId())); + affectedCards.addAll(game.getExile().getCardsInRange(game, controller.getId())); for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { Player player = game.getPlayer(playerId); diff --git a/Mage.Sets/src/mage/cards/p/PalaceJailer.java b/Mage.Sets/src/mage/cards/p/PalaceJailer.java index 541d407572a..f79717e0b94 100644 --- a/Mage.Sets/src/mage/cards/p/PalaceJailer.java +++ b/Mage.Sets/src/mage/cards/p/PalaceJailer.java @@ -20,13 +20,11 @@ import mage.constants.SubType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.StaticFilters; import mage.game.ExileZone; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; import mage.target.TargetPermanent; -import mage.target.common.TargetCreaturePermanent; import mage.util.CardUtil; import static mage.filter.StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE; @@ -85,7 +83,7 @@ class PalaceJailerExileEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { MageObject sourceObject = source.getSourceObject(game); if (sourceObject != null) { - return new ExileTargetEffect(CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()), sourceObject.getIdName()).apply(game, source); + return new ExileTargetEffect(CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()), sourceObject.getIdName()).apply(game, source); } return false; } @@ -140,7 +138,7 @@ class PalaceJailerReturnExiledPermanentsEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = source.getSourceObject(game); if (sourceObject != null && controller != null) { - UUID exileZone = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileZone = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); if (exileZone != null) { ExileZone exile = game.getExile().getExileZone(exileZone); if (exile != null) { diff --git a/Mage.Sets/src/mage/cards/p/ParapetThrasher.java b/Mage.Sets/src/mage/cards/p/ParapetThrasher.java index 48725cea523..382919bec4b 100644 --- a/Mage.Sets/src/mage/cards/p/ParapetThrasher.java +++ b/Mage.Sets/src/mage/cards/p/ParapetThrasher.java @@ -1,7 +1,5 @@ package mage.cards.p; -import java.util.Objects; -import java.util.UUID; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -12,30 +10,38 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.ExileTopXMayPlayUntilEffect; import mage.abilities.hint.common.ModesAlreadyUsedHint; -import mage.constants.*; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.*; import mage.filter.FilterPermanent; import mage.filter.common.FilterArtifactPermanent; import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; +import mage.game.events.DamagedBatchForOnePlayerEvent; +import mage.game.events.DamagedEvent; +import mage.game.events.DamagedPlayerEvent; +import mage.game.events.GameEvent; import mage.players.Player; import mage.target.TargetPermanent; -import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; +import mage.target.targetadjustment.DefineByTriggerTargetAdjuster; +import mage.target.targetpointer.FixedTarget; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; /** - * * @author Jmlundeen */ public final class ParapetThrasher extends CardImpl { private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.DRAGON, "Dragons you control"); - private static final FilterPermanent artifactFilter = new FilterArtifactPermanent("artifact that opponent controls"); public ParapetThrasher(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); - + this.subtype.add(SubType.DRAGON); this.power = new MageInt(4); this.toughness = new MageInt(3); @@ -45,11 +51,7 @@ public final class ParapetThrasher extends CardImpl { // Whenever one or more Dragons you control deal combat damage to an opponent, choose one that hasn't been chosen this turn -- // * Destroy target artifact that opponent controls. - Ability ability = new OneOrMoreDamagePlayerTriggeredAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), - filter, true, true, SetTargetPointer.PLAYER, false) - .setTriggerPhrase("Whenever one or more Dragons you control deal combat damage to an opponent, "); - ability.addTarget(new TargetPermanent(artifactFilter)); - ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); + Ability ability = new ParapetThrasherTriggeredAbility(new DestroyTargetEffect().setText("destroy target artifact that opponent controls"), filter); ability.setModeTag("destroy artifact"); ability.getModes().setLimitUsageByOnce(true); @@ -78,6 +80,47 @@ public final class ParapetThrasher extends CardImpl { } } +class ParapetThrasherTriggeredAbility extends OneOrMoreDamagePlayerTriggeredAbility { + + + public ParapetThrasherTriggeredAbility(Effect effect, FilterPermanent filter) { + super(Zone.BATTLEFIELD, effect, + filter, true, true, SetTargetPointer.PLAYER, false); + setTargetAdjuster(DefineByTriggerTargetAdjuster.instance); + } + + private ParapetThrasherTriggeredAbility(final ParapetThrasherTriggeredAbility ability) { + super(ability); + } + + @Override + public ParapetThrasherTriggeredAbility copy() { + return new ParapetThrasherTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + List events = getFilteredEvents((DamagedBatchForOnePlayerEvent) event, game); + if (events.isEmpty()) { + return false; + } + this.getAllEffects().setValue("damage", events.stream().mapToInt(DamagedEvent::getAmount).sum()); + Player damagedPlayer = game.getPlayer(event.getTargetId()); + + FilterPermanent artifactFilter = new FilterArtifactPermanent("artifact " + damagedPlayer.getLogName() + " controls"); + artifactFilter.add(new ControllerIdPredicate(damagedPlayer.getId())); + this.getTargets().clear(); + this.addTarget(new TargetPermanent(artifactFilter)); + + for (Effect effect : this.getAllEffects()) { + if (effect instanceof ParapetThrasherDamageEffect) { + effect.setTargetPointer(new FixedTarget(damagedPlayer.getId())); + } + } + return true; + } +} + class ParapetThrasherDamageEffect extends OneShotEffect { ParapetThrasherDamageEffect() { diff --git a/Mage.Sets/src/mage/cards/p/ParkerLuck.java b/Mage.Sets/src/mage/cards/p/ParkerLuck.java new file mode 100644 index 00000000000..72c32246e2b --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/ParkerLuck.java @@ -0,0 +1,91 @@ +package mage.cards.p; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetPlayer; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class ParkerLuck extends CardImpl { + + public ParkerLuck(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); + + + // At the beginning of your end step, two target players each reveal the top card of their library. They each lose life equal to the mana value of the card revealed by the other player. Then they each put the card they revealed into their hand. + Ability ability = new BeginningOfEndStepTriggeredAbility(new ParkerLuckEffect()); + ability.addTarget(new TargetPlayer(2)); + this.addAbility(ability); + } + + private ParkerLuck(final ParkerLuck card) { + super(card); + } + + @Override + public ParkerLuck copy() { + return new ParkerLuck(this); + } +} + +class ParkerLuckEffect extends OneShotEffect { + + ParkerLuckEffect() { + super(Outcome.Damage); + staticText = "two target players each reveal the top card of their library. " + + "They each lose life equal to the mana value of the card " + + "revealed by the other player. Then they each put the card they revealed into their hand"; + } + + protected ParkerLuckEffect(final ParkerLuckEffect effect) { + super(effect); + } + + @Override + public ParkerLuckEffect copy() { + return new ParkerLuckEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player targetOne = game.getPlayer(getTargetPointer().getTargets(game, source).get(0)); + Player targetTwo = game.getPlayer(getTargetPointer().getTargets(game, source).get(1)); + if (targetOne == null || targetTwo == null) { + return false; + } + // each reveal top card + Card targetOneCard = targetOne.getLibrary().getFromTop(game); + int targetOneMv = 0; + Card targetTwoCard = targetTwo.getLibrary().getFromTop(game); + int targetTwoMv = 0; + if (targetOneCard != null) { + targetOne.revealCards(source, new CardsImpl(targetOneCard), game); + targetOneMv = targetOneCard.getManaValue(); + } + if (targetTwoCard != null) { + targetTwo.revealCards(source, new CardsImpl(targetTwoCard), game); + targetTwoMv = targetTwoCard.getManaValue(); + } + // lose life to mana value of each others card + targetOne.loseLife(targetTwoMv, game, source, false); + targetTwo.loseLife(targetOneMv, game, source, false); + // each put card into their hand + targetOne.moveCards(targetOneCard, Zone.HAND, source, game); + targetTwo.moveCards(targetTwoCard, Zone.HAND, source, game); + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/p/PassengerFerry.java b/Mage.Sets/src/mage/cards/p/PassengerFerry.java new file mode 100644 index 00000000000..ed5ca4761c8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PassengerFerry.java @@ -0,0 +1,64 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DoWhenCostPaid; +import mage.abilities.effects.common.combat.CantBeBlockedTargetEffect; +import mage.abilities.keyword.CrewAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.common.FilterAttackingCreature; +import mage.filter.predicate.mageobject.AnotherPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class PassengerFerry extends CardImpl { + + static final FilterAttackingCreature filter = new FilterAttackingCreature("another target attacking creature"); + + static { + filter.add(AnotherPredicate.instance); + } + + public PassengerFerry(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Whenever this Vehicle attacks, you may pay {U}. When you do, another target attacking creature can't be blocked this turn. + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new CantBeBlockedTargetEffect(), false + ); + ability.addTarget(new TargetPermanent(filter)); + + Ability triggeredAbility = new AttacksTriggeredAbility( + new DoWhenCostPaid(ability, new ManaCostsImpl<>("{U}"), + "Make another attacking creature unblockable this turn?"), false); + this.addAbility(triggeredAbility); + + // Crew 2 + this.addAbility(new CrewAbility(2)); + + } + + private PassengerFerry(final PassengerFerry card) { + super(card); + } + + @Override + public PassengerFerry copy() { + return new PassengerFerry(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PatriciansScorn.java b/Mage.Sets/src/mage/cards/p/PatriciansScorn.java index fc62084e7a5..ffd9c82afa3 100644 --- a/Mage.Sets/src/mage/cards/p/PatriciansScorn.java +++ b/Mage.Sets/src/mage/cards/p/PatriciansScorn.java @@ -1,4 +1,3 @@ - package mage.cards.p; import mage.ObjectColor; @@ -29,9 +28,8 @@ public final class PatriciansScorn extends CardImpl { public PatriciansScorn(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{W}"); - // If you've cast another white spell this turn, you may cast this spell without paying its mana cost. - this.addAbility(new AlternativeCostSourceAbility(new CastWhiteSpellThisTurnCondition()), new PatriciansScornWatcher()); + this.addAbility(new AlternativeCostSourceAbility(PatriciansScornCondition.instance), new PatriciansScornWatcher()); // Destroy all enchantments. this.getSpellAbility().addEffect(new DestroyAllEffect(StaticFilters.FILTER_PERMANENT_ENCHANTMENTS)); } @@ -46,8 +44,8 @@ public final class PatriciansScorn extends CardImpl { } } - -class CastWhiteSpellThisTurnCondition implements Condition { +enum PatriciansScornCondition implements Condition { + instance; @Override public boolean apply(Game game, Ability source) { @@ -60,7 +58,7 @@ class CastWhiteSpellThisTurnCondition implements Condition { @Override public String toString() { - return "If you've cast another white spell this turn"; + return "you've cast another white spell this turn"; } } diff --git a/Mage.Sets/src/mage/cards/p/PermafrostTrap.java b/Mage.Sets/src/mage/cards/p/PermafrostTrap.java index ca3e5e26a30..a6f2b42dd94 100644 --- a/Mage.Sets/src/mage/cards/p/PermafrostTrap.java +++ b/Mage.Sets/src/mage/cards/p/PermafrostTrap.java @@ -72,6 +72,6 @@ enum PermafrostTrapCondition implements Condition { @Override public String toString() { - return "If an opponent had a green creature enter the battlefield under their control this turn"; + return "an opponent had a green creature enter the battlefield under their control this turn"; } } diff --git a/Mage.Sets/src/mage/cards/p/PeterParker.java b/Mage.Sets/src/mage/cards/p/PeterParker.java new file mode 100644 index 00000000000..12be2293b5b --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PeterParker.java @@ -0,0 +1,130 @@ +package mage.cards.p; + +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.TransformSourceEffect; +import mage.abilities.keyword.ReachAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.abilities.keyword.WebSlingingAbility; +import mage.cards.Card; +import mage.cards.CardSetInfo; +import mage.cards.ModalDoubleFacedCard; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorlessPredicate; +import mage.game.Game; +import mage.game.permanent.token.Spider21Token; +import mage.game.stack.Spell; +import mage.players.Player; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class PeterParker extends ModalDoubleFacedCard { + + public PeterParker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, + new SuperType[]{SuperType.LEGENDARY},new CardType[]{CardType.CREATURE}, new SubType[]{SubType.HUMAN, SubType.SCIENTIST, SubType.HERO}, "{1}{W}", + "Amazing Spider-Man", + new SuperType[]{SuperType.LEGENDARY}, new CardType[]{CardType.CREATURE}, new SubType[]{SubType.SPIDER, SubType.HUMAN, SubType.HERO}, "{1}{G}{W}{U}"); + + this.getLeftHalfCard().setPT(0, 1); + this.getRightHalfCard().setPT(4, 4); + + // When Peter Parker enters, create a 2/1 green Spider creature token with reach. + this.getLeftHalfCard().addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new Spider21Token()))); + + // {1}{G}{W}{U}: Transform Peter Parker. Activate only as a sorcery. + this.getLeftHalfCard().addAbility(new ActivateAsSorceryActivatedAbility( + new TransformSourceEffect(), new ManaCostsImpl<>("{1}{G}{W}{U}") + )); + + // Amazing Spider-Man + // Vigilance + this.getRightHalfCard().addAbility(VigilanceAbility.getInstance()); + + // Reach + this.getRightHalfCard().addAbility(ReachAbility.getInstance()); + + // Each legendary spell you cast that's one or more colors has web-slinging {G}{W}{U}. + this.getRightHalfCard().addAbility(new SimpleStaticAbility(new AmazingSpiderManEffect())); + } + + private PeterParker(final PeterParker card) { + super(card); + } + + @Override + public PeterParker copy() { + return new PeterParker(this); + } +} +class AmazingSpiderManEffect extends ContinuousEffectImpl { + + static final FilterCard filter = new FilterCard("legendary spell that's one or more colors"); + + static { + filter.add(Predicates.not(ColorlessPredicate.instance)); + filter.add(SuperType.LEGENDARY.getPredicate()); + } + + AmazingSpiderManEffect() { + super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + staticText = "Each legendary spell you cast that's one or more colors has web-slinging {G}{W}{U}"; + } + + private AmazingSpiderManEffect(final AmazingSpiderManEffect effect) { + super(effect) ; + } + + @Override + public AmazingSpiderManEffect copy() { + return new AmazingSpiderManEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Set cardsToGainAbility = new HashSet<>(); + cardsToGainAbility.addAll(controller.getHand().getCards(filter, game)); + cardsToGainAbility.addAll(controller.getGraveyard().getCards(filter, game)); + controller.getLibrary().getCards(game).stream() + .filter(c -> filter.match(c, game)) + .forEach(cardsToGainAbility::add); + game.getExile().getCardsInRange(game, controller.getId()).stream() + .filter(c -> filter.match(c, game)) + .forEach(cardsToGainAbility::add); + game.getCommanderCardsFromCommandZone(controller, CommanderCardType.ANY).stream() + .filter(c -> filter.match(c, game)) + .forEach(cardsToGainAbility::add); + game.getStack().stream() + .filter(Spell.class::isInstance) + .filter(s -> s.isControlledBy(controller.getId())) + .filter(s -> filter.match((Spell) s, game)) + .map(s -> game.getCard(s.getSourceId())) + .filter(Objects::nonNull) + .forEach(cardsToGainAbility::add); + for (Card card : cardsToGainAbility) { + Ability ability = new WebSlingingAbility(card, "{G}{W}{U}"); + ability.setSourceId(card.getId()); + ability.setControllerId(card.getControllerOrOwnerId()); + game.getState().addOtherAbility(card, ability); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/p/PeterParkersCamera.java b/Mage.Sets/src/mage/cards/p/PeterParkersCamera.java new file mode 100644 index 00000000000..1cff6f90e94 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PeterParkersCamera.java @@ -0,0 +1,58 @@ +package mage.cards.p; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.RemoveCountersSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CopyTargetStackObjectEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.filter.FilterStackObject; +import mage.filter.common.FilterActivatedOrTriggeredAbility; +import mage.target.TargetStackObject; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PeterParkersCamera extends CardImpl { + + private static final FilterStackObject filter = new FilterActivatedOrTriggeredAbility("activated or triggered ability you control"); + + static { + filter.add(TargetController.YOU.getControllerPredicate()); + } + + public PeterParkersCamera(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); + + // This artifact enters with three film counters on it. + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.FILM.createInstance(3)), + "with three film counters on it" + )); + + // {2}, {T}, Remove a film counter from this artifact: Copy target activated or triggered ability you control. You may choose new targets for the copy. + Ability ability = new SimpleActivatedAbility(new CopyTargetStackObjectEffect(), new GenericManaCost(2)); + ability.addCost(new TapSourceCost()); + ability.addCost(new RemoveCountersSourceCost(CounterType.FILM.createInstance())); + ability.addTarget(new TargetStackObject(filter)); + this.addAbility(ability); + } + + private PeterParkersCamera(final PeterParkersCamera card) { + super(card); + } + + @Override + public PeterParkersCamera copy() { + return new PeterParkersCamera(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PhlageTitanOfFiresFury.java b/Mage.Sets/src/mage/cards/p/PhlageTitanOfFiresFury.java index 8da5502b868..aecefff7e03 100644 --- a/Mage.Sets/src/mage/cards/p/PhlageTitanOfFiresFury.java +++ b/Mage.Sets/src/mage/cards/p/PhlageTitanOfFiresFury.java @@ -79,7 +79,7 @@ class PhlageTitanOfFiresFuryEffect extends OneShotEffect { if (permanent == null) { return false; } - if (EscapeAbility.wasCastedWithEscape(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter())) { + if (EscapeAbility.wasCastedWithEscape(game, source.getSourceId(), source.getStackMomentSourceZCC())) { return false; } return permanent.sacrifice(source, game); diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianDreadnought.java b/Mage.Sets/src/mage/cards/p/PhyrexianDreadnought.java index e08c351495b..dbeecf28f34 100644 --- a/Mage.Sets/src/mage/cards/p/PhyrexianDreadnought.java +++ b/Mage.Sets/src/mage/cards/p/PhyrexianDreadnought.java @@ -17,6 +17,7 @@ import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.TargetPermanent; import java.util.UUID; @@ -83,9 +84,15 @@ class PhyrexianDreadnoughtSacrificeCost extends CostImpl { @Override public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { + Player controller = game.getPlayer(controllerId); + if (controller == null) { + return false; + } int sumPower = 0; for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, controllerId, game)) { - sumPower += permanent.getPower().getValue(); + if (controller.canPaySacrificeCost(permanent, source, controllerId, game)) { + sumPower += permanent.getPower().getValue(); + } } return sumPower >= 12; } diff --git a/Mage.Sets/src/mage/cards/p/PiaNalaarChiefMechanic.java b/Mage.Sets/src/mage/cards/p/PiaNalaarChiefMechanic.java index f52834a4cc9..4db0c524568 100644 --- a/Mage.Sets/src/mage/cards/p/PiaNalaarChiefMechanic.java +++ b/Mage.Sets/src/mage/cards/p/PiaNalaarChiefMechanic.java @@ -12,7 +12,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.counters.CounterType; -import mage.filter.common.FilterCreaturePermanent; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterArtifactCreaturePermanent; import mage.game.Game; import mage.game.permanent.token.NalaarAetherjetToken; import mage.game.permanent.token.Token; @@ -25,7 +26,7 @@ import java.util.UUID; */ public final class PiaNalaarChiefMechanic extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("artifact creatures you control"); + private static final FilterPermanent filter = new FilterArtifactCreaturePermanent("artifact creatures you control"); static { filter.add(TargetController.YOU.getControllerPredicate()); diff --git a/Mage.Sets/src/mage/cards/p/PicturesOfSpiderMan.java b/Mage.Sets/src/mage/cards/p/PicturesOfSpiderMan.java new file mode 100644 index 00000000000..480383b957c --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PicturesOfSpiderMan.java @@ -0,0 +1,48 @@ +package mage.cards.p; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.PutCards; +import mage.filter.StaticFilters; +import mage.game.permanent.token.TreasureToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PicturesOfSpiderMan extends CardImpl { + + public PicturesOfSpiderMan(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{G}"); + + // When this artifact enters, look at the top five cards of your library. You may reveal up to two creature cards from among them and put them into your hand. Put the rest on the bottom of your library in a random order. + this.addAbility(new EntersBattlefieldTriggeredAbility(new LookLibraryAndPickControllerEffect( + 5, 2, StaticFilters.FILTER_CARD_CREATURES, PutCards.HAND, PutCards.BOTTOM_RANDOM + ))); + + // {1}, {T}, Sacrifice this artifact: Create a Treasure token. + Ability ability = new SimpleActivatedAbility(new CreateTokenEffect(new TreasureToken()), new GenericManaCost(1)); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeSourceCost()); + this.addAbility(ability); + } + + private PicturesOfSpiderMan(final PicturesOfSpiderMan card) { + super(card); + } + + @Override + public PicturesOfSpiderMan copy() { + return new PicturesOfSpiderMan(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PillarLaunch.java b/Mage.Sets/src/mage/cards/p/PillarLaunch.java index 53beb87d8d7..310d2ddc829 100644 --- a/Mage.Sets/src/mage/cards/p/PillarLaunch.java +++ b/Mage.Sets/src/mage/cards/p/PillarLaunch.java @@ -21,7 +21,7 @@ public final class PillarLaunch extends CardImpl { // Target creature gets +2/+2 and gains reach until end of turn. Untap it. this.getSpellAbility().addEffect(new BoostTargetEffect(2, 2).setText("target creature gets +2/+2")); - this.getSpellAbility().addEffect(new GainAbilityTargetEffect(ReachAbility.getInstance()).setText("and gain reach until end of turn")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect(ReachAbility.getInstance()).setText("and gains reach until end of turn")); this.getSpellAbility().addEffect(new UntapTargetEffect("Untap it")); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/cards/p/PincerSpider.java b/Mage.Sets/src/mage/cards/p/PincerSpider.java index c7ac7c7f20f..1920d9ccd57 100644 --- a/Mage.Sets/src/mage/cards/p/PincerSpider.java +++ b/Mage.Sets/src/mage/cards/p/PincerSpider.java @@ -35,10 +35,10 @@ public final class PincerSpider extends CardImpl { this.addAbility(ReachAbility.getInstance()); // If Pincer Spider was kicked, it enters with a +1/+1 counter on it. - Ability ability = new EntersBattlefieldAbility( - new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance(1)), KickedCondition.ONCE, ""), - "If {this} was kicked, it enters with a +1/+1 counter on it."); - this.addAbility(ability); + this.addAbility(new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), KickedCondition.ONCE, + "If {this} was kicked, it enters with a +1/+1 counter on it.", "" + )); } diff --git a/Mage.Sets/src/mage/cards/p/PitfallTrap.java b/Mage.Sets/src/mage/cards/p/PitfallTrap.java index 53eeccd0959..a08500412f3 100644 --- a/Mage.Sets/src/mage/cards/p/PitfallTrap.java +++ b/Mage.Sets/src/mage/cards/p/PitfallTrap.java @@ -63,6 +63,6 @@ enum PitfallTrapCondition implements Condition { @Override public String toString() { - return "If exactly one creature is attacking"; + return "exactly one creature is attacking"; } } diff --git a/Mage.Sets/src/mage/cards/p/PlagueReaver.java b/Mage.Sets/src/mage/cards/p/PlagueReaver.java index 8e9eed7c5fd..27bd07c4b44 100644 --- a/Mage.Sets/src/mage/cards/p/PlagueReaver.java +++ b/Mage.Sets/src/mage/cards/p/PlagueReaver.java @@ -128,7 +128,7 @@ class PlagueReaverDelayedTriggeredAbility extends DelayedTriggeredAbility { PlagueReaverDelayedTriggeredAbility(UUID playerId, Ability source) { super(new PlagueReaverReturnEffect(playerId).setTargetPointer( - new FixedTarget(source.getSourceId(), source.getSourceObjectZoneChangeCounter() + 1) + new FixedTarget(source.getSourceId(), source.getStackMomentSourceZCC() + 1) ), Duration.Custom, true, false); this.playerId = playerId; } diff --git a/Mage.Sets/src/mage/cards/p/PolukranosUnchained.java b/Mage.Sets/src/mage/cards/p/PolukranosUnchained.java index b8cf9c491f8..0fd264eeeb2 100644 --- a/Mage.Sets/src/mage/cards/p/PolukranosUnchained.java +++ b/Mage.Sets/src/mage/cards/p/PolukranosUnchained.java @@ -98,7 +98,7 @@ class PolukranosUnchainedEffect extends OneShotEffect { int counters = 12; if (!(spellAbility instanceof EscapeAbility) || !spellAbility.getSourceId().equals(source.getSourceId()) - || permanent.getZoneChangeCounter(game) != spellAbility.getSourceObjectZoneChangeCounter()) { + || permanent.getZoneChangeCounter(game) != spellAbility.getStackMomentSourceZCC()) { counters = 6; } List appliedEffects = (ArrayList) this.getValue("appliedEffects"); diff --git a/Mage.Sets/src/mage/cards/p/PortalManipulator.java b/Mage.Sets/src/mage/cards/p/PortalManipulator.java index b4f39834bba..6ca9febadfb 100644 --- a/Mage.Sets/src/mage/cards/p/PortalManipulator.java +++ b/Mage.Sets/src/mage/cards/p/PortalManipulator.java @@ -38,7 +38,7 @@ import java.util.stream.Collectors; */ public final class PortalManipulator extends CardImpl { - private static final Condition condition = new IsStepCondition(PhaseStep.DECLARE_ATTACKERS); + private static final Condition condition = new IsStepCondition(PhaseStep.DECLARE_ATTACKERS, false); private static final FilterPermanent filter = new FilterAttackingCreature("attacking creatures controlled by that player's opponents"); static { diff --git a/Mage.Sets/src/mage/cards/p/PriceOfBetrayal.java b/Mage.Sets/src/mage/cards/p/PriceOfBetrayal.java index 8de18f96784..1ced9838a30 100644 --- a/Mage.Sets/src/mage/cards/p/PriceOfBetrayal.java +++ b/Mage.Sets/src/mage/cards/p/PriceOfBetrayal.java @@ -1,19 +1,13 @@ package mage.cards.p; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.RemoveUpToAmountCountersEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.counters.Counter; import mage.filter.FilterOpponent; import mage.filter.FilterPermanent; import mage.filter.common.FilterPermanentOrPlayer; import mage.filter.predicate.Predicates; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetPermanentOrPlayer; import java.util.UUID; @@ -39,7 +33,7 @@ public final class PriceOfBetrayal extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{B}"); // Remove up to five counters from target artifact, creature, planeswalker or opponent. - this.getSpellAbility().addEffect(new PriceOfBetrayalEffect()); + this.getSpellAbility().addEffect(new RemoveUpToAmountCountersEffect(5)); this.getSpellAbility().addTarget(new TargetPermanentOrPlayer(1, 1, filter2, false)); } @@ -51,82 +45,4 @@ public final class PriceOfBetrayal extends CardImpl { public PriceOfBetrayal copy() { return new PriceOfBetrayal(this); } -} - -class PriceOfBetrayalEffect extends OneShotEffect { - - PriceOfBetrayalEffect() { - super(Outcome.AIDontUseIt); - staticText = "Remove up to five counters from target artifact, creature, planeswalker, or opponent."; - } - - private PriceOfBetrayalEffect(final PriceOfBetrayalEffect effect) { - super(effect); - } - - @Override - public PriceOfBetrayalEffect copy() { - return new PriceOfBetrayalEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { - return false; - } - - // from permanent - Permanent permanent = game.getPermanent(source.getFirstTarget()); - if (permanent != null) { - int toRemove = 5; - int removed = 0; - String[] counterNames = permanent.getCounters(game).keySet().toArray(new String[0]); - for (String counterName : counterNames) { - if (controller.chooseUse(Outcome.Neutral, "Remove " + counterName + " counters?", source, game)) { - if (permanent.getCounters(game).get(counterName).getCount() == 1 || (toRemove - removed == 1)) { - permanent.removeCounters(counterName, 1, source, game); - removed++; - } else { - int amount = controller.getAmount(1, Math.min(permanent.getCounters(game).get(counterName).getCount(), toRemove - removed), "How many?", source, game); - if (amount > 0) { - removed += amount; - permanent.removeCounters(counterName, amount, source, game); - } - } - } - if (removed >= toRemove) { - break; - } - } - return true; - } - - // from player - Player player = game.getPlayer(source.getFirstTarget()); - if (player != null) { - int toRemove = 5; - int removed = 0; - for (Counter counter : player.getCountersAsCopy().values()) { - String counterName = counter.getName(); - if (controller.chooseUse(Outcome.Neutral, "Remove " + counterName + " counters?", source, game)) { - if (player.getCountersCount(counterName) == 1 || (toRemove - removed == 1)) { - player.loseCounters(counterName, 1, source, game); - removed++; - } else { - int amount = controller.getAmount(1, Math.min(player.getCountersCount(counterName), toRemove - removed), "How many?", source, game); - if (amount > 0) { - removed += amount; - player.loseCounters(counterName, amount, source, game); - } - } - } - if (removed >= toRemove) { - break; - } - } - return true; - } - return false; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/p/PrisonBarricade.java b/Mage.Sets/src/mage/cards/p/PrisonBarricade.java index c24ca483081..505ff2d4fd7 100644 --- a/Mage.Sets/src/mage/cards/p/PrisonBarricade.java +++ b/Mage.Sets/src/mage/cards/p/PrisonBarricade.java @@ -38,7 +38,7 @@ public final class PrisonBarricade extends CardImpl { // If Prison Barricade was kicked, it enters with a +1/+1 counter on it and with "Prison Barricade can attack as though it didn't have defender." Ability ability = new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(1)), - KickedCondition.ONCE, "If {this} was kicked, it enters with a +1/+1 counter on it and with \"{this} can attack as though it didn't have defender.\"", ""); + KickedCondition.ONCE, "If {this} was kicked, it enters with a +1/+1 counter on it and with \"This creature can attack as though it didn't have defender.\"", ""); ability.addEffect(new CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration.WhileOnBattlefield)); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/p/PrisonBreak.java b/Mage.Sets/src/mage/cards/p/PrisonBreak.java new file mode 100644 index 00000000000..3ac0e4aacef --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PrisonBreak.java @@ -0,0 +1,38 @@ +package mage.cards.p; + +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldWithCounterTargetEffect; +import mage.abilities.keyword.MayhemAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PrisonBreak extends CardImpl { + + public PrisonBreak(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}"); + + // Return target creature card from your graveyard to the battlefield with a +1/+1 counter on it. + this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldWithCounterTargetEffect(CounterType.P1P1.createInstance())); + this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); + + // Mayhem {3}{B} + this.addAbility(new MayhemAbility(this, "{3}{B}")); + } + + private PrisonBreak(final PrisonBreak card) { + super(card); + } + + @Override + public PrisonBreak copy() { + return new PrisonBreak(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/ProfessionalWrestler.java b/Mage.Sets/src/mage/cards/p/ProfessionalWrestler.java new file mode 100644 index 00000000000..dadadaec3e1 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/ProfessionalWrestler.java @@ -0,0 +1,45 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.combat.CantBeBlockedByMoreThanOneSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.game.permanent.token.TreasureToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ProfessionalWrestler extends CardImpl { + + public ProfessionalWrestler(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.WARRIOR); + this.subtype.add(SubType.PERFORMER); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // When this creature enters, create a Treasure token. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new TreasureToken()))); + + // This creature can't be blocked by more than one creature. + this.addAbility(new SimpleStaticAbility(new CantBeBlockedByMoreThanOneSourceEffect())); + } + + private ProfessionalWrestler(final ProfessionalWrestler card) { + super(card); + } + + @Override + public ProfessionalWrestler copy() { + return new ProfessionalWrestler(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/ProteanRaider.java b/Mage.Sets/src/mage/cards/p/ProteanRaider.java index b65252b7ee8..943a4d66b65 100644 --- a/Mage.Sets/src/mage/cards/p/ProteanRaider.java +++ b/Mage.Sets/src/mage/cards/p/ProteanRaider.java @@ -30,7 +30,7 @@ public final class ProteanRaider extends CardImpl { // Raid — If you attacked with a creature this turn, you may have Protean Raider enter the battlefield as a copy of any creature on the battlefield. Ability ability = new EntersBattlefieldAbility(new CopyPermanentEffect(), true, RaidCondition.instance, - "Raid — If you attacked this turn, you may have {this} enter the battlefield as a copy of any creature on the battlefield.", ""); + "If you attacked this turn, you may have {this} enter the battlefield as a copy of any creature on the battlefield.", ""); ability.setAbilityWord(AbilityWord.RAID); ability.addHint(RaidHint.instance); this.addAbility(ability, new PlayerAttackedWatcher()); diff --git a/Mage.Sets/src/mage/cards/p/ProwlerClawedThief.java b/Mage.Sets/src/mage/cards/p/ProwlerClawedThief.java new file mode 100644 index 00000000000..cd3983cd176 --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/ProwlerClawedThief.java @@ -0,0 +1,55 @@ +package mage.cards.p; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.effects.keyword.ConniveSourceEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ProwlerClawedThief extends CardImpl { + + private static final FilterPermanent filter + = new FilterControlledPermanent(SubType.VILLAIN, "another Villain you control"); + + static { + filter.add(AnotherPredicate.instance); + } + + public ProwlerClawedThief(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ROGUE); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Menace + this.addAbility(new MenaceAbility()); + + // Whenever another Villain you control enters, Prowler connives. + this.addAbility(new EntersBattlefieldAllTriggeredAbility(new ConniveSourceEffect("{this}"), filter)); + } + + private ProwlerClawedThief(final ProwlerClawedThief card) { + super(card); + } + + @Override + public ProwlerClawedThief copy() { + return new ProwlerClawedThief(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PumpkinBombardment.java b/Mage.Sets/src/mage/cards/p/PumpkinBombardment.java new file mode 100644 index 00000000000..273d286e56e --- /dev/null +++ b/Mage.Sets/src/mage/cards/p/PumpkinBombardment.java @@ -0,0 +1,40 @@ +package mage.cards.p; + +import mage.abilities.costs.OrCost; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class PumpkinBombardment extends CardImpl { + + public PumpkinBombardment(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{B/R}"); + + // As an additional cost to cast this spell, discard a card or pay {2}. + this.getSpellAbility().addCost(new OrCost( + "discard a card or pay {2}", new DiscardCardCost(), new GenericManaCost(2) + )); + + // Pumpkin Bombardment deals 3 damage to target creature. + this.getSpellAbility().addEffect(new DamageTargetEffect(3)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private PumpkinBombardment(final PumpkinBombardment card) { + super(card); + } + + @Override + public PumpkinBombardment copy() { + return new PumpkinBombardment(this); + } +} diff --git a/Mage.Sets/src/mage/cards/p/PuppetMaster.java b/Mage.Sets/src/mage/cards/p/PuppetMaster.java index fef7c930484..f819c91578e 100644 --- a/Mage.Sets/src/mage/cards/p/PuppetMaster.java +++ b/Mage.Sets/src/mage/cards/p/PuppetMaster.java @@ -85,7 +85,7 @@ class PuppetMasterEffect extends OneShotEffect { return false; } card = game.getCard(source.getSourceId()); - if (card == null || card.getZoneChangeCounter(game) != source.getSourceObjectZoneChangeCounter() + 1) { + if (card == null || card.getZoneChangeCounter(game) != source.getStackMomentSourceZCC() + 1) { return false; } Cost cost = new ManaCostsImpl<>("{U}{U}{U}"); diff --git a/Mage.Sets/src/mage/cards/r/RadiantGrace.java b/Mage.Sets/src/mage/cards/r/RadiantGrace.java index 063986bb4aa..b725e33f23a 100644 --- a/Mage.Sets/src/mage/cards/r/RadiantGrace.java +++ b/Mage.Sets/src/mage/cards/r/RadiantGrace.java @@ -69,7 +69,7 @@ class RadiantGraceEffect extends OneShotEffect { RadiantGraceEffect() { super(Outcome.Benefit); - staticText = "return {this} to the battlefield transformed under your control attached to target opponent"; + staticText = "return this card to the battlefield transformed under your control attached to target opponent"; } private RadiantGraceEffect(final RadiantGraceEffect effect) { diff --git a/Mage.Sets/src/mage/cards/r/RadioactiveSpider.java b/Mage.Sets/src/mage/cards/r/RadioactiveSpider.java new file mode 100644 index 00000000000..01ca91ace24 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RadioactiveSpider.java @@ -0,0 +1,61 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterCard; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RadioactiveSpider extends CardImpl { + + private static final FilterCard filter = new FilterCard("Spider Hero card"); + + static { + filter.add(SubType.SPIDER.getPredicate()); + filter.add(SubType.HERO.getPredicate()); + } + + public RadioactiveSpider(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}"); + + this.subtype.add(SubType.SPIDER); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // Fateful Bite -- {2}, Sacrifice this creature: Search your library for a Spider Hero card, reveal it, put it into your hand, then shuffle. Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility( + new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true), new GenericManaCost(2) + ); + ability.addCost(new SacrificeSourceCost()); + this.addAbility(ability.withFlavorWord("Fateful Bite")); + } + + private RadioactiveSpider(final RadioactiveSpider card) { + super(card); + } + + @Override + public RadioactiveSpider copy() { + return new RadioactiveSpider(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RagingGoblinoids.java b/Mage.Sets/src/mage/cards/r/RagingGoblinoids.java new file mode 100644 index 00000000000..6ced9aed0d8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RagingGoblinoids.java @@ -0,0 +1,42 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.MayhemAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RagingGoblinoids extends CardImpl { + + public RagingGoblinoids(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}"); + + this.subtype.add(SubType.GOBLIN); + this.subtype.add(SubType.BERSERKER); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(5); + this.toughness = new MageInt(4); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Mayhem {2}{R} + this.addAbility(new MayhemAbility(this, "{2}{R}")); + } + + private RagingGoblinoids(final RagingGoblinoids card) { + super(card); + } + + @Override + public RagingGoblinoids copy() { + return new RagingGoblinoids(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RagostDeftGastronaut.java b/Mage.Sets/src/mage/cards/r/RagostDeftGastronaut.java index 60441b71c51..1f32d863dae 100644 --- a/Mage.Sets/src/mage/cards/r/RagostDeftGastronaut.java +++ b/Mage.Sets/src/mage/cards/r/RagostDeftGastronaut.java @@ -9,6 +9,7 @@ import mage.abilities.condition.common.YouGainedLifeCondition; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.common.DamagePlayersEffect; import mage.abilities.effects.common.UntapSourceEffect; import mage.abilities.effects.common.continuous.AddCardSubtypeAllEffect; @@ -43,13 +44,17 @@ public final class RagostDeftGastronaut extends CardImpl { this.toughness = new MageInt(2); // Artifacts you control are Foods in addition to their other types and have "{2}, {T}, Sacrifice this artifact: You gain 3 life." - Ability ability = new SimpleStaticAbility(new AddCardSubtypeAllEffect( + ContinuousEffect effect = new AddCardSubtypeAllEffect( StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACTS, SubType.FOOD, null - )); - ability.addEffect(new GainAbilityAllEffect( + ); + effect.getDependedToTypes().add(DependencyType.ArtifactAddingRemoving); + Ability ability = new SimpleStaticAbility(effect); + effect = new GainAbilityAllEffect( new FoodAbility(), Duration.WhileOnBattlefield, StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACTS, "and have \"{2}, {T}, Sacrifice this artifact: You gain 3 life.\"" - )); + ); + effect.getDependedToTypes().add(DependencyType.ArtifactAddingRemoving); + ability.addEffect(effect); this.addAbility(ability); // {1}, {T}, Sacrifice a Food: Ragost deals 3 damage to each opponent. diff --git a/Mage.Sets/src/mage/cards/r/RalLeylineProdigy.java b/Mage.Sets/src/mage/cards/r/RalLeylineProdigy.java index a900ac858f3..10e3fac4eb7 100644 --- a/Mage.Sets/src/mage/cards/r/RalLeylineProdigy.java +++ b/Mage.Sets/src/mage/cards/r/RalLeylineProdigy.java @@ -150,7 +150,7 @@ class RalLeylineProdigyMinusEightEffect extends OneShotEffect { return false; } Set cards = player.getLibrary().getTopCards(game, 8); - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); player.moveCardsToExile(cards, source, game, true, exileId, sourceObject.getIdName()); for (Card card : cards) { if (game.getState().getZone(card.getId()) == Zone.EXILED) { @@ -203,4 +203,4 @@ class RalLeylineProdigyCastEffect extends AsThoughEffectImpl { allowCardToPlayWithoutMana(mainId, source, affectedControllerId, MageIdentifier.WithoutPayingManaCostAlternateCast, game); return true; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/r/RamosianRally.java b/Mage.Sets/src/mage/cards/r/RamosianRally.java index 805cfda6a1d..221542d6063 100644 --- a/Mage.Sets/src/mage/cards/r/RamosianRally.java +++ b/Mage.Sets/src/mage/cards/r/RamosianRally.java @@ -22,7 +22,7 @@ import mage.target.common.TargetControlledPermanent; */ public final class RamosianRally extends CardImpl { - private static final FilterControlledPermanent plainsFilter = new FilterControlledPermanent("If you control a Plains"); + private static final FilterControlledPermanent plainsFilter = new FilterControlledPermanent("you control a Plains"); private static final FilterControlledCreaturePermanent creatureFilter = new FilterControlledCreaturePermanent("an untapped creature you control"); static { diff --git a/Mage.Sets/src/mage/cards/r/RampagingBaloths.java b/Mage.Sets/src/mage/cards/r/RampagingBaloths.java index 24832a04da4..71ff997bab7 100644 --- a/Mage.Sets/src/mage/cards/r/RampagingBaloths.java +++ b/Mage.Sets/src/mage/cards/r/RampagingBaloths.java @@ -26,7 +26,7 @@ public final class RampagingBaloths extends CardImpl { this.power = new MageInt(6); this.toughness = new MageInt(6); this.addAbility(TrampleAbility.getInstance()); - this.addAbility(new LandfallAbility(new CreateTokenEffect(new BeastToken2()), true)); + this.addAbility(new LandfallAbility(new CreateTokenEffect(new BeastToken2()), false)); } private RampagingBaloths(final RampagingBaloths card) { diff --git a/Mage.Sets/src/mage/cards/r/RavenloftAdventurer.java b/Mage.Sets/src/mage/cards/r/RavenloftAdventurer.java index 089f9f62d12..82a912082b6 100644 --- a/Mage.Sets/src/mage/cards/r/RavenloftAdventurer.java +++ b/Mage.Sets/src/mage/cards/r/RavenloftAdventurer.java @@ -124,7 +124,7 @@ class RavenloftAdventurerLifeEffect extends OneShotEffect { } int count = game .getExile() - .getAllCards(game, player.getId()) + .getCardsOwned(game, player.getId()) .stream() .filter(card -> card.getCounters(game).containsKey(CounterType.HIT)) .mapToInt(x -> 1) diff --git a/Mage.Sets/src/mage/cards/r/RavenousSlime.java b/Mage.Sets/src/mage/cards/r/RavenousSlime.java index a0b95f69f27..1e59c281c3c 100644 --- a/Mage.Sets/src/mage/cards/r/RavenousSlime.java +++ b/Mage.Sets/src/mage/cards/r/RavenousSlime.java @@ -68,8 +68,7 @@ class RavenousSlimeEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player controller = game.getPlayer(source.getControllerId()); - Permanent sourceCreature = game.getPermanent(source.getSourceId()); - if (controller == null || sourceCreature == null) { + if (controller == null) { return false; } if (((ZoneChangeEvent) event).getFromZone() != Zone.BATTLEFIELD) { @@ -81,9 +80,8 @@ class RavenousSlimeEffect extends ReplacementEffectImpl { } int power = permanent.getPower().getValue(); controller.moveCards(permanent, Zone.EXILED, source, game); - return new AddCountersSourceEffect( - CounterType.P1P1.createInstance(power) - ).apply(game, source); + new AddCountersSourceEffect(CounterType.P1P1.createInstance(power)).apply(game, source); + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/r/RavenousTrap.java b/Mage.Sets/src/mage/cards/r/RavenousTrap.java index 150cba260ab..415a1e8e36e 100644 --- a/Mage.Sets/src/mage/cards/r/RavenousTrap.java +++ b/Mage.Sets/src/mage/cards/r/RavenousTrap.java @@ -63,6 +63,6 @@ enum RavenousTrapCondition implements Condition { @Override public String toString() { - return "If an opponent had three or more cards put into their graveyard from anywhere this turn"; + return "an opponent had three or more cards put into their graveyard from anywhere this turn"; } } diff --git a/Mage.Sets/src/mage/cards/r/RayamiFirstOfTheFallen.java b/Mage.Sets/src/mage/cards/r/RayamiFirstOfTheFallen.java index c15123ec60a..488fdfe54fc 100644 --- a/Mage.Sets/src/mage/cards/r/RayamiFirstOfTheFallen.java +++ b/Mage.Sets/src/mage/cards/r/RayamiFirstOfTheFallen.java @@ -74,7 +74,7 @@ class RayamiFirstOfTheFallenEffect extends ContinuousEffectImpl { return false; } game.getExile() - .getAllCards(game) + .getCardsInRange(game, sourcePermanent.getControllerId()) .stream() .filter(card1 -> card1.isCreature(game)) .filter(card -> card.getCounters(game).getCount(CounterType.BLOOD) > 0) diff --git a/Mage.Sets/src/mage/cards/r/RazorPendulum.java b/Mage.Sets/src/mage/cards/r/RazorPendulum.java index a611d207e30..6a0e1d0d6b0 100644 --- a/Mage.Sets/src/mage/cards/r/RazorPendulum.java +++ b/Mage.Sets/src/mage/cards/r/RazorPendulum.java @@ -27,7 +27,7 @@ public final class RazorPendulum extends CardImpl { TargetController.EACH_PLAYER, new DamageTargetEffect(2, true, "that player"), false, condition - ).withTargetPointerSet(true)); + )); } private RazorPendulum(final RazorPendulum card) { diff --git a/Mage.Sets/src/mage/cards/r/Realmwright.java b/Mage.Sets/src/mage/cards/r/Realmwright.java index 41d5a7a9ef0..1b45657dfe8 100644 --- a/Mage.Sets/src/mage/cards/r/Realmwright.java +++ b/Mage.Sets/src/mage/cards/r/Realmwright.java @@ -51,6 +51,7 @@ class RealmwrightEffect extends ContinuousEffectImpl { RealmwrightEffect() { super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Neutral); staticText = "Lands you control are the chosen type in addition to their other types"; + dependendToTypes.add(DependencyType.BecomeNonbasicLand); } private RealmwrightEffect(final RealmwrightEffect effect) { diff --git a/Mage.Sets/src/mage/cards/r/RefractionTrap.java b/Mage.Sets/src/mage/cards/r/RefractionTrap.java index 2c9c52c290b..03ccae2e706 100644 --- a/Mage.Sets/src/mage/cards/r/RefractionTrap.java +++ b/Mage.Sets/src/mage/cards/r/RefractionTrap.java @@ -77,7 +77,7 @@ enum RefractionTrapCondition implements Condition { @Override public String toString() { - return "If an opponent cast a red instant or sorcery spell this turn"; + return "an opponent cast a red instant or sorcery spell this turn"; } } diff --git a/Mage.Sets/src/mage/cards/r/RefreshingRain.java b/Mage.Sets/src/mage/cards/r/RefreshingRain.java index c4e356ce03d..fd326226fe3 100644 --- a/Mage.Sets/src/mage/cards/r/RefreshingRain.java +++ b/Mage.Sets/src/mage/cards/r/RefreshingRain.java @@ -33,7 +33,7 @@ public final class RefreshingRain extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{G}"); // If an opponent controls a Swamp and you control a Forest, you may cast this spell without paying its mana cost. - Condition condition = new CompoundCondition("If an opponent controls a Swamp and you control a Forest", + Condition condition = new CompoundCondition("an opponent controls a Swamp and you control a Forest", new OpponentControlsPermanentCondition(filterSwamp), new PermanentsOnTheBattlefieldCondition(filterForest)); this.addAbility(new AlternativeCostSourceAbility(null, condition)); diff --git a/Mage.Sets/src/mage/cards/r/RenderInert.java b/Mage.Sets/src/mage/cards/r/RenderInert.java index eca89427255..b119d3ff9a1 100644 --- a/Mage.Sets/src/mage/cards/r/RenderInert.java +++ b/Mage.Sets/src/mage/cards/r/RenderInert.java @@ -1,15 +1,10 @@ package mage.cards.r; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.RemoveUpToAmountCountersEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.TargetPermanent; import java.util.UUID; @@ -23,7 +18,7 @@ public final class RenderInert extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}"); // Remove up to five counters from target permanent. - this.getSpellAbility().addEffect(new RenderInertEffect()); + this.getSpellAbility().addEffect(new RemoveUpToAmountCountersEffect(5)); this.getSpellAbility().addTarget(new TargetPermanent()); // Draw a card. @@ -38,51 +33,4 @@ public final class RenderInert extends CardImpl { public RenderInert copy() { return new RenderInert(this); } -} - -class RenderInertEffect extends OneShotEffect { - - RenderInertEffect() { - super(Outcome.Benefit); - staticText = "remove up to five counters from target permanent"; - } - - private RenderInertEffect(final RenderInertEffect effect) { - super(effect); - } - - @Override - public RenderInertEffect copy() { - return new RenderInertEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (controller == null || permanent == null) { - return false; - } - int toRemove = 5; - int removed = 0; - String[] counterNames = permanent.getCounters(game).keySet().toArray(new String[0]); - for (String counterName : counterNames) { - if (controller.chooseUse(Outcome.Neutral, "Remove " + counterName + " counters?", source, game)) { - if (permanent.getCounters(game).get(counterName).getCount() == 1 || (toRemove - removed == 1)) { - permanent.removeCounters(counterName, 1, source, game); - removed++; - } else { - int amount = controller.getAmount(1, Math.min(permanent.getCounters(game).get(counterName).getCount(), toRemove - removed), "How many?", source, game); - if (amount > 0) { - removed += amount; - permanent.removeCounters(counterName, amount, source, game); - } - } - } - if (removed >= toRemove) { - break; - } - } - return true; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/r/RentIsDue.java b/Mage.Sets/src/mage/cards/r/RentIsDue.java new file mode 100644 index 00000000000..74d857c89f3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RentIsDue.java @@ -0,0 +1,52 @@ +package mage.cards.r; + +import mage.abilities.costs.common.TapTargetCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.TappedPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RentIsDue extends CardImpl { + + private static final FilterControlledPermanent filter + = new FilterControlledPermanent("untapped creatures and/or Treasures you control"); + + static { + filter.add(TappedPredicate.UNTAPPED); + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + SubType.TREASURE.getPredicate() + )); + } + + public RentIsDue(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + + // At the beginning of your end step, you may tap two untapped creatures and/or Treasures you control. If you do, draw a card. Otherwise, sacrifice this enchantment. + this.addAbility(new BeginningOfEndStepTriggeredAbility(new DoIfCostPaid( + new DrawCardSourceControllerEffect(1), + new SacrificeSourceEffect(), new TapTargetCost(2, filter) + ).setOtherwiseText("Otherwise"))); + } + + private RentIsDue(final RentIsDue card) { + super(card); + } + + @Override + public RentIsDue copy() { + return new RentIsDue(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/ReverentSilence.java b/Mage.Sets/src/mage/cards/r/ReverentSilence.java index 67e52eee542..a4c6f0c9625 100644 --- a/Mage.Sets/src/mage/cards/r/ReverentSilence.java +++ b/Mage.Sets/src/mage/cards/r/ReverentSilence.java @@ -19,7 +19,7 @@ import mage.filter.common.FilterEnchantmentPermanent; */ public final class ReverentSilence extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("If you control a Forest"); + private static final FilterPermanent filter = new FilterPermanent("you control a Forest"); static { filter.add(SubType.FOREST.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/r/RexCyberHound.java b/Mage.Sets/src/mage/cards/r/RexCyberHound.java index 90f7f61121b..dbab4a5c875 100644 --- a/Mage.Sets/src/mage/cards/r/RexCyberHound.java +++ b/Mage.Sets/src/mage/cards/r/RexCyberHound.java @@ -104,7 +104,7 @@ class RexCyberhoundContinuousEffect extends ContinuousEffectImpl { filter.add(CounterType.BRAIN.getPredicate()); } - public RexCyberhoundContinuousEffect() { + RexCyberhoundContinuousEffect() { super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); staticText = "{this} has all activated abilities of all cards in exile with brain counters on them"; addDependencyType(DependencyType.AddingAbility); @@ -125,7 +125,7 @@ class RexCyberhoundContinuousEffect extends ContinuousEffectImpl { if (perm == null) { return false; } - for (Card card : game.getExile().getCards(filter, game)) { + for (Card card : game.getExile().getCardsInRange(filter, source.getControllerId(), source, game)) { for (Ability ability : card.getAbilities(game)) { if (ability.isActivatedAbility()) { ActivatedAbility copyAbility = (ActivatedAbility) ability.copy(); diff --git a/Mage.Sets/src/mage/cards/r/RhinoBarrelingBrute.java b/Mage.Sets/src/mage/cards/r/RhinoBarrelingBrute.java new file mode 100644 index 00000000000..d60ad82328e --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RhinoBarrelingBrute.java @@ -0,0 +1,118 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.watchers.Watcher; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RhinoBarrelingBrute extends CardImpl { + + public RhinoBarrelingBrute(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}{G}{G}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(6); + this.toughness = new MageInt(7); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Whenever Rhino attacks, if you've cast a spell with mana value 4 or greater this turn, draw a card. + this.addAbility(new AttacksTriggeredAbility(new DrawCardSourceControllerEffect(1)) + .withInterveningIf(RhinoBarrelingBruteCondition.instance) + .addHint(RhinoBarrelingBruteCondition.getHint()), new RhinoBarrelingBruteWatcher()); + } + + private RhinoBarrelingBrute(final RhinoBarrelingBrute card) { + super(card); + } + + @Override + public RhinoBarrelingBrute copy() { + return new RhinoBarrelingBrute(this); + } +} + +enum RhinoBarrelingBruteCondition implements Condition { + instance; + private static final Hint hint = new ConditionHint(instance); + + public static Hint getHint() { + return hint; + } + + @Override + public boolean apply(Game game, Ability source) { + return RhinoBarrelingBruteWatcher.checkPlayer(game, source); + } + + @Override + public String toString() { + return "you've cast a spell with mana value 4 or greater this turn"; + } +} + +class RhinoBarrelingBruteWatcher extends Watcher { + + private final Set set = new HashSet<>(); + + RhinoBarrelingBruteWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.SPELL_CAST) { + return; + } + Spell spell = game.getSpell(event.getTargetId()); + if (spell != null && spell.getManaValue() >= 4) { + set.add(event.getPlayerId()); + } + } + + @Override + public void reset() { + super.reset(); + set.clear(); + } + + static boolean checkPlayer(Game game, Ability source) { + return game + .getState() + .getWatcher(RhinoBarrelingBruteWatcher.class) + .set + .contains(source.getControllerId()); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RhinosRampage.java b/Mage.Sets/src/mage/cards/r/RhinosRampage.java new file mode 100644 index 00000000000..8ed68d09334 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RhinosRampage.java @@ -0,0 +1,95 @@ +package mage.cards.r; + +import mage.abilities.Ability; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.FightTargetsEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterArtifactPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class RhinosRampage extends CardImpl { + + private static final FilterPermanent filter = new FilterCreaturePermanent("creature an opponent controls"); + + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + } + + public RhinosRampage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R/G}"); + + + // Target creature you control gets +1/+0 until end of turn. It fights target creature an opponent controls. When excess damage is dealt to the creature an opponent controls this way, destroy up to one target noncreature artifact with mana value 3 or less. + this.getSpellAbility().addEffect(new BoostTargetEffect(1, 0)); + this.getSpellAbility().addEffect(new FightTargetsEffect() + .setText("It fights target creature an opponent controls")); + this.getSpellAbility().addEffect(new RhinosRampageEffect()); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + this.getSpellAbility().addTarget(new TargetPermanent(filter)); + + } + + private RhinosRampage(final RhinosRampage card) { + super(card); + } + + @Override + public RhinosRampage copy() { + return new RhinosRampage(this); + } +} + +class RhinosRampageEffect extends OneShotEffect { + + RhinosRampageEffect() { + super(Outcome.BoostCreature); + staticText = "When excess damage is dealt to the creature an opponent controls this way, destroy up to one target noncreature " + + "artifact with mana value 3 or less"; + } + + protected RhinosRampageEffect(final RhinosRampageEffect effect) { + super(effect); + } + + @Override + public RhinosRampageEffect copy() { + return new RhinosRampageEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanentOrLKIBattlefield(source.getTargets().get(1).getFirstTarget()); + if (permanent == null || permanent.getDamage() <= permanent.getToughness().getBaseValue()) { + return false; + } + FilterPermanent filter = new FilterArtifactPermanent("noncreature artifact with mana value 3 or less"); + filter.add(Predicates.not(CardType.CREATURE.getPredicate())); + filter.add(new ManaValuePredicate(ComparisonType.OR_LESS, 3)); + + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(new DestroyTargetEffect(), false); + ability.addTarget(new TargetPermanent(0, 1, filter)); + game.fireReflexiveTriggeredAbility(ability, source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/r/RicochetTrap.java b/Mage.Sets/src/mage/cards/r/RicochetTrap.java index ca5c724a9c8..e25999133c5 100644 --- a/Mage.Sets/src/mage/cards/r/RicochetTrap.java +++ b/Mage.Sets/src/mage/cards/r/RicochetTrap.java @@ -76,6 +76,6 @@ enum RicochetTrapCondition implements Condition { @Override public String toString() { - return "If an opponent cast a blue spell this turn"; + return "an opponent cast a blue spell this turn"; } } diff --git a/Mage.Sets/src/mage/cards/r/RiggingRunner.java b/Mage.Sets/src/mage/cards/r/RiggingRunner.java index 5357652bb5f..889f3f46058 100644 --- a/Mage.Sets/src/mage/cards/r/RiggingRunner.java +++ b/Mage.Sets/src/mage/cards/r/RiggingRunner.java @@ -35,7 +35,7 @@ public final class RiggingRunner extends CardImpl { // Raid — Rigging Runner enters the battlefield with a +1/+1 counter on it if you attacked this turn. this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(1), false), RaidCondition.instance, - "Raid — {this} enters with a +1/+1 counter on it if you attacked this turn.", + "{this} enters with a +1/+1 counter on it if you attacked this turn.", "{this} enters with a +1/+1 counter") .setAbilityWord(AbilityWord.RAID) .addHint(RaidHint.instance), diff --git a/Mage.Sets/src/mage/cards/r/RoboticsMastery.java b/Mage.Sets/src/mage/cards/r/RoboticsMastery.java new file mode 100644 index 00000000000..6502bf00d18 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RoboticsMastery.java @@ -0,0 +1,55 @@ +package mage.cards.r; + +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.FlashAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.permanent.token.RobotFlyingToken; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RoboticsMastery extends CardImpl { + + public RoboticsMastery(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{U}"); + + this.subtype.add(SubType.AURA); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(new EnchantAbility(auraTarget)); + + // When this Aura enters, create two 1/1 colorless Robot artifact creature tokens with flying. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new RobotFlyingToken(), 2))); + + // Enchanted creature gets +2/+2. + this.addAbility(new SimpleStaticAbility(new BoostEnchantedEffect(2, 2))); + } + + private RoboticsMastery(final RoboticsMastery card) { + super(card); + } + + @Override + public RoboticsMastery copy() { + return new RoboticsMastery(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RocketPoweredGoblinGlider.java b/Mage.Sets/src/mage/cards/r/RocketPoweredGoblinGlider.java new file mode 100644 index 00000000000..aa519064a2a --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RocketPoweredGoblinGlider.java @@ -0,0 +1,59 @@ +package mage.cards.r; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAttachToTarget; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.CastFromGraveyardSourceCondition; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.MayhemAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RocketPoweredGoblinGlider extends CardImpl { + + public RocketPoweredGoblinGlider(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + this.subtype.add(SubType.EQUIPMENT); + + // When this Equipment enters, if it was cast from your graveyard, attach it to target creature you control. + this.addAbility(new EntersBattlefieldAttachToTarget().withInterveningIf(CastFromGraveyardSourceCondition.instance)); + + // Equipped creature gets +2/+0 and has flying and haste. + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(2, 0)); + ability.addEffect(new GainAbilityAttachedEffect( + FlyingAbility.getInstance(), AttachmentType.EQUIPMENT + ).setText("and has flying")); + ability.addEffect(new GainAbilityAttachedEffect( + HasteAbility.getInstance(), AttachmentType.EQUIPMENT + ).setText("and haste")); + this.addAbility(ability); + + // Equip {2} + this.addAbility(new EquipAbility(2)); + + // Mayhem {2} + this.addAbility(new MayhemAbility(this, "{2}")); + } + + private RocketPoweredGoblinGlider(final RocketPoweredGoblinGlider card) { + super(card); + } + + @Override + public RocketPoweredGoblinGlider copy() { + return new RocketPoweredGoblinGlider(this); + } +} diff --git a/Mage.Sets/src/mage/cards/r/RootpathPurifier.java b/Mage.Sets/src/mage/cards/r/RootpathPurifier.java index 29b1953fdf6..056e37f8147 100644 --- a/Mage.Sets/src/mage/cards/r/RootpathPurifier.java +++ b/Mage.Sets/src/mage/cards/r/RootpathPurifier.java @@ -47,6 +47,7 @@ class RootpathPurifierEffect extends ContinuousEffectImpl { RootpathPurifierEffect() { super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit); staticText = "lands you control and land cards in your library are basic"; + dependendToTypes.add(DependencyType.BecomeNonbasicLand); } private RootpathPurifierEffect(final RootpathPurifierEffect effect) { diff --git a/Mage.Sets/src/mage/cards/r/RoseTyler.java b/Mage.Sets/src/mage/cards/r/RoseTyler.java index c823881322f..ef2b90dd4d6 100644 --- a/Mage.Sets/src/mage/cards/r/RoseTyler.java +++ b/Mage.Sets/src/mage/cards/r/RoseTyler.java @@ -85,7 +85,7 @@ enum RoseTylerValue implements DynamicValue { .count(filter.getPermanentFilter(), sourceAbility.getControllerId(), sourceAbility, game) + game .getExile() - .getAllCards(game, sourceAbility.getControllerId()) + .getCardsOwned(game, sourceAbility.getControllerId()) .stream() .filter(card -> card.isOwnedBy(sourceAbility.getControllerId())) .mapToInt(card -> filter.getCardFilter().match(card, game) ? 1 : 0) diff --git a/Mage.Sets/src/mage/cards/r/Rouse.java b/Mage.Sets/src/mage/cards/r/Rouse.java index 303ef2cc3b8..7d6b7d5c553 100644 --- a/Mage.Sets/src/mage/cards/r/Rouse.java +++ b/Mage.Sets/src/mage/cards/r/Rouse.java @@ -20,7 +20,7 @@ import mage.target.common.TargetCreaturePermanent; */ public final class Rouse extends CardImpl { - private static final FilterLandPermanent filterSwamp = new FilterLandPermanent("If you control a Swamp"); + private static final FilterLandPermanent filterSwamp = new FilterLandPermanent("you control a Swamp"); static { filterSwamp.add(SubType.SWAMP.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/r/RuneflareTrap.java b/Mage.Sets/src/mage/cards/r/RuneflareTrap.java index 99fc5c5dbdf..ed5ceedb780 100644 --- a/Mage.Sets/src/mage/cards/r/RuneflareTrap.java +++ b/Mage.Sets/src/mage/cards/r/RuneflareTrap.java @@ -89,6 +89,6 @@ enum RuneflareTrapCondition implements Condition { @Override public String toString() { - return "If an opponent drew three or more cards this turn"; + return "an opponent drew three or more cards this turn"; } } diff --git a/Mage.Sets/src/mage/cards/r/RushwoodLegate.java b/Mage.Sets/src/mage/cards/r/RushwoodLegate.java index dd5cb2ea625..30268883385 100644 --- a/Mage.Sets/src/mage/cards/r/RushwoodLegate.java +++ b/Mage.Sets/src/mage/cards/r/RushwoodLegate.java @@ -35,7 +35,7 @@ public final class RushwoodLegate extends CardImpl { this.toughness = new MageInt(1); // If an opponent controls an Island and you control a Forest, you may cast this spell without paying its mana cost. - Condition condition = new CompoundCondition("If an opponent controls an Island and you control a Forest", + Condition condition = new CompoundCondition("an opponent controls an Island and you control a Forest", new OpponentControlsPermanentCondition(filterIsland), new PermanentsOnTheBattlefieldCondition(filterForest)); this.addAbility(new AlternativeCostSourceAbility(null, condition)); diff --git a/Mage.Sets/src/mage/cards/s/SailorsBane.java b/Mage.Sets/src/mage/cards/s/SailorsBane.java index cdaf2b0da51..a2e70263ee7 100644 --- a/Mage.Sets/src/mage/cards/s/SailorsBane.java +++ b/Mage.Sets/src/mage/cards/s/SailorsBane.java @@ -23,7 +23,6 @@ import mage.filter.predicate.Predicates; import mage.game.Game; import mage.players.Player; -import java.util.Optional; import java.util.UUID; /** @@ -69,7 +68,6 @@ enum SailorsBaneValue implements DynamicValue { private static final FilterCard filter = new FilterCard(); static { - filter.add(TargetController.YOU.getOwnerPredicate()); filter.add(Predicates.or( CardType.INSTANT.getPredicate(), CardType.SORCERY.getPredicate(), @@ -79,15 +77,12 @@ enum SailorsBaneValue implements DynamicValue { @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { - return Optional - .ofNullable(game.getPlayer(sourceAbility.getControllerId())) - .map(Player::getGraveyard) - .map(graveyard -> graveyard.count(filter, game)) - .orElse(0) - + game - .getExile() - .getCards(filter, game) - .size(); + Player player = game.getPlayer(sourceAbility.getControllerId()); + if (player == null) { + return 0; + } + return game.getExile().getCardsOwned(filter, player.getId(), sourceAbility, game).size() + + player.getGraveyard().count(filter, player.getId(), sourceAbility, game); } @Override @@ -100,7 +95,7 @@ enum SailorsBaneValue implements DynamicValue { return ""; } - private static final boolean checkAdventure(Card input, Game game) { + private static boolean checkAdventure(Card input, Game game) { return input instanceof AdventureCard; } } diff --git a/Mage.Sets/src/mage/cards/s/SalvationSwan.java b/Mage.Sets/src/mage/cards/s/SalvationSwan.java index 9b5b56c967d..212423de2ce 100644 --- a/Mage.Sets/src/mage/cards/s/SalvationSwan.java +++ b/Mage.Sets/src/mage/cards/s/SalvationSwan.java @@ -123,7 +123,7 @@ class SalvationSwanTargetEffect extends OneShotEffect { game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), source); // move exiled cards to shared zone for better UX - String exileKey = SalvationSwan.VALUE_PREFIX + "_" + source.getSourceId() + "_" + source.getSourceObjectZoneChangeCounter(); + String exileKey = SalvationSwan.VALUE_PREFIX + "_" + source.getSourceId() + "_" + source.getStackMomentSourceZCC(); ExileZone sharedExileZone = game.getExile().createZone( CardUtil.getExileZoneId(exileKey, game), sourceObject.getIdName() diff --git a/Mage.Sets/src/mage/cards/s/SandmanShiftingScoundrel.java b/Mage.Sets/src/mage/cards/s/SandmanShiftingScoundrel.java new file mode 100644 index 00000000000..ff40e6a5215 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SandmanShiftingScoundrel.java @@ -0,0 +1,65 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.LandsYouControlCount; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; +import mage.abilities.effects.common.continuous.SetBasePowerToughnessSourceEffect; +import mage.abilities.keyword.DauntAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SandmanShiftingScoundrel extends CardImpl { + + public SandmanShiftingScoundrel(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{G}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SAND); + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(0); + this.toughness = new MageInt(0); + + // Sandman's power and toughness are each equal to the number of lands you control. + this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetBasePowerToughnessSourceEffect(LandsYouControlCount.instance))); + + // Sandman can't be blocked by creatures with power 2 or less. + this.addAbility(new DauntAbility()); + + // {3}{G}{G}: Return this card and target land card from your graveyard to the battlefield tapped. + Ability ability = new SimpleActivatedAbility( + new ReturnSourceFromGraveyardToBattlefieldEffect(true) + .setText("return this card"), + new ManaCostsImpl<>("{3}{G}{G}") + ); + ability.addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect(true) + .setText("and target land card from your graveyard to the battlefield tapped")); + ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_LAND)); + this.addAbility(ability); + } + + private SandmanShiftingScoundrel(final SandmanShiftingScoundrel card) { + super(card); + } + + @Override + public SandmanShiftingScoundrel copy() { + return new SandmanShiftingScoundrel(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SandmansQuicksand.java b/Mage.Sets/src/mage/cards/s/SandmansQuicksand.java new file mode 100644 index 00000000000..7b7574f6504 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SandmansQuicksand.java @@ -0,0 +1,54 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.abilities.keyword.MayhemAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.TargetController; +import mage.filter.common.FilterCreaturePermanent; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class SandmansQuicksand extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures your opponents control"); + + static { + filter.add(TargetController.OPPONENT.getControllerPredicate()); + } + + public SandmansQuicksand(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}{B}"); + + + // Mayhem {3}{B} + Ability mayhem = new MayhemAbility(this, "{3}{B}"); + mayhem.addEffect(new BoostAllEffect(-2, -2, Duration.EndOfTurn, filter, false)); + this.addAbility(mayhem); + + // All creatures get -2/-2 until end of turn. If this spell's mayhem cost was paid, creatures your opponents control get -2/-2 until end of turn instead. + ContinuousEffect effect = new BoostAllEffect(-2, -2, Duration.EndOfTurn); + effect.setText("All creatures get -2/-2 until end of turn. If this spell's mayhem cost was paid, " + + "creatures your opponents control get -2/-2 until end of turn instead" + ); + this.getSpellAbility().addEffect(effect); + + } + + private SandmansQuicksand(final SandmansQuicksand card) { + super(card); + } + + @Override + public SandmansQuicksand copy() { + return new SandmansQuicksand(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SandstalkerMoloch.java b/Mage.Sets/src/mage/cards/s/SandstalkerMoloch.java index a86566afa01..dded6dc0ea7 100644 --- a/Mage.Sets/src/mage/cards/s/SandstalkerMoloch.java +++ b/Mage.Sets/src/mage/cards/s/SandstalkerMoloch.java @@ -92,6 +92,7 @@ class SandstalkerMolochWatcher extends Watcher { @Override public void reset() { super.reset(); + players.clear(); } static boolean checkPlayer(Game game, Ability source) { diff --git a/Mage.Sets/src/mage/cards/s/SaprazzanLegate.java b/Mage.Sets/src/mage/cards/s/SaprazzanLegate.java index 6234414c866..a6834c3faaf 100644 --- a/Mage.Sets/src/mage/cards/s/SaprazzanLegate.java +++ b/Mage.Sets/src/mage/cards/s/SaprazzanLegate.java @@ -39,7 +39,7 @@ public final class SaprazzanLegate extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // If an opponent controls a Mountain and you control an Island, you may cast this spell without paying its mana cost. - Condition condition = new CompoundCondition("If an opponent controls a Mountain and you control an Island", + Condition condition = new CompoundCondition("an opponent controls a Mountain and you control an Island", new OpponentControlsPermanentCondition(filterMountain), new PermanentsOnTheBattlefieldCondition(filterIsland)); this.addAbility(new AlternativeCostSourceAbility(null, condition)); diff --git a/Mage.Sets/src/mage/cards/s/SatyrFiredancer.java b/Mage.Sets/src/mage/cards/s/SatyrFiredancer.java index 894acc8f25e..d82848028c9 100644 --- a/Mage.Sets/src/mage/cards/s/SatyrFiredancer.java +++ b/Mage.Sets/src/mage/cards/s/SatyrFiredancer.java @@ -1,29 +1,24 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.MageObject; -import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.dynamicvalue.common.EffectKeyValue; import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetadjustment.TargetAdjuster; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** * @author LevelX2 */ @@ -53,8 +48,9 @@ public final class SatyrFiredancer extends CardImpl { class SatyrFiredancerTriggeredAbility extends TriggeredAbilityImpl { SatyrFiredancerTriggeredAbility() { - super(Zone.BATTLEFIELD, new SatyrFiredancerDamageEffect(), false); - this.setTargetAdjuster(SatyrFiredancerAdjuster.instance); + super(Zone.BATTLEFIELD, new DamageTargetEffect(new EffectKeyValue("damage", "that much")), false); + this.addTarget(new TargetCreaturePermanent().withTargetName("target creature that player controls")); + this.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); setTriggerPhrase("Whenever an instant or sorcery spell you control deals damage to an opponent, "); } @@ -96,48 +92,3 @@ class SatyrFiredancerTriggeredAbility extends TriggeredAbilityImpl { return true; } } - -class SatyrFiredancerDamageEffect extends OneShotEffect { - - SatyrFiredancerDamageEffect() { - super(Outcome.Damage); - this.staticText = "{this} deals that much damage to target creature that player controls"; - } - - private SatyrFiredancerDamageEffect(final SatyrFiredancerDamageEffect effect) { - super(effect); - } - - @Override - public SatyrFiredancerDamageEffect copy() { - return new SatyrFiredancerDamageEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Permanent targetCreature = game.getPermanent(source.getFirstTarget()); - Player controller = game.getPlayer(source.getControllerId()); - if (targetCreature != null && controller != null) { - int damage = (Integer) this.getValue("damage"); - if (damage > 0) { - targetCreature.damage(damage, source.getSourceId(), source, game, false, true); - } - return true; - } - return false; - } -} - -enum SatyrFiredancerAdjuster implements TargetAdjuster { - instance; - - @Override - public void adjustTargets(Ability ability, Game game) { - Player opponent = game.getPlayer(ability.getEffects().get(0).getTargetPointer().getFirst(game, ability)); - if (opponent != null) { - FilterCreaturePermanent filter = new FilterCreaturePermanent("creature controlled by " + opponent.getLogName()); - filter.add(new ControllerIdPredicate(opponent.getId())); - ability.getTargets().add(new TargetPermanent(filter)); - } - } -} diff --git a/Mage.Sets/src/mage/cards/s/SavageMansion.java b/Mage.Sets/src/mage/cards/s/SavageMansion.java new file mode 100644 index 00000000000..3eaebb2799f --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SavageMansion.java @@ -0,0 +1,46 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.keyword.SurveilEffect; +import mage.abilities.mana.GreenManaAbility; +import mage.abilities.mana.RedManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SavageMansion extends CardImpl { + + public SavageMansion(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // This land enters tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {R} or {G}. + this.addAbility(new RedManaAbility()); + this.addAbility(new GreenManaAbility()); + + // {4}, {T}: Surveil 1. + Ability ability = new SimpleActivatedAbility(new SurveilEffect(1), new GenericManaCost(4)); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private SavageMansion(final SavageMansion card) { + super(card); + } + + @Override + public SavageMansion copy() { + return new SavageMansion(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/ScarletSpiderBenReilly.java b/Mage.Sets/src/mage/cards/s/ScarletSpiderBenReilly.java new file mode 100644 index 00000000000..dbe63994f49 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ScarletSpiderBenReilly.java @@ -0,0 +1,100 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.condition.common.WebSlingingCondition; +import mage.abilities.costs.common.ReturnToHandChosenControlledPermanentCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.abilities.keyword.WebSlingingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class ScarletSpiderBenReilly extends CardImpl { + + public ScarletSpiderBenReilly(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{G}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIDER); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.HERO); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Web-slinging {R}{G} + this.addAbility(new WebSlingingAbility(this, "{R}{G}")); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Sensational Save -- If Scarlet Spider was cast using web-slinging, he enters with X +1/+1 counters on him, where X is the mana value of the returned creature. + String ruleText = CardUtil.italicizeWithEmDash("Sensational Save") + "If {this} was cast using web-slinging, " + + "he enters with X +1/+1 counters on him, where X is the mana value of the returned creature."; + this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(), + ScarletSpiderBenReillyValue.instance, false), + WebSlingingCondition.THIS, ruleText, "")); + } + + private ScarletSpiderBenReilly(final ScarletSpiderBenReilly card) { + super(card); + } + + @Override + public ScarletSpiderBenReilly copy() { + return new ScarletSpiderBenReilly(this); + } +} +enum ScarletSpiderBenReillyValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + + Spell spell = game.getSpellOrLKIStack(sourceAbility.getSourceId()); + if (spell == null || spell.getSpellAbility() == null) { + return 0; + } + + ReturnToHandChosenControlledPermanentCost returnCost = (ReturnToHandChosenControlledPermanentCost) spell.getSpellAbility().getCosts().stream() + .filter(cost -> cost instanceof ReturnToHandChosenControlledPermanentCost) + .findFirst() + .orElse(null); + if (returnCost == null || returnCost.getPermanents().isEmpty()) { + return 0; + } + + return returnCost.getPermanents().get(0).getManaValue(); + } + + @Override + public ScarletSpiderBenReillyValue copy() { + return this; + } + + @Override + public String toString() { + return "X"; + } + + @Override + public String getMessage() { + return ""; + } +} diff --git a/Mage.Sets/src/mage/cards/s/ScarletSpiderKaine.java b/Mage.Sets/src/mage/cards/s/ScarletSpiderKaine.java new file mode 100644 index 00000000000..11bf8e86311 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ScarletSpiderKaine.java @@ -0,0 +1,56 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.MayhemAbility; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ScarletSpiderKaine extends CardImpl { + + public ScarletSpiderKaine(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIDER); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.HERO); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Menace + this.addAbility(new MenaceAbility()); + + // When Scarlet Spider enters, you may discard a card. If you do, put a +1/+1 counter on him. + this.addAbility(new EntersBattlefieldTriggeredAbility(new DoIfCostPaid( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()) + .setText("put a +1/+1 counter on him"), + new DiscardCardCost() + ))); + + // Mayhem {B/R} + this.addAbility(new MayhemAbility(this, "{B/R}")); + } + + private ScarletSpiderKaine(final ScarletSpiderKaine card) { + super(card); + } + + @Override + public ScarletSpiderKaine copy() { + return new ScarletSpiderKaine(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SchoolDaze.java b/Mage.Sets/src/mage/cards/s/SchoolDaze.java new file mode 100644 index 00000000000..c48395cf109 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SchoolDaze.java @@ -0,0 +1,41 @@ +package mage.cards.s; + +import mage.abilities.Mode; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.TargetSpell; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SchoolDaze extends CardImpl { + + public SchoolDaze(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}{U}"); + + // Choose one -- + // * Do Homework -- Draw three cards. + this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(3)); + this.getSpellAbility().withFirstModeFlavorWord("Do Homework"); + + // * Fight Crime -- Counter target spell. Draw a card. + this.getSpellAbility().addMode(new Mode(new CounterTargetEffect()) + .addEffect(new DrawCardSourceControllerEffect(1)) + .addTarget(new TargetSpell()) + .withFlavorWord("Fight Crime")); + } + + private SchoolDaze(final SchoolDaze card) { + super(card); + } + + @Override + public SchoolDaze copy() { + return new SchoolDaze(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/ScorchingLava.java b/Mage.Sets/src/mage/cards/s/ScorchingLava.java index dd3143ad363..a177e730c42 100644 --- a/Mage.Sets/src/mage/cards/s/ScorchingLava.java +++ b/Mage.Sets/src/mage/cards/s/ScorchingLava.java @@ -31,7 +31,7 @@ public final class ScorchingLava extends CardImpl { // that creature can't be regenerated this turn and if it would die this turn, exile it instead. this.getSpellAbility().addEffect(new DamageTargetEffect(2)); this.getSpellAbility().addEffect(new ConditionalContinuousRuleModifyingEffect( - new CantRegenerateTargetEffect(Duration.EndOfTurn, "If {this} was kicked, that creature"), + new CantRegenerateTargetEffect(Duration.EndOfTurn, "If this spell was kicked, that creature"), new LockedInCondition(KickedCondition.ONCE))); this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new ExileTargetIfDiesEffect(), diff --git a/Mage.Sets/src/mage/cards/s/SecretIdentity.java b/Mage.Sets/src/mage/cards/s/SecretIdentity.java new file mode 100644 index 00000000000..fa0a7cb8baf --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SecretIdentity.java @@ -0,0 +1,61 @@ +package mage.cards.s; + +import mage.abilities.Mode; +import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.HexproofAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.game.permanent.token.custom.CreatureToken; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class SecretIdentity extends CardImpl { + + public SecretIdentity(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}"); + + + // Choose one -- + this.getSpellAbility().getModes().setMinModes(1); + this.getSpellAbility().getModes().setMaxModes(1); + // * Conceal -- Until end of turn, target creature you control becomes a Citizen with base power and toughness 1/1 and gains hexproof. + this.getSpellAbility().addEffect(new BecomesCreatureTargetEffect( + new CreatureToken(1, 1, "Citizen with base power and toughness 1/1 and gains hexproof") + .withSubType(SubType.CITIZEN) + .withAbility(HexproofAbility.getInstance()), + false, false, Duration.EndOfTurn) + ); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + this.getSpellAbility().withFirstModeFlavorWord("Conceal"); + + // * Reveal -- Until end of turn, target creature you control becomes a Hero with base power and toughness 3/4 and gains flying and vigilance. + Mode mode = new Mode(new BecomesCreatureTargetEffect( + new CreatureToken(3, 4, "Hero with base power and toughness 3/4 and gains flying and vigilance") + .withSubType(SubType.HERO) + .withAbility(FlyingAbility.getInstance()) + .withAbility(VigilanceAbility.getInstance()), + false, false, Duration.EndOfTurn)); + mode.withFlavorWord("Reveal"); + mode.addTarget(new TargetControlledCreaturePermanent()); + this.getSpellAbility().addMode(mode); + } + + private SecretIdentity(final SecretIdentity card) { + super(card); + } + + @Override + public SecretIdentity copy() { + return new SecretIdentity(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SeizeTheStorm.java b/Mage.Sets/src/mage/cards/s/SeizeTheStorm.java index eef63c29577..6dca07f59a8 100644 --- a/Mage.Sets/src/mage/cards/s/SeizeTheStorm.java +++ b/Mage.Sets/src/mage/cards/s/SeizeTheStorm.java @@ -62,7 +62,7 @@ enum SeizeTheStormValue implements DynamicValue { return player.getGraveyard().count( StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, game ) + game.getExile() - .getAllCards(game, sourceAbility.getControllerId()) + .getCardsOwned(game, sourceAbility.getControllerId()) .stream() .filter(card -> card.getAbilities(game).containsClass(FlashbackAbility.class)) .mapToInt(x -> 1).sum(); diff --git a/Mage.Sets/src/mage/cards/s/SensationalSpiderMan.java b/Mage.Sets/src/mage/cards/s/SensationalSpiderMan.java index 0549474c5a1..ae40fe883bd 100644 --- a/Mage.Sets/src/mage/cards/s/SensationalSpiderMan.java +++ b/Mage.Sets/src/mage/cards/s/SensationalSpiderMan.java @@ -95,7 +95,7 @@ class SensationalSpiderManEffect extends OneShotEffect { // TODO: this ideally would be able to prevent the player from choosing a number greater than the number of stun counters available to remove TargetPermanentAmount target = new TargetPermanentAmount(3, 0, filter); target.withNotTarget(true); - player.choose(outcome, target, source, game); + player.chooseTarget(outcome, target, source, game); int amountRemoved = target .getTargets() .stream() diff --git a/Mage.Sets/src/mage/cards/s/SereneSleuth.java b/Mage.Sets/src/mage/cards/s/SereneSleuth.java new file mode 100644 index 00000000000..89a8c8b1393 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SereneSleuth.java @@ -0,0 +1,132 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.keyword.InvestigateEffect; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.GoadedPredicate; +import mage.game.Game; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class SereneSleuth extends CardImpl { + + private final static FilterPermanent filter = new FilterControlledCreaturePermanent("goaded creatures you control"); + + static { + filter.add(GoadedPredicate.instance); + } + + public SereneSleuth(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.DETECTIVE); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // When Serene Sleuth enters the battlefield, investigate. + this.addAbility(new EntersBattlefieldTriggeredAbility(new InvestigateEffect())); + + // At the beginning of combat on your turn, investigate for each goaded creature you control. Then each creature you control is no longer goaded. + Ability triggeredAbility = new BeginningOfCombatTriggeredAbility( + new InvestigateEffect(new PermanentsOnBattlefieldCount(filter)) + .setText("investigate for each goaded creature you control.") + ); + triggeredAbility.addEffect(new SereneSleuthEffect(filter) + .concatBy("Then")); + this.addAbility(triggeredAbility); + } + + private SereneSleuth(final SereneSleuth card) { + super(card); + } + + @Override + public SereneSleuth copy() { + return new SereneSleuth(this); + } +} +class SereneSleuthEffect extends ContinuousEffectImpl { + + private MageObjectReference sourceMor; + private final FilterPermanent filter; + + public SereneSleuthEffect(FilterPermanent filter) { + super(Duration.Custom, Layer.RulesEffects, SubLayer.NA, Outcome.Benefit); + staticText = "each creature you control is no longer goaded"; + this.filter = filter; + } + + private SereneSleuthEffect(final SereneSleuthEffect effect) { + super(effect); + this.sourceMor = effect.sourceMor; + this.filter = effect.filter; + } + + public SereneSleuthEffect copy() { + return new SereneSleuthEffect(this); + } + + public MageObjectReference getSourceMor() { + return sourceMor; + } + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + // discard previous effect if it exists to not clutter + for (ContinuousEffect effect : game.getContinuousEffects().getLayeredEffects(game)) { + if (effect instanceof SereneSleuthEffect) { + MageObjectReference effectsMor = ((SereneSleuthEffect) effect).getSourceMor(); + if (effectsMor != null && effectsMor.refersTo(source.getSourceId(), game)) { + effect.discard(); + this.affectedObjectList.addAll(effect.getAffectedObjects()); + } + } + } + sourceMor = new MageObjectReference(source.getSourceObject(game), game); + setAffectedObjectsSet(true); + game.getBattlefield() + .getActivePermanents( + filter, source.getControllerId(), source, game + ).stream() + .map(permanent -> new MageObjectReference(permanent, game)) + .forEach(mor -> { + if (!this.affectedObjectList.contains(mor)) { + this.affectedObjectList.add(mor); + } + }); + } + + @Override + public boolean apply(Game game, Ability source) { + if (getAffectedObjectsSet()) { + this.affectedObjectList.removeIf(mor -> !mor.zoneCounterIsCurrent(game) + || mor.getPermanent(game) == null); + if (affectedObjectList.isEmpty()) { + discard(); + return false; + } + for (MageObjectReference mor : this.affectedObjectList) { + mor.getPermanent(game).getGoadingPlayers().clear(); + } + return true; + } + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/ServantOfTheScale.java b/Mage.Sets/src/mage/cards/s/ServantOfTheScale.java index 8ecde4bfcbb..0bb686c5766 100644 --- a/Mage.Sets/src/mage/cards/s/ServantOfTheScale.java +++ b/Mage.Sets/src/mage/cards/s/ServantOfTheScale.java @@ -76,8 +76,8 @@ class ServantOfTheScaleEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); Permanent sourcePermanent = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); if (sourcePermanent != null && controller != null - && (sourcePermanent.getZoneChangeCounter(game) == source.getSourceObjectZoneChangeCounter() // Token - || sourcePermanent.getZoneChangeCounter(game) + 1 == source.getSourceObjectZoneChangeCounter())) { // PermanentCard + && (sourcePermanent.getZoneChangeCounter(game) == source.getStackMomentSourceZCC() // Token + || sourcePermanent.getZoneChangeCounter(game) + 1 == source.getStackMomentSourceZCC())) { // PermanentCard int amount = sourcePermanent.getCounters(game).getCount(CounterType.P1P1); if (amount > 0) { Effect effect = new AddCountersTargetEffect(CounterType.P1P1.createInstance(amount)); diff --git a/Mage.Sets/src/mage/cards/s/ShadowOfTheGoblin.java b/Mage.Sets/src/mage/cards/s/ShadowOfTheGoblin.java new file mode 100644 index 00000000000..822dbe8d30f --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ShadowOfTheGoblin.java @@ -0,0 +1,78 @@ +package mage.cards.s; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.triggers.BeginningOfFirstMainTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class ShadowOfTheGoblin extends CardImpl { + + public ShadowOfTheGoblin(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{R}"); + + + // Unreliable Visions -- At the beginning of your first main phase, discard a card. If you do, draw a card. + this.addAbility(new BeginningOfFirstMainTriggeredAbility( + new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new DiscardCardCost()), false) + .withFlavorWord("Unreliable Visions") + ); + + // Undying Vengeance -- Whenever you play a land or cast a spell from anywhere other than your hand, this enchantment deals 1 damage to each opponent. + this.addAbility(new ShadowOfTheGoblinTriggeredAbility(new DamagePlayersEffect(1, TargetController.OPPONENT)) + .withFlavorWord("Undying Vengeance")); + + } + + private ShadowOfTheGoblin(final ShadowOfTheGoblin card) { + super(card); + } + + @Override + public ShadowOfTheGoblin copy() { + return new ShadowOfTheGoblin(this); + } +} + +class ShadowOfTheGoblinTriggeredAbility extends TriggeredAbilityImpl { + + ShadowOfTheGoblinTriggeredAbility(Effect effect) { + super(Zone.BATTLEFIELD, effect, false); + setTriggerPhrase("Whenever you play a land or cast a spell from anywhere other than your hand, "); + } + + protected ShadowOfTheGoblinTriggeredAbility(final ShadowOfTheGoblinTriggeredAbility triggeredAbility) { + super(triggeredAbility); + } + + @Override + public ShadowOfTheGoblinTriggeredAbility copy() { + return new ShadowOfTheGoblinTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.PLAY_LAND || + event.getType() == GameEvent.EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return isControlledBy(event.getPlayerId()) && event.getZone() != Zone.HAND; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SharedFate.java b/Mage.Sets/src/mage/cards/s/SharedFate.java index c66c6ee93e8..85a6b70e521 100644 --- a/Mage.Sets/src/mage/cards/s/SharedFate.java +++ b/Mage.Sets/src/mage/cards/s/SharedFate.java @@ -8,15 +8,21 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; +import mage.filter.FilterPlayer; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.other.PlayerIdPredicate; import mage.game.ExileZone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.players.PlayerList; -import mage.target.common.TargetOpponent; +import mage.target.Target; +import mage.target.TargetPlayer; import mage.util.CardUtil; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; /** @@ -74,13 +80,25 @@ class SharedFateReplacementEffect extends ReplacementEffectImpl { Permanent sourcePermanent = game.getPermanent(source.getSourceId()); Player playerToDraw = game.getPlayer(event.getPlayerId()); Player controller = game.getPlayer(source.getControllerId()); - PlayerList playersInRange = game.getState().getPlayersInRange(source.getControllerId(), game); + PlayerList playersInControllerRange = game.getState().getPlayersInRange(source.getControllerId(), game); + // Check if the player currently drawing is in range of the source if (sourcePermanent == null || playerToDraw == null || controller == null - || !playersInRange.contains(playerToDraw.getId())) { + || !playersInControllerRange.contains(playerToDraw.getId())) { return false; } - TargetOpponent target = new TargetOpponent(true); + + // Create opponent filter list manually because otherwise opponent check prevents controller of this to be valid + PlayerList playersInPlayerRange = game.getState().getPlayersInRange(playerToDraw.getId(), game); + FilterPlayer filter = new FilterPlayer("opponent"); + List opponentPredicates = new ArrayList<>(); + for (UUID opponentId : game.getOpponents(playerToDraw.getId())) { + if (playersInPlayerRange.contains(opponentId) && playersInControllerRange.contains(opponentId)) { + opponentPredicates.add(new PlayerIdPredicate(opponentId)); + } + } + filter.add(Predicates.or(opponentPredicates)); + Target target = new TargetPlayer(1, 1, true, filter); if (!playerToDraw.choose(Outcome.DrawCard, target, source, game)) { return false; } @@ -90,15 +108,6 @@ class SharedFateReplacementEffect extends ReplacementEffectImpl { return false; } - if (!playersInRange.contains(chosenPlayer.getId())) { - game.informPlayers( - "Nothing exiled. " + playerToDraw.getLogName() - + " chose to exile from " + chosenPlayer.getLogName() + "'s library. " - + "That player is outside of " + controller.getLogName() + "'s range of influence." - ); - return false; - } - game.informPlayers(playerToDraw.getLogName() + " chose to exile from " + chosenPlayer.getLogName() + "' library."); Card card = chosenPlayer.getLibrary().getFromTop(game); diff --git a/Mage.Sets/src/mage/cards/s/ShriekTreblemaker.java b/Mage.Sets/src/mage/cards/s/ShriekTreblemaker.java new file mode 100644 index 00000000000..e90bf9be2ef --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/ShriekTreblemaker.java @@ -0,0 +1,60 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.DiesCreatureTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DoWhenCostPaid; +import mage.abilities.effects.common.combat.CantBlockTargetEffect; +import mage.abilities.triggers.BeginningOfFirstMainTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class ShriekTreblemaker extends CardImpl { + + public ShriekTreblemaker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B/R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.MUTANT); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // At the beginning of your first main phase, you may discard a card. When you do, target creature can't block this turn. + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(new CantBlockTargetEffect(Duration.EndOfTurn), false); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(new BeginningOfFirstMainTriggeredAbility(new DoWhenCostPaid(ability, new DiscardCardCost(), + "Discard a card to make target creature unable to block this turn?")) + ); + + // Sonic Blast -- Whenever a creature an opponent controls dies, Shriek deals 1 damage to that player. + this.addAbility(new DiesCreatureTriggeredAbility( + Zone.BATTLEFIELD, + new DamageTargetEffect(1, true, "that player"), + false, + StaticFilters.FILTER_OPPONENTS_PERMANENT_A_CREATURE, + SetTargetPointer.PLAYER) + .withFlavorWord("Sonic Blast") + ); + } + + private ShriekTreblemaker(final ShriekTreblemaker card) { + super(card); + } + + @Override + public ShriekTreblemaker copy() { + return new ShriekTreblemaker(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/Shyft.java b/Mage.Sets/src/mage/cards/s/Shyft.java index e1fe7f8d467..97a99ed3ca6 100644 --- a/Mage.Sets/src/mage/cards/s/Shyft.java +++ b/Mage.Sets/src/mage/cards/s/Shyft.java @@ -63,7 +63,7 @@ class ShyftEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Effect effect = new BecomesColorOrColorsTargetEffect(Duration.Custom); - effect.setTargetPointer(new FixedTarget(source.getSourceId(), source.getSourceObjectZoneChangeCounter())); + effect.setTargetPointer(new FixedTarget(source.getSourceId(), source.getStackMomentSourceZCC())); return effect.apply(game, source); } } diff --git a/Mage.Sets/src/mage/cards/s/SilkWebWeaver.java b/Mage.Sets/src/mage/cards/s/SilkWebWeaver.java new file mode 100644 index 00000000000..d44c2c7ba01 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SilkWebWeaver.java @@ -0,0 +1,71 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.abilities.keyword.WebSlingingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; +import mage.game.permanent.token.HumanCitizenToken; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class SilkWebWeaver extends CardImpl { + + public SilkWebWeaver(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIDER); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.HERO); + this.power = new MageInt(3); + this.toughness = new MageInt(5); + + // Web-slinging {1}{G}{W} + this.addAbility(new WebSlingingAbility(this, "{1}{G}{W}")); + + // Whenever you cast a creature spell, create a 1/1 green and white Human Citizen creature token. + this.addAbility(new SpellCastControllerTriggeredAbility( + new CreateTokenEffect(new HumanCitizenToken()), + StaticFilters.FILTER_SPELL_A_CREATURE, + false + )); + + // {3}{G}{W}: Creatures you control get +2/+2 and gain vigilance until end of turn. + Ability ability = new SimpleActivatedAbility( + new BoostControlledEffect(2, 2, Duration.EndOfTurn).setText("Creatures you control get +2/+2"), + new ManaCostsImpl<>("{3}{G}{W}") + ); + ability.addEffect( + new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES) + .setText("gain vigilance until end of turn") + .concatBy("and") + ); + this.addAbility(ability); + } + + private SilkWebWeaver(final SilkWebWeaver card) { + super(card); + } + + @Override + public SilkWebWeaver copy() { + return new SilkWebWeaver(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SilverSableMercenaryLeader.java b/Mage.Sets/src/mage/cards/s/SilverSableMercenaryLeader.java new file mode 100644 index 00000000000..b04e58a7d28 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SilverSableMercenaryLeader.java @@ -0,0 +1,64 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.permanent.ModifiedPredicate; +import mage.target.TargetPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SilverSableMercenaryLeader extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledCreaturePermanent("modified creature you control"); + + static { + filter.add(ModifiedPredicate.instance); + } + + public SilverSableMercenaryLeader(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.MERCENARY); + this.subtype.add(SubType.HERO); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // When Silver Sable enters, put a +1/+1 counter on another target creature. + Ability ability = new EntersBattlefieldTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_ANOTHER_TARGET_CREATURE)); + this.addAbility(ability); + + // Whenever Silver Sable attacks, target modified creature you control gains lifelink until end of turn. + ability = new AttacksTriggeredAbility(new GainAbilityTargetEffect(LifelinkAbility.getInstance())); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + private SilverSableMercenaryLeader(final SilverSableMercenaryLeader card) { + super(card); + } + + @Override + public SilverSableMercenaryLeader copy() { + return new SilverSableMercenaryLeader(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SinisterConcierge.java b/Mage.Sets/src/mage/cards/s/SinisterConcierge.java index f7ae1aced8b..09ad27db1ce 100644 --- a/Mage.Sets/src/mage/cards/s/SinisterConcierge.java +++ b/Mage.Sets/src/mage/cards/s/SinisterConcierge.java @@ -69,7 +69,7 @@ class SinisterConciergeEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); Card card = game.getCard(source.getSourceId()); if (controller == null || card == null - || card.getZoneChangeCounter(game) != source.getSourceObjectZoneChangeCounter() + || card.getZoneChangeCounter(game) != source.getStackMomentSourceZCC() || !Zone.GRAVEYARD.match(game.getState().getZone(card.getId()))) { return false; } diff --git a/Mage.Sets/src/mage/cards/s/SinisterHideout.java b/Mage.Sets/src/mage/cards/s/SinisterHideout.java new file mode 100644 index 00000000000..921ea121a24 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SinisterHideout.java @@ -0,0 +1,46 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.keyword.SurveilEffect; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.BlueManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SinisterHideout extends CardImpl { + + public SinisterHideout(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // This land enters tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {U} or {B}. + this.addAbility(new BlueManaAbility()); + this.addAbility(new BlackManaAbility()); + + // {4}, {T}: Surveil 1. + Ability ability = new SimpleActivatedAbility(new SurveilEffect(1), new GenericManaCost(4)); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private SinisterHideout(final SinisterHideout card) { + super(card); + } + + @Override + public SinisterHideout copy() { + return new SinisterHideout(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SivvisRuse.java b/Mage.Sets/src/mage/cards/s/SivvisRuse.java index deae00f77c9..d507369b355 100644 --- a/Mage.Sets/src/mage/cards/s/SivvisRuse.java +++ b/Mage.Sets/src/mage/cards/s/SivvisRuse.java @@ -33,7 +33,7 @@ public final class SivvisRuse extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}{W}"); // If an opponent controls a Mountain and you control a Plains, you may cast this spell without paying its mana cost. - Condition condition = new CompoundCondition("If an opponent controls a Mountain and you control a Plains", + Condition condition = new CompoundCondition("an opponent controls a Mountain and you control a Plains", new OpponentControlsPermanentCondition(filterMountain), new PermanentsOnTheBattlefieldCondition(filterPlains)); this.addAbility(new AlternativeCostSourceAbility(null, condition)); diff --git a/Mage.Sets/src/mage/cards/s/SivvisValor.java b/Mage.Sets/src/mage/cards/s/SivvisValor.java index 32f32f2ac49..7f1c8ec2189 100644 --- a/Mage.Sets/src/mage/cards/s/SivvisValor.java +++ b/Mage.Sets/src/mage/cards/s/SivvisValor.java @@ -31,7 +31,7 @@ import mage.target.common.TargetCreaturePermanent; */ public final class SivvisValor extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("If you control a Plains"); + private static final FilterPermanent filter = new FilterPermanent("you control a Plains"); private static final FilterControlledCreaturePermanent filterCreature = new FilterControlledCreaturePermanent("untapped creature you control"); static { diff --git a/Mage.Sets/src/mage/cards/s/SkyclaveShade.java b/Mage.Sets/src/mage/cards/s/SkyclaveShade.java index 3f984c77990..08e7007de18 100644 --- a/Mage.Sets/src/mage/cards/s/SkyclaveShade.java +++ b/Mage.Sets/src/mage/cards/s/SkyclaveShade.java @@ -102,6 +102,6 @@ class SkyclaveShadeEffect extends AsThoughEffectImpl { Card card = game.getCard(source.getSourceId()); return card != null && game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD - && source.getSourceObjectZoneChangeCounter() == card.getZoneChangeCounter(game); + && source.getStackMomentSourceZCC() == card.getZoneChangeCounter(game); } } diff --git a/Mage.Sets/src/mage/cards/s/SkyserpentSeeker.java b/Mage.Sets/src/mage/cards/s/SkyserpentSeeker.java index 6f124c25c80..f84768364d1 100644 --- a/Mage.Sets/src/mage/cards/s/SkyserpentSeeker.java +++ b/Mage.Sets/src/mage/cards/s/SkyserpentSeeker.java @@ -1,23 +1,25 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.RevealCardsFromLibraryUntilEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.abilities.keyword.ExhaustAbility; -import mage.cards.*; -import mage.constants.*; -import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.ExhaustAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.PutCards; +import mage.constants.SubType; import mage.counters.CounterType; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** * * @author Jmlundeen @@ -84,8 +86,9 @@ class SkyserpentSeekerEffect extends OneShotEffect { if (lands.size() == 2) { break; } + } else { + revealedCards.add(card); } - revealedCards.add(card); } controller.revealCards(source, revealedCards, game); PutCards.BATTLEFIELD_TAPPED.moveCards(controller, lands, source, game); diff --git a/Mage.Sets/src/mage/cards/s/SkyshipWeatherlight.java b/Mage.Sets/src/mage/cards/s/SkyshipWeatherlight.java index 18681cad8e2..50590ed0cc3 100644 --- a/Mage.Sets/src/mage/cards/s/SkyshipWeatherlight.java +++ b/Mage.Sets/src/mage/cards/s/SkyshipWeatherlight.java @@ -86,7 +86,7 @@ class SkyshipWeatherlightEffect extends SearchEffect { MageObject sourceObject = source.getSourceObject(game); if (sourceObject != null && controller != null) { if (controller.searchLibrary(target, source, game)) { - UUID exileZone = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileZone = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); if (!target.getTargets().isEmpty()) { for (UUID cardID : target.getTargets()) { Card card = controller.getLibrary().getCard(cardID, game); @@ -125,7 +125,7 @@ class SkyshipWeatherlightEffect2 extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = source.getSourceObject(game); if (sourceObject != null && controller != null) { - ExileZone exZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter())); + ExileZone exZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC())); if (exZone != null) { controller.moveCards(exZone.getRandom(game), Zone.HAND, source, game); } diff --git a/Mage.Sets/src/mage/cards/s/SkyshroudCutter.java b/Mage.Sets/src/mage/cards/s/SkyshroudCutter.java index e774940930b..340993ba430 100644 --- a/Mage.Sets/src/mage/cards/s/SkyshroudCutter.java +++ b/Mage.Sets/src/mage/cards/s/SkyshroudCutter.java @@ -18,7 +18,7 @@ import mage.filter.FilterPermanent; */ public final class SkyshroudCutter extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("If you control a Forest"); + private static final FilterPermanent filter = new FilterPermanent("you control a Forest"); static { filter.add(SubType.FOREST.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/s/SkywardSpider.java b/Mage.Sets/src/mage/cards/s/SkywardSpider.java new file mode 100644 index 00000000000..2edd517b03d --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SkywardSpider.java @@ -0,0 +1,51 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.SourceModifiedCondition; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.WardAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SkywardSpider extends CardImpl { + + public SkywardSpider(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W/U}{W/U}"); + + this.subtype.add(SubType.SPIDER); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.HERO); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Ward {2} + this.addAbility(new WardAbility(new ManaCostsImpl<>("{2}"))); + + // This creature has flying as long as it's modified. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield), + SourceModifiedCondition.instance, "{this} has flying as long as it's modified" + ))); + } + + private SkywardSpider(final SkywardSpider card) { + super(card); + } + + @Override + public SkywardSpider copy() { + return new SkywardSpider(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SkywayRobber.java b/Mage.Sets/src/mage/cards/s/SkywayRobber.java index 25e90be8490..7d526627f66 100644 --- a/Mage.Sets/src/mage/cards/s/SkywayRobber.java +++ b/Mage.Sets/src/mage/cards/s/SkywayRobber.java @@ -85,7 +85,7 @@ class SkywayRobberCastForFreeEffect extends OneShotEffect { if (controller == null) { return false; } - String exileZoneName = CardUtil.getObjectZoneString(CardUtil.SOURCE_EXILE_ZONE_TEXT, source.getSourceId(), game, source.getSourceObjectZoneChangeCounter()-1, false); + String exileZoneName = CardUtil.getObjectZoneString(CardUtil.SOURCE_EXILE_ZONE_TEXT, source.getSourceId(), game, source.getStackMomentSourceZCC()-1, false); UUID exileId = CardUtil.getExileZoneId(exileZoneName, game); ExileZone exileZone = game.getExile().getExileZone(exileId); if (exileZone == null) { diff --git a/Mage.Sets/src/mage/cards/s/SlimeAgainstHumanity.java b/Mage.Sets/src/mage/cards/s/SlimeAgainstHumanity.java index 37ec3901463..2438d4056ab 100644 --- a/Mage.Sets/src/mage/cards/s/SlimeAgainstHumanity.java +++ b/Mage.Sets/src/mage/cards/s/SlimeAgainstHumanity.java @@ -87,7 +87,7 @@ class SlimeAgainstHumanityEffect extends OneShotEffect { int exileCount = game .getState() .getExile() - .getAllCards(game, source.getControllerId()) + .getCardsOwned(game, source.getControllerId()) .stream() .filter(card -> filter.match(card, game)) .mapToInt(x -> 1) diff --git a/Mage.Sets/src/mage/cards/s/SlingbowTrap.java b/Mage.Sets/src/mage/cards/s/SlingbowTrap.java index 444f4a0d190..3249338d423 100644 --- a/Mage.Sets/src/mage/cards/s/SlingbowTrap.java +++ b/Mage.Sets/src/mage/cards/s/SlingbowTrap.java @@ -71,6 +71,6 @@ enum SlingbowTrapCondition implements Condition { @Override public String toString() { - return "If a black creature with flying is attacking"; + return "a black creature with flying is attacking"; } } diff --git a/Mage.Sets/src/mage/cards/s/SnuffOut.java b/Mage.Sets/src/mage/cards/s/SnuffOut.java index 03a750a1f52..c5b936910ba 100644 --- a/Mage.Sets/src/mage/cards/s/SnuffOut.java +++ b/Mage.Sets/src/mage/cards/s/SnuffOut.java @@ -8,10 +8,8 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.StaticFilters; import mage.filter.common.FilterLandPermanent; import mage.target.TargetPermanent; -import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -23,7 +21,7 @@ import static mage.filter.StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK; */ public final class SnuffOut extends CardImpl { - private static final FilterLandPermanent filterSwamp = new FilterLandPermanent("If you control a Swamp"); + private static final FilterLandPermanent filterSwamp = new FilterLandPermanent("you control a Swamp"); static { filterSwamp.add(SubType.SWAMP.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/s/SolarArray.java b/Mage.Sets/src/mage/cards/s/SolarArray.java index 7d974288bbd..daa06ca96b8 100644 --- a/Mage.Sets/src/mage/cards/s/SolarArray.java +++ b/Mage.Sets/src/mage/cards/s/SolarArray.java @@ -1,7 +1,7 @@ package mage.cards.s; import mage.abilities.Ability; -import mage.abilities.common.delayed.CopyNextSpellDelayedTriggeredAbility; +import mage.abilities.common.delayed.CastNextSpellDelayedTriggeredAbility; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.keyword.SunburstAbility; @@ -26,9 +26,8 @@ public final class SolarArray extends CardImpl { // {T}: Add one mana of any color. When you next cast an artifact spell this turn, that spell gains sunburst. AnyColorManaAbility ability = new AnyColorManaAbility(); - ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new CopyNextSpellDelayedTriggeredAbility( - StaticFilters.FILTER_SPELL_AN_ARTIFACT, new SolarArrayEffect(), - "When you next cast an artifact spell this turn, that spell gains sunburst." + ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new CastNextSpellDelayedTriggeredAbility( + new SolarArrayEffect(), StaticFilters.FILTER_SPELL_AN_ARTIFACT, true ))); ability.setUndoPossible(false); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/s/SongOfTheDryads.java b/Mage.Sets/src/mage/cards/s/SongOfTheDryads.java index d34577f660f..1bca8aa2151 100644 --- a/Mage.Sets/src/mage/cards/s/SongOfTheDryads.java +++ b/Mage.Sets/src/mage/cards/s/SongOfTheDryads.java @@ -50,6 +50,7 @@ class BecomesColorlessForestLandEffect extends ContinuousEffectImpl { BecomesColorlessForestLandEffect() { super(Duration.WhileOnBattlefield, Outcome.Detriment); this.staticText = "Enchanted permanent is a colorless Forest land"; + dependencyTypes.add(DependencyType.BecomeNonbasicLand); dependencyTypes.add(DependencyType.BecomeForest); } diff --git a/Mage.Sets/src/mage/cards/s/SoulgorgerOrgg.java b/Mage.Sets/src/mage/cards/s/SoulgorgerOrgg.java index dce856d8e84..d81b8ed0b72 100644 --- a/Mage.Sets/src/mage/cards/s/SoulgorgerOrgg.java +++ b/Mage.Sets/src/mage/cards/s/SoulgorgerOrgg.java @@ -74,7 +74,7 @@ class SoulgorgerOrggLoseLifeEffect extends OneShotEffect { if (player.getLife() > 1) { lifeValue = player.getLife() - 1; } - game.getState().setValue(source.getSourceId().toString() + source.getControllerId().toString() + source.getSourceObjectZoneChangeCounter() + "_lifeValue", lifeValue); + game.getState().setValue(source.getSourceId().toString() + source.getControllerId().toString() + source.getStackMomentSourceZCC() + "_lifeValue", lifeValue); if (lifeValue > 0) { player.loseLife(lifeValue, game, source, false); } @@ -103,7 +103,7 @@ class SoulgorgerOrggGainLifeEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - Object obj = game.getState().getValue(source.getSourceId().toString() + source.getControllerId().toString() + (source.getSourceObjectZoneChangeCounter() - 1) + "_lifeValue"); + Object obj = game.getState().getValue(source.getSourceId().toString() + source.getControllerId().toString() + (source.getStackMomentSourceZCC() - 1) + "_lifeValue"); if (!(obj instanceof Integer)) { return false; } diff --git a/Mage.Sets/src/mage/cards/s/SpectrumSentinel.java b/Mage.Sets/src/mage/cards/s/SpectrumSentinel.java index 790391b7c5c..ff4bbfd94c9 100644 --- a/Mage.Sets/src/mage/cards/s/SpectrumSentinel.java +++ b/Mage.Sets/src/mage/cards/s/SpectrumSentinel.java @@ -44,7 +44,7 @@ public final class SpectrumSentinel extends CardImpl { // Whenever a nonbasic land enters the battlefield under an opponent's control, you gain 1 life. this.addAbility(new EntersBattlefieldAllTriggeredAbility(new GainLifeEffect(1), filter2) - .setTriggerPhrase("Whenever a nonbasic land enters the battlefield under an opponent's control, ")); + .setTriggerPhrase("Whenever a nonbasic land an opponent controls enters, ")); } private SpectrumSentinel(final SpectrumSentinel card) { diff --git a/Mage.Sets/src/mage/cards/s/SpellQueller.java b/Mage.Sets/src/mage/cards/s/SpellQueller.java index c2f829bd6bd..aa3ee3feb0a 100644 --- a/Mage.Sets/src/mage/cards/s/SpellQueller.java +++ b/Mage.Sets/src/mage/cards/s/SpellQueller.java @@ -96,7 +96,7 @@ class SpellQuellerEntersEffect extends OneShotEffect { if (controller != null && sourceObject != null) { Spell spell = game.getStack().getSpell(getTargetPointer().getFirst(game, source)); if (spell != null) { - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); return controller.moveCardsToExile(spell, source, game, true, exileId, sourceObject.getIdName()); } return true; diff --git a/Mage.Sets/src/mage/cards/s/SpellweaverHelix.java b/Mage.Sets/src/mage/cards/s/SpellweaverHelix.java index 89bbba6f492..d7c85e42f62 100644 --- a/Mage.Sets/src/mage/cards/s/SpellweaverHelix.java +++ b/Mage.Sets/src/mage/cards/s/SpellweaverHelix.java @@ -83,7 +83,7 @@ class SpellweaverHelixImprintEffect extends OneShotEffect { for (UUID targetId : this.getTargetPointer().getTargets(game, source)) { Card card = game.getCard(targetId); if (card != null) { - controller.moveCardsToExile(card, source, game, true, CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()), source.getSourceObject(game).getIdName()); + controller.moveCardsToExile(card, source, game, true, CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()), source.getSourceObject(game).getIdName()); if (sourcePermanent != null) { sourcePermanent.imprint(targetId, game); } diff --git a/Mage.Sets/src/mage/cards/s/SpiderGirlLegacyHero.java b/Mage.Sets/src/mage/cards/s/SpiderGirlLegacyHero.java new file mode 100644 index 00000000000..9a541d82cc0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpiderGirlLegacyHero.java @@ -0,0 +1,54 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MyTurnCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.game.permanent.token.HumanCitizenToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpiderGirlLegacyHero extends CardImpl { + + public SpiderGirlLegacyHero(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIDER); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.HERO); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // During your turn, Spider-Girl has flying. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield), + MyTurnCondition.instance, "during your turn, {this} has flying" + ))); + + // When Spider-Girl leaves the battlefield, create a 1/1 green and white Human Citizen creature token. + this.addAbility(new LeavesBattlefieldTriggeredAbility(new CreateTokenEffect(new HumanCitizenToken()))); + } + + private SpiderGirlLegacyHero(final SpiderGirlLegacyHero card) { + super(card); + } + + @Override + public SpiderGirlLegacyHero copy() { + return new SpiderGirlLegacyHero(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpiderManIndia.java b/Mage.Sets/src/mage/cards/s/SpiderManIndia.java new file mode 100644 index 00000000000..24f088ca819 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpiderManIndia.java @@ -0,0 +1,58 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.WebSlingingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpiderManIndia extends CardImpl { + + public SpiderManIndia(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIDER); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.HERO); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Web-slinging {1}{G}{W} + this.addAbility(new WebSlingingAbility(this, "{1}{G}{W}")); + + // Pavitr's Seva -- Whenever you cast a creature spell, put a +1/+1 counter on target creature you control. It gains flying until end of turn. + Ability ability = new SpellCastControllerTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), + StaticFilters.FILTER_SPELL_A_CREATURE, false + ); + ability.addEffect(new GainAbilityTargetEffect(FlyingAbility.getInstance()) + .setText("It gains flying until end of turn")); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability.withFlavorWord("Pavitr's Seva")); + } + + private SpiderManIndia(final SpiderManIndia card) { + super(card); + } + + @Override + public SpiderManIndia copy() { + return new SpiderManIndia(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpiderManNoMore.java b/Mage.Sets/src/mage/cards/s/SpiderManNoMore.java new file mode 100644 index 00000000000..3ea997a0b9e --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpiderManNoMore.java @@ -0,0 +1,58 @@ +package mage.cards.s; + +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.BecomesCreatureAttachedEffect; +import mage.abilities.keyword.DefenderAbility; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.permanent.token.custom.CreatureToken; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class SpiderManNoMore extends CardImpl { + + public SpiderManNoMore(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(new EnchantAbility(auraTarget)); + + // Enchanted creature is a Citizen with base power and toughness 1/1. It has defender and loses all other abilities. + Effect effect = new BecomesCreatureAttachedEffect( + new CreatureToken(1, 1, "Citizen with base power and toughness 1/1") + .withSubType(SubType.CITIZEN) + .withAbility(DefenderAbility.getInstance()), + "Enchanted creature is a Citizen with base power and toughness 1/1. It has defender and loses all other abilities", + Duration.WhileOnBattlefield, + BecomesCreatureAttachedEffect.LoseType.ABILITIES_SUBTYPE + ); + this.addAbility(new SimpleStaticAbility(effect)); + } + + private SpiderManNoMore(final SpiderManNoMore card) { + super(card); + } + + @Override + public SpiderManNoMore copy() { + return new SpiderManNoMore(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpiderManifestation.java b/Mage.Sets/src/mage/cards/s/SpiderManifestation.java new file mode 100644 index 00000000000..f4aa3a9ade6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpiderManifestation.java @@ -0,0 +1,57 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.UntapSourceEffect; +import mage.abilities.keyword.ReachAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.abilities.mana.RedManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.ManaValuePredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpiderManifestation extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("a spell with mana value 4 or greater"); + + static { + filter.add(new ManaValuePredicate(ComparisonType.MORE_THAN, 3)); + } + + public SpiderManifestation(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R/G}"); + + this.subtype.add(SubType.SPIDER); + this.subtype.add(SubType.AVATAR); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Reach + this.addAbility(ReachAbility.getInstance()); + + // {T}: Add {R} or {G}. + this.addAbility(new RedManaAbility()); + this.addAbility(new GreenManaAbility()); + + // Whenever you cast a spell with mana value 4 or greater, untap this creature. + this.addAbility(new SpellCastControllerTriggeredAbility(new UntapSourceEffect(), filter, false)); + } + + private SpiderManifestation(final SpiderManifestation card) { + super(card); + } + + @Override + public SpiderManifestation copy() { + return new SpiderManifestation(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpiderMobile.java b/Mage.Sets/src/mage/cards/s/SpiderMobile.java new file mode 100644 index 00000000000..3d2c86c7e69 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpiderMobile.java @@ -0,0 +1,62 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.AttacksOrBlocksTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.keyword.CrewAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.common.FilterControlledCreaturePermanent; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class SpiderMobile extends CardImpl { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("Spider you control"); + + static { + filter.add(SubType.SPIDER.getPredicate()); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter); + + public SpiderMobile(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}"); + + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Whenever this Vehicle attacks or blocks, it gets +1/+1 until end of turn for each Spider you control. + this.addAbility(new AttacksOrBlocksTriggeredAbility( + new BoostSourceEffect(xValue, xValue, Duration.EndOfTurn), + false + )); + + // Crew 2 + this.addAbility(new CrewAbility(2)); + + } + + private SpiderMobile(final SpiderMobile card) { + super(card); + } + + @Override + public SpiderMobile copy() { + return new SpiderMobile(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpiderPunk.java b/Mage.Sets/src/mage/cards/s/SpiderPunk.java new file mode 100644 index 00000000000..5c9245b121d --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpiderPunk.java @@ -0,0 +1,157 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.continuous.DamageCantBePreventedEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.RiotAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentToken; +import mage.game.stack.Spell; +import mage.game.stack.StackAbility; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class SpiderPunk extends CardImpl { + + static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.SPIDER, "Spiders you control"); + + public SpiderPunk(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIDER); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.HERO); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Riot + this.addAbility(new RiotAbility()); + + // Other Spiders you control have riot. + Ability ability = new SimpleStaticAbility(new SpiderPunkRiotETBEffect()); + ability.addEffect(new GainAbilityControlledEffect(new RiotAbility(), Duration.WhileOnBattlefield, filter, true) + .setText("")); + this.addAbility(ability); + + // Spells and abilities can't be countered. + this.addAbility(new SimpleStaticAbility(new SpiderPunkCantCounterEffect())); + + // Damage can't be prevented. + this.addAbility(new SimpleStaticAbility(new DamageCantBePreventedEffect(Duration.WhileOnBattlefield))); + } + + private SpiderPunk(final SpiderPunk card) { + super(card); + } + + @Override + public SpiderPunk copy() { + return new SpiderPunk(this); + } +} + +class SpiderPunkCantCounterEffect extends ContinuousRuleModifyingEffectImpl { + + SpiderPunkCantCounterEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "Spells and abilities can't be countered"; + } + + + private SpiderPunkCantCounterEffect(final SpiderPunkCantCounterEffect effect) { + super(effect); + } + + @Override + public SpiderPunkCantCounterEffect copy() { + return new SpiderPunkCantCounterEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.COUNTER; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Object object = game.getObject(event.getTargetId()); + return object instanceof Spell || object instanceof StackAbility; + } + +} + +//TODO: Remove after fixing continuous effects working on entering permanents +class SpiderPunkRiotETBEffect extends ReplacementEffectImpl { + + SpiderPunkRiotETBEffect() { + super(Duration.WhileOnBattlefield, Outcome.BoostCreature); + staticText = "Other Spiders you control have riot"; + } + + private SpiderPunkRiotETBEffect(SpiderPunkRiotETBEffect effect) { + super(effect); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget(); + return creature != null + && creature.getId() != source.getSourceId() + && creature.isControlledBy(source.getControllerId()) + && creature.isCreature(game) + && !(creature instanceof PermanentToken); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Permanent creature = ((EntersTheBattlefieldEvent) event).getTarget(); + Player player = game.getPlayer(source.getControllerId()); + if (creature == null || player == null) { + return false; + } + if (player.chooseUse( + outcome, "Have " + creature.getLogName() + " enter the battlefield with a +1/+1 counter on it or with haste?", + null, "+1/+1 counter", "Haste", source, game + )) { + game.informPlayers(player.getLogName() + " choose to put a +1/+1 counter on " + creature.getName()); + creature.addCounters(CounterType.P1P1.createInstance(), source.getControllerId(), source, game, event.getAppliedEffects()); + } else { + ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); + effect.setTargetPointer(new FixedTarget(creature.getId(), creature.getZoneChangeCounter(game) + 1)); + game.addEffect(effect, source); + } + return false; + } + + @Override + public SpiderPunkRiotETBEffect copy() { + return new SpiderPunkRiotETBEffect(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/SpiderSense.java b/Mage.Sets/src/mage/cards/s/SpiderSense.java new file mode 100644 index 00000000000..1b75f437dcc --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpiderSense.java @@ -0,0 +1,61 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.abilities.keyword.WebSlingingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.FilterStackObject; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.game.stack.StackObject; +import mage.target.TargetStackObject; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpiderSense extends CardImpl { + + private static final FilterStackObject filter + = new FilterStackObject("instant spell, sorcery spell, or triggered ability"); + + static { + filter.add(SpiderSensePredicate.instance); + } + + public SpiderSense(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); + + // Web-slinging {U} + this.addAbility(new WebSlingingAbility(this, "{U}")); + + // Counter target instant spell, sorcery spell, or triggered ability. + this.getSpellAbility().addEffect(new CounterTargetEffect()); + this.getSpellAbility().addTarget(new TargetStackObject(filter)); + } + + private SpiderSense(final SpiderSense card) { + super(card); + } + + @Override + public SpiderSense copy() { + return new SpiderSense(this); + } +} + +enum SpiderSensePredicate implements Predicate { + instance; + + @Override + public boolean apply(StackObject input, Game game) { + if (input instanceof Spell) { + return input.isInstantOrSorcery(game); + } + return input instanceof Ability && ((Ability) input).isTriggeredAbility(); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpiderSlayerHatredHoned.java b/Mage.Sets/src/mage/cards/s/SpiderSlayerHatredHoned.java new file mode 100644 index 00000000000..a5640cac784 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpiderSlayerHatredHoned.java @@ -0,0 +1,68 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsDamageToACreatureTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.ExileSourceFromGraveCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.permanent.token.RobotFlyingToken; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class SpiderSlayerHatredHoned extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a Spider"); + + static { + filter.add(SubType.SPIDER.getPredicate()); + } + + public SpiderSlayerHatredHoned(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Whenever Spider-Slayer deals damage to a Spider, destroy that creature. + this.addAbility(new DealsDamageToACreatureTriggeredAbility( + new DestroyTargetEffect(), + false, + false, + true, + filter + )); + + // {6}, Exile this card from your graveyard: Create two tapped 1/1 colorless Robot artifact creature tokens with flying. + Ability ability = new SimpleActivatedAbility( + new CreateTokenEffect(new RobotFlyingToken(), 2, true), + new ManaCostsImpl<>("{6}") + ); + ability.addCost(new ExileSourceFromGraveCost()); + this.addAbility(ability); + } + + private SpiderSlayerHatredHoned(final SpiderSlayerHatredHoned card) { + super(card); + } + + @Override + public SpiderSlayerHatredHoned copy() { + return new SpiderSlayerHatredHoned(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpiderSuit.java b/Mage.Sets/src/mage/cards/s/SpiderSuit.java new file mode 100644 index 00000000000..fdea7d04ab2 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpiderSuit.java @@ -0,0 +1,48 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.continuous.AddCardSubtypeAttachedEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpiderSuit extends CardImpl { + + public SpiderSuit(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Equipped creature gets +2/+2 and is a Spider Hero in addition to its other types. + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(2, 2)); + ability.addEffect(new AddCardSubtypeAttachedEffect( + SubType.SPIDER, AttachmentType.EQUIPMENT + ).setText("and is a Spider")); + ability.addEffect(new AddCardSubtypeAttachedEffect( + SubType.HERO, AttachmentType.EQUIPMENT + ).setText("Hero in addition to its other types")); + this.addAbility(ability); + + // Equip {3} + this.addAbility(new EquipAbility(3)); + } + + private SpiderSuit(final SpiderSuit card) { + super(card); + } + + @Override + public SpiderSuit copy() { + return new SpiderSuit(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpiderUK.java b/Mage.Sets/src/mage/cards/s/SpiderUK.java new file mode 100644 index 00000000000..486e15eef8d --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpiderUK.java @@ -0,0 +1,111 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.keyword.WebSlingingAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.WatcherScope; +import mage.game.Controllable; +import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; +import mage.util.CardUtil; +import mage.watchers.Watcher; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpiderUK extends CardImpl { + + public SpiderUK(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIDER); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.HERO); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Web-slinging {2}{W} + this.addAbility(new WebSlingingAbility(this, "{2}{W}")); + + // At the beginning of your end step, if two or more creatures entered the battlefield under your control this turn, you draw a card and gain 2 life. + Ability ability = new BeginningOfEndStepTriggeredAbility( + new DrawCardSourceControllerEffect(1, true) + ).withInterveningIf(SpiderUKCondition.instance); + ability.addEffect(new GainLifeEffect(2).setText("and gain 2 life")); + this.addAbility(ability, new SpiderUKWatcher()); + } + + private SpiderUK(final SpiderUK card) { + super(card); + } + + @Override + public SpiderUK copy() { + return new SpiderUK(this); + } +} + +enum SpiderUKCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + return SpiderUKWatcher.checkPlayer(game, source); + } + + @Override + public String toString() { + return "two or more creatures entered the battlefield under your control this turn"; + } +} + +class SpiderUKWatcher extends Watcher { + + private final Map map = new HashMap<>(); + + SpiderUKWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.ENTERS_THE_BATTLEFIELD) { + return; + } + Optional.of(event) + .map(EntersTheBattlefieldEvent.class::cast) + .map(EntersTheBattlefieldEvent::getTarget) + .map(Controllable::getControllerId) + .ifPresent(uuid -> map.compute(uuid, CardUtil::setOrIncrementValue)); + } + + @Override + public void reset() { + super.reset(); + map.clear(); + } + + static boolean checkPlayer(Game game, Ability source) { + return game + .getState() + .getWatcher(SpiderUKWatcher.class) + .map + .getOrDefault(source.getControllerId(), 0) >= 2; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpiderVerse.java b/Mage.Sets/src/mage/cards/s/SpiderVerse.java new file mode 100644 index 00000000000..772030ca421 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpiderVerse.java @@ -0,0 +1,113 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ruleModifying.LegendRuleDoesntApplyEffect; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.FilterSpell; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.card.CastFromZonePredicate; +import mage.filter.predicate.mageobject.MageObjectReferencePredicate; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.game.stack.StackObject; +import mage.util.functions.StackObjectCopyApplier; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class SpiderVerse extends CardImpl { + + private static final FilterControlledCreaturePermanent creatureFilter = new FilterControlledCreaturePermanent("Spiders you control"); + + private static final FilterSpell spellFilter = new FilterSpell("a spell from anywhere other than your hand"); + + static { + creatureFilter.add(SubType.SPIDER.getPredicate()); + spellFilter.add(Predicates.not(new CastFromZonePredicate(Zone.HAND))); + } + + public SpiderVerse(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{R}{R}"); + + + // The "legend rule" doesn't apply to Spiders you control. + this.addAbility(new SimpleStaticAbility(new LegendRuleDoesntApplyEffect(creatureFilter))); + + // Whenever you cast a spell from anywhere other than your hand, you may copy it. If you do, you may choose new targets for the copy. If the copy is a permanent spell, it gains haste. Do this only once each turn. + this.addAbility(new SpellCastControllerTriggeredAbility( + new SpiderVerseEffect(), + true + ).setDoOnlyOnceEachTurn(true) + ); + } + + private SpiderVerse(final SpiderVerse card) { + super(card); + } + + @Override + public SpiderVerse copy() { + return new SpiderVerse(this); + } +} + +class SpiderVerseEffect extends OneShotEffect { + + SpiderVerseEffect() { + super(Outcome.Benefit); + staticText = "copy it. If you do, you may choose new targets for the copy. " + + "If the copy is a permanent spell, it gains haste"; + } + + protected SpiderVerseEffect(final SpiderVerseEffect effect) { + super(effect); + } + + @Override + public SpiderVerseEffect copy() { + return new SpiderVerseEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Spell spell = (Spell) getValue("spellCast"); + if (spell == null) { + return false; + } + spell.createCopyOnStack( + game, source, source.getControllerId(), true, + 1, SpiderVerseCopyApplier.instance); + return true; + } +} + +enum SpiderVerseCopyApplier implements StackObjectCopyApplier { + instance; + + @Override + public void modifySpell(StackObject stackObject, Game game) { + if (!stackObject.isPermanent(game)) { + return; + } + Spell spell = (Spell) stackObject; + spell.addAbilityForCopy(HasteAbility.getInstance()); + } + + @Override + public MageObjectReferencePredicate getNextNewTargetType() { + return null; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/SpiderWomanStunningSavior.java b/Mage.Sets/src/mage/cards/s/SpiderWomanStunningSavior.java new file mode 100644 index 00000000000..1d76f864794 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpiderWomanStunningSavior.java @@ -0,0 +1,48 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.PermanentsEnterBattlefieldTappedEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpiderWomanStunningSavior extends CardImpl { + + public SpiderWomanStunningSavior(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W/U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIDER); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.HERO); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Venom Blast -- Artifacts and creatures your opponents control enter tapped. + this.addAbility(new SimpleStaticAbility(new PermanentsEnterBattlefieldTappedEffect( + StaticFilters.FILTER_OPPONENTS_PERMANENT_ARTIFACT_OR_CREATURE + ).setText("artifacts and creatures your opponents control enter tapped"))); + } + + private SpiderWomanStunningSavior(final SpiderWomanStunningSavior card) { + super(card); + } + + @Override + public SpiderWomanStunningSavior copy() { + return new SpiderWomanStunningSavior(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpidersManHeroicHorde.java b/Mage.Sets/src/mage/cards/s/SpidersManHeroicHorde.java new file mode 100644 index 00000000000..9aac68caee9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpidersManHeroicHorde.java @@ -0,0 +1,50 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.common.WebSlingingCondition; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.keyword.WebSlingingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.game.permanent.token.Spider21Token; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpidersManHeroicHorde extends CardImpl { + + public SpidersManHeroicHorde(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIDER); + this.subtype.add(SubType.HERO); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Web-slinging {4}{G}{G} + this.addAbility(new WebSlingingAbility(this, "{4}{G}{G}")); + + // When Spiders-Man enters, if they were cast using web-slinging, you gain 3 life and create two 2/1 green Spider creature tokens with reach. + Ability ability = new EntersBattlefieldTriggeredAbility(new GainLifeEffect(3)).withInterveningIf(WebSlingingCondition.THEY); + ability.addEffect(new CreateTokenEffect(new Spider21Token(), 2).concatBy("and")); + this.addAbility(ability); + } + + private SpidersManHeroicHorde(final SpidersManHeroicHorde card) { + super(card); + } + + @Override + public SpidersManHeroicHorde copy() { + return new SpidersManHeroicHorde(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SpinneretAndSpiderling.java b/Mage.Sets/src/mage/cards/s/SpinneretAndSpiderling.java new file mode 100644 index 00000000000..b4dfef440a3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpinneretAndSpiderling.java @@ -0,0 +1,77 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; +import mage.abilities.common.DealsDamageSourceTriggeredAbility; +import mage.abilities.effects.common.ExileTopXMayPlayUntilEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.events.GameEvent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpinneretAndSpiderling extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.SPIDER, "Spiders"); + + public SpinneretAndSpiderling(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIDER); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.HERO); + this.power = new MageInt(1); + this.toughness = new MageInt(2); + + // Whenever you attack with two or more Spiders, put a +1/+1 counter on Spinneret and Spiderling. + this.addAbility(new AttacksWithCreaturesTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance()), 2, filter + )); + + // Whenever Spinneret and Spiderling deals 4 or more damage, exile the top card of your library. Until the end of your next turn, you may play that card. + this.addAbility(new SpinneretAndSpiderlingTriggeredAbility()); + } + + private SpinneretAndSpiderling(final SpinneretAndSpiderling card) { + super(card); + } + + @Override + public SpinneretAndSpiderling copy() { + return new SpinneretAndSpiderling(this); + } +} + +class SpinneretAndSpiderlingTriggeredAbility extends DealsDamageSourceTriggeredAbility { + + SpinneretAndSpiderlingTriggeredAbility() { + super(new ExileTopXMayPlayUntilEffect(1, Duration.UntilEndOfYourNextTurn)); + setTriggerPhrase("Whenever {this} deals 4 or more damage, "); + } + + private SpinneretAndSpiderlingTriggeredAbility(final SpinneretAndSpiderlingTriggeredAbility ability) { + super(ability); + } + + @Override + public SpinneretAndSpiderlingTriggeredAbility copy() { + return new SpinneretAndSpiderlingTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return super.checkTrigger(event, game) && event.getAmount() >= 4; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SteelWreckingBall.java b/Mage.Sets/src/mage/cards/s/SteelWreckingBall.java new file mode 100644 index 00000000000..39c98d94484 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SteelWreckingBall.java @@ -0,0 +1,50 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Zone; +import mage.target.common.TargetArtifactPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class SteelWreckingBall extends CardImpl { + + public SteelWreckingBall(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{5}"); + + + // When this artifact enters, it deals 5 damage to target creature. + Ability ability = new EntersBattlefieldTriggeredAbility(new DamageTargetEffect(5, "it")); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + + // {1}{R}, Discard this card: Destroy target artifact. + Ability discardAbility = new SimpleActivatedAbility(Zone.HAND, new DestroyTargetEffect(), + new ManaCostsImpl<>("{1}{R}")); + discardAbility.addCost(new DiscardSourceCost()); + discardAbility.addTarget(new TargetArtifactPermanent()); + this.addAbility(discardAbility); + } + + private SteelWreckingBall(final SteelWreckingBall card) { + super(card); + } + + @Override + public SteelWreckingBall copy() { + return new SteelWreckingBall(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/StingerbackTerror.java b/Mage.Sets/src/mage/cards/s/StingerbackTerror.java index 78a699c7efd..c66d7ea6e9e 100644 --- a/Mage.Sets/src/mage/cards/s/StingerbackTerror.java +++ b/Mage.Sets/src/mage/cards/s/StingerbackTerror.java @@ -22,7 +22,7 @@ import java.util.UUID; */ public final class StingerbackTerror extends CardImpl { - private static final DynamicValue xValue = new SignInversionDynamicValue(CardsInControllerHandCount.ANY); + private static final DynamicValue xValue = new SignInversionDynamicValue(CardsInControllerHandCount.ANY_SINGULAR); public StingerbackTerror(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); diff --git a/Mage.Sets/src/mage/cards/s/StoneIdolTrap.java b/Mage.Sets/src/mage/cards/s/StoneIdolTrap.java index 1cf094a6191..f82c02e03b6 100644 --- a/Mage.Sets/src/mage/cards/s/StoneIdolTrap.java +++ b/Mage.Sets/src/mage/cards/s/StoneIdolTrap.java @@ -104,7 +104,7 @@ class StoneIdolTrapEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { CreateTokenEffect effect = new CreateTokenEffect(new StoneIdolToken()); if (effect.apply(game, source)) { - effect.exileTokensCreatedAtNextEndStep(game, source); + effect.removeTokensCreatedAt(game, source, false, PhaseStep.END_TURN, TargetController.YOU); return true; } return false; diff --git a/Mage.Sets/src/mage/cards/s/StonespeakerCrystal.java b/Mage.Sets/src/mage/cards/s/StonespeakerCrystal.java index ba1cab38591..c9e124b3dae 100644 --- a/Mage.Sets/src/mage/cards/s/StonespeakerCrystal.java +++ b/Mage.Sets/src/mage/cards/s/StonespeakerCrystal.java @@ -6,21 +6,15 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.ExileGraveyardAllTargetPlayerEffect; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Zone; -import mage.game.Game; -import mage.players.Player; import mage.target.TargetPlayer; -import java.util.Objects; import java.util.UUID; /** @@ -35,7 +29,7 @@ public final class StonespeakerCrystal extends CardImpl { this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(2), new TapSourceCost())); // {2}, {T}, Sacrifice Stonespeaker Crystal: Exile any number of target players' graveyards. Draw a card. - Ability ability = new SimpleActivatedAbility(new StonespeakerCrystalEffect(), new GenericManaCost(2)); + Ability ability = new SimpleActivatedAbility(new ExileGraveyardAllTargetPlayerEffect(), new GenericManaCost(2)); ability.addEffect(new DrawCardSourceControllerEffect(1)); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); @@ -52,38 +46,3 @@ public final class StonespeakerCrystal extends CardImpl { return new StonespeakerCrystal(this); } } - -class StonespeakerCrystalEffect extends OneShotEffect { - - StonespeakerCrystalEffect() { - super(Outcome.Benefit); - staticText = "exile any number of target players' graveyards"; - } - - private StonespeakerCrystalEffect(final StonespeakerCrystalEffect effect) { - super(effect); - } - - @Override - public StonespeakerCrystalEffect copy() { - return new StonespeakerCrystalEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { - return false; - } - Cards cards = new CardsImpl(); - this.getTargetPointer() - .getTargets(game, source) - .stream() - .map(game::getPlayer) - .filter(Objects::nonNull) - .map(Player::getGraveyard) - .forEach(cards::addAll); - controller.moveCards(cards, Zone.EXILED, source, game); - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/s/StormFleetAerialist.java b/Mage.Sets/src/mage/cards/s/StormFleetAerialist.java index b433435d668..de4a2bef003 100644 --- a/Mage.Sets/src/mage/cards/s/StormFleetAerialist.java +++ b/Mage.Sets/src/mage/cards/s/StormFleetAerialist.java @@ -35,7 +35,7 @@ public final class StormFleetAerialist extends CardImpl { // Raid - Storm Fleet Aerialist enters the battlefield with a +1/+1 counter on it if you attacked this turn. this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(1), false), RaidCondition.instance, - "Raid — {this} enters with a +1/+1 counter on it if you attacked this turn.", + "{this} enters with a +1/+1 counter on it if you attacked this turn.", "{this} enters with a +1/+1 counter") .setAbilityWord(AbilityWord.RAID) .addHint(RaidHint.instance), diff --git a/Mage.Sets/src/mage/cards/s/StrengthOfWill.java b/Mage.Sets/src/mage/cards/s/StrengthOfWill.java new file mode 100644 index 00000000000..a17711346d0 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StrengthOfWill.java @@ -0,0 +1,45 @@ +package mage.cards.s; + +import mage.abilities.common.DealtDamageToSourceTriggeredAbility; +import mage.abilities.dynamicvalue.common.SavedDamageValue; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.counters.CounterType; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class StrengthOfWill extends CardImpl { + + public StrengthOfWill(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); + + + // Until end of turn, target creature you control gains indestructible and "Whenever this creature is dealt damage, put that many +1/+1 counters on it." + this.getSpellAbility().addEffect(new GainAbilityTargetEffect(IndestructibleAbility.getInstance()) + .setText("Until end of turn, target creature you control gains indestructible")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + new DealtDamageToSourceTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(),SavedDamageValue.MANY), false)) + .setText("\"Whenever this creature is dealt damage, put that many +1/+1 counters on it.\"") + .concatBy("and") + ); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + } + + private StrengthOfWill(final StrengthOfWill card) { + super(card); + } + + @Override + public StrengthOfWill copy() { + return new StrengthOfWill(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/StrionicResonator.java b/Mage.Sets/src/mage/cards/s/StrionicResonator.java index 7154eed7fb6..c521659aad9 100644 --- a/Mage.Sets/src/mage/cards/s/StrionicResonator.java +++ b/Mage.Sets/src/mage/cards/s/StrionicResonator.java @@ -10,7 +10,10 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.TargetController; import mage.filter.FilterStackObject; -import mage.target.common.TargetTriggeredAbility; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.stack.StackObject; +import mage.target.TargetStackObject; import java.util.UUID; @@ -22,6 +25,7 @@ public final class StrionicResonator extends CardImpl { private static final FilterStackObject filter = new FilterStackObject("triggered ability you control"); static { + filter.add(StrionicResonatorPredicate.instance); filter.add(TargetController.YOU.getControllerPredicate()); } @@ -31,7 +35,7 @@ public final class StrionicResonator extends CardImpl { // {2}, {T}: Copy target triggered ability you control. You may choose new targets for the copy. Ability ability = new SimpleActivatedAbility(new CopyTargetStackObjectEffect(), new ManaCostsImpl<>("{2}")); ability.addCost(new TapSourceCost()); - ability.addTarget(new TargetTriggeredAbility(filter)); + ability.addTarget(new TargetStackObject(filter)); this.addAbility(ability); } @@ -44,3 +48,12 @@ public final class StrionicResonator extends CardImpl { return new StrionicResonator(this); } } + +enum StrionicResonatorPredicate implements Predicate { + instance; + + @Override + public boolean apply(StackObject input, Game game) { + return input instanceof Ability && ((Ability) input).isTriggeredAbility(); + } +} diff --git a/Mage.Sets/src/mage/cards/s/Submerge.java b/Mage.Sets/src/mage/cards/s/Submerge.java index 1316d639014..7ae4bf53185 100644 --- a/Mage.Sets/src/mage/cards/s/Submerge.java +++ b/Mage.Sets/src/mage/cards/s/Submerge.java @@ -34,7 +34,7 @@ public final class Submerge extends CardImpl { // If an opponent controls a Forest and you control an Island, you may cast this spell without paying its mana cost. - Condition condition = new CompoundCondition("If an opponent controls a Forest and you control an Island", + Condition condition = new CompoundCondition("an opponent controls a Forest and you control an Island", new OpponentControlsPermanentCondition(filterForest), new PermanentsOnTheBattlefieldCondition(filterIsland)); this.addAbility(new AlternativeCostSourceAbility(null, condition)); diff --git a/Mage.Sets/src/mage/cards/s/SuburbanSanctuary.java b/Mage.Sets/src/mage/cards/s/SuburbanSanctuary.java new file mode 100644 index 00000000000..efebac766ed --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SuburbanSanctuary.java @@ -0,0 +1,46 @@ +package mage.cards.s; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.keyword.SurveilEffect; +import mage.abilities.mana.GreenManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SuburbanSanctuary extends CardImpl { + + public SuburbanSanctuary(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // This land enters tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {G} or {W}. + this.addAbility(new GreenManaAbility()); + this.addAbility(new WhiteManaAbility()); + + // {4}, {T}: Surveil 1. + Ability ability = new SimpleActivatedAbility(new SurveilEffect(1), new GenericManaCost(4)); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private SuburbanSanctuary(final SuburbanSanctuary card) { + super(card); + } + + @Override + public SuburbanSanctuary copy() { + return new SuburbanSanctuary(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SubwayTrain.java b/Mage.Sets/src/mage/cards/s/SubwayTrain.java new file mode 100644 index 00000000000..152f21e40cb --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SubwayTrain.java @@ -0,0 +1,47 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.abilities.keyword.CrewAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SubwayTrain extends CardImpl { + + public SubwayTrain(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); + + this.subtype.add(SubType.VEHICLE); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // When this Vehicle enters, you may pay {G}. If you do, search your library for a basic land card, reveal it, put it into your hand, then shuffle. + this.addAbility(new EntersBattlefieldTriggeredAbility(new DoIfCostPaid(new SearchLibraryPutInHandEffect( + new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true + ), new ManaCostsImpl<>("{G}")))); + + // Crew 2 + this.addAbility(new CrewAbility(2)); + } + + private SubwayTrain(final SubwayTrain card) { + super(card); + } + + @Override + public SubwayTrain copy() { + return new SubwayTrain(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SuddenStrike.java b/Mage.Sets/src/mage/cards/s/SuddenStrike.java new file mode 100644 index 00000000000..5c3cd824dd3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SuddenStrike.java @@ -0,0 +1,32 @@ +package mage.cards.s; + +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.target.common.TargetAttackingOrBlockingCreature; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SuddenStrike extends CardImpl { + + public SuddenStrike(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}"); + + // Destroy target attacking or blocking creature. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetAttackingOrBlockingCreature()); + } + + private SuddenStrike(final SuddenStrike card) { + super(card); + } + + @Override + public SuddenStrike copy() { + return new SuddenStrike(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SummonBrynhildr.java b/Mage.Sets/src/mage/cards/s/SummonBrynhildr.java new file mode 100644 index 00000000000..c78cc9eb20d --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SummonBrynhildr.java @@ -0,0 +1,194 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.SagaAbility; +import mage.abilities.common.delayed.CastNextSpellDelayedTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.keyword.HasteAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; +import mage.watchers.Watcher; + +import java.util.*; + +/** + * @author TheElk801 + */ +public final class SummonBrynhildr extends CardImpl { + + public SummonBrynhildr(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.SAGA); + this.subtype.add(SubType.KNIGHT); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I -- Chain -- Exile the top card of your library. During any turn you put a lore counter on this Saga, you may play that card. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, ability -> { + ability.addEffect(new SummonBrynhildrExileEffect()); + ability.withFlavorWord("Chain"); + }); + + // II, III -- Gestalt Mode -- When you next cast a creature spell this turn, it gains haste until end of turn. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II, SagaChapter.CHAPTER_III, ability -> { + ability.addEffect(new CreateDelayedTriggeredAbilityEffect( + new CastNextSpellDelayedTriggeredAbility( + new SummonBrynhildrHasteEffect(), StaticFilters.FILTER_SPELL_A_CREATURE, true + ) + )); + ability.withFlavorWord("Gestalt Mode"); + }); + this.addAbility(sagaAbility.addHint(SummonBrynhildrCondition.getHint()), new SummonBrynhildrWatcher()); + } + + private SummonBrynhildr(final SummonBrynhildr card) { + super(card); + } + + @Override + public SummonBrynhildr copy() { + return new SummonBrynhildr(this); + } +} + +class SummonBrynhildrExileEffect extends OneShotEffect { + + SummonBrynhildrExileEffect() { + super(Outcome.Benefit); + staticText = "exile the top card of your library. During any turn you " + + "put a lore counter on this Saga, you may play that card"; + } + + private SummonBrynhildrExileEffect(final SummonBrynhildrExileEffect effect) { + super(effect); + } + + @Override + public SummonBrynhildrExileEffect copy() { + return new SummonBrynhildrExileEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Card card = player.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + player.moveCards(card, Zone.EXILED, source, game); + CardUtil.makeCardPlayable( + game, source, card, false, Duration.Custom, false, + source.getControllerId(), SummonBrynhildrCondition.instance + ); + return true; + } +} + +enum SummonBrynhildrCondition implements Condition { + instance; + private static final Hint hint = new ConditionHint(instance); + + public static Hint getHint() { + return hint; + } + + @Override + public boolean apply(Game game, Ability source) { + return SummonBrynhildrWatcher.check(game, source); + } + + @Override + public String toString() { + return "You put a lore counter on this permanent this turn"; + } +} + +class SummonBrynhildrWatcher extends Watcher { + + private final Map> map = new HashMap<>(); + + SummonBrynhildrWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.COUNTER_ADDED + || !CounterType.STUN.getName().equals(event.getData())) { + return; + } + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null) { + map.computeIfAbsent(new MageObjectReference(permanent, game), x -> new HashSet<>()) + .add(event.getPlayerId()); + } + } + + @Override + public void reset() { + super.reset(); + map.clear(); + } + + static boolean check(Game game, Ability source) { + return game.getState() + .getWatcher(SummonBrynhildrWatcher.class) + .map + .getOrDefault(new MageObjectReference( + source.getSourceId(), source.getStackMomentSourceZCC(), game + ), Collections.emptySet()) + .contains(source.getControllerId()); + } +} + +class SummonBrynhildrHasteEffect extends OneShotEffect { + + SummonBrynhildrHasteEffect() { + super(Outcome.Benefit); + staticText = "it gains haste until end of turn"; + } + + private SummonBrynhildrHasteEffect(final SummonBrynhildrHasteEffect effect) { + super(effect); + } + + @Override + public SummonBrynhildrHasteEffect copy() { + return new SummonBrynhildrHasteEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Spell spell = (Spell) getValue("spellCast"); + if (spell != null) { + game.getState().addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance()) + .setTargetPointer(new FixedTarget(spell.getCard().getId())), source); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SummonGFCerberus.java b/Mage.Sets/src/mage/cards/s/SummonGFCerberus.java index 65592498f6d..ba6431f9aff 100644 --- a/Mage.Sets/src/mage/cards/s/SummonGFCerberus.java +++ b/Mage.Sets/src/mage/cards/s/SummonGFCerberus.java @@ -1,21 +1,16 @@ package mage.cards.s; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.SagaAbility; import mage.abilities.common.delayed.CopyNextSpellDelayedTriggeredAbility; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.keyword.SurveilEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SagaChapter; import mage.constants.SubType; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.stack.Spell; import java.util.UUID; @@ -47,9 +42,7 @@ public final class SummonGFCerberus extends CardImpl { // III -- Triple -- When you next cast an instant or sorcery spell this turn, copy it twice. You may choose new targets for the copies. sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, ability -> { ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new CopyNextSpellDelayedTriggeredAbility( - StaticFilters.FILTER_SPELL_INSTANT_OR_SORCERY, new SummonGFCerberusEffect(), - "When you next cast an instant or sorcery spell this turn, " + - "copy it twice. You may choose new targets for the copies." + StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, 2 ))); ability.withFlavorWord("Triple"); }); @@ -65,29 +58,3 @@ public final class SummonGFCerberus extends CardImpl { return new SummonGFCerberus(this); } } - -class SummonGFCerberusEffect extends OneShotEffect { - - SummonGFCerberusEffect() { - super(Outcome.Benefit); - } - - private SummonGFCerberusEffect(final SummonGFCerberusEffect effect) { - super(effect); - } - - @Override - public SummonGFCerberusEffect copy() { - return new SummonGFCerberusEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Spell spell = (Spell) getValue("spellCast"); - if (spell != null) { - spell.createCopyOnStack(game, source, source.getControllerId(), true, 2); - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/s/SummoningTrap.java b/Mage.Sets/src/mage/cards/s/SummoningTrap.java index e9307e9c36a..d99dac32570 100644 --- a/Mage.Sets/src/mage/cards/s/SummoningTrap.java +++ b/Mage.Sets/src/mage/cards/s/SummoningTrap.java @@ -63,7 +63,7 @@ enum SummoningTrapCondition implements Condition { @Override public String toString() { - return "If a creature spell you cast this turn was countered by a spell or ability an opponent controlled"; + return "a creature spell you cast this turn was countered by a spell or ability an opponent controlled"; } } diff --git a/Mage.Sets/src/mage/cards/s/SunSpiderNimbleWebber.java b/Mage.Sets/src/mage/cards/s/SunSpiderNimbleWebber.java new file mode 100644 index 00000000000..618c633b206 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SunSpiderNimbleWebber.java @@ -0,0 +1,67 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.MyTurnCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SunSpiderNimbleWebber extends CardImpl { + + private static final FilterCard filter = new FilterCard("an Aura or Equipment card"); + + static { + filter.add(Predicates.or( + SubType.AURA.getPredicate(), + SubType.EQUIPMENT.getPredicate() + )); + } + + public SunSpiderNimbleWebber(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W/U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIDER); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.HERO); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // During your turn, Sun-Spider has flying. + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield), + MyTurnCondition.instance, "during your turn, {this} has flying" + ))); + + // When Sun-Spider enters, search your library for an Aura or Equipment card, reveal it, put it into your hand, then shuffle. + this.addAbility(new EntersBattlefieldTriggeredAbility( + new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true) + )); + } + + private SunSpiderNimbleWebber(final SunSpiderNimbleWebber card) { + super(card); + } + + @Override + public SunSpiderNimbleWebber copy() { + return new SunSpiderNimbleWebber(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SuperiorFoesOfSpiderMan.java b/Mage.Sets/src/mage/cards/s/SuperiorFoesOfSpiderMan.java new file mode 100644 index 00000000000..7de71b2b575 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SuperiorFoesOfSpiderMan.java @@ -0,0 +1,57 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.ExileTopCardPlayUntilExileAnotherEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.ManaValuePredicate; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class SuperiorFoesOfSpiderMan extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("a spell with mana value 4 or greater"); + + static { + filter.add(new ManaValuePredicate(ComparisonType.OR_GREATER, 4)); + } + + public SuperiorFoesOfSpiderMan(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ROGUE); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Whenever you cast a spell with mana value 4 or greater, you may exile the top card of your library. If you do, you may play that card until you exile another card with this creature. + this.addAbility(new SpellCastControllerTriggeredAbility( + new ExileTopCardPlayUntilExileAnotherEffect(true), + filter, + true + )); + } + + private SuperiorFoesOfSpiderMan(final SuperiorFoesOfSpiderMan card) { + super(card); + } + + @Override + public SuperiorFoesOfSpiderMan copy() { + return new SuperiorFoesOfSpiderMan(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SuperiorSpiderMan.java b/Mage.Sets/src/mage/cards/s/SuperiorSpiderMan.java new file mode 100644 index 00000000000..1e7959688c6 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SuperiorSpiderMan.java @@ -0,0 +1,114 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CopyEffect; +import mage.abilities.effects.common.ExileTargetEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.common.FilterCreatureCard; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentCard; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetCardInGraveyard; +import mage.target.targetpointer.FixedTarget; +import mage.util.functions.CopyApplier; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class SuperiorSpiderMan extends CardImpl { + + public SuperiorSpiderMan(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SPIDER); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.HERO); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Mind Swap -- You may have Superior Spider-Man enter as a copy of any creature card in a graveyard, except his name is Superior Spider-Man and he's a 4/4 Spider Human Hero in addition to his other types. When you do, exile that card. + this.addAbility(new EntersBattlefieldAbility(new SuperiorSpiderManCopyEffect(), true)); + } + + private SuperiorSpiderMan(final SuperiorSpiderMan card) { + super(card); + } + + @Override + public SuperiorSpiderMan copy() { + return new SuperiorSpiderMan(this); + } +} + +class SuperiorSpiderManCopyEffect extends OneShotEffect { + + SuperiorSpiderManCopyEffect() { + super(Outcome.Copy); + this.staticText = "as a copy of any creature card in a graveyard, except his name is Superior Spider-Man " + + "and he's a 4/4 Spider Human Hero in addition to his other types. When you do, exile that card."; + } + + private SuperiorSpiderManCopyEffect(final SuperiorSpiderManCopyEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player != null) { + Target target = new TargetCardInGraveyard(new FilterCreatureCard("creature card in a graveyard")); + target.withNotTarget(true); + if (target.canChoose(source.getControllerId(), source, game)) { + player.choose(outcome, target, source, game); + Card copyFromCard = game.getCard(target.getFirstTarget()); + if (copyFromCard != null) { + Permanent newBluePrint = new PermanentCard(copyFromCard, source.getControllerId(), game); + newBluePrint.assignNewId(); + SuperiorSpiderManCopyApplier applier = new SuperiorSpiderManCopyApplier(); + applier.apply(game, newBluePrint, source, source.getSourceId()); + CopyEffect copyEffect = new CopyEffect(Duration.Custom, newBluePrint, source.getSourceId()); + game.addEffect(copyEffect, source); + + ReflexiveTriggeredAbility triggeredAbility = new ReflexiveTriggeredAbility( + new ExileTargetEffect().setTargetPointer(new FixedTarget(copyFromCard.getId())), + false + ); + game.fireReflexiveTriggeredAbility(triggeredAbility, source); + } + } + return true; + } + return false; + } + + @Override + public SuperiorSpiderManCopyEffect copy() { + return new SuperiorSpiderManCopyEffect(this); + } +} + +class SuperiorSpiderManCopyApplier extends CopyApplier { + + @Override + public boolean apply(Game game, MageObject blueprint, Ability source, UUID targetObjectId) { + blueprint.setName("Superior Spider-Man"); + blueprint.getPower().setModifiedBaseValue(4); + blueprint.getToughness().setModifiedBaseValue(4); + blueprint.addSubType(SubType.SPIDER, SubType.HUMAN, SubType.HERO); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/s/SupportiveParents.java b/Mage.Sets/src/mage/cards/s/SupportiveParents.java new file mode 100644 index 00000000000..22a8f5701bd --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SupportiveParents.java @@ -0,0 +1,41 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.costs.common.TapTargetCost; +import mage.abilities.mana.AnyColorManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SupportiveParents extends CardImpl { + + public SupportiveParents(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.CITIZEN); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // Tap two untapped creatures you control: Add one mana of any color. + this.addAbility(new AnyColorManaAbility(new TapTargetCost( + 2, StaticFilters.FILTER_CONTROLLED_UNTAPPED_CREATURES + ))); + } + + private SupportiveParents(final SupportiveParents card) { + super(card); + } + + @Override + public SupportiveParents copy() { + return new SupportiveParents(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SwaggeringCorsair.java b/Mage.Sets/src/mage/cards/s/SwaggeringCorsair.java index 2a04ce8f55f..4dbd6a254e8 100644 --- a/Mage.Sets/src/mage/cards/s/SwaggeringCorsair.java +++ b/Mage.Sets/src/mage/cards/s/SwaggeringCorsair.java @@ -32,7 +32,7 @@ public final class SwaggeringCorsair extends CardImpl { this.addAbility(new EntersBattlefieldAbility( new AddCountersSourceEffect(CounterType.P1P1.createInstance(1)), RaidCondition.instance, - "Raid — {this} enters with a +1/+1 counter on it if you attacked this turn.", "") + "{this} enters with a +1/+1 counter on it if you attacked this turn.", "") .setAbilityWord(AbilityWord.RAID) .addHint(RaidHint.instance), new PlayerAttackedWatcher()); diff --git a/Mage.Sets/src/mage/cards/s/SwarmBeingOfBees.java b/Mage.Sets/src/mage/cards/s/SwarmBeingOfBees.java new file mode 100644 index 00000000000..ff0235dacba --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SwarmBeingOfBees.java @@ -0,0 +1,47 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.keyword.FlashAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.MayhemAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SwarmBeingOfBees extends CardImpl { + + public SwarmBeingOfBees(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.INSECT); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Flash + this.addAbility(FlashAbility.getInstance()); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Mayhem {B} + this.addAbility(new MayhemAbility(this, "{B}")); + } + + private SwarmBeingOfBees(final SwarmBeingOfBees card) { + super(card); + } + + @Override + public SwarmBeingOfBees copy() { + return new SwarmBeingOfBees(this); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SymbioteSpiderMan.java b/Mage.Sets/src/mage/cards/s/SymbioteSpiderMan.java new file mode 100644 index 00000000000..188e9d2b69e --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SymbioteSpiderMan.java @@ -0,0 +1,62 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.SavedDamageValue; +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.counters.CounterType; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SymbioteSpiderMan extends CardImpl { + + public SymbioteSpiderMan(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U/B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.SYMBIOTE); + this.subtype.add(SubType.SPIDER); + this.subtype.add(SubType.HERO); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Whenever this creature deals combat damage to a player, look at that many cards from the top of your library. Put one of them into your hand and the rest into your graveyard. + this.addAbility(makeAbility()); + + // Find New Host -- {2}{U/B}, Exile this card from your graveyard: Put a +1/+1 counter on target creature you control. It gains this card's other abilities. Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), new ManaCostsImpl<>("{2}{U/B}") + ); + ability.addEffect(new GainAbilityTargetEffect(makeAbility(), Duration.Custom) + .setText("It gains this card's other abilities")); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability.withFlavorWord("Find New Host")); + } + + private SymbioteSpiderMan(final SymbioteSpiderMan card) { + super(card); + } + + @Override + public SymbioteSpiderMan copy() { + return new SymbioteSpiderMan(this); + } + + private static Ability makeAbility() { + return new DealsCombatDamageToAPlayerTriggeredAbility(new LookLibraryAndPickControllerEffect( + SavedDamageValue.MANY, 1, PutCards.HAND, PutCards.GRAVEYARD + )); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SynodSanctum.java b/Mage.Sets/src/mage/cards/s/SynodSanctum.java index 3f300841cdf..2f4f54cf0ac 100644 --- a/Mage.Sets/src/mage/cards/s/SynodSanctum.java +++ b/Mage.Sets/src/mage/cards/s/SynodSanctum.java @@ -83,7 +83,7 @@ class SynodSanctumEffect extends OneShotEffect { if (getTargetPointer().getFirst(game, source) != null) { Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (permanent != null) { - UUID exileZone = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileZone = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); if (exileZone != null) { controller.moveCardToExileWithInfo(permanent, exileZone, sourceObject.getIdName(), source, game, Zone.BATTLEFIELD, true); } @@ -113,7 +113,7 @@ class SynodSanctumEffect2 extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); ExileZone exileZone = game.getExile().getExileZone(exileId); if (exileZone == null) { return true; diff --git a/Mage.Sets/src/mage/cards/t/TalionTheKindlyLord.java b/Mage.Sets/src/mage/cards/t/TalionTheKindlyLord.java index 19ccefbf0f6..bb9e7e6c6d6 100644 --- a/Mage.Sets/src/mage/cards/t/TalionTheKindlyLord.java +++ b/Mage.Sets/src/mage/cards/t/TalionTheKindlyLord.java @@ -111,7 +111,7 @@ class TalionTheKindlyLordEffect extends OneShotEffect { } int numberChoice = controller.getAmount(1, 10, "Choose a number.", source, game); game.getState().setValue("chosenNumber_" + source.getSourceId() - + '_' + source.getSourceObjectZoneChangeCounter(), numberChoice); + + '_' + source.getStackMomentSourceZCC(), numberChoice); Permanent permanent = game.getPermanentEntering(source.getSourceId()); if (permanent != null) { permanent.addInfo("chosen players", "Chosen Number: " + numberChoice + "", game); diff --git a/Mage.Sets/src/mage/cards/t/TandemTakedown.java b/Mage.Sets/src/mage/cards/t/TandemTakedown.java index 50f7b03cc53..7620676e8b1 100644 --- a/Mage.Sets/src/mage/cards/t/TandemTakedown.java +++ b/Mage.Sets/src/mage/cards/t/TandemTakedown.java @@ -1,18 +1,13 @@ package mage.cards.t; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.TargetsDamageTargetsEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.other.AnotherTargetPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.target.Target; import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; @@ -23,7 +18,7 @@ import java.util.UUID; */ public final class TandemTakedown extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("creature, planeswalker, or battle"); + private static final FilterPermanent filter = new FilterPermanent("another target creature, planeswalker, or battle"); static { filter.add(Predicates.or( @@ -40,9 +35,9 @@ public final class TandemTakedown extends CardImpl { // Up to two target creatures you control each get +1/+0 until end of turn. They each deal damage equal to their power to another target creature, planeswalker, or battle. this.getSpellAbility().addEffect(new BoostTargetEffect(1, 0) .setText("up to two target creatures you control each get +1/+0 until end of turn")); - this.getSpellAbility().addEffect(new TandemTakedownEffect()); + this.getSpellAbility().addEffect(new TargetsDamageTargetsEffect(false)); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent(0, 2).setTargetTag(1)); - this.getSpellAbility().addTarget(new TargetPermanent(filter).setTargetTag(2)); + this.getSpellAbility().addTarget(new TargetPermanent(filter).setTargetTag(3)); } private TandemTakedown(final TandemTakedown card) { @@ -54,48 +49,3 @@ public final class TandemTakedown extends CardImpl { return new TandemTakedown(this); } } - -class TandemTakedownEffect extends OneShotEffect { - - TandemTakedownEffect() { - super(Outcome.Benefit); - staticText = "They each deal damage equal to their power to another target creature, planeswalker, or battle"; - } - - private TandemTakedownEffect(final TandemTakedownEffect effect) { - super(effect); - } - - @Override - public TandemTakedownEffect copy() { - return new TandemTakedownEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - if (source.getTargets().size() < 2) { - return false; - } - - Target damageTarget = source.getTargets().get(0); - Target destTarget = source.getTargets().get(1); - if (damageTarget.getTargets().isEmpty() || destTarget.getTargets().isEmpty()) { - return false; - } - - Permanent permanentDamage1 = damageTarget.getTargets().isEmpty() ? null : game.getPermanent(damageTarget.getTargets().get(0)); - Permanent permanentDamage2 = damageTarget.getTargets().size() < 2 ? null : game.getPermanent(damageTarget.getTargets().get(1)); - Permanent permanentDest = game.getPermanent(destTarget.getTargets().get(0)); - if (permanentDest == null) { - return false; - } - - if (permanentDamage1 != null) { - permanentDest.damage(permanentDamage1.getPower().getValue(), permanentDamage1.getId(), source, game, false, true); - } - if (permanentDamage2 != null) { - permanentDest.damage(permanentDamage2.getPower().getValue(), permanentDamage2.getId(), source, game, false, true); - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/t/TashaTheWitchQueen.java b/Mage.Sets/src/mage/cards/t/TashaTheWitchQueen.java index 41142ca3a90..438e7ddd491 100644 --- a/Mage.Sets/src/mage/cards/t/TashaTheWitchQueen.java +++ b/Mage.Sets/src/mage/cards/t/TashaTheWitchQueen.java @@ -132,7 +132,7 @@ class TashaTheWitchQueenCastEffect extends OneShotEffect { if (player == null) { return false; } - Cards cards = new CardsImpl(game.getExile().getCards(filter, game)); + Cards cards = new CardsImpl(game.getExile().getCardsInRange(filter, source.getControllerId(), source, game)); return !cards.isEmpty() && CardUtil.castSpellWithAttributesForFree(player, source, game, cards, StaticFilters.FILTER_CARD); } } diff --git a/Mage.Sets/src/mage/cards/t/TeferiMageOfZhalfir.java b/Mage.Sets/src/mage/cards/t/TeferiMageOfZhalfir.java index 283a6df888f..898f1e27b30 100644 --- a/Mage.Sets/src/mage/cards/t/TeferiMageOfZhalfir.java +++ b/Mage.Sets/src/mage/cards/t/TeferiMageOfZhalfir.java @@ -87,14 +87,14 @@ class TeferiMageOfZhalfirAddFlashEffect extends ContinuousEffectImpl { } } // in Exile - for (Card card : game.getState().getExile().getAllCards(game)) { - if (card.isOwnedBy(controller.getId()) && card.isCreature(game)) { + for (Card card : game.getState().getExile().getCardsOwned(game, controller.getId())) { + if (card.isCreature(game)) { game.getState().addOtherAbility(card, FlashAbility.getInstance()); } } // in Library (e.g. for Mystical Teachings) for (Card card : controller.getLibrary().getCards(game)) { - if (card.isOwnedBy(controller.getId()) && card.isCreature(game)) { + if (card.isCreature(game)) { game.getState().addOtherAbility(card, FlashAbility.getInstance()); } } diff --git a/Mage.Sets/src/mage/cards/t/TerrificTeamUp.java b/Mage.Sets/src/mage/cards/t/TerrificTeamUp.java new file mode 100644 index 00000000000..ce5d251e0e5 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TerrificTeamUp.java @@ -0,0 +1,66 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.effects.common.TargetsDamageTargetsEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Zone; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class TerrificTeamUp extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("you control a permanent with mana value 4 or greater"); + + static { + filter.add(new ManaValuePredicate(ComparisonType.OR_GREATER, 4)); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter); + private static final Hint hint = new ConditionHint(condition, "You control a permanent with mana value 4 or greater"); + + public TerrificTeamUp(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{G}"); + + + // This spell costs {2} less to cast if you control a permanent with mana value 4 or greater. + Ability ability = new SimpleStaticAbility(Zone.ALL, new SpellCostReductionSourceEffect(2, condition)); + ability.setRuleAtTheTop(true); + ability.addHint(hint); + this.addAbility(ability); + + // One or two target creatures you control each get +1/+0 until end of turn. They each deal damage equal to their power to target creature an opponent controls. + this.getSpellAbility().addEffect(new BoostTargetEffect(1, 0)); + this.getSpellAbility().addEffect(new TargetsDamageTargetsEffect(false)); + this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent(1, 2).setTargetTag(1).withChooseHint("boost and deal damage")); + this.getSpellAbility().addTarget(new TargetOpponentsCreaturePermanent().setTargetTag(3)); + } + + private TerrificTeamUp(final TerrificTeamUp card) { + super(card); + } + + @Override + public TerrificTeamUp copy() { + return new TerrificTeamUp(this); + } +} + + diff --git a/Mage.Sets/src/mage/cards/t/TheAesirEscapeValhalla.java b/Mage.Sets/src/mage/cards/t/TheAesirEscapeValhalla.java index 59e83c71b2d..cb659de5718 100644 --- a/Mage.Sets/src/mage/cards/t/TheAesirEscapeValhalla.java +++ b/Mage.Sets/src/mage/cards/t/TheAesirEscapeValhalla.java @@ -116,7 +116,7 @@ class TheAesirEscapeValhallaTwoEffect extends OneShotEffect { if (controller == null || sourceObject == null) { return false; } - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); ExileZone exileZone = game.getExile().getExileZone(exileId); if (exileZone == null || exileZone.isEmpty()) { return false; @@ -154,7 +154,7 @@ class TheAesirEscapeValhallaThreeEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); ExileZone exileZone = game.getExile().getExileZone(exileId); Player controller = game.getPlayer(source.getControllerId()); Permanent sourcePermanent = game.getPermanent(source.getSourceId()); diff --git a/Mage.Sets/src/mage/cards/t/TheCabbageMerchant.java b/Mage.Sets/src/mage/cards/t/TheCabbageMerchant.java index d79315922f4..afdf6cfa761 100644 --- a/Mage.Sets/src/mage/cards/t/TheCabbageMerchant.java +++ b/Mage.Sets/src/mage/cards/t/TheCabbageMerchant.java @@ -12,9 +12,11 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; +import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.permanent.TappedPredicate; +import mage.filter.predicate.permanent.TokenPredicate; import mage.game.permanent.token.FoodToken; import mage.target.common.TargetControlledPermanent; @@ -27,9 +29,11 @@ public final class TheCabbageMerchant extends CardImpl { private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.FOOD, "untapped Foods you control"); + private static final FilterPermanent filter2 = new FilterControlledPermanent(SubType.FOOD, "Food token"); static { filter.add(TappedPredicate.UNTAPPED); + filter2.add(TokenPredicate.TRUE); } public TheCabbageMerchant(UUID ownerId, CardSetInfo setInfo) { @@ -43,13 +47,13 @@ public final class TheCabbageMerchant extends CardImpl { // Whenever an opponent casts a noncreature spell, create a Food token. this.addAbility(new SpellCastOpponentTriggeredAbility( - new CreateTokenEffect(new FoodToken()), StaticFilters.FILTER_SPELL_NON_CREATURE, false + new CreateTokenEffect(new FoodToken()), StaticFilters.FILTER_SPELL_A_NON_CREATURE, false )); // Whenever a creature deals combat damage to you, sacrifice a Food token. this.addAbility(new DealsDamageToYouAllTriggeredAbility( StaticFilters.FILTER_PERMANENT_CREATURE, - new SacrificeControllerEffect(StaticFilters.FILTER_CONTROLLED_FOOD, 1, ""), true + new SacrificeControllerEffect(filter2, 1, ""), true )); // Tap two untapped Foods you control: Add one mana of any color. diff --git a/Mage.Sets/src/mage/cards/t/TheCloneSaga.java b/Mage.Sets/src/mage/cards/t/TheCloneSaga.java new file mode 100644 index 00000000000..d53ae570f21 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheCloneSaga.java @@ -0,0 +1,108 @@ +package mage.cards.t; + +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.SagaAbility; +import mage.abilities.common.delayed.CastNextSpellDelayedTriggeredAbility; +import mage.abilities.effects.common.ChooseACardNameEffect; +import mage.abilities.effects.common.CopyTargetStackObjectEffect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.keyword.SurveilEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.ChosenNamePredicate; +import mage.game.Game; +import mage.game.events.DamagedPlayerEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.util.functions.RemoveTypeCopyApplier; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class TheCloneSaga extends CardImpl { + + + + public TheCloneSaga(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}"); + + this.subtype.add(SubType.SAGA); + + // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I -- Surveil 3. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new SurveilEffect(3)); + + // II -- When you next cast a creature spell this turn, copy it, except the copy isn't legendary. + DelayedTriggeredAbility ability = new CastNextSpellDelayedTriggeredAbility( + new CopyTargetStackObjectEffect(false, true, false, 1, new RemoveTypeCopyApplier(SuperType.LEGENDARY)) + .setText("copy it, except the copy isn't legendary"), + StaticFilters.FILTER_SPELL_A_CREATURE, true + ); + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II, new CreateDelayedTriggeredAbilityEffect(ability)); + + // III -- Choose a card name. Whenever a creature with the chosen name deals combat damage to a player this turn, draw a card. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new ChooseACardNameEffect(ChooseACardNameEffect.TypeOfName.ALL), + new CreateDelayedTriggeredAbilityEffect(new TheCloneSagaDelayedTrigger())); + + this.addAbility(sagaAbility); + } + + private TheCloneSaga(final TheCloneSaga card) { + super(card); + } + + @Override + public TheCloneSaga copy() { + return new TheCloneSaga(this); + } +} +class TheCloneSagaDelayedTrigger extends DelayedTriggeredAbility { + + private static final FilterPermanent filter = new FilterCreaturePermanent("creature with the chosen name"); + + static { + filter.add(ChosenNamePredicate.instance); + } + + TheCloneSagaDelayedTrigger() { + super(new DrawCardSourceControllerEffect(1), Duration.EndOfTurn, false); + } + + private TheCloneSagaDelayedTrigger(final TheCloneSagaDelayedTrigger ability) { + super(ability); + } + + @Override + public TheCloneSagaDelayedTrigger copy() { + return new TheCloneSagaDelayedTrigger(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (((DamagedPlayerEvent) event).isCombatDamage()) { + Permanent creature = game.getPermanent(event.getSourceId()); + return creature != null && filter.match(creature, getControllerId(), this, game); + } + return false; + } + + @Override + public String getRule() { + return "Whenever a creature with the chosen name deals combat damage to a player this turn, draw a card."; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheCreationOfAvacyn.java b/Mage.Sets/src/mage/cards/t/TheCreationOfAvacyn.java index 17b28d39754..1e89f2a9483 100644 --- a/Mage.Sets/src/mage/cards/t/TheCreationOfAvacyn.java +++ b/Mage.Sets/src/mage/cards/t/TheCreationOfAvacyn.java @@ -119,7 +119,7 @@ class TheCreationOfAvacynTwoEffect extends OneShotEffect { // ability (likely because the triggered ability was copied or the ability triggered a second time), the second // chapter ability will turn all of the exiled cards face up. If at least one of them is a creature card, you'll // lose life equal to the combined mana value of all the exiled cards. - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); ExileZone exileZone = game.getExile().getExileZone(exileId); if (exileZone == null || exileZone.isEmpty()) { return false; @@ -163,7 +163,7 @@ class TheCreationOfAvacynThreeEffect extends OneShotEffect { // when the third chapter ability resolves, if at least one of the exiled cards is a creature card, you may choose // to put all or none of the exiled cards that are permanent cards onto the battlefield. Regardless of what you // choose, any remaining exiled cards will be put into their owners' hands. - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); ExileZone exileZone = game.getExile().getExileZone(exileId); Player controller = game.getPlayer(source.getControllerId()); if (controller == null || exileZone == null || exileZone.isEmpty()) { diff --git a/Mage.Sets/src/mage/cards/t/TheDeathOfGwenStacy.java b/Mage.Sets/src/mage/cards/t/TheDeathOfGwenStacy.java new file mode 100644 index 00000000000..4dccbe02158 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheDeathOfGwenStacy.java @@ -0,0 +1,116 @@ +package mage.cards.t; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.ExileGraveyardAllTargetPlayerEffect; +import mage.cards.*; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.players.Player; +import mage.players.PlayerList; +import mage.target.Target; +import mage.target.TargetPlayer; +import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetDiscard; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class TheDeathOfGwenStacy extends CardImpl { + + public TheDeathOfGwenStacy(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); + + this.subtype.add(SubType.SAGA); + + // (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I -- Destroy target creature. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, new DestroyTargetEffect(), new TargetCreaturePermanent()); + + // II -- Each player may discard a card. Each player who doesn't loses 3 life. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II, new TheDeathOfGwenStacyEffect()); + + // III -- Exile any number of target players' graveyards. + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new ExileGraveyardAllTargetPlayerEffect(), + new TargetPlayer(0, Integer.MAX_VALUE, false)); + + this.addAbility(sagaAbility); + } + + private TheDeathOfGwenStacy(final TheDeathOfGwenStacy card) { + super(card); + } + + @Override + public TheDeathOfGwenStacy copy() { + return new TheDeathOfGwenStacy(this); + } +} + +class TheDeathOfGwenStacyEffect extends OneShotEffect { + + TheDeathOfGwenStacyEffect() { + super(Outcome.Neutral); + this.staticText = "each player may discard a card. Each player who doesn't loses 3 life"; + } + + private TheDeathOfGwenStacyEffect(final TheDeathOfGwenStacyEffect effect) { + super(effect); + } + + @Override + public TheDeathOfGwenStacyEffect copy() { + return new TheDeathOfGwenStacyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = source.getSourceObject(game); + if (controller == null || sourceObject == null) { + return false; + } + // Store for each player the cards to discard, that's important because all discard shall happen at the same time + Map cardsToDiscard = new HashMap<>(); + PlayerList playersInRange = game.getState().getPlayersInRange(controller.getId(), game); + + // choose cards to discard + for (UUID playerId : playersInRange) { + Player player = game.getPlayer(playerId); + if (player != null) { + Target target = new TargetDiscard(0, 1, new FilterCard(), playerId) + .withChooseHint("Choose a card to discard or lose 3 life"); + player.chooseTarget(outcome, target, source, game); + Cards cards = new CardsImpl(target.getTargets()); + cardsToDiscard.put(playerId, cards); + } + } + // discard all chosen cards + for (UUID playerId : playersInRange) { + Player player = game.getPlayer(playerId); + if (player != null) { + Cards cardsPlayer = cardsToDiscard.get(playerId); + if (cardsPlayer != null && !cardsPlayer.isEmpty()) { + for (UUID cardId : cardsPlayer) { + Card card = game.getCard(cardId); + player.discard(card, false, source, game); + } + } else { + player.loseLife(3, game, source, false); + } + } + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheFourthDoctor.java b/Mage.Sets/src/mage/cards/t/TheFourthDoctor.java new file mode 100644 index 00000000000..6332f639c3a --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheFourthDoctor.java @@ -0,0 +1,197 @@ +package mage.cards.t; + +import java.util.*; +import mage.MageIdentifier; +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.hint.Hint; +import mage.abilities.hint.common.ConditionPermanentHint; +import mage.abilities.effects.common.continuous.PlayFromTopOfLibraryEffect; +import mage.abilities.effects.common.continuous.LookAtTopCardOfLibraryAnyTimeEffect; +import mage.constants.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.HistoricPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.FoodToken; +import mage.players.Player; +import mage.watchers.Watcher; + + +/** + * @author padfoothelix + */ +public final class TheFourthDoctor extends CardImpl { + + public TheFourthDoctor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.TIME_LORD); + this.subtype.add(SubType.DOCTOR); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // You may look at the top card of your library any time. + this.addAbility(new SimpleStaticAbility(new LookAtTopCardOfLibraryAnyTimeEffect())); + + // Would You Like A...? -- Once each turn, you may play a historic land or cast a historic spell from the top of your library. When you do, create a Food token. + this.addAbility( + new SimpleStaticAbility( + new TheFourthDoctorPlayFromTopEffect()) + .setIdentifier(MageIdentifier.TheFourthDoctorWatcher) + .addHint(new ConditionPermanentHint( + TheFourthDoctorCondition.instance, + "You may play a historic land or cast a historic spell from the top of your library.", + null, + "You have already played a historic land or cast a historic spell from the top of your library this turn", + null, + true + )) + .withFlavorWord("Would You Like A...?"), + new TheFourthDoctorWatcher() + ); + this.addAbility(new TheFourthDoctorTriggeredAbility()); + } + + private TheFourthDoctor(final TheFourthDoctor card) { + super(card); + } + + @Override + public TheFourthDoctor copy() { + return new TheFourthDoctor(this); + } +} + +class TheFourthDoctorPlayFromTopEffect extends PlayFromTopOfLibraryEffect { + + private static final FilterCard filter = new FilterCard(""); + + static { + filter.add(HistoricPredicate.instance); + } + + TheFourthDoctorPlayFromTopEffect() { + super(filter); + staticText = "Once each turn, you may play a historic land or cast a historic spell from the top of your library. " + + "When you do, create a Food token."; + } + + private TheFourthDoctorPlayFromTopEffect(final TheFourthDoctorPlayFromTopEffect effect) { + super(effect); + } + + @Override + public TheFourthDoctorPlayFromTopEffect copy() { + return new TheFourthDoctorPlayFromTopEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) { + + if (!super.applies(objectId, affectedAbility, source, game, playerId)) { + return false; + } + + Player controller = game.getPlayer(source.getControllerId()); + TheFourthDoctorWatcher watcher = game.getState().getWatcher(TheFourthDoctorWatcher.class); + Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game); + + return controller != null + && watcher != null + && !watcher.isAbilityUsed( + controller.getId(), + new MageObjectReference(sourcePermanent, game) + ); + } +} + +class TheFourthDoctorTriggeredAbility extends TriggeredAbilityImpl { + + TheFourthDoctorTriggeredAbility() { + super(Zone.BATTLEFIELD, new CreateTokenEffect(new FoodToken())); + this.setRuleVisible(false); + } + + private TheFourthDoctorTriggeredAbility(final TheFourthDoctorTriggeredAbility ability) { + super(ability); + } + + @Override + public TheFourthDoctorTriggeredAbility copy() { + return new TheFourthDoctorTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SPELL_CAST + || event.getType() == GameEvent.EventType.LAND_PLAYED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + // returns true if a card was played via this card's ability + // (i.e. same identifier and same approving object) + if (event.hasApprovingIdentifier(MageIdentifier.TheFourthDoctorWatcher)) { + return event + .getApprovingObject() + .getApprovingAbility() + .getSourceId() + .equals(this.getSourceId()); + } + return false; + } +} + +// adapted from OnceEachTurnCastWatcher +class TheFourthDoctorWatcher extends Watcher { + + // we store a map of playerIds linked to a set of used approving objects. + private final Map> usedFrom = new HashMap<>(); + + TheFourthDoctorWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if ((event.getType() == GameEvent.EventType.SPELL_CAST + ||event.getType() == GameEvent.EventType.LAND_PLAYED) + && event.getPlayerId()!= null + && event.hasApprovingIdentifier(MageIdentifier.TheFourthDoctorWatcher)) { + usedFrom.computeIfAbsent(event.getPlayerId(), k -> new HashSet<>()) + .add(event.getApprovingObject().getApprovingMageObjectReference()); + + } + } + + @Override + public void reset() { + super.reset(); + usedFrom.clear(); + } + + boolean isAbilityUsed(UUID playerId, MageObjectReference mor) { + return usedFrom.getOrDefault(playerId, Collections.emptySet()).contains(mor); + } +} + +enum TheFourthDoctorCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + TheFourthDoctorWatcher watcher = game.getState().getWatcher(TheFourthDoctorWatcher.class); + return watcher != null + && !watcher.isAbilityUsed(source.getControllerId(), new MageObjectReference(source.getSourceId(), game)); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheLocustGod.java b/Mage.Sets/src/mage/cards/t/TheLocustGod.java index f1d0f8526c1..62afa8bb57a 100644 --- a/Mage.Sets/src/mage/cards/t/TheLocustGod.java +++ b/Mage.Sets/src/mage/cards/t/TheLocustGod.java @@ -22,7 +22,6 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Outcome; import mage.constants.SuperType; -import mage.constants.Zone; import mage.game.Game; import mage.game.permanent.token.TheLocustGodInsectToken; import mage.target.targetpointer.FixedTarget; @@ -83,7 +82,7 @@ class TheLocustGodEffect extends OneShotEffect { // Create delayed triggered ability Effect effect = new ReturnToHandTargetEffect(); effect.setText("return {this} to its owner's hand"); - effect.setTargetPointer(new FixedTarget(source.getSourceId(), source.getSourceObjectZoneChangeCounter())); + effect.setTargetPointer(new FixedTarget(source.getSourceId(), source.getStackMomentSourceZCC())); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); game.addDelayedTriggeredAbility(delayedAbility, source); return true; diff --git a/Mage.Sets/src/mage/cards/t/TheNinthDoctor.java b/Mage.Sets/src/mage/cards/t/TheNinthDoctor.java index eaa7ab95595..9b791bfdeb7 100644 --- a/Mage.Sets/src/mage/cards/t/TheNinthDoctor.java +++ b/Mage.Sets/src/mage/cards/t/TheNinthDoctor.java @@ -2,16 +2,14 @@ package mage.cards.t; import mage.MageInt; import mage.abilities.Ability; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.IsStepCondition; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.InspiredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.SuperType; +import mage.constants.*; import mage.game.Game; import mage.game.turn.TurnMod; import mage.game.turn.UpkeepStep; @@ -23,6 +21,8 @@ import java.util.UUID; */ public final class TheNinthDoctor extends CardImpl { + private static final Condition condition = new IsStepCondition(PhaseStep.UNTAP); + public TheNinthDoctor(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{R}"); this.supertype.add(SuperType.LEGENDARY); @@ -35,7 +35,7 @@ public final class TheNinthDoctor extends CardImpl { // Into the TARDIS — Whenever The Ninth Doctor becomes untapped during your untap step, you get an additional upkeep step after this step. this.addAbility(new InspiredAbility(new TheNinthDoctorEffect(), false, false) - .withTriggerCondition(IsStepCondition.getMyUpkeep()) + .withTriggerCondition(condition) .withFlavorWord("Into the TARDIS")); } diff --git a/Mage.Sets/src/mage/cards/t/TheScarabGod.java b/Mage.Sets/src/mage/cards/t/TheScarabGod.java index 500c819a2ec..17deb6d479a 100644 --- a/Mage.Sets/src/mage/cards/t/TheScarabGod.java +++ b/Mage.Sets/src/mage/cards/t/TheScarabGod.java @@ -127,7 +127,7 @@ class TheScarabGodEffectDieEffect extends OneShotEffect { // Create delayed triggered ability Effect effect = new ReturnToHandTargetEffect(); effect.setText("return {this} to its owner's hand"); - effect.setTargetPointer(new FixedTarget(source.getSourceId(), source.getSourceObjectZoneChangeCounter())); + effect.setTargetPointer(new FixedTarget(source.getSourceId(), source.getStackMomentSourceZCC())); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); game.addDelayedTriggeredAbility(delayedAbility, source); return true; diff --git a/Mage.Sets/src/mage/cards/t/TheScorpionGod.java b/Mage.Sets/src/mage/cards/t/TheScorpionGod.java index 67ecd405fac..32a52560d5a 100644 --- a/Mage.Sets/src/mage/cards/t/TheScorpionGod.java +++ b/Mage.Sets/src/mage/cards/t/TheScorpionGod.java @@ -24,13 +24,11 @@ import mage.constants.Outcome; import mage.constants.SuperType; import mage.constants.Zone; import mage.counters.CounterType; -import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; -import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; import static mage.filter.StaticFilters.FILTER_ANOTHER_TARGET_CREATURE; @@ -135,7 +133,7 @@ class TheScorpionGodEffect extends OneShotEffect { // Create delayed triggered ability Effect effect = new ReturnToHandTargetEffect(); effect.setText("return {this} to its owner's hand"); - effect.setTargetPointer(new FixedTarget(source.getSourceId(), source.getSourceObjectZoneChangeCounter())); + effect.setTargetPointer(new FixedTarget(source.getSourceId(), source.getStackMomentSourceZCC())); DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); game.addDelayedTriggeredAbility(delayedAbility, source); return true; diff --git a/Mage.Sets/src/mage/cards/t/TheSpotLivingPortal.java b/Mage.Sets/src/mage/cards/t/TheSpotLivingPortal.java new file mode 100644 index 00000000000..56e8588a572 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheSpotLivingPortal.java @@ -0,0 +1,82 @@ +package mage.cards.t; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.costs.common.PutSourceOnBottomOwnerLibraryCost; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.ExileTargetForSourceEffect; +import mage.abilities.effects.common.ReturnFromExileForSourceEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.target.common.TargetCardInGraveyard; +import mage.target.common.TargetNonlandPermanent; +import mage.target.targetpointer.EachTargetPointer; + +import java.util.Arrays; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * + * @author Jmlundeen + */ +public final class TheSpotLivingPortal extends CardImpl { + + private static final FilterCard filter = new FilterCard("nonland permanent card from a graveyard"); + + static { + filter.add(Predicates.or( + Arrays.stream(CardType.values()) + .filter(CardType::isPermanentType) + .filter(type -> type != CardType.LAND) + .map(CardType::getPredicate) + .collect(Collectors.toSet())) + ); + } + + public TheSpotLivingPortal(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.SCIENTIST); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // When The Spot enters, exile up to one target nonland permanent and up to one target nonland permanent card from a graveyard. + Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetForSourceEffect() + .setTargetPointer(new EachTargetPointer())); + ability.addTarget(new TargetNonlandPermanent(0, 1)); + ability.addTarget(new TargetCardInGraveyard(0, 1, filter)); + this.addAbility(ability); + + // When The Spot dies, put him on the bottom of his owner's library. If you do, return the exiled cards to their owners' hands. + DoIfCostPaid effect = new DoIfCostPaid( + new ReturnFromExileForSourceEffect(Zone.HAND) + .withText(true, true, false), + null, + new PutSourceOnBottomOwnerLibraryCost() + .setText("put him on the bottom of his owner's library"), + false + ); + this.addAbility(new DiesSourceTriggeredAbility(effect, false)); + } + + private TheSpotLivingPortal(final TheSpotLivingPortal card) { + super(card); + } + + @Override + public TheSpotLivingPortal copy() { + return new TheSpotLivingPortal(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheSpotsPortal.java b/Mage.Sets/src/mage/cards/t/TheSpotsPortal.java new file mode 100644 index 00000000000..77fa680c528 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheSpotsPortal.java @@ -0,0 +1,47 @@ +package mage.cards.t; + +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.LoseLifeSourceControllerEffect; +import mage.abilities.effects.common.PutOnLibraryTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.filter.common.FilterControlledPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheSpotsPortal extends CardImpl { + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition( + new FilterControlledPermanent(SubType.VILLAIN), ComparisonType.EQUAL_TO, 0 + ); + + public TheSpotsPortal(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{B}"); + + // Put target creature on the bottom of its owner's library. You lose 2 life unless you control a Villain. + this.getSpellAbility().addEffect(new PutOnLibraryTargetEffect(false)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addEffect(new ConditionalOneShotEffect( + new LoseLifeSourceControllerEffect(2), condition, + "You lose 2 life unless you control a Villain" + )); + } + + private TheSpotsPortal(final TheSpotsPortal card) { + super(card); + } + + @Override + public TheSpotsPortal copy() { + return new TheSpotsPortal(this); + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheTombOfAclazotz.java b/Mage.Sets/src/mage/cards/t/TheTombOfAclazotz.java index 13faadff179..18e338ca97f 100644 --- a/Mage.Sets/src/mage/cards/t/TheTombOfAclazotz.java +++ b/Mage.Sets/src/mage/cards/t/TheTombOfAclazotz.java @@ -152,14 +152,14 @@ class TheTombOfAclazotzWatcher extends Watcher { return false; } MageObjectReference mor = new MageObjectReference( - source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game + source.getSourceId(), source.getStackMomentSourceZCC(), game ); return morMap.computeIfAbsent(mor, m -> new HashMap<>()).getOrDefault(playerId, 0) > 0; } void addPlayable(Ability source, Game game) { MageObjectReference mor = new MageObjectReference( - source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game + source.getSourceId(), source.getStackMomentSourceZCC(), game ); morMap.computeIfAbsent(mor, m -> new HashMap<>()) .compute(source.getControllerId(), CardUtil::setOrIncrementValue); diff --git a/Mage.Sets/src/mage/cards/t/TheToymakersTrap.java b/Mage.Sets/src/mage/cards/t/TheToymakersTrap.java index 3207b2c743c..0ddd5af43eb 100644 --- a/Mage.Sets/src/mage/cards/t/TheToymakersTrap.java +++ b/Mage.Sets/src/mage/cards/t/TheToymakersTrap.java @@ -121,7 +121,7 @@ class TheToymakersTrapEffect extends OneShotEffect { } private static List getOrSetValue(Game game, Ability source) { - String key = "chosenNumbers_" + source.getControllerId() + '_' + source.getSourceObjectZoneChangeCounter(); + String key = "chosenNumbers_" + source.getControllerId() + '_' + source.getStackMomentSourceZCC(); List list = (List) game.getState().getValue(key); if (list != null) { return list; diff --git a/Mage.Sets/src/mage/cards/t/ThrabenCharm.java b/Mage.Sets/src/mage/cards/t/ThrabenCharm.java index b1b63f894cc..7bb28685eb1 100644 --- a/Mage.Sets/src/mage/cards/t/ThrabenCharm.java +++ b/Mage.Sets/src/mage/cards/t/ThrabenCharm.java @@ -1,28 +1,20 @@ package mage.cards.t; -import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.MultipliedValue; import mage.abilities.dynamicvalue.common.CreaturesYouControlCount; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.ExileGraveyardAllTargetPlayerEffect; import mage.abilities.hint.common.CreaturesYouControlHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.players.Player; import mage.target.TargetPlayer; import mage.target.common.TargetCreaturePermanent; import mage.target.common.TargetEnchantmentPermanent; -import java.util.Objects; import java.util.UUID; /** @@ -48,7 +40,7 @@ public final class ThrabenCharm extends CardImpl { this.getSpellAbility().addMode(mode); // * Exile any number of target players' graveyards. - mode = new Mode(new ThrabenCharmEffect()); + mode = new Mode(new ExileGraveyardAllTargetPlayerEffect()); mode.addTarget(new TargetPlayer(0, Integer.MAX_VALUE, false)); this.getSpellAbility().addMode(mode); } @@ -62,38 +54,3 @@ public final class ThrabenCharm extends CardImpl { return new ThrabenCharm(this); } } - -class ThrabenCharmEffect extends OneShotEffect { - - ThrabenCharmEffect() { - super(Outcome.Benefit); - staticText = "exile any number of target players' graveyards"; - } - - private ThrabenCharmEffect(final ThrabenCharmEffect effect) { - super(effect); - } - - @Override - public ThrabenCharmEffect copy() { - return new ThrabenCharmEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { - return false; - } - Cards cards = new CardsImpl(); - this.getTargetPointer() - .getTargets(game, source) - .stream() - .map(game::getPlayer) - .filter(Objects::nonNull) - .map(Player::getGraveyard) - .forEach(cards::addAll); - controller.moveCards(cards, Zone.EXILED, source, game); - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/t/ThranPowerSuit.java b/Mage.Sets/src/mage/cards/t/ThranPowerSuit.java index 1522f930017..545a5244eec 100644 --- a/Mage.Sets/src/mage/cards/t/ThranPowerSuit.java +++ b/Mage.Sets/src/mage/cards/t/ThranPowerSuit.java @@ -4,7 +4,7 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.effects.Effect; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; import mage.abilities.effects.common.continuous.BoostEquippedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EquipAbility; @@ -14,10 +14,10 @@ import mage.cards.CardSetInfo; import mage.constants.AttachmentType; import mage.constants.CardType; import mage.constants.SubType; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.AttachedToAttachedPredicate; -import java.util.Objects; import java.util.UUID; /** @@ -25,15 +25,25 @@ import java.util.UUID; */ public final class ThranPowerSuit extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent("Aura and Equipment attached to it"); + + static { + filter.add(Predicates.or( + SubType.AURA.getPredicate(), + SubType.EQUIPMENT.getPredicate() + )); + filter.add(AttachedToAttachedPredicate.instance); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, 1); + public ThranPowerSuit(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); this.subtype.add(SubType.EQUIPMENT); // Equipped creature gets +1/+1 for each Aura and Equipment attached to it and has ward {2}. - Ability ability = new SimpleStaticAbility(new BoostEquippedEffect( - ThranPowerSuitValue.instance, ThranPowerSuitValue.instance - )); + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(xValue, xValue)); ability.addEffect(new GainAbilityAttachedEffect(new WardAbility( new GenericManaCost(2), false ), AttachmentType.EQUIPMENT).setText("and has ward {2}. (Whenever equipped creature becomes the target of a spell or ability an opponent controls, counter it unless that player pays {2}.)")); @@ -52,39 +62,3 @@ public final class ThranPowerSuit extends CardImpl { return new ThranPowerSuit(this); } } - -enum ThranPowerSuitValue implements DynamicValue { - instance; - - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - Permanent sourcePermanent = sourceAbility.getSourcePermanentOrLKI(game); - if (sourcePermanent == null) { - return 0; - } - Permanent permanent = game.getPermanent(sourcePermanent.getAttachedTo()); - return permanent == null ? 0 : permanent - .getAttachments() - .stream() - .map(game::getPermanentOrLKIBattlefield) - .filter(Objects::nonNull) - .map(p -> p.hasSubtype(SubType.EQUIPMENT, game) || p.hasSubtype(SubType.AURA, game)) - .mapToInt(b -> b ? 1 : 0) - .sum(); - } - - @Override - public ThranPowerSuitValue copy() { - return instance; - } - - @Override - public String getMessage() { - return "Aura and Equipment attached to it"; - } - - @Override - public String toString() { - return "1"; - } -} diff --git a/Mage.Sets/src/mage/cards/t/ThunderclapDrake.java b/Mage.Sets/src/mage/cards/t/ThunderclapDrake.java index 1d726ddfb87..01f7bec5656 100644 --- a/Mage.Sets/src/mage/cards/t/ThunderclapDrake.java +++ b/Mage.Sets/src/mage/cards/t/ThunderclapDrake.java @@ -4,7 +4,7 @@ import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.common.delayed.CopyNextSpellDelayedTriggeredAbility; +import mage.abilities.common.delayed.CastNextSpellDelayedTriggeredAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.dynamicvalue.common.CommanderCastFromCommandZoneValue; @@ -49,15 +49,9 @@ public final class ThunderclapDrake extends CardImpl { // {2}{U}, Sacrifice Thunderclap Drake: When you cast your next instant or sorcery spell this turn, copy it for each time you've cast your commander from the command zone this game. You may choose new targets for the copies. Ability ability = new SimpleActivatedAbility( - new CreateDelayedTriggeredAbilityEffect( - new CopyNextSpellDelayedTriggeredAbility( - StaticFilters.FILTER_SPELL_INSTANT_OR_SORCERY, - new ThunderclapDrakeEffect(), - "When you next cast an instant or sorcery spell this turn, " - + "copy it for each time you've cast your commander from the command zone this game. " - + "You may choose new targets for the copies." - ) - ), + new CreateDelayedTriggeredAbilityEffect(new CastNextSpellDelayedTriggeredAbility( + new ThunderclapDrakeEffect(), StaticFilters.FILTER_SPELL_INSTANT_OR_SORCERY, true + )), new ManaCostsImpl<>("{2}{U}") ); ability.addCost(new SacrificeSourceCost()); diff --git a/Mage.Sets/src/mage/cards/t/ThundermaneDragon.java b/Mage.Sets/src/mage/cards/t/ThundermaneDragon.java index e1a32328e2c..29933f71508 100644 --- a/Mage.Sets/src/mage/cards/t/ThundermaneDragon.java +++ b/Mage.Sets/src/mage/cards/t/ThundermaneDragon.java @@ -1,7 +1,5 @@ package mage.cards.t; -import java.util.UUID; - import mage.MageIdentifier; import mage.MageInt; import mage.abilities.Ability; @@ -11,19 +9,24 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.effects.common.continuous.LookAtTopCardOfLibraryAnyTimeEffect; import mage.abilities.effects.common.continuous.PlayFromTopOfLibraryEffect; -import mage.abilities.keyword.HasteAbility; -import mage.constants.*; import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.constants.WatcherScope; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.filter.predicate.mageobject.PowerPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; import mage.target.targetpointer.FixedTarget; import mage.watchers.Watcher; +import java.util.UUID; + /** * * @author Jmlundeen @@ -33,7 +36,7 @@ public final class ThundermaneDragon extends CardImpl { private static final FilterCreatureCard filter = new FilterCreatureCard("cast creature spells with power 4 or greater"); static { - filter.add(new ManaValuePredicate(ComparisonType.OR_GREATER, 4)); + filter.add(new PowerPredicate(ComparisonType.OR_GREATER, 4)); } public ThundermaneDragon(UUID ownerId, CardSetInfo setInfo) { @@ -86,4 +89,4 @@ class ThundermaneDragonWatcher extends Watcher { } } } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/t/TidehollowSculler.java b/Mage.Sets/src/mage/cards/t/TidehollowSculler.java index 39ac31df872..b8c51248c0e 100644 --- a/Mage.Sets/src/mage/cards/t/TidehollowSculler.java +++ b/Mage.Sets/src/mage/cards/t/TidehollowSculler.java @@ -92,7 +92,7 @@ class TidehollowScullerExileEffect extends OneShotEffect { true, CardUtil.getExileZoneId(game, source.getSourceId(), - source.getSourceObjectZoneChangeCounter()), + source.getStackMomentSourceZCC()), "Tidehollow Sculler"); } } @@ -124,8 +124,8 @@ class TidehollowScullerLeaveEffect extends OneShotEffect { MageObject sourceObject = source.getSourceObject(game); if (controller != null && sourceObject != null) { int zoneChangeCounter = (sourceObject instanceof PermanentToken) - ? source.getSourceObjectZoneChangeCounter() - : source.getSourceObjectZoneChangeCounter() - 1; + ? source.getStackMomentSourceZCC() + : source.getStackMomentSourceZCC() - 1; ExileZone exZone = game.getExile().getExileZone( CardUtil.getExileZoneId(game, source.getSourceId(), zoneChangeCounter)); if (exZone != null) { diff --git a/Mage.Sets/src/mage/cards/t/TizerusCharger.java b/Mage.Sets/src/mage/cards/t/TizerusCharger.java index 729d2e28491..4d9c78e58c3 100644 --- a/Mage.Sets/src/mage/cards/t/TizerusCharger.java +++ b/Mage.Sets/src/mage/cards/t/TizerusCharger.java @@ -82,7 +82,7 @@ class TizerusChargerEffect extends OneShotEffect { SpellAbility spellAbility = (SpellAbility) getValue(EntersBattlefieldEffect.SOURCE_CAST_SPELL_ABILITY); if (!(spellAbility instanceof EscapeAbility) || !spellAbility.getSourceId().equals(source.getSourceId()) - || permanent.getZoneChangeCounter(game) != spellAbility.getSourceObjectZoneChangeCounter()) { + || permanent.getZoneChangeCounter(game) != spellAbility.getStackMomentSourceZCC()) { return false; } List appliedEffects = (ArrayList) this.getValue("appliedEffects"); diff --git a/Mage.Sets/src/mage/cards/t/ToluzCleverConductor.java b/Mage.Sets/src/mage/cards/t/ToluzCleverConductor.java index c011bfd9f15..7a44c7687b6 100644 --- a/Mage.Sets/src/mage/cards/t/ToluzCleverConductor.java +++ b/Mage.Sets/src/mage/cards/t/ToluzCleverConductor.java @@ -114,7 +114,7 @@ class ToluzCleverConductorEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter() - 1)); + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC() - 1)); return player != null && exileZone != null && !exileZone.isEmpty() diff --git a/Mage.Sets/src/mage/cards/t/TophTheFirstMetalbender.java b/Mage.Sets/src/mage/cards/t/TophTheFirstMetalbender.java index 480c7237efd..4dc2ef8e110 100644 --- a/Mage.Sets/src/mage/cards/t/TophTheFirstMetalbender.java +++ b/Mage.Sets/src/mage/cards/t/TophTheFirstMetalbender.java @@ -63,6 +63,8 @@ class TophTheFirstMetalbenderEffect extends ContinuousEffectImpl { TophTheFirstMetalbenderEffect() { super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit); staticText = "nontoken artifacts you control are lands in addition to their other types"; + this.dependendToTypes.add(DependencyType.ArtifactAddingRemoving); + this.dependencyTypes.add(DependencyType.BecomeNonbasicLand); } private TophTheFirstMetalbenderEffect(final TophTheFirstMetalbenderEffect effect) { diff --git a/Mage.Sets/src/mage/cards/t/TritonWavebreaker.java b/Mage.Sets/src/mage/cards/t/TritonWavebreaker.java index bdee57e59d3..b26046ee80f 100644 --- a/Mage.Sets/src/mage/cards/t/TritonWavebreaker.java +++ b/Mage.Sets/src/mage/cards/t/TritonWavebreaker.java @@ -42,7 +42,7 @@ public final class TritonWavebreaker extends CardImpl { // As long as Triton Wavebreaker is a creature, it has prowess. this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( new GainAbilitySourceEffect(new ProwessAbility(), Duration.WhileOnBattlefield), - condition, "as long as {this} is a creature, it has prowess" + condition, "as long as this permanent is a creature, it has prowess" ))); // Enchanted creature gets +1/+1 and has prowess. diff --git a/Mage.Sets/src/mage/cards/t/TriumphOfSaintKatherine.java b/Mage.Sets/src/mage/cards/t/TriumphOfSaintKatherine.java index ea217662dfb..1d66ae330e1 100644 --- a/Mage.Sets/src/mage/cards/t/TriumphOfSaintKatherine.java +++ b/Mage.Sets/src/mage/cards/t/TriumphOfSaintKatherine.java @@ -74,7 +74,7 @@ class TriumphOfSaintKatherineEffect extends OneShotEffect { return false; } Cards cards = new CardsImpl(); - if (card.getZoneChangeCounter(game) == source.getSourceObjectZoneChangeCounter()) { + if (card.getZoneChangeCounter(game) == source.getStackMomentSourceZCC()) { cards.add(card); } cards.addAllCards(player.getLibrary().getTopCards(game, 6)); diff --git a/Mage.Sets/src/mage/cards/t/TurfWound.java b/Mage.Sets/src/mage/cards/t/TurfWound.java index fe9f47b0127..8ee45353c9f 100644 --- a/Mage.Sets/src/mage/cards/t/TurfWound.java +++ b/Mage.Sets/src/mage/cards/t/TurfWound.java @@ -46,7 +46,7 @@ class TurfWoundEffect extends ContinuousRuleModifyingEffectImpl { TurfWoundEffect() { super(Duration.EndOfTurn, Outcome.Detriment); - staticText = "Target player can't play land cards this turn"; + staticText = "Target player can't play lands this turn"; } private TurfWoundEffect(final TurfWoundEffect effect) { @@ -74,10 +74,7 @@ class TurfWoundEffect extends ContinuousRuleModifyingEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getPlayerId().equals(source.getFirstTarget())) { - return true; - } - return false; + return event.getPlayerId().equals(source.getFirstTarget()); } } diff --git a/Mage.Sets/src/mage/cards/u/UginTheIneffable.java b/Mage.Sets/src/mage/cards/u/UginTheIneffable.java index 848ee849bca..d18b19a832c 100644 --- a/Mage.Sets/src/mage/cards/u/UginTheIneffable.java +++ b/Mage.Sets/src/mage/cards/u/UginTheIneffable.java @@ -110,7 +110,7 @@ class UginTheIneffableEffect extends OneShotEffect { } // exile and look - UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); if (player.moveCardsToExile(card, source, game, false, exileZoneId, sourceObject.getIdName() + " (" + player.getName() + ")")) { card.turnFaceDown(source, game, source.getControllerId()); player.lookAtCards(player.getName() + " - " + card.getIdName() + " - " + CardUtil.sdf.format(System.currentTimeMillis()), card, game); diff --git a/Mage.Sets/src/mage/cards/u/UlamogTheDefiler.java b/Mage.Sets/src/mage/cards/u/UlamogTheDefiler.java index 48af2ecb999..3702aa12fc9 100644 --- a/Mage.Sets/src/mage/cards/u/UlamogTheDefiler.java +++ b/Mage.Sets/src/mage/cards/u/UlamogTheDefiler.java @@ -116,7 +116,7 @@ enum UlamogTheDefilerValue implements DynamicValue { @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { return game.getExile() - .getAllCardsByRange(game, sourceAbility.getControllerId()) + .getCardsInRange(game, sourceAbility.getControllerId()) .stream() .mapToInt(Card::getManaValue) .max() diff --git a/Mage.Sets/src/mage/cards/u/UltimateGreenGoblin.java b/Mage.Sets/src/mage/cards/u/UltimateGreenGoblin.java new file mode 100644 index 00000000000..682ba3002cf --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UltimateGreenGoblin.java @@ -0,0 +1,49 @@ +package mage.cards.u; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.discard.DiscardControllerEffect; +import mage.abilities.keyword.MayhemAbility; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.game.permanent.token.TreasureToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UltimateGreenGoblin extends CardImpl { + + public UltimateGreenGoblin(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B/R}{B/R}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.GOBLIN); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(5); + this.toughness = new MageInt(4); + + // At the beginning of your upkeep, discard a card, then create a Treasure token. + Ability ability = new BeginningOfUpkeepTriggeredAbility(new DiscardControllerEffect(1)); + ability.addEffect(new CreateTokenEffect(new TreasureToken()).concatBy(", then")); + this.addAbility(ability); + + // Mayhem {2}{B/R} + this.addAbility(new MayhemAbility(this, "{2}{B/R}")); + } + + private UltimateGreenGoblin(final UltimateGreenGoblin card) { + super(card); + } + + @Override + public UltimateGreenGoblin copy() { + return new UltimateGreenGoblin(this); + } +} diff --git a/Mage.Sets/src/mage/cards/u/UndeadSprinter.java b/Mage.Sets/src/mage/cards/u/UndeadSprinter.java new file mode 100644 index 00000000000..94bf71690a9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UndeadSprinter.java @@ -0,0 +1,140 @@ +package mage.cards.u; + +import java.util.UUID; + +import mage.MageIdentifier; +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.common.counter.AddCounterEnteringCreatureEffect; +import mage.cards.Card; +import mage.constants.*; +import mage.abilities.keyword.TrampleAbility; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.counters.CounterType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.stack.Spell; +import mage.watchers.Watcher; + +/** + * + * @author anonymous + */ +public final class UndeadSprinter extends CardImpl { + + public UndeadSprinter(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{R}"); + + this.subtype.add(SubType.ZOMBIE); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // You may cast this card from your graveyard if a non-Zombie creature died this turn. If you do, this creature enters with a +1/+1 counter on it. + Ability ability = new SimpleStaticAbility(Zone.ALL, new UndeadSprinterEffect()).setIdentifier(MageIdentifier.UndeadSprinterAlternateCast); + ability.addWatcher(new UndeadSprinterCanCastWatcher()); + ability.addWatcher(new UndeadSprinterAlternateCastWatcher()); + this.addAbility(ability); + } + + private UndeadSprinter(final UndeadSprinter card) { + super(card); + } + + @Override + public UndeadSprinter copy() { + return new UndeadSprinter(this); + } +} + +class UndeadSprinterEffect extends AsThoughEffectImpl { + + UndeadSprinterEffect() { + super(AsThoughEffectType.CAST_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfGame, Outcome.Benefit); + this.staticText = "You may cast this card from your graveyard if a non-Zombie creature died this turn"; + } + + private UndeadSprinterEffect(final UndeadSprinterEffect effect) { + super(effect); + } + + @Override + public UndeadSprinterEffect copy() { + return new UndeadSprinterEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + if (sourceId.equals(source.getSourceId()) && source.isControlledBy(affectedControllerId)) { + Card card = game.getCard(source.getSourceId()); + Watcher watcher = game.getState().getWatcher(UndeadSprinterCanCastWatcher.class); + if (card != null && watcher != null && game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD) { + return watcher.conditionMet(); + } + } + return false; + } +} + +class UndeadSprinterCanCastWatcher extends Watcher { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a non-Zombie creature"); + + static { + filter.add(Predicates.not(SubType.ZOMBIE.getPredicate())); + } + + public UndeadSprinterCanCastWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (condition) { + return; + } + if (event.getType() == GameEvent.EventType.ZONE_CHANGE + && ((ZoneChangeEvent) event).isDiesEvent() + && filter.match(((ZoneChangeEvent) event).getTarget(), game)) { + condition = true; + } + } +} + +class UndeadSprinterAlternateCastWatcher extends Watcher { + + UndeadSprinterAlternateCastWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (GameEvent.EventType.SPELL_CAST.equals(event.getType()) + && event.hasApprovingIdentifier(MageIdentifier.UndeadSprinterAlternateCast)) { + Spell target = game.getSpell(event.getTargetId()); + if (target != null) { + game.getState().addEffect(new AddCounterEnteringCreatureEffect(new MageObjectReference(target.getCard(), game), + CounterType.P1P1.createInstance(), Outcome.BoostCreature), + target.getSpellAbility()); + } + } + } +} diff --git a/Mage.Sets/src/mage/cards/u/UnderworldCerberus.java b/Mage.Sets/src/mage/cards/u/UnderworldCerberus.java index ab7e17a10ff..9d7908d7c4c 100644 --- a/Mage.Sets/src/mage/cards/u/UnderworldCerberus.java +++ b/Mage.Sets/src/mage/cards/u/UnderworldCerberus.java @@ -38,7 +38,8 @@ public final class UnderworldCerberus extends CardImpl { // When Underworld Cerberus dies, exile it and each player returns all creature cards from their graveyard to their hand. Ability ability = new DiesSourceTriggeredAbility(new ExileSourceEffect()); - ability.addEffect(new ReturnToHandFromGraveyardAllEffect(new FilterCreatureCard("creature cards"))); + ability.addEffect(new ReturnToHandFromGraveyardAllEffect(new FilterCreatureCard("creature cards")) + .concatBy("and")); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/u/UnderworldSentinel.java b/Mage.Sets/src/mage/cards/u/UnderworldSentinel.java index 40db7ee5970..293aabd7b23 100644 --- a/Mage.Sets/src/mage/cards/u/UnderworldSentinel.java +++ b/Mage.Sets/src/mage/cards/u/UnderworldSentinel.java @@ -76,7 +76,7 @@ class UnderworldSentinelEffect extends OneShotEffect { return false; } ExileZone exileZone = game.getExile().getExileZone( - CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()) + CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()) ); return exileZone != null && controller.moveCards(exileZone, Zone.BATTLEFIELD, source, game); } diff --git a/Mage.Sets/src/mage/cards/u/UnexplainedAbsence.java b/Mage.Sets/src/mage/cards/u/UnexplainedAbsence.java new file mode 100644 index 00000000000..8de9468f9bc --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UnexplainedAbsence.java @@ -0,0 +1,108 @@ +package mage.cards.u; + +import java.util.*; +import java.util.stream.Collectors; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.keyword.ManifestEffect; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetPermanent; +import mage.target.targetadjustment.ForEachPlayerTargetsAdjuster; +import mage.target.targetpointer.EachTargetPointer; + +/** + * + * @author anonymous + */ +public final class UnexplainedAbsence extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("target nonland permanent"); + + static { + filter.add(Predicates.not(CardType.LAND.getPredicate())); + } + + public UnexplainedAbsence(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{W}"); + + // For each player, exile up to one target nonland permanent that player controls. For each permanent exiled this way, its controller cloaks the top card of their library. + this.getSpellAbility().addEffect( + new UnexplainedAbsenceEffect() + .setTargetPointer(new EachTargetPointer()) + .setText("For each player, exile up to one target nonland permanent that player controls. " + + "For each permanent exiled this way, its controller cloaks the top card of their library.") + ); + this.getSpellAbility().addTarget(new TargetPermanent(0, 1, filter)); + this.getSpellAbility().setTargetAdjuster(new ForEachPlayerTargetsAdjuster(false, false)); + } + + private UnexplainedAbsence(final UnexplainedAbsence card) { + super(card); + } + + @Override + public UnexplainedAbsence copy() { + return new UnexplainedAbsence(this); + } +} + +class UnexplainedAbsenceEffect extends OneShotEffect { + + UnexplainedAbsenceEffect() { + super(Outcome.Neutral); + } + + private UnexplainedAbsenceEffect(final UnexplainedAbsenceEffect effect) { + super(effect); + } + + @Override + public UnexplainedAbsenceEffect copy() { + return new UnexplainedAbsenceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Set permanents = source + .getTargets() + .stream() + .map(Target::getTargets) + .flatMap(Collection::stream) + .map(game::getPermanent) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + // set of controllers for the second part of the effect. + Set controllers = new HashSet<>(); + for (Permanent permanent : permanents) { + Player effectedController = game.getPlayer(permanent.getControllerId()); + + if (effectedController != null) { + controllers.add(effectedController.getId()); + } + } + controller.moveCards(permanents, Zone.EXILED, source, game); + for (UUID controllerId : controllers) { + Player player = game.getPlayer(controllerId); + ManifestEffect.doManifestCards( + game, source, player, + player.getLibrary().getTopCards(game, 1), true + ); + } + return true; + } + +} diff --git a/Mage.Sets/src/mage/cards/u/UnidentifiedHovership.java b/Mage.Sets/src/mage/cards/u/UnidentifiedHovership.java index bd8dc5aa691..d32e31e7827 100644 --- a/Mage.Sets/src/mage/cards/u/UnidentifiedHovership.java +++ b/Mage.Sets/src/mage/cards/u/UnidentifiedHovership.java @@ -88,7 +88,7 @@ class UnidentifiedHovershipEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source, -1)); if (exileZone == null || exileZone.isEmpty()) { return false; } diff --git a/Mage.Sets/src/mage/cards/u/UniversityCampus.java b/Mage.Sets/src/mage/cards/u/UniversityCampus.java new file mode 100644 index 00000000000..e8d1f840e24 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UniversityCampus.java @@ -0,0 +1,46 @@ +package mage.cards.u; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.keyword.SurveilEffect; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UniversityCampus extends CardImpl { + + public UniversityCampus(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // This land enters tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {W} or {U}. + this.addAbility(new WhiteManaAbility()); + this.addAbility(new BlueManaAbility()); + + // {4}, {T}: Surveil 1. + Ability ability = new SimpleActivatedAbility(new SurveilEffect(1), new GenericManaCost(4)); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + private UniversityCampus(final UniversityCampus card) { + super(card); + } + + @Override + public UniversityCampus copy() { + return new UniversityCampus(this); + } +} diff --git a/Mage.Sets/src/mage/cards/u/UnstableAmulet.java b/Mage.Sets/src/mage/cards/u/UnstableAmulet.java index d2a6859ac73..ccc888ff559 100644 --- a/Mage.Sets/src/mage/cards/u/UnstableAmulet.java +++ b/Mage.Sets/src/mage/cards/u/UnstableAmulet.java @@ -6,25 +6,16 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.costs.common.PayEnergyCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.effects.common.ExileTopCardPlayUntilExileAnotherEffect; import mage.abilities.effects.common.counter.GetEnergyCountersControllerEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.FilterSpell; import mage.filter.predicate.Predicates; import mage.filter.predicate.card.CastFromZonePredicate; -import mage.game.ExileZone; -import mage.game.Game; -import mage.players.Player; -import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; -import java.util.Set; import java.util.UUID; /** @@ -52,7 +43,7 @@ public final class UnstableAmulet extends CardImpl { // {T}, Pay {E}{E}: Exile the top card of your library. You may play it until you exile another card with Unstable Amulet. Ability ability = new SimpleActivatedAbility( - new UnstableAmuletEffect(), + new ExileTopCardPlayUntilExileAnotherEffect("it"), new TapSourceCost() ); ability.addCost(new PayEnergyCost(2)); @@ -67,104 +58,4 @@ public final class UnstableAmulet extends CardImpl { public UnstableAmulet copy() { return new UnstableAmulet(this); } -} - -class UnstableAmuletEffect extends OneShotEffect { - - UnstableAmuletEffect() { - super(Outcome.DrawCard); - staticText = "Exile the top card of your library. You may play it until you exile another card with {this}."; - } - - private UnstableAmuletEffect(final UnstableAmuletEffect effect) { - super(effect); - } - - @Override - public UnstableAmuletEffect copy() { - return new UnstableAmuletEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null || !controller.getLibrary().hasCards()) { - return false; - } - Card card = controller.getLibrary().getFromTop(game); - if (card == null) { - return false; - } - UUID exileId = CardUtil.getExileZoneId(game, source); - String exileName = CardUtil.getSourceIdName(game, source); - controller.moveCardsToExile(card, source, game, true, exileId, exileName); - game.processAction(); - if (!Zone.EXILED.equals(game.getState().getZone(card.getId()))) { - return true; - } - // Allow the card to be played until it leaves that exile zone. - ContinuousEffect effect = new UnstableAmuletPlayEffect(exileId); - effect.setTargetPointer(new FixedTarget(card.getMainCard(), game)); - game.addEffect(effect, source); - // Clean the exile Zone from other cards, that can no longer be played. - ExileZone exileZone = game.getExile().getExileZone(exileId); - if (exileZone == null) { - return true; - } - Set inExileZone = exileZone.getCards(game); - for (Card cardInExile : inExileZone) { - if (cardInExile.getMainCard().getId().equals(card.getMainCard().getId())) { - continue; - } - game.getExile().moveToMainExileZone(cardInExile, game); - } - return true; - } -} - -class UnstableAmuletPlayEffect extends AsThoughEffectImpl { - - // The exile zone the card should be in for the effect to work. - private final UUID exileId; - - UnstableAmuletPlayEffect(UUID exileId) { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit); - this.exileId = exileId; - } - - private UnstableAmuletPlayEffect(final UnstableAmuletPlayEffect effect) { - super(effect); - this.exileId = effect.exileId; - } - - @Override - public UnstableAmuletPlayEffect copy() { - return new UnstableAmuletPlayEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - Card mainTargetCard = game.getCard(getTargetPointer().getFirst(game, source)); - if (mainTargetCard == null) { - this.discard(); - return false; - } - ExileZone exileZone = game.getExile().getExileZone(exileId); - if (exileZone == null || !exileZone.contains(mainTargetCard.getId())) { - // Clean the Continuous effect if the target card is no longer in the exile zone - this.discard(); - return false; - } - Card objectCard = game.getCard(objectId); - if (objectCard == null) { - return false; - } - return mainTargetCard.getId().equals(objectCard.getMainCard().getId()) // using main card to work with split/mdfc/adventures - && affectedControllerId.equals(source.getControllerId()); - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/u/UrbanRetreat.java b/Mage.Sets/src/mage/cards/u/UrbanRetreat.java new file mode 100644 index 00000000000..f47fcec2a02 --- /dev/null +++ b/Mage.Sets/src/mage/cards/u/UrbanRetreat.java @@ -0,0 +1,87 @@ +package mage.cards.u; + +import mage.abilities.Ability; +import mage.abilities.common.ActivateAsSorceryActivatedAbility; +import mage.abilities.common.EntersBattlefieldTappedAbility; +import mage.abilities.costs.common.ReturnToHandChosenControlledPermanentCost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.GreenManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.permanent.TappedPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class UrbanRetreat extends CardImpl { + + private static final FilterControlledPermanent filter = new FilterControlledCreaturePermanent("tapped creature you control"); + + static { + filter.add(TappedPredicate.TAPPED); + } + + public UrbanRetreat(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // This land enters tapped. + this.addAbility(new EntersBattlefieldTappedAbility()); + + // {T}: Add {G}, {W}, or {U}. + this.addAbility(new GreenManaAbility()); + this.addAbility(new WhiteManaAbility()); + this.addAbility(new BlueManaAbility()); + + // {2}, Return a tapped creature you control to its owner's hand: Put this card from your hand onto the battlefield. Activate only as a sorcery. + Ability ability = new ActivateAsSorceryActivatedAbility(Zone.HAND, new UrbanRetreatEffect(), new GenericManaCost(2)); + ability.addCost(new ReturnToHandChosenControlledPermanentCost(new TargetControlledPermanent())); + this.addAbility(ability); + } + + private UrbanRetreat(final UrbanRetreat card) { + super(card); + } + + @Override + public UrbanRetreat copy() { + return new UrbanRetreat(this); + } +} + +class UrbanRetreatEffect extends OneShotEffect { + + UrbanRetreatEffect() { + super(Outcome.Benefit); + staticText = "put this card from your hand onto the battlefield"; + } + + private UrbanRetreatEffect(final UrbanRetreatEffect effect) { + super(effect); + } + + @Override + public UrbanRetreatEffect copy() { + return new UrbanRetreatEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = source.getSourceCardIfItStillExists(game); + return player != null && card != null && player.moveCards(card, Zone.BATTLEFIELD, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/u/UroTitanOfNaturesWrath.java b/Mage.Sets/src/mage/cards/u/UroTitanOfNaturesWrath.java index 4007b7a02ec..9d373da06b3 100644 --- a/Mage.Sets/src/mage/cards/u/UroTitanOfNaturesWrath.java +++ b/Mage.Sets/src/mage/cards/u/UroTitanOfNaturesWrath.java @@ -80,7 +80,7 @@ class UroTitanOfNaturesWrathEffect extends OneShotEffect { if (permanent == null) { return false; } - if (EscapeAbility.wasCastedWithEscape(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter())) { + if (EscapeAbility.wasCastedWithEscape(game, source.getSourceId(), source.getStackMomentSourceZCC())) { return false; } return permanent.sacrifice(source, game); diff --git a/Mage.Sets/src/mage/cards/u/UrzaChiefArtificer.java b/Mage.Sets/src/mage/cards/u/UrzaChiefArtificer.java index 7962bb9fd6d..659221a6238 100644 --- a/Mage.Sets/src/mage/cards/u/UrzaChiefArtificer.java +++ b/Mage.Sets/src/mage/cards/u/UrzaChiefArtificer.java @@ -24,6 +24,10 @@ public final class UrzaChiefArtificer extends CardImpl { private static final FilterControlledPermanent filter = new FilterControlledCreaturePermanent("artifact creatures"); + static { + filter.add(CardType.ARTIFACT.getPredicate()); + } + public UrzaChiefArtificer(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{U}{B}"); diff --git a/Mage.Sets/src/mage/cards/v/ValakutExploration.java b/Mage.Sets/src/mage/cards/v/ValakutExploration.java index 796840a0131..0a41204bdbb 100644 --- a/Mage.Sets/src/mage/cards/v/ValakutExploration.java +++ b/Mage.Sets/src/mage/cards/v/ValakutExploration.java @@ -97,7 +97,7 @@ class ValakutExplorationExileEffect extends OneShotEffect { } controller.moveCardsToExile( card, source, game, true, CardUtil.getExileZoneId( - game, source.getSourceId(), source.getSourceObjectZoneChangeCounter() + game, source.getSourceId(), source.getStackMomentSourceZCC() ), sourcePermanent.getIdName() ); ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Duration.EndOfGame); @@ -130,7 +130,7 @@ class ValakutExplorationDamageEffect extends OneShotEffect { return false; } ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId( - game, source.getSourceId(), source.getSourceObjectZoneChangeCounter() + game, source.getSourceId(), source.getStackMomentSourceZCC() )); if (exileZone == null) { return false; diff --git a/Mage.Sets/src/mage/cards/v/VenomizedCat.java b/Mage.Sets/src/mage/cards/v/VenomizedCat.java new file mode 100644 index 00000000000..7c8f9aac106 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VenomizedCat.java @@ -0,0 +1,43 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.MillCardsControllerEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VenomizedCat extends CardImpl { + + public VenomizedCat(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}"); + + this.subtype.add(SubType.SYMBIOTE); + this.subtype.add(SubType.CAT); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Deathtouch + this.addAbility(DeathtouchAbility.getInstance()); + + // When this creature enters, mill two cards. + this.addAbility(new EntersBattlefieldTriggeredAbility(new MillCardsControllerEffect(2))); + } + + private VenomizedCat(final VenomizedCat card) { + super(card); + } + + @Override + public VenomizedCat copy() { + return new VenomizedCat(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VibrantCityscape.java b/Mage.Sets/src/mage/cards/v/VibrantCityscape.java new file mode 100644 index 00000000000..f4d1f691025 --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VibrantCityscape.java @@ -0,0 +1,40 @@ +package mage.cards.v; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VibrantCityscape extends CardImpl { + + public VibrantCityscape(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); + + // {T}, Sacrifice this land: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle. + Ability ability = new SimpleActivatedAbility(new SearchLibraryPutInPlayEffect( + new TargetCardInLibrary(StaticFilters.FILTER_CARD_BASIC_LAND), true + ), new TapSourceCost()); + ability.addCost(new SacrificeSourceCost()); + this.addAbility(ability); + } + + private VibrantCityscape(final VibrantCityscape card) { + super(card); + } + + @Override + public VibrantCityscape copy() { + return new VibrantCityscape(this); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VillainousWrath.java b/Mage.Sets/src/mage/cards/v/VillainousWrath.java new file mode 100644 index 00000000000..a896a4eabda --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VillainousWrath.java @@ -0,0 +1,66 @@ +package mage.cards.v; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyAllEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VillainousWrath extends CardImpl { + + public VillainousWrath(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}{B}"); + + // Target opponent loses life equal to the number of creatures they control. Then destroy all creatures. + this.getSpellAbility().addEffect(new VillainousWrathEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + this.getSpellAbility().addEffect(new DestroyAllEffect(StaticFilters.FILTER_PERMANENT_CREATURES).concatBy("Then")); + } + + private VillainousWrath(final VillainousWrath card) { + super(card); + } + + @Override + public VillainousWrath copy() { + return new VillainousWrath(this); + } +} + +class VillainousWrathEffect extends OneShotEffect { + + VillainousWrathEffect() { + super(Outcome.Benefit); + staticText = "target opponent loses life equal to the number of creatures they control"; + } + + private VillainousWrathEffect(final VillainousWrathEffect effect) { + super(effect); + } + + @Override + public VillainousWrathEffect copy() { + return new VillainousWrathEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(getTargetPointer().getFirst(game, source)); + if (player == null) { + return false; + } + int amount = game.getBattlefield().count(StaticFilters.FILTER_CONTROLLED_CREATURE, player.getId(), source, game); + return amount > 0 && player.loseLife(amount, game, source, false) > 0; + } +} diff --git a/Mage.Sets/src/mage/cards/v/VirulentSilencer.java b/Mage.Sets/src/mage/cards/v/VirulentSilencer.java index 2186ec9df40..d336578a7ea 100644 --- a/Mage.Sets/src/mage/cards/v/VirulentSilencer.java +++ b/Mage.Sets/src/mage/cards/v/VirulentSilencer.java @@ -38,7 +38,7 @@ public final class VirulentSilencer extends CardImpl { // Whenever a nontoken artifact creature you control deals combat damage to a player, that player gets two poison counters. this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility( new AddPoisonCounterTargetEffect(2), filter, - false, SetTargetPointer.PLAYER, true + false, SetTargetPointer.PLAYER, true, true )); } diff --git a/Mage.Sets/src/mage/cards/v/VolrathsCurse.java b/Mage.Sets/src/mage/cards/v/VolrathsCurse.java index ad01f63f065..e02f19c48e4 100644 --- a/Mage.Sets/src/mage/cards/v/VolrathsCurse.java +++ b/Mage.Sets/src/mage/cards/v/VolrathsCurse.java @@ -176,7 +176,7 @@ class VolrathsCurseIgnoreEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - String key = source.getSourceId().toString() + source.getSourceObjectZoneChangeCounter() + VolrathsCurse.keyString + game.getTurnNum() + ((ActivatedAbilityImpl) source).getActivatorId(); + String key = source.getSourceId().toString() + source.getStackMomentSourceZCC() + VolrathsCurse.keyString + game.getTurnNum() + ((ActivatedAbilityImpl) source).getActivatorId(); game.getState().setValue(key, true); return true; } diff --git a/Mage.Sets/src/mage/cards/v/VultureSchemingScavenger.java b/Mage.Sets/src/mage/cards/v/VultureSchemingScavenger.java new file mode 100644 index 00000000000..4449374150e --- /dev/null +++ b/Mage.Sets/src/mage/cards/v/VultureSchemingScavenger.java @@ -0,0 +1,51 @@ +package mage.cards.v; + +import mage.MageInt; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class VultureSchemingScavenger extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent(SubType.VILLAIN, "Villains"); + + public VultureSchemingScavenger(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{U/B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ARTIFICER); + this.subtype.add(SubType.VILLAIN); + this.power = new MageInt(4); + this.toughness = new MageInt(6); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Whenever Vulture attacks, other Villains you control gain flying until end of turn. + this.addAbility(new AttacksTriggeredAbility(new GainAbilityControlledEffect( + FlyingAbility.getInstance(), Duration.EndOfTurn, filter, true + ))); + } + + private VultureSchemingScavenger(final VultureSchemingScavenger card) { + super(card); + } + + @Override + public VultureSchemingScavenger copy() { + return new VultureSchemingScavenger(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WallCrawl.java b/Mage.Sets/src/mage/cards/w/WallCrawl.java new file mode 100644 index 00000000000..ba1278fbfb4 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WallCrawl.java @@ -0,0 +1,61 @@ +package mage.cards.w; + +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.combat.CantBeBlockedAllEffect; +import mage.abilities.effects.common.continuous.BoostAllEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.game.permanent.token.Spider21Token; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WallCrawl extends CardImpl { + + private static final FilterControlledCreaturePermanent filter + = new FilterControlledCreaturePermanent(SubType.SPIDER, "Spiders you control"); + private static final DynamicValue xValue + = new PermanentsOnBattlefieldCount(new FilterControlledPermanent(SubType.SPIDER)); + private static final Hint hint = new ValueHint("Spiders you control", xValue); + + public WallCrawl(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}"); + + // When this enchantment enters, create a 2/1 green Spider creature token with reach, then you gain 1 life for each Spider you control. + Ability ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new Spider21Token())); + ability.addEffect(new GainLifeEffect(xValue).concatBy(", then")); + this.addAbility(ability.addHint(hint)); + + // Spiders you control get +1/+1 and can't be blocked by creatures with defender. + ability = new SimpleStaticAbility(new BoostAllEffect( + 1, 1, Duration.WhileControlled, filter, false + )); + ability.addEffect(new CantBeBlockedAllEffect(filter, Duration.WhileControlled) + .setText("and can't be blocked by creatures with defender")); + this.addAbility(ability); + } + + private WallCrawl(final WallCrawl card) { + super(card); + } + + @Override + public WallCrawl copy() { + return new WallCrawl(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WallOfDeceit.java b/Mage.Sets/src/mage/cards/w/WallOfDeceit.java index 1b6dddd9013..0e7fac077b1 100644 --- a/Mage.Sets/src/mage/cards/w/WallOfDeceit.java +++ b/Mage.Sets/src/mage/cards/w/WallOfDeceit.java @@ -14,7 +14,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; @@ -70,7 +69,7 @@ class WallOfDeceitEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Permanent sourcePermanent = game.getPermanent(source.getSourceId()); if (sourcePermanent != null - && source.getSourceObjectZoneChangeCounter() == sourcePermanent.getZoneChangeCounter(game) // in case source was blinked after ability was set to stack + && source.getStackMomentSourceZCC() == sourcePermanent.getZoneChangeCounter(game) // in case source was blinked after ability was set to stack && !sourcePermanent.isFaceDown(game)) { sourcePermanent.setFaceDown(true, game); } diff --git a/Mage.Sets/src/mage/cards/w/WarNameAspirant.java b/Mage.Sets/src/mage/cards/w/WarNameAspirant.java index 740e85dda93..c2856279800 100644 --- a/Mage.Sets/src/mage/cards/w/WarNameAspirant.java +++ b/Mage.Sets/src/mage/cards/w/WarNameAspirant.java @@ -39,7 +39,7 @@ public final class WarNameAspirant extends CardImpl { // Raid — War-Name Aspirant enters the battlefield with a +1/+1 counter on it if you attacked this turn. this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(1), false), RaidCondition.instance, - "Raid — {this} enters with a +1/+1 counter on it if you attacked this turn.", + "{this} enters with a +1/+1 counter on it if you attacked this turn.", "{this} enters with a +1/+1 counter") .setAbilityWord(AbilityWord.RAID) .addHint(RaidHint.instance), diff --git a/Mage.Sets/src/mage/cards/w/WardenOfTheBeyond.java b/Mage.Sets/src/mage/cards/w/WardenOfTheBeyond.java index a630c707c76..99b6c464a24 100644 --- a/Mage.Sets/src/mage/cards/w/WardenOfTheBeyond.java +++ b/Mage.Sets/src/mage/cards/w/WardenOfTheBeyond.java @@ -1,7 +1,5 @@ - package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; @@ -13,12 +11,13 @@ import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.SubType; import mage.game.Game; import mage.players.Player; +import java.util.UUID; + /** * * @author LevelX2 @@ -26,7 +25,7 @@ import mage.players.Player; public final class WardenOfTheBeyond extends CardImpl { public WardenOfTheBeyond(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.WIZARD); @@ -35,9 +34,10 @@ public final class WardenOfTheBeyond extends CardImpl { // Vigilance this.addAbility(VigilanceAbility.getInstance()); + // Warden of the Beyond gets +2/+2 as long as an opponent owns a card in exile. this.addAbility(new SimpleStaticAbility( - new ConditionalContinuousEffect(new BoostSourceEffect(2,2,Duration.WhileOnBattlefield), OpponentOwnsCardInExileCondition.instance, + new ConditionalContinuousEffect(new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), WardenOfTheBeyondCondition.instance, "{this} gets +2/+2 as long as an opponent owns a card in exile"))); } @@ -51,15 +51,15 @@ public final class WardenOfTheBeyond extends CardImpl { } } -enum OpponentOwnsCardInExileCondition implements Condition { +enum WardenOfTheBeyondCondition implements Condition { - instance; + instance; @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - for (Card card :game.getExile().getAllCards(game)) { + for (Card card : game.getExile().getCardsInRange(game, controller.getId())) { if (controller.hasOpponent(card.getOwnerId(), game)) { return true; } diff --git a/Mage.Sets/src/mage/cards/w/WebOfLifeAndDestiny.java b/Mage.Sets/src/mage/cards/w/WebOfLifeAndDestiny.java new file mode 100644 index 00000000000..ad48b81c379 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WebOfLifeAndDestiny.java @@ -0,0 +1,40 @@ +package mage.cards.w; + +import mage.abilities.effects.common.LookLibraryAndPickControllerEffect; +import mage.abilities.keyword.ConvokeAbility; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.PutCards; +import mage.filter.StaticFilters; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WebOfLifeAndDestiny extends CardImpl { + + public WebOfLifeAndDestiny(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{6}{G}{G}"); + + // Convoke + this.addAbility(new ConvokeAbility()); + + // At the beginning of combat on your turn, look at the top five cards of your library. You may put a creature card from among them onto the battlefield. Put the rest on the bottom of your library in a random order. + this.addAbility(new BeginningOfCombatTriggeredAbility(new LookLibraryAndPickControllerEffect( + 5, 1, StaticFilters.FILTER_CARD_CREATURE, + PutCards.BATTLEFIELD, PutCards.BOTTOM_RANDOM + ))); + } + + private WebOfLifeAndDestiny(final WebOfLifeAndDestiny card) { + super(card); + } + + @Override + public WebOfLifeAndDestiny copy() { + return new WebOfLifeAndDestiny(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WebShooters.java b/Mage.Sets/src/mage/cards/w/WebShooters.java new file mode 100644 index 00000000000..9f24ca79589 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WebShooters.java @@ -0,0 +1,57 @@ +package mage.cards.w; + +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.target.common.TargetOpponentsCreaturePermanent; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class WebShooters extends CardImpl { + + public WebShooters(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{W}"); + + this.subtype.add(SubType.EQUIPMENT); + + // Equipped creature gets +1/+1 and has reach and "Whenever this creature attacks, tap target creature an opponent controls." + Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(1, 1)); + ability.addEffect(new GainAbilityAttachedEffect(ReachAbility.getInstance(), AttachmentType.EQUIPMENT) + .setText("has reach") + .concatBy("and") + ); + Ability gainedAbility = new AttacksTriggeredAbility(new TapTargetEffect()); + gainedAbility.addTarget(new TargetOpponentsCreaturePermanent()); + ability.addEffect(new GainAbilityAttachedEffect(gainedAbility, null) + .setText("\"Whenever this creature attacks, tap target creature an opponent controls.\"") + .concatBy("and") + ); + this.addAbility(ability); + + // Equip {2} + this.addAbility(new EquipAbility(2)); + } + + private WebShooters(final WebShooters card) { + super(card); + } + + @Override + public WebShooters copy() { + return new WebShooters(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WhiplashTrap.java b/Mage.Sets/src/mage/cards/w/WhiplashTrap.java index fb28b691a4d..0ab8a851bde 100644 --- a/Mage.Sets/src/mage/cards/w/WhiplashTrap.java +++ b/Mage.Sets/src/mage/cards/w/WhiplashTrap.java @@ -74,6 +74,6 @@ enum WhiplashTrapCondition implements Condition { @Override public String toString() { - return "If an opponent had two or more creatures enter the battlefield under their control this turn"; + return "an opponent had two or more creatures enter the battlefield under their control this turn"; } } diff --git a/Mage.Sets/src/mage/cards/w/WhispersteelDagger.java b/Mage.Sets/src/mage/cards/w/WhispersteelDagger.java index 74c8976c803..b958fb4edae 100644 --- a/Mage.Sets/src/mage/cards/w/WhispersteelDagger.java +++ b/Mage.Sets/src/mage/cards/w/WhispersteelDagger.java @@ -176,7 +176,7 @@ class WhispersteelDaggerWatcher extends Watcher { return false; } MageObjectReference mor = new MageObjectReference( - source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game + source.getSourceId(), source.getStackMomentSourceZCC(), game ); if (!morMap.containsKey(mor)) { return false; @@ -188,7 +188,7 @@ class WhispersteelDaggerWatcher extends Watcher { void addPlayable(Ability source, UUID ownerId, Game game) { MageObjectReference mor = new MageObjectReference( - source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game + source.getSourceId(), source.getStackMomentSourceZCC(), game ); morMap.computeIfAbsent(mor, m -> new HashMap<>()) .computeIfAbsent(ownerId, m -> new HashMap<>()) diff --git a/Mage.Sets/src/mage/cards/w/Wisecrack.java b/Mage.Sets/src/mage/cards/w/Wisecrack.java new file mode 100644 index 00000000000..5b133dcfa0b --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/Wisecrack.java @@ -0,0 +1,72 @@ +package mage.cards.w; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.game.Controllable; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class Wisecrack extends CardImpl { + + public Wisecrack(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}"); + + // Target creature deals damage equal to its power to itself. If that creature is attacking, Wisecrack deals 2 damage to that creature's controller. + this.getSpellAbility().addEffect(new WisecrackEffect()); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + private Wisecrack(final Wisecrack card) { + super(card); + } + + @Override + public Wisecrack copy() { + return new Wisecrack(this); + } +} + +class WisecrackEffect extends OneShotEffect { + + WisecrackEffect() { + super(Outcome.Benefit); + staticText = "target creature deals damage equal to its power to itself. " + + "If that creature is attacking, {this} deals 2 damage to that creature's controller"; + } + + private WisecrackEffect(final WisecrackEffect effect) { + super(effect); + } + + @Override + public WisecrackEffect copy() { + return new WisecrackEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (permanent == null) { + return false; + } + permanent.damage(permanent.getPower().getValue(), permanent.getId(), source, game); + if (permanent.isAttacking()) { + Optional.ofNullable(permanent) + .map(Controllable::getControllerId) + .map(game::getPlayer) + .map(player -> player.damage(2, source, game)); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/w/WithGreatPower.java b/Mage.Sets/src/mage/cards/w/WithGreatPower.java new file mode 100644 index 00000000000..9af1610bde3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WithGreatPower.java @@ -0,0 +1,114 @@ +package mage.cards.w; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.BoostEquippedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.permanent.AttachedToAttachedPredicate; +import mage.game.Game; +import mage.game.events.DamagePlayerEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WithGreatPower extends CardImpl { + + private static final FilterPermanent filter = new FilterPermanent("Aura and Equipment attached to it"); + + static { + filter.add(Predicates.or( + SubType.AURA.getPredicate(), + SubType.EQUIPMENT.getPredicate() + )); + filter.add(AttachedToAttachedPredicate.instance); + } + + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, 2); + + public WithGreatPower(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}"); + + this.subtype.add(SubType.AURA); + + // Enchant creature you control + TargetPermanent auraTarget = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + this.addAbility(new EnchantAbility(auraTarget)); + + // Enchanted creature gets +2/+2 for each Aura and Equipment attached to it. + this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(xValue, xValue))); + + // All damage that would be dealt to you is dealt to enchanted creature instead. + this.addAbility(new SimpleStaticAbility(new WithGreatPowerEffect())); + } + + private WithGreatPower(final WithGreatPower card) { + super(card); + } + + @Override + public WithGreatPower copy() { + return new WithGreatPower(this); + } +} + +class WithGreatPowerEffect extends ReplacementEffectImpl { + WithGreatPowerEffect() { + super(Duration.WhileOnBattlefield, Outcome.RedirectDamage); + staticText = "all damage that would be dealt to you is dealt to enchanted creature instead"; + } + + private WithGreatPowerEffect(final WithGreatPowerEffect effect) { + super(effect); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + DamagePlayerEvent damageEvent = (DamagePlayerEvent) event; + Optional.ofNullable(event) + .map(GameEvent::getSourceId) + .map(game::getPermanent) + .map(Permanent::getAttachedTo) + .map(game::getPermanent) + .ifPresent(permanent -> permanent.damage( + damageEvent.getAmount(), event.getSourceId(), source, + game, damageEvent.isCombatDamage(), damageEvent.isPreventable() + )); + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGE_PLAYER; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return source.isControlledBy(event.getPlayerId()); + } + + @Override + public WithGreatPowerEffect copy() { + return new WithGreatPowerEffect(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WoebringerDemon.java b/Mage.Sets/src/mage/cards/w/WoebringerDemon.java index 26dbfd48dea..c8fc750d63b 100644 --- a/Mage.Sets/src/mage/cards/w/WoebringerDemon.java +++ b/Mage.Sets/src/mage/cards/w/WoebringerDemon.java @@ -84,7 +84,7 @@ class WoebringerDemonEffect extends OneShotEffect { } } Permanent sourceObject = game.getPermanent(source.getSourceId()); - if (sourceObject != null && sourceObject.getZoneChangeCounter(game) == source.getSourceObjectZoneChangeCounter()) { + if (sourceObject != null && sourceObject.getZoneChangeCounter(game) == source.getStackMomentSourceZCC()) { sourceObject.sacrifice(source, game); } return true; diff --git a/Mage.Sets/src/mage/cards/w/WorldgorgerDragon.java b/Mage.Sets/src/mage/cards/w/WorldgorgerDragon.java index ea9d06a53da..b64f0fdec2d 100644 --- a/Mage.Sets/src/mage/cards/w/WorldgorgerDragon.java +++ b/Mage.Sets/src/mage/cards/w/WorldgorgerDragon.java @@ -86,7 +86,7 @@ class WorldgorgerDragonEntersEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = source.getSourceObject(game); if (controller != null) { - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); if (exileId != null) { Set cardsToExile = new LinkedHashSet<>(); cardsToExile.addAll(game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source, game)); @@ -119,7 +119,7 @@ class WorldgorgerDragonLeavesEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = source.getSourceObject(game); if (controller != null && sourceObject != null) { - int zoneChangeCounter = (sourceObject instanceof PermanentToken) ? source.getSourceObjectZoneChangeCounter() : source.getSourceObjectZoneChangeCounter() - 1; + int zoneChangeCounter = (sourceObject instanceof PermanentToken) ? source.getStackMomentSourceZCC() : source.getStackMomentSourceZCC() - 1; ExileZone exile = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), zoneChangeCounter)); if (exile != null) { return controller.moveCards(exile.getCards(game), Zone.BATTLEFIELD, source, game, false, false, true, null); diff --git a/Mage.Sets/src/mage/cards/w/WraithViciousVigilante.java b/Mage.Sets/src/mage/cards/w/WraithViciousVigilante.java new file mode 100644 index 00000000000..9f3b15ae9d8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WraithViciousVigilante.java @@ -0,0 +1,44 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.abilities.keyword.CantBeBlockedSourceAbility; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WraithViciousVigilante extends CardImpl { + + public WraithViciousVigilante(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.DETECTIVE); + this.subtype.add(SubType.HERO); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // Double strike + this.addAbility(DoubleStrikeAbility.getInstance()); + + // Fear Gas -- Wraith can't be blocked. + this.addAbility(new CantBeBlockedSourceAbility().withFlavorWord("Fear Gas")); + } + + private WraithViciousVigilante(final WraithViciousVigilante card) { + super(card); + } + + @Override + public WraithViciousVigilante copy() { + return new WraithViciousVigilante(this); + } +} diff --git a/Mage.Sets/src/mage/cards/y/YannikScavengingSentinel.java b/Mage.Sets/src/mage/cards/y/YannikScavengingSentinel.java index 9c4997e3944..63400e1b2d6 100644 --- a/Mage.Sets/src/mage/cards/y/YannikScavengingSentinel.java +++ b/Mage.Sets/src/mage/cards/y/YannikScavengingSentinel.java @@ -93,7 +93,7 @@ class YannikScavengingSentinelEffect extends OneShotEffect { } int power = permanent.getPower().getValue(); new ExileTargetEffect(CardUtil.getExileZoneId( - game, source.getSourceId(), source.getSourceObjectZoneChangeCounter() + game, source.getSourceId(), source.getStackMomentSourceZCC() ), permanent.getIdName()).setTargetPointer(new FixedTarget(permanent, game)).apply(game, source); game.addDelayedTriggeredAbility(new OnLeaveReturnExiledAbility(), source); if (game.getState().getZone(permanent.getId()) != Zone.BATTLEFIELD) { diff --git a/Mage.Sets/src/mage/cards/z/ZethiArcaneBlademaster.java b/Mage.Sets/src/mage/cards/z/ZethiArcaneBlademaster.java index 31f5819e3c0..bfcf9c62c51 100644 --- a/Mage.Sets/src/mage/cards/z/ZethiArcaneBlademaster.java +++ b/Mage.Sets/src/mage/cards/z/ZethiArcaneBlademaster.java @@ -115,7 +115,7 @@ class ZethiArcaneBlademasterCastEffect extends OneShotEffect { if (controller == null) { return false; } - Cards cards = new CardsImpl(game.getExile().getAllCards(game, source.getControllerId())); + Cards cards = new CardsImpl(game.getExile().getCardsOwned(game, source.getControllerId())); cards.removeIf(uuid -> !game.getCard(uuid).getCounters(game).containsKey(CounterType.KICK)); if (cards.isEmpty()) { return false; diff --git a/Mage.Sets/src/mage/cards/z/ZodiarkUmbralGod.java b/Mage.Sets/src/mage/cards/z/ZodiarkUmbralGod.java index e3e13d4075f..c684fa0655d 100644 --- a/Mage.Sets/src/mage/cards/z/ZodiarkUmbralGod.java +++ b/Mage.Sets/src/mage/cards/z/ZodiarkUmbralGod.java @@ -14,7 +14,7 @@ import mage.constants.*; import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicate; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.CanBeSacrificedPredicate; @@ -66,8 +66,8 @@ public final class ZodiarkUmbralGod extends CardImpl { class ZodiarkUmbralGodEffect extends OneShotEffect { - private static final FilterPermanent filter = new FilterControlledPermanent("non-God creatures you control"); - private static final FilterPermanent filter2 = new FilterControlledPermanent("non-God creatures you control"); + private static final FilterPermanent filter = new FilterControlledCreaturePermanent("non-God creatures you control"); + private static final FilterPermanent filter2 = new FilterControlledCreaturePermanent("non-God creatures you control"); private static final Predicate predicate = Predicates.not(SubType.GOD.getPredicate()); static { diff --git a/Mage.Sets/src/mage/sets/Aetherdrift.java b/Mage.Sets/src/mage/sets/Aetherdrift.java index 2ee9386e3c4..18b97dd34f8 100644 --- a/Mage.Sets/src/mage/sets/Aetherdrift.java +++ b/Mage.Sets/src/mage/sets/Aetherdrift.java @@ -184,12 +184,12 @@ public final class Aetherdrift extends ExpansionSet { cards.add(new SetCardInfo("Fearless Swashbuckler", 481, Rarity.RARE, mage.cards.f.FearlessSwashbuckler.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fearless Swashbuckler", 545, Rarity.RARE, mage.cards.f.FearlessSwashbuckler.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Flood the Engine", 42, Rarity.COMMON, mage.cards.f.FloodTheEngine.class)); - cards.add(new SetCardInfo("Forest", 276, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 276, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Forest", 289, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 290, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 291, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 511, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 516, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 511, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Forest", 516, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Foul Roads", 255, Rarity.UNCOMMON, mage.cards.f.FoulRoads.class)); cards.add(new SetCardInfo("Fuel the Flames", 126, Rarity.UNCOMMON, mage.cards.f.FuelTheFlames.class)); cards.add(new SetCardInfo("Full Throttle", 127, Rarity.RARE, mage.cards.f.FullThrottle.class, NON_FULL_USE_VARIOUS)); @@ -248,12 +248,12 @@ public final class Aetherdrift extends ExpansionSet { cards.add(new SetCardInfo("Hulldrifter", 300, Rarity.COMMON, mage.cards.h.Hulldrifter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Interface Ace", 17, Rarity.COMMON, mage.cards.i.InterfaceAce.class)); cards.add(new SetCardInfo("Intimidation Tactics", 92, Rarity.UNCOMMON, mage.cards.i.IntimidationTactics.class)); - cards.add(new SetCardInfo("Island", 273, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 273, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Island", 280, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 281, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 282, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 508, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 513, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 508, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Island", 513, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Jibbirik Omnivore", 166, Rarity.COMMON, mage.cards.j.JibbirikOmnivore.class)); cards.add(new SetCardInfo("Jungle Hollow", 256, Rarity.COMMON, mage.cards.j.JungleHollow.class)); cards.add(new SetCardInfo("Kalakscion, Hunger Tyrant", 93, Rarity.UNCOMMON, mage.cards.k.KalakscionHungerTyrant.class)); @@ -324,12 +324,12 @@ public final class Aetherdrift extends ExpansionSet { cards.add(new SetCardInfo("Monument to Endurance", 237, Rarity.RARE, mage.cards.m.MonumentToEndurance.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Monument to Endurance", 394, Rarity.RARE, mage.cards.m.MonumentToEndurance.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Monument to Endurance", 499, Rarity.RARE, mage.cards.m.MonumentToEndurance.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 275, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 275, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Mountain", 286, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 287, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 288, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 510, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 515, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 510, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 515, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Mu Yanling, Wind Rider", 52, Rarity.MYTHIC, mage.cards.m.MuYanlingWindRider.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mu Yanling, Wind Rider", 379, Rarity.MYTHIC, mage.cards.m.MuYanlingWindRider.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mu Yanling, Wind Rider", 399, Rarity.MYTHIC, mage.cards.m.MuYanlingWindRider.class, NON_FULL_USE_VARIOUS)); @@ -359,12 +359,12 @@ public final class Aetherdrift extends ExpansionSet { cards.add(new SetCardInfo("Perilous Snare", 377, Rarity.RARE, mage.cards.p.PerilousSnare.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Perilous Snare", 430, Rarity.RARE, mage.cards.p.PerilousSnare.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Pit Automaton", 238, Rarity.UNCOMMON, mage.cards.p.PitAutomaton.class)); - cards.add(new SetCardInfo("Plains", 272, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 272, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Plains", 277, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 278, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 279, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 507, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 512, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 507, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Plains", 512, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Plow Through", 174, Rarity.UNCOMMON, mage.cards.p.PlowThrough.class)); cards.add(new SetCardInfo("Point the Way", 175, Rarity.UNCOMMON, mage.cards.p.PointTheWay.class)); cards.add(new SetCardInfo("Possession Engine", 54, Rarity.RARE, mage.cards.p.PossessionEngine.class, NON_FULL_USE_VARIOUS)); @@ -484,12 +484,12 @@ public final class Aetherdrift extends ExpansionSet { cards.add(new SetCardInfo("Sunbillow Verge", 373, Rarity.RARE, mage.cards.s.SunbillowVerge.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sunbillow Verge", 504, Rarity.RARE, mage.cards.s.SunbillowVerge.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sundial, Dawn Tyrant", 31, Rarity.UNCOMMON, mage.cards.s.SundialDawnTyrant.class)); - cards.add(new SetCardInfo("Swamp", 274, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 274, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Swamp", 283, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 284, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 285, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 509, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 514, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 509, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 514, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Swiftwater Cliffs", 265, Rarity.COMMON, mage.cards.s.SwiftwaterCliffs.class)); cards.add(new SetCardInfo("Swiftwing Assailant", 32, Rarity.COMMON, mage.cards.s.SwiftwingAssailant.class)); cards.add(new SetCardInfo("Syphon Fuel", 108, Rarity.COMMON, mage.cards.s.SyphonFuel.class)); diff --git a/Mage.Sets/src/mage/sets/AmonkhetRemastered.java b/Mage.Sets/src/mage/sets/AmonkhetRemastered.java index 0eb262a06d4..ad31187ba92 100644 --- a/Mage.Sets/src/mage/sets/AmonkhetRemastered.java +++ b/Mage.Sets/src/mage/sets/AmonkhetRemastered.java @@ -124,9 +124,9 @@ public class AmonkhetRemastered extends ExpansionSet { cards.add(new SetCardInfo("Forest", 294, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 295, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 296, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 297, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 297, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Forest", 298, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 299, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 299, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Forest", 300, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forsake the Worldly", 18, Rarity.COMMON, mage.cards.f.ForsakeTheWorldly.class)); cards.add(new SetCardInfo("Gate to the Afterlife", 271, Rarity.UNCOMMON, mage.cards.g.GateToTheAfterlife.class)); @@ -170,9 +170,9 @@ public class AmonkhetRemastered extends ExpansionSet { cards.add(new SetCardInfo("Island", 305, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 306, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 307, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 308, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 308, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Island", 309, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 310, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 310, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Island", 311, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jace, Unraveler of Secrets", 65, Rarity.MYTHIC, mage.cards.j.JaceUnravelerOfSecrets.class)); cards.add(new SetCardInfo("Kefnet the Mindful", 66, Rarity.MYTHIC, mage.cards.k.KefnetTheMindful.class)); @@ -202,9 +202,9 @@ public class AmonkhetRemastered extends ExpansionSet { cards.add(new SetCardInfo("Mountain", 312, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 313, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 314, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 315, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 315, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Mountain", 316, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 317, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 317, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Mountain", 318, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mouth // Feed", 202, Rarity.RARE, mage.cards.m.MouthFeed.class)); cards.add(new SetCardInfo("Naga Oracle", 69, Rarity.COMMON, mage.cards.n.NagaOracle.class)); @@ -238,9 +238,9 @@ public class AmonkhetRemastered extends ExpansionSet { cards.add(new SetCardInfo("Plains", 319, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 320, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 321, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 322, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 322, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Plains", 323, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 324, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 324, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Plains", 325, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Pouncing Cheetah", 207, Rarity.COMMON, mage.cards.p.PouncingCheetah.class)); cards.add(new SetCardInfo("Prepare // Fight", 251, Rarity.RARE, mage.cards.p.PrepareFight.class)); @@ -313,9 +313,9 @@ public class AmonkhetRemastered extends ExpansionSet { cards.add(new SetCardInfo("Swamp", 332, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 333, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 334, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 335, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 335, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Swamp", 336, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 337, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 337, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Swamp", 338, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sweltering Suns", 176, Rarity.RARE, mage.cards.s.SwelteringSuns.class)); cards.add(new SetCardInfo("Synchronized Strike", 222, Rarity.UNCOMMON, mage.cards.s.SynchronizedStrike.class)); diff --git a/Mage.Sets/src/mage/sets/AvatarTheLastAirbender.java b/Mage.Sets/src/mage/sets/AvatarTheLastAirbender.java index 10881e59c8a..920084b6117 100644 --- a/Mage.Sets/src/mage/sets/AvatarTheLastAirbender.java +++ b/Mage.Sets/src/mage/sets/AvatarTheLastAirbender.java @@ -59,7 +59,7 @@ public final class AvatarTheLastAirbender extends ExpansionSet { cards.add(new SetCardInfo("Fire Lord Sozin", 117, Rarity.MYTHIC, mage.cards.f.FireLordSozin.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fire Lord Sozin", 356, Rarity.MYTHIC, mage.cards.f.FireLordSozin.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fire Lord Zuko", 221, Rarity.RARE, mage.cards.f.FireLordZuko.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Fire Lord Zuko", 360, Rarity.RARE, mage.cards.f.FireLordZuko.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fire Lord Zuko", 360, Rarity.RARE, mage.cards.f.FireLordZuko.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Fire Nation Attacks", 133, Rarity.UNCOMMON, mage.cards.f.FireNationAttacks.class)); cards.add(new SetCardInfo("Fire Nation Engineer", 99, Rarity.UNCOMMON, mage.cards.f.FireNationEngineer.class)); cards.add(new SetCardInfo("Fire Sages", 136, Rarity.UNCOMMON, mage.cards.f.FireSages.class)); @@ -87,7 +87,7 @@ public final class AvatarTheLastAirbender extends ExpansionSet { cards.add(new SetCardInfo("Katara, Water Tribe's Hope", 351, Rarity.RARE, mage.cards.k.KataraWaterTribesHope.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Katara, the Fearless", 230, Rarity.RARE, mage.cards.k.KataraTheFearless.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Katara, the Fearless", 350, Rarity.RARE, mage.cards.k.KataraTheFearless.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Katara, the Fearless", 361, Rarity.RARE, mage.cards.k.KataraTheFearless.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Katara, the Fearless", 361, Rarity.RARE, mage.cards.k.KataraTheFearless.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Lightning Strike", 146, Rarity.COMMON, mage.cards.l.LightningStrike.class)); cards.add(new SetCardInfo("Long Feng, Grand Secretariat", 233, Rarity.UNCOMMON, mage.cards.l.LongFengGrandSecretariat.class)); cards.add(new SetCardInfo("Master Pakku", 63, Rarity.UNCOMMON, mage.cards.m.MasterPakku.class)); @@ -130,7 +130,7 @@ public final class AvatarTheLastAirbender extends ExpansionSet { cards.add(new SetCardInfo("Toph, the Blind Bandit", 198, Rarity.UNCOMMON, mage.cards.t.TophTheBlindBandit.class)); cards.add(new SetCardInfo("Toph, the First Metalbender", 247, Rarity.RARE, mage.cards.t.TophTheFirstMetalbender.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Toph, the First Metalbender", 353, Rarity.RARE, mage.cards.t.TophTheFirstMetalbender.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Toph, the First Metalbender", 362, Rarity.RARE, mage.cards.t.TophTheFirstMetalbender.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Toph, the First Metalbender", 362, Rarity.RARE, mage.cards.t.TophTheFirstMetalbender.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Turtle-Duck", 200, Rarity.COMMON, mage.cards.t.TurtleDuck.class)); cards.add(new SetCardInfo("Vindictive Warden", 249, Rarity.COMMON, mage.cards.v.VindictiveWarden.class)); cards.add(new SetCardInfo("Waterbending Lesson", 80, Rarity.COMMON, mage.cards.w.WaterbendingLesson.class)); diff --git a/Mage.Sets/src/mage/sets/DoctorWho.java b/Mage.Sets/src/mage/sets/DoctorWho.java index e90aca9d94d..9a907dce2a1 100644 --- a/Mage.Sets/src/mage/sets/DoctorWho.java +++ b/Mage.Sets/src/mage/sets/DoctorWho.java @@ -942,14 +942,14 @@ public final class DoctorWho extends ExpansionSet { //cards.add(new SetCardInfo("The Foretold Soldier", 395, Rarity.RARE, mage.cards.t.TheForetoldSoldier.class, NON_FULL_USE_VARIOUS)); //cards.add(new SetCardInfo("The Foretold Soldier", 707, Rarity.RARE, mage.cards.t.TheForetoldSoldier.class, NON_FULL_USE_VARIOUS)); //cards.add(new SetCardInfo("The Foretold Soldier", 986, Rarity.RARE, mage.cards.t.TheForetoldSoldier.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Fourth Doctor", "555z", Rarity.MYTHIC, mage.cards.t.TheFourthDoctor.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Fourth Doctor", 1006, Rarity.MYTHIC, mage.cards.t.TheFourthDoctor.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Fourth Doctor", 1146, Rarity.MYTHIC, mage.cards.t.TheFourthDoctor.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Fourth Doctor", 193, Rarity.MYTHIC, mage.cards.t.TheFourthDoctor.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Fourth Doctor", 2, Rarity.MYTHIC, mage.cards.t.TheFourthDoctor.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Fourth Doctor", 415, Rarity.MYTHIC, mage.cards.t.TheFourthDoctor.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Fourth Doctor", 555, Rarity.MYTHIC, mage.cards.t.TheFourthDoctor.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("The Fourth Doctor", 607, Rarity.MYTHIC, mage.cards.t.TheFourthDoctor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Fourth Doctor", "555z", Rarity.MYTHIC, mage.cards.t.TheFourthDoctor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Fourth Doctor", 1006, Rarity.MYTHIC, mage.cards.t.TheFourthDoctor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Fourth Doctor", 1146, Rarity.MYTHIC, mage.cards.t.TheFourthDoctor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Fourth Doctor", 193, Rarity.MYTHIC, mage.cards.t.TheFourthDoctor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Fourth Doctor", 2, Rarity.MYTHIC, mage.cards.t.TheFourthDoctor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Fourth Doctor", 415, Rarity.MYTHIC, mage.cards.t.TheFourthDoctor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Fourth Doctor", 555, Rarity.MYTHIC, mage.cards.t.TheFourthDoctor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Fourth Doctor", 607, Rarity.MYTHIC, mage.cards.t.TheFourthDoctor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Fugitive Doctor", 1008, Rarity.RARE, mage.cards.t.TheFugitiveDoctor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Fugitive Doctor", 1132, Rarity.RARE, mage.cards.t.TheFugitiveDoctor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Fugitive Doctor", 130, Rarity.RARE, mage.cards.t.TheFugitiveDoctor.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java b/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java index 019c578519d..476e986740b 100644 --- a/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java +++ b/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java @@ -353,6 +353,8 @@ public final class DuskmournHouseOfHorror extends ExpansionSet { cards.add(new SetCardInfo("Tyvar, the Pummeler", 353, Rarity.MYTHIC, mage.cards.t.TyvarThePummeler.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Tyvar, the Pummeler", 408, Rarity.MYTHIC, mage.cards.t.TyvarThePummeler.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Unable to Scream", 78, Rarity.COMMON, mage.cards.u.UnableToScream.class)); + cards.add(new SetCardInfo("Undead Sprinter", 237, Rarity.RARE, mage.cards.u.UndeadSprinter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Undead Sprinter", 350, Rarity.RARE, mage.cards.u.UndeadSprinter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Under the Skin", 203, Rarity.UNCOMMON, mage.cards.u.UnderTheSkin.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Under the Skin", 323, Rarity.UNCOMMON, mage.cards.u.UnderTheSkin.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Unidentified Hovership", 305, Rarity.RARE, mage.cards.u.UnidentifiedHovership.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage.Sets/src/mage/sets/DuskmournHouseOfHorrorCommander.java b/Mage.Sets/src/mage/sets/DuskmournHouseOfHorrorCommander.java index 7a2f0b977f2..353c7fc6d8a 100644 --- a/Mage.Sets/src/mage/sets/DuskmournHouseOfHorrorCommander.java +++ b/Mage.Sets/src/mage/sets/DuskmournHouseOfHorrorCommander.java @@ -29,7 +29,7 @@ public final class DuskmournHouseOfHorrorCommander extends ExpansionSet { cards.add(new SetCardInfo("Arcane Sanctum", 259, Rarity.UNCOMMON, mage.cards.a.ArcaneSanctum.class)); cards.add(new SetCardInfo("Arcane Signet", 92, Rarity.COMMON, mage.cards.a.ArcaneSignet.class)); cards.add(new SetCardInfo("Archetype of Imagination", 111, Rarity.UNCOMMON, mage.cards.a.ArchetypeOfImagination.class)); - cards.add(new SetCardInfo("Archon of Cruelty", 371, Rarity.MYTHIC, mage.cards.a.ArchonOfCruelty.class)); + cards.add(new SetCardInfo("Archon of Cruelty", 371, Rarity.MYTHIC, mage.cards.a.ArchonOfCruelty.class, FULL_ART)); cards.add(new SetCardInfo("Arixmethes, Slumbering Isle", 211, Rarity.RARE, mage.cards.a.ArixmethesSlumberingIsle.class)); cards.add(new SetCardInfo("Arvinox, the Mind Flail", 130, Rarity.MYTHIC, mage.cards.a.ArvinoxTheMindFlail.class)); cards.add(new SetCardInfo("Ash Barrens", 260, Rarity.COMMON, mage.cards.a.AshBarrens.class)); @@ -126,7 +126,7 @@ public final class DuskmournHouseOfHorrorCommander extends ExpansionSet { cards.add(new SetCardInfo("Gnarlwood Dryad", 180, Rarity.UNCOMMON, mage.cards.g.GnarlwoodDryad.class)); cards.add(new SetCardInfo("Golgari Rot Farm", 279, Rarity.UNCOMMON, mage.cards.g.GolgariRotFarm.class)); cards.add(new SetCardInfo("Golgari Signet", 246, Rarity.UNCOMMON, mage.cards.g.GolgariSignet.class)); - cards.add(new SetCardInfo("Goryo's Vengeance", 372, Rarity.MYTHIC, mage.cards.g.GoryosVengeance.class)); + cards.add(new SetCardInfo("Goryo's Vengeance", 372, Rarity.MYTHIC, mage.cards.g.GoryosVengeance.class, FULL_ART)); cards.add(new SetCardInfo("Grapple with the Past", 82, Rarity.COMMON, mage.cards.g.GrappleWithThePast.class)); cards.add(new SetCardInfo("Graven Cairns", 280, Rarity.RARE, mage.cards.g.GravenCairns.class)); cards.add(new SetCardInfo("Gray Merchant of Asphodel", 142, Rarity.UNCOMMON, mage.cards.g.GrayMerchantOfAsphodel.class)); @@ -161,7 +161,7 @@ public final class DuskmournHouseOfHorrorCommander extends ExpansionSet { cards.add(new SetCardInfo("Life Insurance", 224, Rarity.RARE, mage.cards.l.LifeInsurance.class)); cards.add(new SetCardInfo("Light Up the Stage", 166, Rarity.UNCOMMON, mage.cards.l.LightUpTheStage.class)); cards.add(new SetCardInfo("Lightning Greaves", 93, Rarity.UNCOMMON, mage.cards.l.LightningGreaves.class)); - cards.add(new SetCardInfo("Living Death", 373, Rarity.MYTHIC, mage.cards.l.LivingDeath.class)); + cards.add(new SetCardInfo("Living Death", 373, Rarity.MYTHIC, mage.cards.l.LivingDeath.class, FULL_ART)); cards.add(new SetCardInfo("Llanowar Wastes", 287, Rarity.RARE, mage.cards.l.LlanowarWastes.class)); cards.add(new SetCardInfo("Mask of Griselbrand", 145, Rarity.RARE, mage.cards.m.MaskOfGriselbrand.class)); cards.add(new SetCardInfo("Massacre Girl", 146, Rarity.RARE, mage.cards.m.MassacreGirl.class)); diff --git a/Mage.Sets/src/mage/sets/EdgeOfEternities.java b/Mage.Sets/src/mage/sets/EdgeOfEternities.java index 59cb657ee78..241e780d155 100644 --- a/Mage.Sets/src/mage/sets/EdgeOfEternities.java +++ b/Mage.Sets/src/mage/sets/EdgeOfEternities.java @@ -312,7 +312,7 @@ public final class EdgeOfEternities extends ExpansionSet { cards.add(new SetCardInfo("Sledge-Class Seedship", 346, Rarity.RARE, mage.cards.s.SledgeClassSeedship.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sothera, the Supervoid", 115, Rarity.MYTHIC, mage.cards.s.SotheraTheSupervoid.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sothera, the Supervoid", 360, Rarity.MYTHIC, mage.cards.s.SotheraTheSupervoid.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Sothera, the Supervoid", 382, Rarity.MYTHIC, mage.cards.s.SotheraTheSupervoid.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sothera, the Supervoid", 382, Rarity.MYTHIC, mage.cards.s.SotheraTheSupervoid.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Sothera, the Supervoid", 386, Rarity.MYTHIC, mage.cards.s.SotheraTheSupervoid.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Space-Time Anomaly", 229, Rarity.RARE, mage.cards.s.SpaceTimeAnomaly.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Space-Time Anomaly", 315, Rarity.RARE, mage.cards.s.SpaceTimeAnomaly.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage.Sets/src/mage/sets/EldritchMoonPromos.java b/Mage.Sets/src/mage/sets/EldritchMoonPromos.java index 38d1aabb59c..8083a9daf47 100644 --- a/Mage.Sets/src/mage/sets/EldritchMoonPromos.java +++ b/Mage.Sets/src/mage/sets/EldritchMoonPromos.java @@ -26,7 +26,7 @@ public class EldritchMoonPromos extends ExpansionSet { cards.add(new SetCardInfo("Bedlam Reveler", "118s", Rarity.RARE, mage.cards.b.BedlamReveler.class)); cards.add(new SetCardInfo("Bloodhall Priest", "181s", Rarity.RARE, mage.cards.b.BloodhallPriest.class)); cards.add(new SetCardInfo("Brisela, Voice of Nightmares", "15bs", Rarity.MYTHIC, mage.cards.b.BriselaVoiceOfNightmares.class)); - cards.add(new SetCardInfo("Bruna, the Fading Light", "15as", Rarity.RARE, mage.cards.b.BrunaTheFadingLight.class)); + cards.add(new SetCardInfo("Bruna, the Fading Light", "15s", Rarity.RARE, mage.cards.b.BrunaTheFadingLight.class)); cards.add(new SetCardInfo("Coax from the Blind Eternities", "51s", Rarity.RARE, mage.cards.c.CoaxFromTheBlindEternities.class)); cards.add(new SetCardInfo("Collective Brutality", "85s", Rarity.RARE, mage.cards.c.CollectiveBrutality.class)); cards.add(new SetCardInfo("Collective Defiance", "123s", Rarity.RARE, mage.cards.c.CollectiveDefiance.class)); @@ -48,7 +48,7 @@ public class EldritchMoonPromos extends ExpansionSet { cards.add(new SetCardInfo("Gisela, the Broken Blade", "28s", Rarity.MYTHIC, mage.cards.g.GiselaTheBrokenBlade.class)); cards.add(new SetCardInfo("Grim Flayer", "184s", Rarity.MYTHIC, mage.cards.g.GrimFlayer.class)); cards.add(new SetCardInfo("Hanweir Battlements", "204s", Rarity.RARE, mage.cards.h.HanweirBattlements.class)); - cards.add(new SetCardInfo("Hanweir Garrison", "130as", Rarity.RARE, mage.cards.h.HanweirGarrison.class)); + cards.add(new SetCardInfo("Hanweir Garrison", "130s", Rarity.RARE, mage.cards.h.HanweirGarrison.class)); cards.add(new SetCardInfo("Hanweir, the Writhing Township", "130bs", Rarity.RARE, mage.cards.h.HanweirTheWrithingTownship.class)); cards.add(new SetCardInfo("Harmless Offering", "131s", Rarity.RARE, mage.cards.h.HarmlessOffering.class)); cards.add(new SetCardInfo("Heron's Grace Champion", 185, Rarity.RARE, mage.cards.h.HeronsGraceChampion.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage.Sets/src/mage/sets/FinalFantasy.java b/Mage.Sets/src/mage/sets/FinalFantasy.java index 6d98c247387..b638ad98011 100644 --- a/Mage.Sets/src/mage/sets/FinalFantasy.java +++ b/Mage.Sets/src/mage/sets/FinalFantasy.java @@ -512,6 +512,8 @@ public final class FinalFantasy extends ExpansionSet { cards.add(new SetCardInfo("Summon: Anima", 364, Rarity.UNCOMMON, mage.cards.s.SummonAnima.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summon: Bahamut", 1, Rarity.MYTHIC, mage.cards.s.SummonBahamut.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summon: Bahamut", 356, Rarity.MYTHIC, mage.cards.s.SummonBahamut.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Summon: Brynhildr", 160, Rarity.RARE, mage.cards.s.SummonBrynhildr.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Summon: Brynhildr", 366, Rarity.RARE, mage.cards.s.SummonBrynhildr.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summon: Choco/Mog", 35, Rarity.COMMON, mage.cards.s.SummonChocoMog.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summon: Choco/Mog", 358, Rarity.COMMON, mage.cards.s.SummonChocoMog.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Summon: Esper Maduin", 185, Rarity.RARE, mage.cards.s.SummonEsperMaduin.class, NON_FULL_USE_VARIOUS)); @@ -601,7 +603,7 @@ public final class FinalFantasy extends ExpansionSet { cards.add(new SetCardInfo("Traveling Chocobo", "551b", Rarity.MYTHIC, mage.cards.t.TravelingChocobo.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Traveling Chocobo", "551c", Rarity.MYTHIC, mage.cards.t.TravelingChocobo.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Traveling Chocobo", "551d", Rarity.MYTHIC, mage.cards.t.TravelingChocobo.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Traveling Chocobo", "551f", Rarity.MYTHIC, mage.cards.t.TravelingChocobo.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Traveling Chocobo", "551f", Rarity.MYTHIC, mage.cards.t.TravelingChocobo.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Traveling Chocobo", 210, Rarity.MYTHIC, mage.cards.t.TravelingChocobo.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Traveling Chocobo", 406, Rarity.MYTHIC, mage.cards.t.TravelingChocobo.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Traveling Chocobo", 551, Rarity.MYTHIC, mage.cards.t.TravelingChocobo.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage.Sets/src/mage/sets/FromTheVaultTransform.java b/Mage.Sets/src/mage/sets/FromTheVaultTransform.java index 47f7159343c..6bbc1f7e337 100644 --- a/Mage.Sets/src/mage/sets/FromTheVaultTransform.java +++ b/Mage.Sets/src/mage/sets/FromTheVaultTransform.java @@ -28,7 +28,7 @@ public final class FromTheVaultTransform extends ExpansionSet { cards.add(new SetCardInfo("Arlinn, Embraced by the Moon", 3, Rarity.MYTHIC, mage.cards.a.ArlinnEmbracedByTheMoon.class)); cards.add(new SetCardInfo("Bloodline Keeper", 4, Rarity.MYTHIC, mage.cards.b.BloodlineKeeper.class)); cards.add(new SetCardInfo("Lord of Lineage", 4, Rarity.MYTHIC, mage.cards.l.LordOfLineage.class)); - cards.add(new SetCardInfo("Bruna, the Fading Light", "5a", Rarity.MYTHIC, mage.cards.b.BrunaTheFadingLight.class)); + cards.add(new SetCardInfo("Bruna, the Fading Light", 5, Rarity.MYTHIC, mage.cards.b.BrunaTheFadingLight.class)); cards.add(new SetCardInfo("Brisela, Voice of Nightmares", "5b", Rarity.MYTHIC, mage.cards.b.BriselaVoiceOfNightmares.class)); cards.add(new SetCardInfo("Chandra, Fire of Kaladesh", 6, Rarity.MYTHIC, mage.cards.c.ChandraFireOfKaladesh.class)); cards.add(new SetCardInfo("Chandra, Roaring Flame", 6, Rarity.MYTHIC, mage.cards.c.ChandraRoaringFlame.class)); diff --git a/Mage.Sets/src/mage/sets/InnistradRemastered.java b/Mage.Sets/src/mage/sets/InnistradRemastered.java index 6a3c2cdc809..cb192ab1fb0 100644 --- a/Mage.Sets/src/mage/sets/InnistradRemastered.java +++ b/Mage.Sets/src/mage/sets/InnistradRemastered.java @@ -107,7 +107,7 @@ public class InnistradRemastered extends ExpansionSet { cards.add(new SetCardInfo("Bramble Wurm", 187, Rarity.COMMON, mage.cards.b.BrambleWurm.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Bramble Wurm", 407, Rarity.COMMON, mage.cards.b.BrambleWurm.class, RETRO_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Brisela, Voice of Nightmares", "14b", Rarity.MYTHIC, mage.cards.b.BriselaVoiceOfNightmares.class)); - cards.add(new SetCardInfo("Bruna, the Fading Light", "14a", Rarity.RARE, mage.cards.b.BrunaTheFadingLight.class)); + cards.add(new SetCardInfo("Bruna, the Fading Light", 14, Rarity.RARE, mage.cards.b.BrunaTheFadingLight.class)); cards.add(new SetCardInfo("Burning Vengeance", 147, Rarity.UNCOMMON, mage.cards.b.BurningVengeance.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Burning Vengeance", 397, Rarity.UNCOMMON, mage.cards.b.BurningVengeance.class,RETRO_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Butcher Ghoul", 373, Rarity.COMMON, mage.cards.b.ButcherGhoul.class, RETRO_ART_USE_VARIOUS)); @@ -272,7 +272,7 @@ public class InnistradRemastered extends ExpansionSet { cards.add(new SetCardInfo("Hamlet Captain", 201, Rarity.UNCOMMON, mage.cards.h.HamletCaptain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Hamlet Captain", 413, Rarity.UNCOMMON, mage.cards.h.HamletCaptain.class, RETRO_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Hanweir Battlements", 279, Rarity.RARE, mage.cards.h.HanweirBattlements.class)); - cards.add(new SetCardInfo("Hanweir Garrison", "157a", Rarity.RARE, mage.cards.h.HanweirGarrison.class)); + cards.add(new SetCardInfo("Hanweir Garrison", 157, Rarity.RARE, mage.cards.h.HanweirGarrison.class)); cards.add(new SetCardInfo("Hanweir Watchkeep", 158, Rarity.COMMON, mage.cards.h.HanweirWatchkeep.class)); cards.add(new SetCardInfo("Hanweir, the Writhing Township", "157b", Rarity.RARE, mage.cards.h.HanweirTheWrithingTownship.class)); cards.add(new SetCardInfo("Harvest Hand", 265, Rarity.COMMON, mage.cards.h.HarvestHand.class)); diff --git a/Mage.Sets/src/mage/sets/LoveYourLGS2020.java b/Mage.Sets/src/mage/sets/LoveYourLGS2020.java new file mode 100644 index 00000000000..c7a9e96739b --- /dev/null +++ b/Mage.Sets/src/mage/sets/LoveYourLGS2020.java @@ -0,0 +1,26 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * https://scryfall.com/sets/plg20 + */ +public class LoveYourLGS2020 extends ExpansionSet { + + private static final LoveYourLGS2020 instance = new LoveYourLGS2020(); + + public static LoveYourLGS2020 getInstance() { + return instance; + } + + private LoveYourLGS2020() { + super("Love Your LGS 2020", "PLG20", ExpansionSet.buildDate(2020, 5, 18), SetType.PROMOTIONAL); + this.hasBoosters = false; + this.hasBasicLands = false; + + cards.add(new SetCardInfo("Hangarback Walker", 2, Rarity.RARE, mage.cards.h.HangarbackWalker.class)); + cards.add(new SetCardInfo("Reliquary Tower", 1, Rarity.RARE, mage.cards.r.ReliquaryTower.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/LoveYourLGS2022.java b/Mage.Sets/src/mage/sets/LoveYourLGS2022.java new file mode 100644 index 00000000000..14bf53b4299 --- /dev/null +++ b/Mage.Sets/src/mage/sets/LoveYourLGS2022.java @@ -0,0 +1,26 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * https://scryfall.com/sets/plg22 + */ +public class LoveYourLGS2022 extends ExpansionSet { + + private static final LoveYourLGS2022 instance = new LoveYourLGS2022(); + + public static LoveYourLGS2022 getInstance() { + return instance; + } + + private LoveYourLGS2022() { + super("Love Your LGS 2022", "PLG22", ExpansionSet.buildDate(2022, 7, 1), SetType.PROMOTIONAL); + this.hasBoosters = false; + this.hasBasicLands = false; + + cards.add(new SetCardInfo("Sol Ring", 1, Rarity.RARE, mage.cards.s.SolRing.class, RETRO_ART)); + cards.add(new SetCardInfo("Thought Vessel", 2, Rarity.RARE, mage.cards.t.ThoughtVessel.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/LoveYourLGS2024.java b/Mage.Sets/src/mage/sets/LoveYourLGS2024.java new file mode 100644 index 00000000000..c1dfa7f99b7 --- /dev/null +++ b/Mage.Sets/src/mage/sets/LoveYourLGS2024.java @@ -0,0 +1,29 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * https://scryfall.com/sets/plg24 + */ +public class LoveYourLGS2024 extends ExpansionSet { + + private static final LoveYourLGS2024 instance = new LoveYourLGS2024(); + + public static LoveYourLGS2024 getInstance() { + return instance; + } + + private LoveYourLGS2024() { + super("Love Your LGS 2024", "PLG24", ExpansionSet.buildDate(2024, 8, 6), SetType.PROMOTIONAL); + this.hasBoosters = false; + this.hasBasicLands = false; + + cards.add(new SetCardInfo("Cut Down", "3J", Rarity.RARE, mage.cards.c.CutDown.class)); + cards.add(new SetCardInfo("Lay Down Arms", "1J", Rarity.RARE, mage.cards.l.LayDownArms.class)); + cards.add(new SetCardInfo("Sakura-Tribe Elder", 1, Rarity.RARE, mage.cards.s.SakuraTribeElder.class, FULL_ART)); + cards.add(new SetCardInfo("Sheoldred's Edict", "4J", Rarity.RARE, mage.cards.s.SheoldredsEdict.class)); + cards.add(new SetCardInfo("Sleight of Hand", "2J", Rarity.RARE, mage.cards.s.SleightOfHand.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/MKMStandardShowdown.java b/Mage.Sets/src/mage/sets/MKMStandardShowdown.java new file mode 100644 index 00000000000..13e96e388ac --- /dev/null +++ b/Mage.Sets/src/mage/sets/MKMStandardShowdown.java @@ -0,0 +1,29 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * https://scryfall.com/sets/pss4 + */ +public class MKMStandardShowdown extends ExpansionSet { + + private static final MKMStandardShowdown instance = new MKMStandardShowdown(); + + public static MKMStandardShowdown getInstance() { + return instance; + } + + private MKMStandardShowdown() { + super("MKM Standard Showdown", "PSS4", ExpansionSet.buildDate(2024, 2, 10), SetType.PROMOTIONAL); + this.hasBoosters = false; + this.hasBasicLands = true; + + cards.add(new SetCardInfo("Forest", 5, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Island", 2, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 4, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Plains", 1, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 3, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); + } +} diff --git a/Mage.Sets/src/mage/sets/MTGArenaPromos.java b/Mage.Sets/src/mage/sets/MTGArenaPromos.java index e0d576176eb..0a74ed8705f 100644 --- a/Mage.Sets/src/mage/sets/MTGArenaPromos.java +++ b/Mage.Sets/src/mage/sets/MTGArenaPromos.java @@ -23,56 +23,56 @@ public class MTGArenaPromos extends ExpansionSet { cards.add(new SetCardInfo("Bladewing the Risen", 104, Rarity.RARE, mage.cards.b.BladewingTheRisen.class)); cards.add(new SetCardInfo("Duress", 6, Rarity.COMMON, mage.cards.d.Duress.class)); cards.add(new SetCardInfo("Firemind's Research", 5, Rarity.RARE, mage.cards.f.FiremindsResearch.class)); - cards.add(new SetCardInfo("Forest", 205, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 205, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Forest", 210, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 215, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 220, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 225, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 230, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 235, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 240, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 245, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 235, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Forest", 240, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Forest", 245, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Ghalta, Primal Hunger", 7, Rarity.RARE, mage.cards.g.GhaltaPrimalHunger.class)); cards.add(new SetCardInfo("Hanna, Ship's Navigator", 105, Rarity.RARE, mage.cards.h.HannaShipsNavigator.class)); - cards.add(new SetCardInfo("Island", 202, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 202, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Island", 207, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 212, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 217, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 222, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 227, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 232, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 237, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 242, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 232, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Island", 237, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Island", 242, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Llanowar Elves", 4, Rarity.COMMON, mage.cards.l.LlanowarElves.class)); - cards.add(new SetCardInfo("Mountain", 204, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 204, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Mountain", 209, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 214, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 219, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 224, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 229, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 234, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 239, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 244, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 201, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 234, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 239, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 244, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Plains", 201, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Plains", 206, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 211, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 216, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 221, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 226, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 231, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 236, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 241, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 231, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Plains", 236, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Plains", 241, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Ral, Izzet Viceroy", 1, Rarity.MYTHIC, mage.cards.r.RalIzzetViceroy.class)); cards.add(new SetCardInfo("Rhys the Redeemed", 101, Rarity.RARE, mage.cards.r.RhysTheRedeemed.class)); - cards.add(new SetCardInfo("Swamp", 203, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 203, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Swamp", 208, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 213, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 218, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 223, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 228, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 233, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 238, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 243, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 233, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 238, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 243, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Talrand, Sky Summoner", 102, Rarity.RARE, mage.cards.t.TalrandSkySummoner.class)); cards.add(new SetCardInfo("Teferi, Hero of Dominaria", 2, Rarity.MYTHIC, mage.cards.t.TeferiHeroOfDominaria.class)); cards.add(new SetCardInfo("The Gitrog Monster", 103, Rarity.MYTHIC, mage.cards.t.TheGitrogMonster.class)); diff --git a/Mage.Sets/src/mage/sets/MagicFest2019.java b/Mage.Sets/src/mage/sets/MagicFest2019.java index fd2552c3be9..aba996a243c 100644 --- a/Mage.Sets/src/mage/sets/MagicFest2019.java +++ b/Mage.Sets/src/mage/sets/MagicFest2019.java @@ -22,7 +22,7 @@ public final class MagicFest2019 extends ExpansionSet { cards.add(new SetCardInfo("Forest", 6, Rarity.LAND, mage.cards.basiclands.Forest.class)); cards.add(new SetCardInfo("Island", 3, Rarity.LAND, mage.cards.basiclands.Island.class)); - cards.add(new SetCardInfo("Lightning Bolt", 1, Rarity.RARE, mage.cards.l.LightningBolt.class)); + cards.add(new SetCardInfo("Lightning Bolt", 1, Rarity.RARE, mage.cards.l.LightningBolt.class, FULL_ART)); cards.add(new SetCardInfo("Mountain", 5, Rarity.LAND, mage.cards.basiclands.Mountain.class)); cards.add(new SetCardInfo("Plains", 2, Rarity.LAND, mage.cards.basiclands.Plains.class)); cards.add(new SetCardInfo("Sol Ring", 7, Rarity.RARE, mage.cards.s.SolRing.class)); diff --git a/Mage.Sets/src/mage/sets/MagicFest2020.java b/Mage.Sets/src/mage/sets/MagicFest2020.java index eddf156086f..25f30549164 100644 --- a/Mage.Sets/src/mage/sets/MagicFest2020.java +++ b/Mage.Sets/src/mage/sets/MagicFest2020.java @@ -23,7 +23,7 @@ public class MagicFest2020 extends ExpansionSet { cards.add(new SetCardInfo("Forest", 6, Rarity.LAND, mage.cards.basiclands.Forest.class)); cards.add(new SetCardInfo("Island", 3, Rarity.LAND, mage.cards.basiclands.Island.class)); cards.add(new SetCardInfo("Mountain", 5, Rarity.LAND, mage.cards.basiclands.Mountain.class)); - cards.add(new SetCardInfo("Path to Exile", 1, Rarity.RARE, mage.cards.p.PathToExile.class)); + cards.add(new SetCardInfo("Path to Exile", 1, Rarity.RARE, mage.cards.p.PathToExile.class, FULL_ART)); cards.add(new SetCardInfo("Plains", 2, Rarity.LAND, mage.cards.basiclands.Plains.class)); cards.add(new SetCardInfo("Swamp", 4, Rarity.LAND, mage.cards.basiclands.Swamp.class)); } diff --git a/Mage.Sets/src/mage/sets/MagicFest2023.java b/Mage.Sets/src/mage/sets/MagicFest2023.java new file mode 100644 index 00000000000..7d352cfd7d5 --- /dev/null +++ b/Mage.Sets/src/mage/sets/MagicFest2023.java @@ -0,0 +1,29 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * https://scryfall.com/sets/pf23 + * + * @author resech + */ +public class MagicFest2023 extends ExpansionSet { + + private static final MagicFest2023 instance = new MagicFest2023(); + + public static MagicFest2023 getInstance() { + return instance; + } + + private MagicFest2023() { + super("MagicFest 2023", "PF23", ExpansionSet.buildDate(2023, 7, 1), SetType.PROMOTIONAL); + hasBasicLands = false; + + cards.add(new SetCardInfo("Gandalf, Friend of the Shire", 1, Rarity.RARE, mage.cards.g.GandalfFriendOfTheShire.class)); + cards.add(new SetCardInfo("Reliquary Tower", 3, Rarity.RARE, mage.cards.r.ReliquaryTower.class, FULL_ART)); + cards.add(new SetCardInfo("TARDIS", 4, Rarity.RARE, mage.cards.t.TARDIS.class)); + cards.add(new SetCardInfo("Tranquil Thicket", 2, Rarity.RARE, mage.cards.t.TranquilThicket.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/MagicFest2024.java b/Mage.Sets/src/mage/sets/MagicFest2024.java new file mode 100644 index 00000000000..1afd639a404 --- /dev/null +++ b/Mage.Sets/src/mage/sets/MagicFest2024.java @@ -0,0 +1,26 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * https://scryfall.com/sets/pf24 + * + * @author resech + */ +public class MagicFest2024 extends ExpansionSet { + + private static final MagicFest2024 instance = new MagicFest2024(); + + public static MagicFest2024 getInstance() { + return instance; + } + + private MagicFest2024() { + super("MagicFest 2024", "PF24", ExpansionSet.buildDate(2024, 1, 1), SetType.PROMOTIONAL); + hasBasicLands = false; + + cards.add(new SetCardInfo("Counterspell", 1, Rarity.RARE, mage.cards.c.Counterspell.class, FULL_ART)); + } +} diff --git a/Mage.Sets/src/mage/sets/MagicFest2025.java b/Mage.Sets/src/mage/sets/MagicFest2025.java index 072c4c22240..e623b9d710b 100644 --- a/Mage.Sets/src/mage/sets/MagicFest2025.java +++ b/Mage.Sets/src/mage/sets/MagicFest2025.java @@ -21,16 +21,19 @@ public class MagicFest2025 extends ExpansionSet { super("MagicFest 2025", "PF25", ExpansionSet.buildDate(2025, 2, 3), SetType.PROMOTIONAL); hasBasicLands = false; - cards.add(new SetCardInfo("Avacyn's Pilgrim", "1F", Rarity.RARE, mage.cards.a.AvacynsPilgrim.class, FULL_ART)); - cards.add(new SetCardInfo("Ponder", 2, Rarity.RARE, mage.cards.p.Ponder.class)); - cards.add(new SetCardInfo("Serra the Benevolent", 1, Rarity.MYTHIC, mage.cards.s.SerraTheBenevolent.class, RETRO_ART)); - cards.add(new SetCardInfo("The First Sliver", 3, Rarity.MYTHIC, mage.cards.t.TheFirstSliver.class)); - cards.add(new SetCardInfo("Yoshimaru, Ever Faithful", 5, Rarity.MYTHIC, mage.cards.y.YoshimaruEverFaithful.class)); - cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 6, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class, RETRO_ART)); - cards.add(new SetCardInfo("Sliver Hive", 7, Rarity.RARE, mage.cards.s.SliverHive.class, RETRO_ART)); - cards.add(new SetCardInfo("Tifa Lockhart", 9, Rarity.RARE, mage.cards.t.TifaLockhart.class)); cards.add(new SetCardInfo("Arcane Signet", 10, Rarity.RARE, mage.cards.a.ArcaneSignet.class)); + cards.add(new SetCardInfo("Avacyn's Pilgrim", "1F", Rarity.RARE, mage.cards.a.AvacynsPilgrim.class, FULL_ART)); + cards.add(new SetCardInfo("Lightning Bolt", 13, Rarity.RARE, mage.cards.l.LightningBolt.class)); + cards.add(new SetCardInfo("Ponder", 2, Rarity.RARE, mage.cards.p.Ponder.class)); cards.add(new SetCardInfo("Rograkh, Son of Rohgahh", 11, Rarity.RARE, mage.cards.r.RograkhSonOfRohgahh.class)); - cards.add(new SetCardInfo("Swords to Plowshares", 13, Rarity.RARE, mage.cards.s.SwordsToPlowshares.class)); + cards.add(new SetCardInfo("Scourge of Valkas", 14, Rarity.RARE, mage.cards.s.ScourgeOfValkas.class, RETRO_ART)); + cards.add(new SetCardInfo("Serra the Benevolent", 1, Rarity.MYTHIC, mage.cards.s.SerraTheBenevolent.class, RETRO_ART)); + cards.add(new SetCardInfo("Sliver Hive", 7, Rarity.RARE, mage.cards.s.SliverHive.class, RETRO_ART)); + cards.add(new SetCardInfo("Swords to Plowshares", 12, Rarity.RARE, mage.cards.s.SwordsToPlowshares.class)); + cards.add(new SetCardInfo("The First Sliver", 3, Rarity.MYTHIC, mage.cards.t.TheFirstSliver.class)); + cards.add(new SetCardInfo("The Ur-Dragon", 15, Rarity.MYTHIC, mage.cards.t.TheUrDragon.class)); + cards.add(new SetCardInfo("Tifa Lockhart", 9, Rarity.RARE, mage.cards.t.TifaLockhart.class)); + cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 6, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class, RETRO_ART)); + cards.add(new SetCardInfo("Yoshimaru, Ever Faithful", 5, Rarity.MYTHIC, mage.cards.y.YoshimaruEverFaithful.class)); } } diff --git a/Mage.Sets/src/mage/sets/MagicOnlinePromos.java b/Mage.Sets/src/mage/sets/MagicOnlinePromos.java index c58cb67c6a9..bcb19295bb5 100644 --- a/Mage.Sets/src/mage/sets/MagicOnlinePromos.java +++ b/Mage.Sets/src/mage/sets/MagicOnlinePromos.java @@ -963,7 +963,7 @@ public class MagicOnlinePromos extends ExpansionSet { cards.add(new SetCardInfo("Forest", 279, Rarity.LAND, mage.cards.basiclands.Forest.class, RETRO_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 293, Rarity.LAND, mage.cards.basiclands.Forest.class, RETRO_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 297, Rarity.LAND, mage.cards.basiclands.Forest.class, RETRO_ART_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 302, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 302, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 31983, Rarity.LAND, mage.cards.basiclands.Forest.class, RETRO_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 31991, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 32005, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); @@ -972,7 +972,7 @@ public class MagicOnlinePromos extends ExpansionSet { cards.add(new SetCardInfo("Forest", 40050, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 40060, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 40094, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Forest", 53875, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 53875, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_ZEN_VARIOUS )); cards.add(new SetCardInfo("Forest", 58261, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 73628, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 81872, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); @@ -1352,7 +1352,7 @@ public class MagicOnlinePromos extends ExpansionSet { cards.add(new SetCardInfo("Island", 40052, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 40062, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 40100, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Island", 53881, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 53881, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_ZEN_VARIOUS )); cards.add(new SetCardInfo("Island", 58255, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 73634, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 81846, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); @@ -1546,7 +1546,7 @@ public class MagicOnlinePromos extends ExpansionSet { cards.add(new SetCardInfo("Light Up the Night", 94000, Rarity.RARE, mage.cards.l.LightUpTheNight.class)); cards.add(new SetCardInfo("Light-Paws, Emperor's Voice", 97865, Rarity.RARE, mage.cards.l.LightPawsEmperorsVoice.class)); cards.add(new SetCardInfo("Lightning Bolt", 35932, Rarity.COMMON, mage.cards.l.LightningBolt.class, RETRO_ART_USE_VARIOUS)); - cards.add(new SetCardInfo("Lightning Bolt", 36224, Rarity.COMMON, mage.cards.l.LightningBolt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lightning Bolt", 36224, Rarity.COMMON, mage.cards.l.LightningBolt.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Lightning Bolt", 72886, Rarity.RARE, mage.cards.l.LightningBolt.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Lightning Bolt", 102261, Rarity.UNCOMMON, mage.cards.l.LightningBolt.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lightning Dragon", 32196, Rarity.RARE, mage.cards.l.LightningDragon.class, RETRO_ART)); @@ -1778,7 +1778,7 @@ public class MagicOnlinePromos extends ExpansionSet { cards.add(new SetCardInfo("Mountain", 40054, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 40064, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 40096, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mountain", 53877, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 53877, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_ZEN_VARIOUS )); cards.add(new SetCardInfo("Mountain", 58259, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 73630, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 81864, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); @@ -2037,7 +2037,7 @@ public class MagicOnlinePromos extends ExpansionSet { cards.add(new SetCardInfo("Plains", 40066, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 40098, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 48582, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Plains", 53879, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 53879, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_ZEN_VARIOUS )); cards.add(new SetCardInfo("Plains", 58253, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 73626, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 81830, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); @@ -2646,7 +2646,7 @@ public class MagicOnlinePromos extends ExpansionSet { cards.add(new SetCardInfo("Swamp", 40058, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 40068, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 40092, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Swamp", 53883, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 53883, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_ZEN_VARIOUS )); cards.add(new SetCardInfo("Swamp", 58257, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 73632, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 81856, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); @@ -2756,7 +2756,7 @@ public class MagicOnlinePromos extends ExpansionSet { cards.add(new SetCardInfo("Terramorphic Expanse", 86110, Rarity.COMMON, mage.cards.t.TerramorphicExpanse.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Territorial Kavu", 91339, Rarity.RARE, mage.cards.t.TerritorialKavu.class)); cards.add(new SetCardInfo("Territorial Scythecat", 83704, Rarity.COMMON, mage.cards.t.TerritorialScythecat.class)); - cards.add(new SetCardInfo("Terror", 31483, Rarity.COMMON, mage.cards.t.Terror.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Terror", 31483, Rarity.COMMON, mage.cards.t.Terror.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Terror", 35948, Rarity.COMMON, mage.cards.t.Terror.class, RETRO_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Terror Ballista", 105822, Rarity.RARE, mage.cards.t.TerrorBallista.class)); cards.add(new SetCardInfo("Terror of the Peaks", 81976, Rarity.MYTHIC, mage.cards.t.TerrorOfThePeaks.class)); @@ -3105,7 +3105,7 @@ public class MagicOnlinePromos extends ExpansionSet { cards.add(new SetCardInfo("Woolly Thoctar", 31449, Rarity.UNCOMMON, mage.cards.w.WoollyThoctar.class)); cards.add(new SetCardInfo("Workshop Warchief", 99715, Rarity.RARE, mage.cards.w.WorkshopWarchief.class)); cards.add(new SetCardInfo("Worship", 77955, Rarity.RARE, mage.cards.w.Worship.class)); - cards.add(new SetCardInfo("Wrath of God", 35048, Rarity.RARE, mage.cards.w.WrathOfGod.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Wrath of God", 35048, Rarity.RARE, mage.cards.w.WrathOfGod.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Wrath of God", 82882, Rarity.RARE, mage.cards.w.WrathOfGod.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Wreck Hunter", 105686, Rarity.RARE, mage.cards.w.WreckHunter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Wreck Hunter", 105690, Rarity.RARE, mage.cards.w.WreckHunter.class, RETRO_ART_USE_VARIOUS)); diff --git a/Mage.Sets/src/mage/sets/MagicPlayerRewards2008.java b/Mage.Sets/src/mage/sets/MagicPlayerRewards2008.java index 716ec7b3ac2..0aaeec31ee9 100644 --- a/Mage.Sets/src/mage/sets/MagicPlayerRewards2008.java +++ b/Mage.Sets/src/mage/sets/MagicPlayerRewards2008.java @@ -1,8 +1,6 @@ package mage.sets; -import mage.cards.CardGraphicInfo; import mage.cards.ExpansionSet; -import mage.cards.FrameStyle; import mage.constants.Rarity; import mage.constants.SetType; @@ -22,14 +20,12 @@ public class MagicPlayerRewards2008 extends ExpansionSet { this.hasBoosters = false; this.hasBasicLands = false; - final CardGraphicInfo MPR_FULL_ART = new CardGraphicInfo(FrameStyle.MPRP_FULL_ART_BASIC, false); - - cards.add(new SetCardInfo("Corrupt", 7, Rarity.RARE, mage.cards.c.Corrupt.class)); - cards.add(new SetCardInfo("Damnation", 1, Rarity.RARE, mage.cards.d.Damnation.class)); - cards.add(new SetCardInfo("Harmonize", 5, Rarity.RARE, mage.cards.h.Harmonize.class)); - cards.add(new SetCardInfo("Incinerate", 3, Rarity.RARE, mage.cards.i.Incinerate.class)); - cards.add(new SetCardInfo("Mana Tithe", 4, Rarity.RARE, mage.cards.m.ManaTithe.class)); - cards.add(new SetCardInfo("Ponder", 6, Rarity.RARE, mage.cards.p.Ponder.class)); - cards.add(new SetCardInfo("Tidings", 2, Rarity.RARE, mage.cards.t.Tidings.class)); + cards.add(new SetCardInfo("Corrupt", 7, Rarity.RARE, mage.cards.c.Corrupt.class, FULL_ART)); + cards.add(new SetCardInfo("Damnation", 1, Rarity.RARE, mage.cards.d.Damnation.class, FULL_ART)); + cards.add(new SetCardInfo("Harmonize", 5, Rarity.RARE, mage.cards.h.Harmonize.class, FULL_ART)); + cards.add(new SetCardInfo("Incinerate", 3, Rarity.RARE, mage.cards.i.Incinerate.class, FULL_ART)); + cards.add(new SetCardInfo("Mana Tithe", 4, Rarity.RARE, mage.cards.m.ManaTithe.class, FULL_ART)); + cards.add(new SetCardInfo("Ponder", 6, Rarity.RARE, mage.cards.p.Ponder.class, FULL_ART)); + cards.add(new SetCardInfo("Tidings", 2, Rarity.RARE, mage.cards.t.Tidings.class, FULL_ART)); } } diff --git a/Mage.Sets/src/mage/sets/MarvelsSpiderMan.java b/Mage.Sets/src/mage/sets/MarvelsSpiderMan.java index f2d899ed330..8d8db43064d 100644 --- a/Mage.Sets/src/mage/sets/MarvelsSpiderMan.java +++ b/Mage.Sets/src/mage/sets/MarvelsSpiderMan.java @@ -1,14 +1,19 @@ package mage.sets; import mage.cards.ExpansionSet; +import mage.cards.s.SilkWebWeaver; import mage.constants.Rarity; import mage.constants.SetType; +import java.util.Arrays; +import java.util.List; + /** * @author TheElk801 */ public final class MarvelsSpiderMan extends ExpansionSet { + private static final List unfinished = Arrays.asList("Eddie Brock", "Gwen Stacy", "Miles Morales", "Norman Osborn", "Peter Parker"); private static final MarvelsSpiderMan instance = new MarvelsSpiderMan(); public static MarvelsSpiderMan getInstance() { @@ -20,55 +25,201 @@ public final class MarvelsSpiderMan extends ExpansionSet { this.blockName = "Marvel's Spider-Man"; // for sorting in GUI this.hasBasicLands = true; + cards.add(new SetCardInfo("Agent Venom", 255, Rarity.RARE, mage.cards.a.AgentVenom.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Agent Venom", 49, Rarity.RARE, mage.cards.a.AgentVenom.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Alien Symbiosis", 50, Rarity.UNCOMMON, mage.cards.a.AlienSymbiosis.class)); + cards.add(new SetCardInfo("Amazing Acrobatics", 25, Rarity.COMMON, mage.cards.a.AmazingAcrobatics.class)); cards.add(new SetCardInfo("Angry Rabble", 75, Rarity.COMMON, mage.cards.a.AngryRabble.class)); cards.add(new SetCardInfo("Anti-Venom, Horrifying Healer", 1, Rarity.MYTHIC, mage.cards.a.AntiVenomHorrifyingHealer.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Anti-Venom, Horrifying Healer", 244, Rarity.MYTHIC, mage.cards.a.AntiVenomHorrifyingHealer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arachne, Psionic Weaver", 2, Rarity.RARE, mage.cards.a.ArachnePsionicWeaver.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arachne, Psionic Weaver", 245, Rarity.RARE, mage.cards.a.ArachnePsionicWeaver.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arana, Heart of the Spider", 123, Rarity.RARE, mage.cards.a.AranaHeartOfTheSpider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arana, Heart of the Spider", 213, Rarity.RARE, mage.cards.a.AranaHeartOfTheSpider.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Aunt May", 3, Rarity.UNCOMMON, mage.cards.a.AuntMay.class)); + cards.add(new SetCardInfo("Bagel and Schmear", 161, Rarity.COMMON, mage.cards.b.BagelAndSchmear.class)); cards.add(new SetCardInfo("Beetle, Legacy Criminal", 26, Rarity.COMMON, mage.cards.b.BeetleLegacyCriminal.class)); + cards.add(new SetCardInfo("Behold the Sinister Six!", 221, Rarity.MYTHIC, mage.cards.b.BeholdTheSinisterSix.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Behold the Sinister Six!", 51, Rarity.MYTHIC, mage.cards.b.BeholdTheSinisterSix.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Biorganic Carapace", 124, Rarity.RARE, mage.cards.b.BiorganicCarapace.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Biorganic Carapace", 269, Rarity.RARE, mage.cards.b.BiorganicCarapace.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Black Cat, Cunning Thief", 222, Rarity.RARE, mage.cards.b.BlackCatCunningThief.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Black Cat, Cunning Thief", 52, Rarity.RARE, mage.cards.b.BlackCatCunningThief.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Carnage, Crimson Chaos", 125, Rarity.RARE, mage.cards.c.CarnageCrimsonChaos.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Carnage, Crimson Chaos", 227, Rarity.RARE, mage.cards.c.CarnageCrimsonChaos.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Chameleon, Master of Disguise", 27, Rarity.UNCOMMON, mage.cards.c.ChameleonMasterOfDisguise.class)); + cards.add(new SetCardInfo("Cheering Crowd", 126, Rarity.RARE, mage.cards.c.CheeringCrowd.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cheering Crowd", 270, Rarity.RARE, mage.cards.c.CheeringCrowd.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("City Pigeon", 4, Rarity.COMMON, mage.cards.c.CityPigeon.class)); + cards.add(new SetCardInfo("Common Crook", 53, Rarity.COMMON, mage.cards.c.CommonCrook.class)); + cards.add(new SetCardInfo("Cosmic Spider-Man", 127, Rarity.MYTHIC, mage.cards.c.CosmicSpiderMan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cosmic Spider-Man", 271, Rarity.MYTHIC, mage.cards.c.CosmicSpiderMan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Costume Closet", 5, Rarity.UNCOMMON, mage.cards.c.CostumeCloset.class)); + cards.add(new SetCardInfo("Daily Bugle Building", 179, Rarity.UNCOMMON, mage.cards.d.DailyBugleBuilding.class)); cards.add(new SetCardInfo("Daily Bugle Reporters", 6, Rarity.COMMON, mage.cards.d.DailyBugleReporters.class)); + cards.add(new SetCardInfo("Damage Control Crew", 99, Rarity.UNCOMMON, mage.cards.d.DamageControlCrew.class)); cards.add(new SetCardInfo("Doc Ock's Henchmen", 30, Rarity.COMMON, mage.cards.d.DocOcksHenchmen.class)); + cards.add(new SetCardInfo("Doc Ock's Tentacles", 162, Rarity.RARE, mage.cards.d.DocOcksTentacles.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Doc Ock's Tentacles", 277, Rarity.RARE, mage.cards.d.DocOcksTentacles.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Doc Ock, Sinister Scientist", 29, Rarity.COMMON, mage.cards.d.DocOckSinisterScientist.class)); cards.add(new SetCardInfo("Doctor Octopus, Master Planner", 128, Rarity.MYTHIC, mage.cards.d.DoctorOctopusMasterPlanner.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Doctor Octopus, Master Planner", 228, Rarity.MYTHIC, mage.cards.d.DoctorOctopusMasterPlanner.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eddie Brock", 224, Rarity.MYTHIC, mage.cards.e.EddieBrock.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eddie Brock", 233, Rarity.MYTHIC, mage.cards.e.EddieBrock.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eddie Brock", 55, Rarity.MYTHIC, mage.cards.e.EddieBrock.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Eerie Gravestone", 163, Rarity.COMMON, mage.cards.e.EerieGravestone.class)); cards.add(new SetCardInfo("Electro's Bolt", 77, Rarity.COMMON, mage.cards.e.ElectrosBolt.class)); + cards.add(new SetCardInfo("Electro, Assaulting Battery", 260, Rarity.RARE, mage.cards.e.ElectroAssaultingBattery.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Electro, Assaulting Battery", 76, Rarity.RARE, mage.cards.e.ElectroAssaultingBattery.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ezekiel Sims, Spider-Totem", 100, Rarity.UNCOMMON, mage.cards.e.EzekielSimsSpiderTotem.class)); + cards.add(new SetCardInfo("Flash Thompson, Spider-Fan", 7, Rarity.UNCOMMON, mage.cards.f.FlashThompsonSpiderFan.class)); cards.add(new SetCardInfo("Flying Octobot", 31, Rarity.UNCOMMON, mage.cards.f.FlyingOctobot.class)); cards.add(new SetCardInfo("Forest", 193, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 198, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Friendly Neighborhood", 246, Rarity.RARE, mage.cards.f.FriendlyNeighborhood.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Friendly Neighborhood", 8, Rarity.RARE, mage.cards.f.FriendlyNeighborhood.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gallant Citizen", 129, Rarity.COMMON, mage.cards.g.GallantCitizen.class)); cards.add(new SetCardInfo("Green Goblin, Revenant", 130, Rarity.UNCOMMON, mage.cards.g.GreenGoblinRevenant.class)); cards.add(new SetCardInfo("Grow Extra Arms", 101, Rarity.COMMON, mage.cards.g.GrowExtraArms.class)); cards.add(new SetCardInfo("Guy in the Chair", 102, Rarity.COMMON, mage.cards.g.GuyInTheChair.class)); + cards.add(new SetCardInfo("Gwen Stacy", 202, Rarity.MYTHIC, mage.cards.g.GwenStacy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gwen Stacy", 209, Rarity.MYTHIC, mage.cards.g.GwenStacy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gwen Stacy", 78, Rarity.MYTHIC, mage.cards.g.GwenStacy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gwenom, Remorseless", 256, Rarity.MYTHIC, mage.cards.g.GwenomRemorseless.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gwenom, Remorseless", 286, Rarity.MYTHIC, mage.cards.g.GwenomRemorseless.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gwenom, Remorseless", 56, Rarity.MYTHIC, mage.cards.g.GwenomRemorseless.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Heroes' Hangout", 79, Rarity.UNCOMMON, mage.cards.h.HeroesHangout.class)); + cards.add(new SetCardInfo("Hide on the Ceiling", 249, Rarity.RARE, mage.cards.h.HideOnTheCeiling.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hide on the Ceiling", 32, Rarity.RARE, mage.cards.h.HideOnTheCeiling.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hobgoblin, Mantled Marauder", 80, Rarity.UNCOMMON, mage.cards.h.HobgoblinMantledMarauder.class)); + cards.add(new SetCardInfo("Hot Dog Cart", 164, Rarity.COMMON, mage.cards.h.HotDogCart.class)); + cards.add(new SetCardInfo("Hydro-Man, Fluid Felon", 250, Rarity.RARE, mage.cards.h.HydroManFluidFelon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hydro-Man, Fluid Felon", 33, Rarity.RARE, mage.cards.h.HydroManFluidFelon.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Impostor Syndrome", 251, Rarity.MYTHIC, mage.cards.i.ImpostorSyndrome.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Impostor Syndrome", 34, Rarity.MYTHIC, mage.cards.i.ImpostorSyndrome.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Inner Demons Gangsters", 57, Rarity.COMMON, mage.cards.i.InnerDemonsGangsters.class)); + cards.add(new SetCardInfo("Interdimensional Web Watch", 165, Rarity.RARE, mage.cards.i.InterdimensionalWebWatch.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Interdimensional Web Watch", 278, Rarity.RARE, mage.cards.i.InterdimensionalWebWatch.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Iron Spider, Stark Upgrade", 166, Rarity.RARE, mage.cards.i.IronSpiderStarkUpgrade.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Iron Spider, Stark Upgrade", 279, Rarity.RARE, mage.cards.i.IronSpiderStarkUpgrade.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Island", 190, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Island", 195, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("J. Jonah Jameson", 261, Rarity.RARE, mage.cards.j.JJonahJameson.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("J. Jonah Jameson", 81, Rarity.RARE, mage.cards.j.JJonahJameson.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jackal, Genius Geneticist", 131, Rarity.RARE, mage.cards.j.JackalGeniusGeneticist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jackal, Genius Geneticist", 272, Rarity.RARE, mage.cards.j.JackalGeniusGeneticist.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Kapow!", 103, Rarity.COMMON, mage.cards.k.Kapow.class)); + cards.add(new SetCardInfo("Kraven the Hunter", 133, Rarity.RARE, mage.cards.k.KravenTheHunter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kraven the Hunter", 273, Rarity.RARE, mage.cards.k.KravenTheHunter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Kraven's Cats", 104, Rarity.COMMON, mage.cards.k.KravensCats.class)); cards.add(new SetCardInfo("Kraven's Last Hunt", 105, Rarity.RARE, mage.cards.k.KravensLastHunt.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Kraven's Last Hunt", 226, Rarity.RARE, mage.cards.k.KravensLastHunt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kraven's Last Hunt", 226, Rarity.RARE, mage.cards.k.KravensLastHunt.class, FULL_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Kraven, Proud Predator", 132, Rarity.UNCOMMON, mage.cards.k.KravenProudPredator.class)); + cards.add(new SetCardInfo("Lady Octopus, Inspired Inventor", 252, Rarity.RARE, mage.cards.l.LadyOctopusInspiredInventor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lady Octopus, Inspired Inventor", 35, Rarity.RARE, mage.cards.l.LadyOctopusInspiredInventor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Living Brain, Mechanical Marvel", 167, Rarity.UNCOMMON, mage.cards.l.LivingBrainMechanicalMarvel.class)); + cards.add(new SetCardInfo("Lizard, Connors's Curse", 106, Rarity.RARE, mage.cards.l.LizardConnorssCurse.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lizard, Connors's Curse", 265, Rarity.RARE, mage.cards.l.LizardConnorssCurse.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lurking Lizards", 107, Rarity.COMMON, mage.cards.l.LurkingLizards.class)); + cards.add(new SetCardInfo("Madame Web, Clairvoyant", 36, Rarity.UNCOMMON, mage.cards.m.MadameWebClairvoyant.class)); + cards.add(new SetCardInfo("Mary Jane Watson", 134, Rarity.RARE, mage.cards.m.MaryJaneWatson.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mary Jane Watson", 229, Rarity.RARE, mage.cards.m.MaryJaneWatson.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Masked Meower", 82, Rarity.COMMON, mage.cards.m.MaskedMeower.class)); + cards.add(new SetCardInfo("Maximum Carnage", 225, Rarity.RARE, mage.cards.m.MaximumCarnage.class, FULL_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Maximum Carnage", 83, Rarity.RARE, mage.cards.m.MaximumCarnage.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mechanical Mobster", 168, Rarity.COMMON, mage.cards.m.MechanicalMobster.class)); cards.add(new SetCardInfo("Merciless Enforcers", 58, Rarity.COMMON, mage.cards.m.MercilessEnforcers.class)); + cards.add(new SetCardInfo("Miles Morales", 108, Rarity.MYTHIC, mage.cards.m.MilesMorales.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Miles Morales", 200, Rarity.MYTHIC, mage.cards.m.MilesMorales.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Miles Morales", 211, Rarity.MYTHIC, mage.cards.m.MilesMorales.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Miles Morales", 234, Rarity.MYTHIC, mage.cards.m.MilesMorales.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mister Negative", 135, Rarity.MYTHIC, mage.cards.m.MisterNegative.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mister Negative", 274, Rarity.MYTHIC, mage.cards.m.MisterNegative.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mob Lookout", 136, Rarity.COMMON, mage.cards.m.MobLookout.class)); + cards.add(new SetCardInfo("Molten Man, Inferno Incarnate", 84, Rarity.UNCOMMON, mage.cards.m.MoltenManInfernoIncarnate.class)); + cards.add(new SetCardInfo("Morbius the Living Vampire", 137, Rarity.UNCOMMON, mage.cards.m.MorbiusTheLivingVampire.class)); + cards.add(new SetCardInfo("Morlun, Devourer of Spiders", 257, Rarity.RARE, mage.cards.m.MorlunDevourerOfSpiders.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Morlun, Devourer of Spiders", 59, Rarity.RARE, mage.cards.m.MorlunDevourerOfSpiders.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 192, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 197, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Origin of Spider-Man", 218, Rarity.RARE, mage.cards.o.OriginOfSpiderMan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Multiversal Passage", 180, Rarity.RARE, mage.cards.m.MultiversalPassage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Multiversal Passage", 206, Rarity.RARE, mage.cards.m.MultiversalPassage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mysterio's Phantasm", 38, Rarity.COMMON, mage.cards.m.MysteriosPhantasm.class)); + cards.add(new SetCardInfo("Mysterio, Master of Illusion", 253, Rarity.RARE, mage.cards.m.MysterioMasterOfIllusion.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mysterio, Master of Illusion", 37, Rarity.RARE, mage.cards.m.MysterioMasterOfIllusion.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("News Helicopter", 169, Rarity.COMMON, mage.cards.n.NewsHelicopter.class)); + cards.add(new SetCardInfo("Norman Osborn", 220, Rarity.MYTHIC, mage.cards.n.NormanOsborn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Norman Osborn", 39, Rarity.MYTHIC, mage.cards.n.NormanOsborn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ominous Asylum", 181, Rarity.COMMON, mage.cards.o.OminousAsylum.class)); + cards.add(new SetCardInfo("Origin of Spider-Man", 218, Rarity.RARE, mage.cards.o.OriginOfSpiderMan.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Origin of Spider-Man", 9, Rarity.RARE, mage.cards.o.OriginOfSpiderMan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Oscorp Industries", 182, Rarity.RARE, mage.cards.o.OscorpIndustries.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Oscorp Industries", 282, Rarity.RARE, mage.cards.o.OscorpIndustries.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Oscorp Research Team", 40, Rarity.COMMON, mage.cards.o.OscorpResearchTeam.class)); + cards.add(new SetCardInfo("Parker Luck", 258, Rarity.RARE, mage.cards.p.ParkerLuck.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Parker Luck", 60, Rarity.RARE, mage.cards.p.ParkerLuck.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Passenger Ferry", 170, Rarity.COMMON, mage.cards.p.PassengerFerry.class)); + cards.add(new SetCardInfo("Peter Parker", 10, Rarity.MYTHIC, mage.cards.p.PeterParker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Peter Parker", 208, Rarity.MYTHIC, mage.cards.p.PeterParker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Peter Parker", 232, Rarity.MYTHIC, mage.cards.p.PeterParker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Peter Parker's Camera", 171, Rarity.RARE, mage.cards.p.PeterParkersCamera.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Peter Parker's Camera", 280, Rarity.RARE, mage.cards.p.PeterParkersCamera.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Pictures of Spider-Man", 109, Rarity.UNCOMMON, mage.cards.p.PicturesOfSpiderMan.class)); cards.add(new SetCardInfo("Plains", 189, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 194, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Prison Break", 61, Rarity.UNCOMMON, mage.cards.p.PrisonBreak.class)); + cards.add(new SetCardInfo("Professional Wrestler", 110, Rarity.COMMON, mage.cards.p.ProfessionalWrestler.class)); + cards.add(new SetCardInfo("Prowler, Clawed Thief", 138, Rarity.UNCOMMON, mage.cards.p.ProwlerClawedThief.class)); + cards.add(new SetCardInfo("Pumpkin Bombardment", 139, Rarity.COMMON, mage.cards.p.PumpkinBombardment.class)); + cards.add(new SetCardInfo("Radioactive Spider", 111, Rarity.RARE, mage.cards.r.RadioactiveSpider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Radioactive Spider", 212, Rarity.RARE, mage.cards.r.RadioactiveSpider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Radioactive Spider", 285, Rarity.RARE, mage.cards.r.RadioactiveSpider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Raging Goblinoids", 85, Rarity.UNCOMMON, mage.cards.r.RagingGoblinoids.class)); + cards.add(new SetCardInfo("Rent Is Due", 11, Rarity.RARE, mage.cards.r.RentIsDue.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rent Is Due", 247, Rarity.RARE, mage.cards.r.RentIsDue.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rhino's Rampage", 141, Rarity.UNCOMMON, mage.cards.r.RhinosRampage.class)); + cards.add(new SetCardInfo("Rhino, Barreling Brute", 140, Rarity.UNCOMMON, mage.cards.r.RhinoBarrelingBrute.class)); cards.add(new SetCardInfo("Risky Research", 62, Rarity.COMMON, mage.cards.r.RiskyResearch.class)); + cards.add(new SetCardInfo("Robotics Mastery", 41, Rarity.UNCOMMON, mage.cards.r.RoboticsMastery.class)); + cards.add(new SetCardInfo("Rocket-Powered Goblin Glider", 172, Rarity.RARE, mage.cards.r.RocketPoweredGoblinGlider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rocket-Powered Goblin Glider", 281, Rarity.RARE, mage.cards.r.RocketPoweredGoblinGlider.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Romantic Rendezvous", 86, Rarity.COMMON, mage.cards.r.RomanticRendezvous.class)); + cards.add(new SetCardInfo("Sandman's Quicksand", 63, Rarity.UNCOMMON, mage.cards.s.SandmansQuicksand.class)); + cards.add(new SetCardInfo("Sandman, Shifting Scoundrel", 112, Rarity.RARE, mage.cards.s.SandmanShiftingScoundrel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sandman, Shifting Scoundrel", 266, Rarity.RARE, mage.cards.s.SandmanShiftingScoundrel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Savage Mansion", 183, Rarity.COMMON, mage.cards.s.SavageMansion.class)); + cards.add(new SetCardInfo("Scarlet Spider, Ben Reilly", 142, Rarity.RARE, mage.cards.s.ScarletSpiderBenReilly.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Scarlet Spider, Ben Reilly", 214, Rarity.RARE, mage.cards.s.ScarletSpiderBenReilly.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Scarlet Spider, Kaine", 143, Rarity.UNCOMMON, mage.cards.s.ScarletSpiderKaine.class)); + cards.add(new SetCardInfo("School Daze", 42, Rarity.UNCOMMON, mage.cards.s.SchoolDaze.class)); cards.add(new SetCardInfo("Scorpion's Sting", 65, Rarity.COMMON, mage.cards.s.ScorpionsSting.class)); cards.add(new SetCardInfo("Scorpion, Seething Striker", 64, Rarity.UNCOMMON, mage.cards.s.ScorpionSeethingStriker.class)); cards.add(new SetCardInfo("Scout the City", 113, Rarity.COMMON, mage.cards.s.ScoutTheCity.class)); + cards.add(new SetCardInfo("Secret Identity", 43, Rarity.UNCOMMON, mage.cards.s.SecretIdentity.class)); cards.add(new SetCardInfo("Selfless Police Captain", 12, Rarity.COMMON, mage.cards.s.SelflessPoliceCaptain.class)); + cards.add(new SetCardInfo("Shadow of the Goblin", 262, Rarity.RARE, mage.cards.s.ShadowOfTheGoblin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shadow of the Goblin", 87, Rarity.RARE, mage.cards.s.ShadowOfTheGoblin.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shock", 88, Rarity.COMMON, mage.cards.s.Shock.class)); cards.add(new SetCardInfo("Shocker, Unshakable", 89, Rarity.UNCOMMON, mage.cards.s.ShockerUnshakable.class)); - cards.add(new SetCardInfo("Spectacular Spider-Man", 14, Rarity.RARE, mage.cards.s.SpectacularSpiderMan.class)); + cards.add(new SetCardInfo("Shriek, Treblemaker", 144, Rarity.UNCOMMON, mage.cards.s.ShriekTreblemaker.class)); + cards.add(new SetCardInfo("Silk, Web Weaver", 145, Rarity.RARE, SilkWebWeaver.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Silk, Web Weaver", 215, Rarity.RARE, SilkWebWeaver.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Silver Sable, Mercenary Leader", 13, Rarity.UNCOMMON, mage.cards.s.SilverSableMercenaryLeader.class)); + cards.add(new SetCardInfo("Sinister Hideout", 184, Rarity.COMMON, mage.cards.s.SinisterHideout.class)); + cards.add(new SetCardInfo("Skyward Spider", 146, Rarity.COMMON, mage.cards.s.SkywardSpider.class)); + cards.add(new SetCardInfo("Spectacular Spider-Man", 14, Rarity.RARE, mage.cards.s.SpectacularSpiderMan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spectacular Spider-Man", 235, Rarity.RARE, mage.cards.s.SpectacularSpiderMan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spectacular Spider-Man", 236, Rarity.RARE, mage.cards.s.SpectacularSpiderMan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spectacular Spider-Man", 237, Rarity.RARE, mage.cards.s.SpectacularSpiderMan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spectacular Spider-Man", 238, Rarity.RARE, mage.cards.s.SpectacularSpiderMan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spectacular Spider-Man", 239, Rarity.RARE, mage.cards.s.SpectacularSpiderMan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spectacular Spider-Man", 240, Rarity.RARE, mage.cards.s.SpectacularSpiderMan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spectacular Spider-Man", 241, Rarity.RARE, mage.cards.s.SpectacularSpiderMan.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Spectacular Tactics", 15, Rarity.COMMON, mage.cards.s.SpectacularTactics.class)); + cards.add(new SetCardInfo("Spider Manifestation", 148, Rarity.COMMON, mage.cards.s.SpiderManifestation.class)); cards.add(new SetCardInfo("Spider-Bot", 173, Rarity.COMMON, mage.cards.s.SpiderBot.class)); cards.add(new SetCardInfo("Spider-Byte, Web Warden", 44, Rarity.UNCOMMON, mage.cards.s.SpiderByteWebWarden.class)); + cards.add(new SetCardInfo("Spider-Girl, Legacy Hero", 149, Rarity.UNCOMMON, mage.cards.s.SpiderGirlLegacyHero.class)); cards.add(new SetCardInfo("Spider-Gwen, Free Spirit", 90, Rarity.COMMON, mage.cards.s.SpiderGwenFreeSpirit.class)); cards.add(new SetCardInfo("Spider-Ham, Peter Porker", 114, Rarity.RARE, mage.cards.s.SpiderHamPeterPorker.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Spider-Ham, Peter Porker", 201, Rarity.RARE, mage.cards.s.SpiderHamPeterPorker.class, NON_FULL_USE_VARIOUS)); @@ -76,25 +227,86 @@ public final class MarvelsSpiderMan extends ExpansionSet { cards.add(new SetCardInfo("Spider-Man 2099", 150, Rarity.RARE, mage.cards.s.SpiderMan2099.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Spider-Man 2099", 205, Rarity.RARE, mage.cards.s.SpiderMan2099.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Spider-Man 2099", 216, Rarity.RARE, mage.cards.s.SpiderMan2099.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spider-Man India", 151, Rarity.UNCOMMON, mage.cards.s.SpiderManIndia.class)); + cards.add(new SetCardInfo("Spider-Man No More", 45, Rarity.COMMON, mage.cards.s.SpiderManNoMore.class)); cards.add(new SetCardInfo("Spider-Man Noir", 204, Rarity.UNCOMMON, mage.cards.s.SpiderManNoir.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Spider-Man Noir", 67, Rarity.UNCOMMON, mage.cards.s.SpiderManNoir.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Spider-Man, Brooklyn Visionary", 115, Rarity.COMMON, mage.cards.s.SpiderManBrooklynVisionary.class)); cards.add(new SetCardInfo("Spider-Man, Web-Slinger", 16, Rarity.COMMON, mage.cards.s.SpiderManWebSlinger.class)); + cards.add(new SetCardInfo("Spider-Mobile", 174, Rarity.UNCOMMON, mage.cards.s.SpiderMobile.class)); + cards.add(new SetCardInfo("Spider-Punk", 207, Rarity.RARE, mage.cards.s.SpiderPunk.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spider-Punk", 210, Rarity.RARE, mage.cards.s.SpiderPunk.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spider-Punk", 92, Rarity.RARE, mage.cards.s.SpiderPunk.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Spider-Rex, Daring Dino", 116, Rarity.COMMON, mage.cards.s.SpiderRexDaringDino.class)); + cards.add(new SetCardInfo("Spider-Sense", 254, Rarity.RARE, mage.cards.s.SpiderSense.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spider-Sense", 284, Rarity.RARE, mage.cards.s.SpiderSense.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spider-Sense", 46, Rarity.RARE, mage.cards.s.SpiderSense.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spider-Slayer, Hatred Honed", 175, Rarity.UNCOMMON, mage.cards.s.SpiderSlayerHatredHoned.class)); + cards.add(new SetCardInfo("Spider-Suit", 176, Rarity.UNCOMMON, mage.cards.s.SpiderSuit.class)); + cards.add(new SetCardInfo("Spider-UK", 17, Rarity.UNCOMMON, mage.cards.s.SpiderUK.class)); + cards.add(new SetCardInfo("Spider-Verse", 263, Rarity.MYTHIC, mage.cards.s.SpiderVerse.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spider-Verse", 93, Rarity.MYTHIC, mage.cards.s.SpiderVerse.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spider-Woman, Stunning Savior", 152, Rarity.RARE, mage.cards.s.SpiderWomanStunningSavior.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spider-Woman, Stunning Savior", 230, Rarity.RARE, mage.cards.s.SpiderWomanStunningSavior.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spiders-Man, Heroic Horde", 117, Rarity.UNCOMMON, mage.cards.s.SpidersManHeroicHorde.class)); + cards.add(new SetCardInfo("Spinneret and Spiderling", 264, Rarity.RARE, mage.cards.s.SpinneretAndSpiderling.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spinneret and Spiderling", 94, Rarity.RARE, mage.cards.s.SpinneretAndSpiderling.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Starling, Aerial Ally", 18, Rarity.COMMON, mage.cards.s.StarlingAerialAlly.class)); + cards.add(new SetCardInfo("Steel Wrecking Ball", 177, Rarity.COMMON, mage.cards.s.SteelWreckingBall.class)); cards.add(new SetCardInfo("Stegron the Dinosaur Man", 95, Rarity.COMMON, mage.cards.s.StegronTheDinosaurMan.class)); + cards.add(new SetCardInfo("Strength of Will", 118, Rarity.RARE, mage.cards.s.StrengthOfWill.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Strength of Will", 267, Rarity.RARE, mage.cards.s.StrengthOfWill.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Suburban Sanctuary", 185, Rarity.COMMON, mage.cards.s.SuburbanSanctuary.class)); + cards.add(new SetCardInfo("Subway Train", 178, Rarity.COMMON, mage.cards.s.SubwayTrain.class)); + cards.add(new SetCardInfo("Sudden Strike", 19, Rarity.UNCOMMON, mage.cards.s.SuddenStrike.class)); + cards.add(new SetCardInfo("Sun-Spider, Nimble Webber", 154, Rarity.UNCOMMON, mage.cards.s.SunSpiderNimbleWebber.class)); + cards.add(new SetCardInfo("Superior Foes of Spider-Man", 96, Rarity.UNCOMMON, mage.cards.s.SuperiorFoesOfSpiderMan.class)); + cards.add(new SetCardInfo("Superior Spider-Man", 155, Rarity.RARE, mage.cards.s.SuperiorSpiderMan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Superior Spider-Man", 275, Rarity.RARE, mage.cards.s.SuperiorSpiderMan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Supportive Parents", 119, Rarity.UNCOMMON, mage.cards.s.SupportiveParents.class)); cards.add(new SetCardInfo("Swamp", 191, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 196, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swarm, Being of Bees", 69, Rarity.COMMON, mage.cards.s.SwarmBeingOfBees.class)); + cards.add(new SetCardInfo("Symbiote Spider-Man", 156, Rarity.RARE, mage.cards.s.SymbioteSpiderMan.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Symbiote Spider-Man", 217, Rarity.RARE, mage.cards.s.SymbioteSpiderMan.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Taxi Driver", 97, Rarity.COMMON, mage.cards.t.TaxiDriver.class)); + cards.add(new SetCardInfo("Terrific Team-Up", 120, Rarity.UNCOMMON, mage.cards.t.TerrificTeamUp.class)); + cards.add(new SetCardInfo("The Clone Saga", 219, Rarity.RARE, mage.cards.t.TheCloneSaga.class, FULL_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("The Clone Saga", 28, Rarity.RARE, mage.cards.t.TheCloneSaga.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Death of Gwen Stacy", 223, Rarity.RARE, mage.cards.t.TheDeathOfGwenStacy.class, FULL_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("The Death of Gwen Stacy", 54, Rarity.RARE, mage.cards.t.TheDeathOfGwenStacy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Spot's Portal", 68, Rarity.UNCOMMON, mage.cards.t.TheSpotsPortal.class)); + cards.add(new SetCardInfo("The Spot, Living Portal", 153, Rarity.RARE, mage.cards.t.TheSpotLivingPortal.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Spot, Living Portal", 231, Rarity.RARE, mage.cards.t.TheSpotLivingPortal.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Thwip!", 20, Rarity.COMMON, mage.cards.t.Thwip.class)); cards.add(new SetCardInfo("Tombstone, Career Criminal", 70, Rarity.UNCOMMON, mage.cards.t.TombstoneCareerCriminal.class)); + cards.add(new SetCardInfo("Ultimate Green Goblin", 157, Rarity.RARE, mage.cards.u.UltimateGreenGoblin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ultimate Green Goblin", 276, Rarity.RARE, mage.cards.u.UltimateGreenGoblin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("University Campus", 186, Rarity.COMMON, mage.cards.u.UniversityCampus.class)); cards.add(new SetCardInfo("Unstable Experiment", 47, Rarity.COMMON, mage.cards.u.UnstableExperiment.class)); + cards.add(new SetCardInfo("Urban Retreat", 187, Rarity.RARE, mage.cards.u.UrbanRetreat.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Urban Retreat", 283, Rarity.RARE, mage.cards.u.UrbanRetreat.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Venom's Hunger", 73, Rarity.COMMON, mage.cards.v.VenomsHunger.class)); cards.add(new SetCardInfo("Venom, Evil Unleashed", 71, Rarity.COMMON, mage.cards.v.VenomEvilUnleashed.class)); + cards.add(new SetCardInfo("Venomized Cat", 72, Rarity.COMMON, mage.cards.v.VenomizedCat.class)); + cards.add(new SetCardInfo("Vibrant Cityscape", 188, Rarity.COMMON, mage.cards.v.VibrantCityscape.class)); + cards.add(new SetCardInfo("Villainous Wrath", 259, Rarity.RARE, mage.cards.v.VillainousWrath.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Villainous Wrath", 74, Rarity.RARE, mage.cards.v.VillainousWrath.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vulture, Scheming Scavenger", 158, Rarity.UNCOMMON, mage.cards.v.VultureSchemingScavenger.class)); + cards.add(new SetCardInfo("Wall Crawl", 121, Rarity.UNCOMMON, mage.cards.w.WallCrawl.class)); cards.add(new SetCardInfo("Web Up", 21, Rarity.COMMON, mage.cards.w.WebUp.class)); + cards.add(new SetCardInfo("Web of Life and Destiny", 122, Rarity.MYTHIC, mage.cards.w.WebOfLifeAndDestiny.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Web of Life and Destiny", 268, Rarity.MYTHIC, mage.cards.w.WebOfLifeAndDestiny.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Web-Shooters", 22, Rarity.UNCOMMON, mage.cards.w.WebShooters.class)); cards.add(new SetCardInfo("Web-Warriors", 159, Rarity.UNCOMMON, mage.cards.w.WebWarriors.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Web-Warriors", 203, Rarity.UNCOMMON, mage.cards.w.WebWarriors.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Whoosh!", 48, Rarity.COMMON, mage.cards.w.Whoosh.class)); cards.add(new SetCardInfo("Wild Pack Squad", 23, Rarity.COMMON, mage.cards.w.WildPackSquad.class)); + cards.add(new SetCardInfo("Wisecrack", 98, Rarity.UNCOMMON, mage.cards.w.Wisecrack.class)); + cards.add(new SetCardInfo("With Great Power...", 24, Rarity.RARE, mage.cards.w.WithGreatPower.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("With Great Power...", 248, Rarity.RARE, mage.cards.w.WithGreatPower.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Wraith, Vicious Vigilante", 160, Rarity.UNCOMMON, mage.cards.w.WraithViciousVigilante.class)); + + cards.removeIf(setCardInfo -> unfinished.contains(setCardInfo.getName())); } } diff --git a/Mage.Sets/src/mage/sets/MurdersAtKarlovManorCommander.java b/Mage.Sets/src/mage/sets/MurdersAtKarlovManorCommander.java index 5054600618e..6c51be43bc5 100644 --- a/Mage.Sets/src/mage/sets/MurdersAtKarlovManorCommander.java +++ b/Mage.Sets/src/mage/sets/MurdersAtKarlovManorCommander.java @@ -244,6 +244,8 @@ public final class MurdersAtKarlovManorCommander extends ExpansionSet { cards.add(new SetCardInfo("Selesnya Sanctuary", 290, Rarity.UNCOMMON, mage.cards.s.SelesnyaSanctuary.class)); cards.add(new SetCardInfo("Selfless Squire", 82, Rarity.RARE, mage.cards.s.SelflessSquire.class)); cards.add(new SetCardInfo("Selvala, Explorer Returned", 218, Rarity.RARE, mage.cards.s.SelvalaExplorerReturned.class)); + cards.add(new SetCardInfo("Serene Sleuth", 14, Rarity.RARE, mage.cards.s.SereneSleuth.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Serene Sleuth", 325, Rarity.RARE, mage.cards.s.SereneSleuth.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sevinne's Reclamation", 83, Rarity.RARE, mage.cards.s.SevinnesReclamation.class)); cards.add(new SetCardInfo("Sheltered Thicket", 291, Rarity.RARE, mage.cards.s.ShelteredThicket.class)); cards.add(new SetCardInfo("Shimmer Dragon", 117, Rarity.RARE, mage.cards.s.ShimmerDragon.class)); @@ -311,6 +313,8 @@ public final class MurdersAtKarlovManorCommander extends ExpansionSet { cards.add(new SetCardInfo("Twilight Prophet", 143, Rarity.MYTHIC, mage.cards.t.TwilightProphet.class)); cards.add(new SetCardInfo("Ugin's Mastery", 53, Rarity.RARE, mage.cards.u.UginsMastery.class)); cards.add(new SetCardInfo("Ulvenwald Mysteries", 193, Rarity.UNCOMMON, mage.cards.u.UlvenwaldMysteries.class)); + cards.add(new SetCardInfo("Unexplained Absence", 17, Rarity.RARE, mage.cards.u.UnexplainedAbsence.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Unexplained Absence", 328, Rarity.RARE, mage.cards.u.UnexplainedAbsence.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Unshakable Tail", 29, Rarity.RARE, mage.cards.u.UnshakableTail.class)); cards.add(new SetCardInfo("Vengeful Ancestor", 163, Rarity.RARE, mage.cards.v.VengefulAncestor.class)); cards.add(new SetCardInfo("Vizier of Many Faces", 123, Rarity.RARE, mage.cards.v.VizierOfManyFaces.class)); diff --git a/Mage.Sets/src/mage/sets/OutlawsOfThunderJunctionCommander.java b/Mage.Sets/src/mage/sets/OutlawsOfThunderJunctionCommander.java index 2623fcab354..eeaeff0a5c7 100644 --- a/Mage.Sets/src/mage/sets/OutlawsOfThunderJunctionCommander.java +++ b/Mage.Sets/src/mage/sets/OutlawsOfThunderJunctionCommander.java @@ -126,6 +126,7 @@ public final class OutlawsOfThunderJunctionCommander extends ExpansionSet { cards.add(new SetCardInfo("Finale of Promise", 166, Rarity.MYTHIC, mage.cards.f.FinaleOfPromise.class)); cards.add(new SetCardInfo("Finale of Revelation", 97, Rarity.MYTHIC, mage.cards.f.FinaleOfRevelation.class)); cards.add(new SetCardInfo("Flooded Grove", 297, Rarity.RARE, mage.cards.f.FloodedGrove.class)); + cards.add(new SetCardInfo("Forger's Foundry", 14, Rarity.RARE, mage.cards.f.ForgersFoundry.class)); cards.add(new SetCardInfo("Frostboil Snarl", 298, Rarity.RARE, mage.cards.f.FrostboilSnarl.class)); cards.add(new SetCardInfo("Galvanic Iteration", 227, Rarity.RARE, mage.cards.g.GalvanicIteration.class)); cards.add(new SetCardInfo("Genesis Hydra", 193, Rarity.RARE, mage.cards.g.GenesisHydra.class)); diff --git a/Mage.Sets/src/mage/sets/RegionalChampionshipQualifiers2022.java b/Mage.Sets/src/mage/sets/RegionalChampionshipQualifiers2022.java new file mode 100644 index 00000000000..5a0a537eef1 --- /dev/null +++ b/Mage.Sets/src/mage/sets/RegionalChampionshipQualifiers2022.java @@ -0,0 +1,27 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * https://scryfall.com/sets/prcq + */ +public class RegionalChampionshipQualifiers2022 extends ExpansionSet { + + private static final RegionalChampionshipQualifiers2022 instance = new RegionalChampionshipQualifiers2022(); + + public static RegionalChampionshipQualifiers2022 getInstance() { + return instance; + } + + private RegionalChampionshipQualifiers2022() { + super("Regional Championship Qualifiers 2022", "PRCQ", ExpansionSet.buildDate(2022, 10, 1), SetType.PROMOTIONAL); + this.hasBoosters = false; + this.hasBasicLands = false; + + cards.add(new SetCardInfo("Gideon, Ally of Zendikar", 1, Rarity.MYTHIC, mage.cards.g.GideonAllyOfZendikar.class)); + cards.add(new SetCardInfo("Selfless Spirit", 2, Rarity.RARE, mage.cards.s.SelflessSpirit.class)); + cards.add(new SetCardInfo("Thraben Inspector", 3, Rarity.RARE, mage.cards.t.ThrabenInspector.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/SecretLairDrop.java b/Mage.Sets/src/mage/sets/SecretLairDrop.java index e1b5bf885e5..b9fff33cee5 100644 --- a/Mage.Sets/src/mage/sets/SecretLairDrop.java +++ b/Mage.Sets/src/mage/sets/SecretLairDrop.java @@ -158,11 +158,11 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Scavenging Ooze", 135, Rarity.RARE, mage.cards.s.ScavengingOoze.class)); cards.add(new SetCardInfo("The Mimeoplasm", 136, Rarity.MYTHIC, mage.cards.t.TheMimeoplasm.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Voidslime", 137, Rarity.RARE, mage.cards.v.Voidslime.class)); - cards.add(new SetCardInfo("Anguished Unmaking", 138, Rarity.RARE, mage.cards.a.AnguishedUnmaking.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Assassin's Trophy", 139, Rarity.RARE, mage.cards.a.AssassinsTrophy.class)); - cards.add(new SetCardInfo("Decimate", 140, Rarity.RARE, mage.cards.d.Decimate.class)); - cards.add(new SetCardInfo("Dreadbore", 141, Rarity.RARE, mage.cards.d.Dreadbore.class)); - cards.add(new SetCardInfo("Thraximundar", 142, Rarity.MYTHIC, mage.cards.t.Thraximundar.class)); + cards.add(new SetCardInfo("Anguished Unmaking", 138, Rarity.RARE, mage.cards.a.AnguishedUnmaking.class, FULL_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Assassin's Trophy", 139, Rarity.RARE, mage.cards.a.AssassinsTrophy.class, FULL_ART)); + cards.add(new SetCardInfo("Decimate", 140, Rarity.RARE, mage.cards.d.Decimate.class, FULL_ART)); + cards.add(new SetCardInfo("Dreadbore", 141, Rarity.RARE, mage.cards.d.Dreadbore.class, FULL_ART)); + cards.add(new SetCardInfo("Thraximundar", 142, Rarity.MYTHIC, mage.cards.t.Thraximundar.class, FULL_ART)); cards.add(new SetCardInfo("Greymond, Avacyn's Stalwart", 143, Rarity.MYTHIC, mage.cards.g.GreymondAvacynsStalwart.class)); cards.add(new SetCardInfo("Hansk, Slayer Zealot", 144, Rarity.MYTHIC, mage.cards.h.HanskSlayerZealot.class)); cards.add(new SetCardInfo("Gregor, Shrewd Magistrate", 145, Rarity.MYTHIC, mage.cards.g.GregorShrewdMagistrate.class)); @@ -193,11 +193,11 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Birds of Paradise", 176, Rarity.RARE, mage.cards.b.BirdsOfParadise.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Howling Mine", 177, Rarity.RARE, mage.cards.h.HowlingMine.class)); cards.add(new SetCardInfo("Wasteland", 178, Rarity.RARE, mage.cards.w.Wasteland.class)); - cards.add(new SetCardInfo("Wrath of God", 185, Rarity.RARE, mage.cards.w.WrathOfGod.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Preordain", 186, Rarity.RARE, mage.cards.p.Preordain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Decree of Pain", 187, Rarity.RARE, mage.cards.d.DecreeOfPain.class)); - cards.add(new SetCardInfo("Gamble", 188, Rarity.RARE, mage.cards.g.Gamble.class)); - cards.add(new SetCardInfo("Nature's Lore", 189, Rarity.RARE, mage.cards.n.NaturesLore.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Wrath of God", 185, Rarity.RARE, mage.cards.w.WrathOfGod.class, FULL_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Preordain", 186, Rarity.RARE, mage.cards.p.Preordain.class, FULL_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Decree of Pain", 187, Rarity.RARE, mage.cards.d.DecreeOfPain.class, FULL_ART)); + cards.add(new SetCardInfo("Gamble", 188, Rarity.RARE, mage.cards.g.Gamble.class, FULL_ART)); + cards.add(new SetCardInfo("Nature's Lore", 189, Rarity.RARE, mage.cards.n.NaturesLore.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Soul-Scar Mage", 190, Rarity.RARE, mage.cards.s.SoulScarMage.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dryad of the Ilysian Grove", 191, Rarity.RARE, mage.cards.d.DryadOfTheIlysianGrove.class)); cards.add(new SetCardInfo("Sakura-Tribe Elder", 192, Rarity.RARE, mage.cards.s.SakuraTribeElder.class)); @@ -234,7 +234,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Kroxa, Titan of Death's Hunger", 225, Rarity.MYTHIC, mage.cards.k.KroxaTitanOfDeathsHunger.class)); cards.add(new SetCardInfo("Path to Exile", 226, Rarity.RARE, mage.cards.p.PathToExile.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Well of Lost Dreams", 227, Rarity.RARE, mage.cards.w.WellOfLostDreams.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Frantic Search", 228, Rarity.RARE, mage.cards.f.FranticSearch.class)); + cards.add(new SetCardInfo("Frantic Search", 228, Rarity.RARE, mage.cards.f.FranticSearch.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Intruder Alarm", 229, Rarity.RARE, mage.cards.i.IntruderAlarm.class)); cards.add(new SetCardInfo("Shelldock Isle", 230, Rarity.RARE, mage.cards.s.ShelldockIsle.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gravecrawler", 231, Rarity.RARE, mage.cards.g.Gravecrawler.class)); @@ -315,7 +315,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Unbreakable Formation", 310, Rarity.RARE, mage.cards.u.UnbreakableFormation.class)); cards.add(new SetCardInfo("Whir of Invention", 311, Rarity.RARE, mage.cards.w.WhirOfInvention.class)); cards.add(new SetCardInfo("Hero's Downfall", 312, Rarity.RARE, mage.cards.h.HerosDownfall.class)); - cards.add(new SetCardInfo("Impact Tremors", 313, Rarity.RARE, mage.cards.i.ImpactTremors.class)); + cards.add(new SetCardInfo("Impact Tremors", 313, Rarity.RARE, mage.cards.i.ImpactTremors.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Primal Vigor", 314, Rarity.RARE, mage.cards.p.PrimalVigor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Commander's Sphere", 315, Rarity.RARE, mage.cards.c.CommandersSphere.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fleet Swallower", 316, Rarity.RARE, mage.cards.f.FleetSwallower.class)); @@ -323,10 +323,10 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Ilharg, the Raze-Boar", 318, Rarity.MYTHIC, mage.cards.i.IlhargTheRazeBoar.class)); cards.add(new SetCardInfo("Protean Hulk", 319, Rarity.RARE, mage.cards.p.ProteanHulk.class)); cards.add(new SetCardInfo("Gishath, Sun's Avatar", 320, Rarity.MYTHIC, mage.cards.g.GishathSunsAvatar.class)); - cards.add(new SetCardInfo("Dismember", 321, Rarity.RARE, mage.cards.d.Dismember.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Blasphemous Act", 322, Rarity.RARE, mage.cards.b.BlasphemousAct.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Beast Within", 323, Rarity.RARE, mage.cards.b.BeastWithin.class)); - cards.add(new SetCardInfo("Grafdigger's Cage", 324, Rarity.RARE, mage.cards.g.GrafdiggersCage.class)); + cards.add(new SetCardInfo("Dismember", 321, Rarity.RARE, mage.cards.d.Dismember.class, FULL_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Blasphemous Act", 322, Rarity.RARE, mage.cards.b.BlasphemousAct.class, FULL_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Beast Within", 323, Rarity.RARE, mage.cards.b.BeastWithin.class, FULL_ART)); + cards.add(new SetCardInfo("Grafdigger's Cage", 324, Rarity.RARE, mage.cards.g.GrafdiggersCage.class, FULL_ART)); cards.add(new SetCardInfo("Snow-Covered Plains", 325, Rarity.LAND, mage.cards.s.SnowCoveredPlains.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Snow-Covered Island", 326, Rarity.LAND, mage.cards.s.SnowCoveredIsland.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Snow-Covered Swamp", 327, Rarity.LAND, mage.cards.s.SnowCoveredSwamp.class, FULL_ART_BFZ_VARIOUS)); @@ -335,7 +335,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Aether Gust", 330, Rarity.RARE, mage.cards.a.AetherGust.class)); cards.add(new SetCardInfo("Counterspell", 331, Rarity.RARE, mage.cards.c.Counterspell.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fabricate", 332, Rarity.RARE, mage.cards.f.Fabricate.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Fact or Fiction", 333, Rarity.RARE, mage.cards.f.FactOrFiction.class)); + cards.add(new SetCardInfo("Fact or Fiction", 333, Rarity.RARE, mage.cards.f.FactOrFiction.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mystical Tutor", 334, Rarity.RARE, mage.cards.m.MysticalTutor.class)); cards.add(new SetCardInfo("Arvinox, the Mind Flail", 340, Rarity.MYTHIC, mage.cards.a.ArvinoxTheMindFlail.class)); cards.add(new SetCardInfo("Sophina, Spearsage Deserter", 341, Rarity.RARE, mage.cards.s.SophinaSpearsageDeserter.class)); @@ -742,7 +742,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Colossal Dreadmaw", "740*", Rarity.RARE, mage.cards.c.ColossalDreadmaw.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Chaos Warp", 741, Rarity.RARE, mage.cards.c.ChaosWarp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Chaos Warp", "741*", Rarity.RARE, mage.cards.c.ChaosWarp.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Command Tower", 744, Rarity.RARE, mage.cards.c.CommandTower.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Command Tower", 744, Rarity.RARE, mage.cards.c.CommandTower.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Ajani Goldmane", 745, Rarity.MYTHIC, mage.cards.a.AjaniGoldmane.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ajani Goldmane", "745b", Rarity.MYTHIC, mage.cards.a.AjaniGoldmane.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jace Beleren", 746, Rarity.MYTHIC, mage.cards.j.JaceBeleren.class, NON_FULL_USE_VARIOUS)); @@ -760,7 +760,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Relentless Rats", 755, Rarity.RARE, mage.cards.r.RelentlessRats.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Relentless Rats", 756, Rarity.RARE, mage.cards.r.RelentlessRats.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Relentless Rats", 757, Rarity.RARE, mage.cards.r.RelentlessRats.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Command Tower", 758, Rarity.RARE, mage.cards.c.CommandTower.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Command Tower", 758, Rarity.RARE, mage.cards.c.CommandTower.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Skemfar Shadowsage", 759, Rarity.RARE, mage.cards.s.SkemfarShadowsage.class)); cards.add(new SetCardInfo("Elvish Champion", 761, Rarity.RARE, mage.cards.e.ElvishChampion.class)); cards.add(new SetCardInfo("Elvish Vanguard", 762, Rarity.RARE, mage.cards.e.ElvishVanguard.class)); @@ -802,7 +802,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Sonic Screwdriver", 803, Rarity.RARE, mage.cards.s.SonicScrewdriver.class)); cards.add(new SetCardInfo("Beloved Princess", 804, Rarity.RARE, mage.cards.b.BelovedPrincess.class)); cards.add(new SetCardInfo("Elvish Mystic", 805, Rarity.RARE, mage.cards.e.ElvishMystic.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Command Tower", 806, Rarity.RARE, mage.cards.c.CommandTower.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Command Tower", 806, Rarity.RARE, mage.cards.c.CommandTower.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Chandra, Flamecaller", 807, Rarity.MYTHIC, mage.cards.c.ChandraFlamecaller.class)); cards.add(new SetCardInfo("Snapcaster Mage", 808, Rarity.MYTHIC, mage.cards.s.SnapcasterMage.class)); cards.add(new SetCardInfo("Immerwolf", 809, Rarity.RARE, mage.cards.i.Immerwolf.class)); @@ -880,12 +880,12 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Changeling Outcast", 894, Rarity.RARE, mage.cards.c.ChangelingOutcast.class)); cards.add(new SetCardInfo("Helpful Hunter", 895, Rarity.RARE, mage.cards.h.HelpfulHunter.class)); cards.add(new SetCardInfo("Spirited Companion", 896, Rarity.RARE, mage.cards.s.SpiritedCompanion.class)); - cards.add(new SetCardInfo("The Scarab God", 900, Rarity.MYTHIC, mage.cards.t.TheScarabGod.class)); + cards.add(new SetCardInfo("The Scarab God", 900, Rarity.MYTHIC, mage.cards.t.TheScarabGod.class, FULL_ART)); cards.add(new SetCardInfo("Lightning Bolt", 901, Rarity.RARE, mage.cards.l.LightningBolt.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Deadeye Navigator", 902, Rarity.RARE, mage.cards.d.DeadeyeNavigator.class)); cards.add(new SetCardInfo("The Locust God", 903, Rarity.MYTHIC, mage.cards.t.TheLocustGod.class)); cards.add(new SetCardInfo("The Scorpion God", 904, Rarity.MYTHIC, mage.cards.t.TheScorpionGod.class)); - cards.add(new SetCardInfo("Ignoble Hierarch", 906, Rarity.RARE, mage.cards.i.IgnobleHierarch.class)); + cards.add(new SetCardInfo("Ignoble Hierarch", 906, Rarity.RARE, mage.cards.i.IgnobleHierarch.class, FULL_ART)); cards.add(new SetCardInfo("Seedborn Muse", 907, Rarity.RARE, mage.cards.s.SeedbornMuse.class)); cards.add(new SetCardInfo("Arcane Signet", 908, Rarity.RARE, mage.cards.a.ArcaneSignet.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gilded Lotus", 909, Rarity.RARE, mage.cards.g.GildedLotus.class, NON_FULL_USE_VARIOUS)); @@ -1025,10 +1025,10 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Swamp", 1132, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Mountain", 1133, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Forest", 1134, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Abundant Growth", 1135, Rarity.RARE, mage.cards.a.AbundantGrowth.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mycoloth", 1136, Rarity.RARE, mage.cards.m.Mycoloth.class)); - cards.add(new SetCardInfo("Ghave, Guru of Spores", 1137, Rarity.MYTHIC, mage.cards.g.GhaveGuruOfSpores.class)); - cards.add(new SetCardInfo("Slimefoot, the Stowaway", 1138, Rarity.RARE, mage.cards.s.SlimefootTheStowaway.class)); + cards.add(new SetCardInfo("Abundant Growth", 1135, Rarity.RARE, mage.cards.a.AbundantGrowth.class, FULL_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Mycoloth", 1136, Rarity.RARE, mage.cards.m.Mycoloth.class, FULL_ART)); + cards.add(new SetCardInfo("Ghave, Guru of Spores", 1137, Rarity.MYTHIC, mage.cards.g.GhaveGuruOfSpores.class, FULL_ART)); + cards.add(new SetCardInfo("Slimefoot, the Stowaway", 1138, Rarity.RARE, mage.cards.s.SlimefootTheStowaway.class, FULL_ART)); cards.add(new SetCardInfo("Elspeth, Sun's Champion", 1140, Rarity.MYTHIC, mage.cards.e.ElspethSunsChampion.class)); cards.add(new SetCardInfo("Narset, Parter of Veils", 1141, Rarity.RARE, mage.cards.n.NarsetParterOfVeils.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Garruk Wildspeaker", 1142, Rarity.RARE, mage.cards.g.GarrukWildspeaker.class, NON_FULL_USE_VARIOUS)); @@ -1088,14 +1088,14 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Swamp", 1192, Rarity.LAND, mage.cards.basiclands.Swamp.class, RETRO_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 1193, Rarity.LAND, mage.cards.basiclands.Mountain.class, RETRO_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Forest", 1194, Rarity.LAND, mage.cards.basiclands.Forest.class, RETRO_ART_USE_VARIOUS)); - cards.add(new SetCardInfo("Phage the Untouchable", 1195, Rarity.MYTHIC, mage.cards.p.PhageTheUntouchable.class)); - cards.add(new SetCardInfo("Yisan, the Wanderer Bard", 1196, Rarity.RARE, mage.cards.y.YisanTheWandererBard.class)); - cards.add(new SetCardInfo("Alela, Artful Provocateur", 1197, Rarity.MYTHIC, mage.cards.a.AlelaArtfulProvocateur.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Sen Triplets", 1198, Rarity.MYTHIC, mage.cards.s.SenTriplets.class)); - cards.add(new SetCardInfo("Tevesh Szat, Doom of Fools", 1199, Rarity.MYTHIC, mage.cards.t.TeveshSzatDoomOfFools.class)); - cards.add(new SetCardInfo("Godo, Bandit Warlord", 1200, Rarity.RARE, mage.cards.g.GodoBanditWarlord.class)); - cards.add(new SetCardInfo("Jeska, Thrice Reborn", 1201, Rarity.MYTHIC, mage.cards.j.JeskaThriceReborn.class)); - cards.add(new SetCardInfo("Vial Smasher the Fierce", 1202, Rarity.MYTHIC, mage.cards.v.VialSmasherTheFierce.class)); + cards.add(new SetCardInfo("Phage the Untouchable", 1195, Rarity.MYTHIC, mage.cards.p.PhageTheUntouchable.class, FULL_ART)); + cards.add(new SetCardInfo("Yisan, the Wanderer Bard", 1196, Rarity.RARE, mage.cards.y.YisanTheWandererBard.class, FULL_ART)); + cards.add(new SetCardInfo("Alela, Artful Provocateur", 1197, Rarity.MYTHIC, mage.cards.a.AlelaArtfulProvocateur.class, FULL_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Sen Triplets", 1198, Rarity.MYTHIC, mage.cards.s.SenTriplets.class, FULL_ART)); + cards.add(new SetCardInfo("Tevesh Szat, Doom of Fools", 1199, Rarity.MYTHIC, mage.cards.t.TeveshSzatDoomOfFools.class, FULL_ART)); + cards.add(new SetCardInfo("Godo, Bandit Warlord", 1200, Rarity.RARE, mage.cards.g.GodoBanditWarlord.class, FULL_ART)); + cards.add(new SetCardInfo("Jeska, Thrice Reborn", 1201, Rarity.MYTHIC, mage.cards.j.JeskaThriceReborn.class, FULL_ART)); + cards.add(new SetCardInfo("Vial Smasher the Fierce", 1202, Rarity.MYTHIC, mage.cards.v.VialSmasherTheFierce.class, FULL_ART)); cards.add(new SetCardInfo("Blighted Agent", 1203, Rarity.RARE, mage.cards.b.BlightedAgent.class)); cards.add(new SetCardInfo("K'rrik, Son of Yawgmoth", 1204, Rarity.RARE, mage.cards.k.KrrikSonOfYawgmoth.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Glistener Elf", 1205, Rarity.RARE, mage.cards.g.GlistenerElf.class)); @@ -1115,10 +1115,10 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Plague Engineer", 1215, Rarity.RARE, mage.cards.p.PlagueEngineer.class)); cards.add(new SetCardInfo("Ertai, the Corrupted", 1216, Rarity.RARE, mage.cards.e.ErtaiTheCorrupted.class)); cards.add(new SetCardInfo("Glissa, the Traitor", 1217, Rarity.MYTHIC, mage.cards.g.GlissaTheTraitor.class)); - cards.add(new SetCardInfo("Eldrazi Conscription", 1218, Rarity.RARE, mage.cards.e.EldraziConscription.class)); - cards.add(new SetCardInfo("Deafening Silence", 1219, Rarity.RARE, mage.cards.d.DeafeningSilence.class)); - cards.add(new SetCardInfo("Counterbalance", 1220, Rarity.RARE, mage.cards.c.Counterbalance.class)); - cards.add(new SetCardInfo("Bruna, Light of Alabaster", 1221, Rarity.MYTHIC, mage.cards.b.BrunaLightOfAlabaster.class)); + cards.add(new SetCardInfo("Eldrazi Conscription", 1218, Rarity.RARE, mage.cards.e.EldraziConscription.class, FULL_ART)); + cards.add(new SetCardInfo("Deafening Silence", 1219, Rarity.RARE, mage.cards.d.DeafeningSilence.class, FULL_ART)); + cards.add(new SetCardInfo("Counterbalance", 1220, Rarity.RARE, mage.cards.c.Counterbalance.class, FULL_ART)); + cards.add(new SetCardInfo("Bruna, Light of Alabaster", 1221, Rarity.MYTHIC, mage.cards.b.BrunaLightOfAlabaster.class, FULL_ART)); cards.add(new SetCardInfo("Hexdrinker", 1222, Rarity.MYTHIC, mage.cards.h.Hexdrinker.class)); cards.add(new SetCardInfo("Lotus Cobra", 1223, Rarity.RARE, mage.cards.l.LotusCobra.class)); cards.add(new SetCardInfo("Seshiro the Anointed", 1224, Rarity.RARE, mage.cards.s.SeshiroTheAnointed.class)); @@ -1201,6 +1201,10 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Gaea's Blessing", 1303, Rarity.RARE, mage.cards.g.GaeasBlessing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Twilight Prophet", 1304, Rarity.MYTHIC, mage.cards.t.TwilightProphet.class)); cards.add(new SetCardInfo("Worldspine Wurm", 1305, Rarity.MYTHIC, mage.cards.w.WorldspineWurm.class)); + cards.add(new SetCardInfo("Pack Rat", 1307, Rarity.RARE, mage.cards.p.PackRat.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shared Summons", 1308, Rarity.RARE, mage.cards.s.SharedSummons.class)); + cards.add(new SetCardInfo("Sylvan Offering", 1309, Rarity.RARE, mage.cards.s.SylvanOffering.class)); + cards.add(new SetCardInfo("Sliver Legion", 1310, Rarity.MYTHIC, mage.cards.s.SliverLegion.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Goblin Lackey", 1311, Rarity.RARE, mage.cards.g.GoblinLackey.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Goblin Lackey", "1311*", Rarity.RARE, mage.cards.g.GoblinLackey.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Goblin Matron", 1312, Rarity.RARE, mage.cards.g.GoblinMatron.class, NON_FULL_USE_VARIOUS)); @@ -1220,14 +1224,14 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Gilt-Leaf Palace", 1322, Rarity.RARE, mage.cards.g.GiltLeafPalace.class)); cards.add(new SetCardInfo("Secluded Glen", 1323, Rarity.RARE, mage.cards.s.SecludedGlen.class)); cards.add(new SetCardInfo("Wanderwine Hub", 1324, Rarity.RARE, mage.cards.w.WanderwineHub.class)); - cards.add(new SetCardInfo("Estrid's Invocation", 1325, Rarity.RARE, mage.cards.e.EstridsInvocation.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Estrid's Invocation", "1325b", Rarity.RARE, mage.cards.e.EstridsInvocation.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Steely Resolve", 1326, Rarity.RARE, mage.cards.s.SteelyResolve.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Steely Resolve", "1326b", Rarity.RARE, mage.cards.s.SteelyResolve.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Estrid, the Masked", 1327, Rarity.MYTHIC, mage.cards.e.EstridTheMasked.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Estrid, the Masked", "1327b", Rarity.MYTHIC, mage.cards.e.EstridTheMasked.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Tuvasa the Sunlit", 1328, Rarity.MYTHIC, mage.cards.t.TuvasaTheSunlit.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Tuvasa the Sunlit", "1328b", Rarity.MYTHIC, mage.cards.t.TuvasaTheSunlit.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Estrid's Invocation", 1325, Rarity.RARE, mage.cards.e.EstridsInvocation.class, FULL_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Estrid's Invocation", "1325b", Rarity.RARE, mage.cards.e.EstridsInvocation.class, FULL_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Steely Resolve", 1326, Rarity.RARE, mage.cards.s.SteelyResolve.class, FULL_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Steely Resolve", "1326b", Rarity.RARE, mage.cards.s.SteelyResolve.class, FULL_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Estrid, the Masked", 1327, Rarity.MYTHIC, mage.cards.e.EstridTheMasked.class, FULL_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Estrid, the Masked", "1327b", Rarity.MYTHIC, mage.cards.e.EstridTheMasked.class, FULL_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Tuvasa the Sunlit", 1328, Rarity.MYTHIC, mage.cards.t.TuvasaTheSunlit.class, FULL_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Tuvasa the Sunlit", "1328b", Rarity.MYTHIC, mage.cards.t.TuvasaTheSunlit.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Gargos, Vicious Watcher", 1329, Rarity.RARE, mage.cards.g.GargosViciousWatcher.class)); cards.add(new SetCardInfo("Primordial Hydra", 1330, Rarity.MYTHIC, mage.cards.p.PrimordialHydra.class)); cards.add(new SetCardInfo("Unbound Flourishing", 1331, Rarity.MYTHIC, mage.cards.u.UnboundFlourishing.class)); @@ -1807,8 +1811,8 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Stranglehold", 1804, Rarity.RARE, mage.cards.s.Stranglehold.class)); cards.add(new SetCardInfo("Thrill of Possibility", 1805, Rarity.RARE, mage.cards.t.ThrillOfPossibility.class)); cards.add(new SetCardInfo("Dolmen Gate", 1806, Rarity.RARE, mage.cards.d.DolmenGate.class)); - cards.add(new SetCardInfo("Kardur, Doomscourge", 1807, Rarity.MYTHIC, mage.cards.k.KardurDoomscourge.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Kardur, Doomscourge", "1807b", Rarity.MYTHIC, mage.cards.k.KardurDoomscourge.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kardur, Doomscourge", 1807, Rarity.MYTHIC, mage.cards.k.KardurDoomscourge.class, FULL_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Kardur, Doomscourge", "1807b", Rarity.MYTHIC, mage.cards.k.KardurDoomscourge.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Phyrexian Reclamation", 1808, Rarity.RARE, mage.cards.p.PhyrexianReclamation.class)); cards.add(new SetCardInfo("Varragoth, Bloodsky Sire", 1809, Rarity.RARE, mage.cards.v.VarragothBloodskySire.class)); cards.add(new SetCardInfo("Twinflame", 1810, Rarity.RARE, mage.cards.t.Twinflame.class)); @@ -1967,6 +1971,11 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Zulaport Cutthroat", 1982, Rarity.RARE, mage.cards.z.ZulaportCutthroat.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Aggravated Assault", 1983, Rarity.RARE, mage.cards.a.AggravatedAssault.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Desperate Ritual", 1984, Rarity.RARE, mage.cards.d.DesperateRitual.class)); + cards.add(new SetCardInfo("Fact or Fiction", 1995, Rarity.RARE, mage.cards.f.FactOrFiction.class, FULL_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Frantic Search", 1996, Rarity.RARE, mage.cards.f.FranticSearch.class, FULL_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Scheming Symmetry", 1997, Rarity.RARE, mage.cards.s.SchemingSymmetry.class, FULL_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Blasphemous Act", 1998, Rarity.RARE, mage.cards.b.BlasphemousAct.class, FULL_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Impact Tremors", 1999, Rarity.RARE, mage.cards.i.ImpactTremors.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Agent of Treachery", 2005, Rarity.RARE, mage.cards.a.AgentOfTreachery.class)); cards.add(new SetCardInfo("Priest of Forgotten Gods", 2006, Rarity.RARE, mage.cards.p.PriestOfForgottenGods.class)); cards.add(new SetCardInfo("Treasonous Ogre", 2007, Rarity.RARE, mage.cards.t.TreasonousOgre.class)); @@ -2069,7 +2078,7 @@ public class SecretLairDrop extends ExpansionSet { cards.add(new SetCardInfo("Mirage Mirror", 7025, Rarity.RARE, mage.cards.m.MirageMirror.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Scion of Draco", 7026, Rarity.MYTHIC, mage.cards.s.ScionOfDraco.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Tibalt's Trickery", 7027, Rarity.RARE, mage.cards.t.TibaltsTrickery.class)); - cards.add(new SetCardInfo("Minds Aglow", 7028, Rarity.RARE, mage.cards.m.MindsAglow.class)); + cards.add(new SetCardInfo("Minds Aglow", 7028, Rarity.RARE, mage.cards.m.MindsAglow.class, FULL_ART)); cards.add(new SetCardInfo("Command Tower", 7029, Rarity.RARE, mage.cards.c.CommandTower.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Command Tower", 7030, Rarity.RARE, mage.cards.c.CommandTower.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lotus Petal", 7031, Rarity.MYTHIC, mage.cards.l.LotusPetal.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage.Sets/src/mage/sets/SecretLairShowdown.java b/Mage.Sets/src/mage/sets/SecretLairShowdown.java index dfc3fecd8be..b951d3351fe 100644 --- a/Mage.Sets/src/mage/sets/SecretLairShowdown.java +++ b/Mage.Sets/src/mage/sets/SecretLairShowdown.java @@ -30,7 +30,7 @@ public class SecretLairShowdown extends ExpansionSet { cards.add(new SetCardInfo("Expressive Iteration", 13, Rarity.RARE, mage.cards.e.ExpressiveIteration.class)); cards.add(new SetCardInfo("Fatal Push", 3, Rarity.RARE, mage.cards.f.FatalPush.class)); cards.add(new SetCardInfo("Fauna Shaman", 41, Rarity.RARE, mage.cards.f.FaunaShaman.class)); - cards.add(new SetCardInfo("Force of Despair", 29, Rarity.RARE, mage.cards.f.ForceOfDespair.class)); + cards.add(new SetCardInfo("Force of Despair", 29, Rarity.RARE, mage.cards.f.ForceOfDespair.class, FULL_ART)); cards.add(new SetCardInfo("Garruk Wildspeaker", 42, Rarity.MYTHIC, mage.cards.g.GarrukWildspeaker.class)); cards.add(new SetCardInfo("Goblin Guide", 23, Rarity.RARE, mage.cards.g.GoblinGuide.class)); cards.add(new SetCardInfo("Island", 32, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); @@ -42,21 +42,21 @@ public class SecretLairShowdown extends ExpansionSet { cards.add(new SetCardInfo("Mayhem Devil", 28, Rarity.RARE, mage.cards.m.MayhemDevil.class)); cards.add(new SetCardInfo("Mountain", 34, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Murktide Regent", 17, Rarity.MYTHIC, mage.cards.m.MurktideRegent.class)); - cards.add(new SetCardInfo("Nexus of Fate", 27, Rarity.RARE, mage.cards.n.NexusOfFate.class)); + cards.add(new SetCardInfo("Nexus of Fate", 27, Rarity.RARE, mage.cards.n.NexusOfFate.class, FULL_ART)); cards.add(new SetCardInfo("Plains", 31, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); - cards.add(new SetCardInfo("Ponder", 19, Rarity.RARE, mage.cards.p.Ponder.class)); - cards.add(new SetCardInfo("Prosperous Innkeeper", 40, Rarity.RARE, mage.cards.p.ProsperousInnkeeper.class)); + cards.add(new SetCardInfo("Ponder", 19, Rarity.RARE, mage.cards.p.Ponder.class, FULL_ART)); + cards.add(new SetCardInfo("Prosperous Innkeeper", 40, Rarity.RARE, mage.cards.p.ProsperousInnkeeper.class, FULL_ART)); cards.add(new SetCardInfo("Questing Druid", 38, Rarity.RARE, mage.cards.q.QuestingDruid.class)); cards.add(new SetCardInfo("Ragavan, Nimble Pilferer", 2, Rarity.MYTHIC, mage.cards.r.RagavanNimblePilferer.class)); cards.add(new SetCardInfo("Relentless Rats", 10, Rarity.RARE, mage.cards.r.RelentlessRats.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Relentless Rats", 11, Rarity.RARE, mage.cards.r.RelentlessRats.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Seasoned Pyromancer", 24, Rarity.MYTHIC, mage.cards.s.SeasonedPyromancer.class)); - cards.add(new SetCardInfo("Shoot the Sheriff", 43, Rarity.RARE, mage.cards.s.ShootTheSheriff.class)); - cards.add(new SetCardInfo("Sleight of Hand", 25, Rarity.RARE, mage.cards.s.SleightOfHand.class)); + cards.add(new SetCardInfo("Shoot the Sheriff", 43, Rarity.RARE, mage.cards.s.ShootTheSheriff.class, FULL_ART)); + cards.add(new SetCardInfo("Sleight of Hand", 25, Rarity.RARE, mage.cards.s.SleightOfHand.class, FULL_ART)); cards.add(new SetCardInfo("Spell Pierce", 18, Rarity.RARE, mage.cards.s.SpellPierce.class)); cards.add(new SetCardInfo("Springleaf Drum", 22, Rarity.RARE, mage.cards.s.SpringleafDrum.class)); - cards.add(new SetCardInfo("Sudden Edict", 39, Rarity.RARE, mage.cards.s.SuddenEdict.class)); - cards.add(new SetCardInfo("Supreme Verdict", 26, Rarity.RARE, mage.cards.s.SupremeVerdict.class)); + cards.add(new SetCardInfo("Sudden Edict", 39, Rarity.RARE, mage.cards.s.SuddenEdict.class, FULL_ART)); + cards.add(new SetCardInfo("Supreme Verdict", 26, Rarity.RARE, mage.cards.s.SupremeVerdict.class, FULL_ART)); cards.add(new SetCardInfo("Swamp", 33, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Swords to Plowshares", 20, Rarity.RARE, mage.cards.s.SwordsToPlowshares.class)); cards.add(new SetCardInfo("Ugin, the Spirit Dragon", 6, Rarity.MYTHIC, mage.cards.u.UginTheSpiritDragon.class)); diff --git a/Mage.Sets/src/mage/sets/ShadowsOverInnistradRemastered.java b/Mage.Sets/src/mage/sets/ShadowsOverInnistradRemastered.java index 60d38578703..8739ee9b6ea 100644 --- a/Mage.Sets/src/mage/sets/ShadowsOverInnistradRemastered.java +++ b/Mage.Sets/src/mage/sets/ShadowsOverInnistradRemastered.java @@ -61,7 +61,7 @@ public class ShadowsOverInnistradRemastered extends ExpansionSet { cards.add(new SetCardInfo("Brain in a Jar", 247, Rarity.UNCOMMON, mage.cards.b.BrainInAJar.class)); cards.add(new SetCardInfo("Briarbridge Patrol", 187, Rarity.COMMON, mage.cards.b.BriarbridgePatrol.class)); cards.add(new SetCardInfo("Brisela, Voice of Nightmares", "17b", Rarity.MYTHIC, mage.cards.b.BriselaVoiceOfNightmares.class)); - cards.add(new SetCardInfo("Bruna, the Fading Light", "17a", Rarity.RARE, mage.cards.b.BrunaTheFadingLight.class)); + cards.add(new SetCardInfo("Bruna, the Fading Light", 17, Rarity.RARE, mage.cards.b.BrunaTheFadingLight.class)); cards.add(new SetCardInfo("Burn from Within", 147, Rarity.RARE, mage.cards.b.BurnFromWithin.class)); cards.add(new SetCardInfo("Bygone Bishop", 18, Rarity.RARE, mage.cards.b.BygoneBishop.class)); cards.add(new SetCardInfo("Byway Courier", 188, Rarity.COMMON, mage.cards.b.BywayCourier.class)); @@ -158,7 +158,7 @@ public class ShadowsOverInnistradRemastered extends ExpansionSet { cards.add(new SetCardInfo("Goldnight Castigator", 160, Rarity.MYTHIC, mage.cards.g.GoldnightCastigator.class)); cards.add(new SetCardInfo("Graf Harvest", 114, Rarity.UNCOMMON, mage.cards.g.GrafHarvest.class)); cards.add(new SetCardInfo("Graf Mole", 197, Rarity.UNCOMMON, mage.cards.g.GrafMole.class)); - cards.add(new SetCardInfo("Graf Rats", "115a", Rarity.COMMON, mage.cards.g.GrafRats.class)); + cards.add(new SetCardInfo("Graf Rats", 115, Rarity.COMMON, mage.cards.g.GrafRats.class)); cards.add(new SetCardInfo("Grapple with the Past", 198, Rarity.COMMON, mage.cards.g.GrappleWithThePast.class)); cards.add(new SetCardInfo("Grim Flayer", 234, Rarity.RARE, mage.cards.g.GrimFlayer.class)); cards.add(new SetCardInfo("Grotesque Mutation", 116, Rarity.COMMON, mage.cards.g.GrotesqueMutation.class)); @@ -167,7 +167,7 @@ public class ShadowsOverInnistradRemastered extends ExpansionSet { cards.add(new SetCardInfo("Guardian of Pilgrims", 32, Rarity.COMMON, mage.cards.g.GuardianOfPilgrims.class)); cards.add(new SetCardInfo("Hamlet Captain", 200, Rarity.UNCOMMON, mage.cards.h.HamletCaptain.class)); cards.add(new SetCardInfo("Hanweir Battlements", 271, Rarity.RARE, mage.cards.h.HanweirBattlements.class)); - cards.add(new SetCardInfo("Hanweir Garrison", "161a", Rarity.RARE, mage.cards.h.HanweirGarrison.class)); + cards.add(new SetCardInfo("Hanweir Garrison", 161, Rarity.RARE, mage.cards.h.HanweirGarrison.class)); cards.add(new SetCardInfo("Hanweir, the Writhing Township", "161b", Rarity.RARE, mage.cards.h.HanweirTheWrithingTownship.class)); cards.add(new SetCardInfo("Harvest Hand", 252, Rarity.UNCOMMON, mage.cards.h.HarvestHand.class)); cards.add(new SetCardInfo("Haunted Dead", 117, Rarity.UNCOMMON, mage.cards.h.HauntedDead.class)); diff --git a/Mage.Sets/src/mage/sets/StoreChampionships.java b/Mage.Sets/src/mage/sets/StoreChampionships.java index dd7571ca9b4..b9a6e890afe 100644 --- a/Mage.Sets/src/mage/sets/StoreChampionships.java +++ b/Mage.Sets/src/mage/sets/StoreChampionships.java @@ -26,6 +26,7 @@ public final class StoreChampionships extends ExpansionSet { cards.add(new SetCardInfo("Angel of Despair", 22, Rarity.RARE, mage.cards.a.AngelOfDespair.class)); cards.add(new SetCardInfo("Annex Sentry", 7, Rarity.RARE, mage.cards.a.AnnexSentry.class)); cards.add(new SetCardInfo("Archmage's Charm", 2, Rarity.RARE, mage.cards.a.ArchmagesCharm.class)); + cards.add(new SetCardInfo("Bitter Triumph", 42, Rarity.RARE, mage.cards.b.BitterTriumph.class)); cards.add(new SetCardInfo("Blazing Rootwalla", 24, Rarity.RARE, mage.cards.b.BlazingRootwalla.class)); cards.add(new SetCardInfo("Cauldron Familiar", 18, Rarity.RARE, mage.cards.c.CauldronFamiliar.class)); cards.add(new SetCardInfo("Charming Scoundrel", 37, Rarity.RARE, mage.cards.c.CharmingScoundrel.class)); @@ -37,6 +38,7 @@ public final class StoreChampionships extends ExpansionSet { cards.add(new SetCardInfo("Death's Shadow", 40, Rarity.RARE, mage.cards.d.DeathsShadow.class)); cards.add(new SetCardInfo("Deep-Cavern Bat", 33, Rarity.RARE, mage.cards.d.DeepCavernBat.class)); cards.add(new SetCardInfo("Eidolon of the Great Revel", 14, Rarity.RARE, mage.cards.e.EidolonOfTheGreatRevel.class)); + cards.add(new SetCardInfo("Fable of the Mirror-Breaker", 44, Rarity.RARE, mage.cards.f.FableOfTheMirrorBreaker.class)); cards.add(new SetCardInfo("Flame Slash", 1, Rarity.RARE, mage.cards.f.FlameSlash.class)); cards.add(new SetCardInfo("Gifted Aetherborn", 13, Rarity.RARE, mage.cards.g.GiftedAetherborn.class)); cards.add(new SetCardInfo("Gilded Goose", 5, Rarity.RARE, mage.cards.g.GildedGoose.class)); @@ -44,7 +46,7 @@ public final class StoreChampionships extends ExpansionSet { cards.add(new SetCardInfo("Goddric, Cloaked Reveler", 38, Rarity.RARE, mage.cards.g.GoddricCloakedReveler.class, FULL_ART)); cards.add(new SetCardInfo("Hollow One", 25, Rarity.RARE, mage.cards.h.HollowOne.class)); cards.add(new SetCardInfo("Koth, Fire of Resistance", 9, Rarity.RARE, mage.cards.k.KothFireOfResistance.class)); - cards.add(new SetCardInfo("Lier, Disciple of the Drowned", 20, Rarity.RARE, mage.cards.l.LierDiscipleOfTheDrowned.class)); + cards.add(new SetCardInfo("Lier, Disciple of the Drowned", 20, Rarity.RARE, mage.cards.l.LierDiscipleOfTheDrowned.class, FULL_ART)); cards.add(new SetCardInfo("Memory Deluge", 8, Rarity.RARE, mage.cards.m.MemoryDeluge.class)); cards.add(new SetCardInfo("Monastery Swiftspear", 27, Rarity.RARE, mage.cards.m.MonasterySwiftspear.class)); cards.add(new SetCardInfo("Moonshaker Cavalry", 17, Rarity.MYTHIC, mage.cards.m.MoonshakerCavalry.class, FULL_ART)); @@ -53,7 +55,9 @@ public final class StoreChampionships extends ExpansionSet { cards.add(new SetCardInfo("Preacher of the Schism", 34, Rarity.RARE, mage.cards.p.PreacherOfTheSchism.class)); cards.add(new SetCardInfo("Preordain", 39, Rarity.RARE, mage.cards.p.Preordain.class)); cards.add(new SetCardInfo("Reality Smasher", 31, Rarity.RARE, mage.cards.r.RealitySmasher.class)); + cards.add(new SetCardInfo("Reflection of Kiki-Jiki", 44, Rarity.RARE, mage.cards.r.ReflectionOfKikiJiki.class)); cards.add(new SetCardInfo("Shark Typhoon", 28, Rarity.RARE, mage.cards.s.SharkTyphoon.class)); + cards.add(new SetCardInfo("Slickshot Show-Off", 43, Rarity.RARE, mage.cards.s.SlickshotShowOff.class)); cards.add(new SetCardInfo("Spell Pierce", 4, Rarity.RARE, mage.cards.s.SpellPierce.class)); cards.add(new SetCardInfo("Strangle", 10, Rarity.RARE, mage.cards.s.Strangle.class)); cards.add(new SetCardInfo("Tail Swipe", 15, Rarity.RARE, mage.cards.t.TailSwipe.class)); diff --git a/Mage.Sets/src/mage/sets/The30thAnniversaryHistoryPromos.java b/Mage.Sets/src/mage/sets/The30thAnniversaryHistoryPromos.java new file mode 100644 index 00000000000..cf403856707 --- /dev/null +++ b/Mage.Sets/src/mage/sets/The30thAnniversaryHistoryPromos.java @@ -0,0 +1,36 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * https://scryfall.com/sets/p30h + * + * @author resech + */ +public class The30thAnniversaryHistoryPromos extends ExpansionSet { + + private static final The30thAnniversaryHistoryPromos instance = new The30thAnniversaryHistoryPromos(); + + public static The30thAnniversaryHistoryPromos getInstance() { + return instance; + } + + private The30thAnniversaryHistoryPromos() { + super("30th Anniversary History Promos", "P30H", ExpansionSet.buildDate(2022, 9, 9), SetType.PROMOTIONAL); + hasBasicLands = false; + + + cards.add(new SetCardInfo("Llanowar Elves", "5*", Rarity.RARE, mage.cards.l.LlanowarElves.class, RETRO_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Llanowar Elves", 5, Rarity.RARE, mage.cards.l.LlanowarElves.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lord of Atlantis", "2*", Rarity.RARE, mage.cards.l.LordOfAtlantis.class, RETRO_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Lord of Atlantis", 2, Rarity.RARE, mage.cards.l.LordOfAtlantis.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sengir Vampire", "3*", Rarity.RARE, mage.cards.s.SengirVampire.class, RETRO_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Sengir Vampire", 3, Rarity.RARE, mage.cards.s.SengirVampire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Serra Angel", "1*", Rarity.RARE, mage.cards.s.SerraAngel.class, RETRO_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Serra Angel", 1, Rarity.RARE, mage.cards.s.SerraAngel.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shivan Dragon", "4*", Rarity.RARE, mage.cards.s.ShivanDragon.class, RETRO_ART_USE_VARIOUS)); + cards.add(new SetCardInfo("Shivan Dragon", 4, Rarity.RARE, mage.cards.s.ShivanDragon.class, NON_FULL_USE_VARIOUS)); + } +} diff --git a/Mage.Sets/src/mage/sets/TheBrothersWar.java b/Mage.Sets/src/mage/sets/TheBrothersWar.java index c1c78f27745..2d098019895 100644 --- a/Mage.Sets/src/mage/sets/TheBrothersWar.java +++ b/Mage.Sets/src/mage/sets/TheBrothersWar.java @@ -47,7 +47,7 @@ public final class TheBrothersWar extends ExpansionSet { cards.add(new SetCardInfo("Arcane Proxy", 319, Rarity.MYTHIC, mage.cards.a.ArcaneProxy.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Arcane Proxy", 75, Rarity.MYTHIC, mage.cards.a.ArcaneProxy.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Argivian Avenger", 232, Rarity.UNCOMMON, mage.cards.a.ArgivianAvenger.class)); - cards.add(new SetCardInfo("Argoth, Sanctum of Nature", "256a", Rarity.RARE, mage.cards.a.ArgothSanctumOfNature.class)); + cards.add(new SetCardInfo("Argoth, Sanctum of Nature", 256, Rarity.RARE, mage.cards.a.ArgothSanctumOfNature.class)); cards.add(new SetCardInfo("Argothian Opportunist", 167, Rarity.COMMON, mage.cards.a.ArgothianOpportunist.class)); cards.add(new SetCardInfo("Argothian Sprite", 168, Rarity.COMMON, mage.cards.a.ArgothianSprite.class)); cards.add(new SetCardInfo("Arms Race", 126, Rarity.UNCOMMON, mage.cards.a.ArmsRace.class)); @@ -268,7 +268,7 @@ public final class TheBrothersWar extends ExpansionSet { cards.add(new SetCardInfo("Perennial Behemoth", 350, Rarity.RARE, mage.cards.p.PerennialBehemoth.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Perimeter Patrol", 188, Rarity.COMMON, mage.cards.p.PerimeterPatrol.class)); cards.add(new SetCardInfo("Phalanx Vanguard", 19, Rarity.COMMON, mage.cards.p.PhalanxVanguard.class)); - cards.add(new SetCardInfo("Phyrexian Dragon Engine", "163a", Rarity.RARE, mage.cards.p.PhyrexianDragonEngine.class)); + cards.add(new SetCardInfo("Phyrexian Dragon Engine", 163, Rarity.RARE, mage.cards.p.PhyrexianDragonEngine.class)); cards.add(new SetCardInfo("Phyrexian Fleshgorger", 121, Rarity.MYTHIC, mage.cards.p.PhyrexianFleshgorger.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Phyrexian Fleshgorger", 332, Rarity.MYTHIC, mage.cards.p.PhyrexianFleshgorger.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 268, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage.Sets/src/mage/sets/TheLordOfTheRingsTalesOfMiddleEarth.java b/Mage.Sets/src/mage/sets/TheLordOfTheRingsTalesOfMiddleEarth.java index 695b777e50e..93acfb49741 100644 --- a/Mage.Sets/src/mage/sets/TheLordOfTheRingsTalesOfMiddleEarth.java +++ b/Mage.Sets/src/mage/sets/TheLordOfTheRingsTalesOfMiddleEarth.java @@ -27,7 +27,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet { cards.add(new SetCardInfo("Anduril, Flame of the West", 236, Rarity.MYTHIC, mage.cards.a.AndurilFlameOfTheWest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Anduril, Flame of the West", 375, Rarity.MYTHIC, mage.cards.a.AndurilFlameOfTheWest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Anduril, Flame of the West", 687, Rarity.MYTHIC, mage.cards.a.AndurilFlameOfTheWest.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Anduril, Flame of the West", 746, Rarity.MYTHIC, mage.cards.a.AndurilFlameOfTheWest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Anduril, Flame of the West", 746, Rarity.MYTHIC, mage.cards.a.AndurilFlameOfTheWest.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Anduril, Flame of the West", 786, Rarity.MYTHIC, mage.cards.a.AndurilFlameOfTheWest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Aragorn and Arwen, Wed", 287, Rarity.MYTHIC, mage.cards.a.AragornAndArwenWed.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Aragorn and Arwen, Wed", 394, Rarity.MYTHIC, mage.cards.a.AragornAndArwenWed.class, NON_FULL_USE_VARIOUS)); @@ -40,7 +40,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet { cards.add(new SetCardInfo("Aragorn, the Uniter", 317, Rarity.MYTHIC, mage.cards.a.AragornTheUniter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Aragorn, the Uniter", 434, Rarity.MYTHIC, mage.cards.a.AragornTheUniter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Aragorn, the Uniter", 643, Rarity.MYTHIC, mage.cards.a.AragornTheUniter.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Aragorn, the Uniter", 741, Rarity.MYTHIC, mage.cards.a.AragornTheUniter.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Aragorn, the Uniter", 741, Rarity.MYTHIC, mage.cards.a.AragornTheUniter.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Aragorn, the Uniter", 809, Rarity.MYTHIC, mage.cards.a.AragornTheUniter.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Arwen Undomiel", 194, Rarity.UNCOMMON, mage.cards.a.ArwenUndomiel.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Arwen Undomiel", 645, Rarity.UNCOMMON, mage.cards.a.ArwenUndomiel.class, NON_FULL_USE_VARIOUS)); @@ -49,7 +49,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet { cards.add(new SetCardInfo("Arwen, Mortal Queen", 193, Rarity.MYTHIC, mage.cards.a.ArwenMortalQueen.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Arwen, Mortal Queen", 367, Rarity.MYTHIC, mage.cards.a.ArwenMortalQueen.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Arwen, Mortal Queen", 644, Rarity.MYTHIC, mage.cards.a.ArwenMortalQueen.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Arwen, Mortal Queen", 742, Rarity.MYTHIC, mage.cards.a.ArwenMortalQueen.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arwen, Mortal Queen", 742, Rarity.MYTHIC, mage.cards.a.ArwenMortalQueen.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Arwen, Mortal Queen", 778, Rarity.MYTHIC, mage.cards.a.ArwenMortalQueen.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Assault on Osgiliath", 285, Rarity.RARE, mage.cards.a.AssaultOnOsgiliath.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Assault on Osgiliath", 386, Rarity.RARE, mage.cards.a.AssaultOnOsgiliath.class, NON_FULL_USE_VARIOUS)); @@ -121,7 +121,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet { cards.add(new SetCardInfo("Dawn of a New Age", 347, Rarity.MYTHIC, mage.cards.d.DawnOfANewAge.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dawn of a New Age", 456, Rarity.MYTHIC, mage.cards.d.DawnOfANewAge.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dawn of a New Age", 5, Rarity.MYTHIC, mage.cards.d.DawnOfANewAge.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Dawn of a New Age", 731, Rarity.MYTHIC, mage.cards.d.DawnOfANewAge.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dawn of a New Age", 731, Rarity.MYTHIC, mage.cards.d.DawnOfANewAge.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Dawn of a New Age", 758, Rarity.MYTHIC, mage.cards.d.DawnOfANewAge.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Deceive the Messenger", 47, Rarity.COMMON, mage.cards.d.DeceiveTheMessenger.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Deceive the Messenger", 498, Rarity.COMMON, mage.cards.d.DeceiveTheMessenger.class, NON_FULL_USE_VARIOUS)); @@ -287,7 +287,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet { cards.add(new SetCardInfo("Gandalf the White", 305, Rarity.MYTHIC, mage.cards.g.GandalfTheWhite.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gandalf the White", 442, Rarity.MYTHIC, mage.cards.g.GandalfTheWhite.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gandalf the White", 470, Rarity.MYTHIC, mage.cards.g.GandalfTheWhite.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Gandalf the White", 732, Rarity.MYTHIC, mage.cards.g.GandalfTheWhite.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Gandalf the White", 732, Rarity.MYTHIC, mage.cards.g.GandalfTheWhite.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Gandalf the White", 797, Rarity.MYTHIC, mage.cards.g.GandalfTheWhite.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gandalf's Sanction", 208, Rarity.UNCOMMON, mage.cards.g.GandalfsSanction.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gandalf's Sanction", 659, Rarity.UNCOMMON, mage.cards.g.GandalfsSanction.class, NON_FULL_USE_VARIOUS)); @@ -318,7 +318,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet { cards.add(new SetCardInfo("Glamdring", 239, Rarity.MYTHIC, mage.cards.g.Glamdring.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Glamdring", 376, Rarity.MYTHIC, mage.cards.g.Glamdring.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Glamdring", 690, Rarity.MYTHIC, mage.cards.g.Glamdring.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Glamdring", 747, Rarity.MYTHIC, mage.cards.g.Glamdring.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Glamdring", 747, Rarity.MYTHIC, mage.cards.g.Glamdring.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Glamdring", 787, Rarity.MYTHIC, mage.cards.g.Glamdring.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gloin, Dwarf Emissary", 132, Rarity.RARE, mage.cards.g.GloinDwarfEmissary.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gloin, Dwarf Emissary", 360, Rarity.RARE, mage.cards.g.GloinDwarfEmissary.class, NON_FULL_USE_VARIOUS)); @@ -368,7 +368,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet { cards.add(new SetCardInfo("Hew the Entwood", 136, Rarity.MYTHIC, mage.cards.h.HewTheEntwood.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Hew the Entwood", 361, Rarity.MYTHIC, mage.cards.h.HewTheEntwood.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Hew the Entwood", 587, Rarity.MYTHIC, mage.cards.h.HewTheEntwood.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Hew the Entwood", 737, Rarity.MYTHIC, mage.cards.h.HewTheEntwood.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hew the Entwood", 737, Rarity.MYTHIC, mage.cards.h.HewTheEntwood.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Hew the Entwood", 772, Rarity.MYTHIC, mage.cards.h.HewTheEntwood.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Hithlain Knots", 505, Rarity.COMMON, mage.cards.h.HithlainKnots.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Hithlain Knots", 54, Rarity.COMMON, mage.cards.h.HithlainKnots.class, NON_FULL_USE_VARIOUS)); @@ -420,7 +420,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet { cards.add(new SetCardInfo("Last March of the Ents", 172, Rarity.MYTHIC, mage.cards.l.LastMarchOfTheEnts.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Last March of the Ents", 418, Rarity.MYTHIC, mage.cards.l.LastMarchOfTheEnts.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Last March of the Ents", 623, Rarity.MYTHIC, mage.cards.l.LastMarchOfTheEnts.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Last March of the Ents", 739, Rarity.MYTHIC, mage.cards.l.LastMarchOfTheEnts.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Last March of the Ents", 739, Rarity.MYTHIC, mage.cards.l.LastMarchOfTheEnts.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Legolas, Counter of Kills", 212, Rarity.UNCOMMON, mage.cards.l.LegolasCounterOfKills.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Legolas, Counter of Kills", 324, Rarity.UNCOMMON, mage.cards.l.LegolasCounterOfKills.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Legolas, Counter of Kills", 663, Rarity.UNCOMMON, mage.cards.l.LegolasCounterOfKills.class, NON_FULL_USE_VARIOUS)); @@ -509,7 +509,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet { cards.add(new SetCardInfo("Mount Doom", 258, Rarity.MYTHIC, mage.cards.m.MountDoom.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mount Doom", 343, Rarity.MYTHIC, mage.cards.m.MountDoom.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mount Doom", 709, Rarity.MYTHIC, mage.cards.m.MountDoom.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mount Doom", 750, Rarity.MYTHIC, mage.cards.m.MountDoom.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mount Doom", 750, Rarity.MYTHIC, mage.cards.m.MountDoom.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Mount Doom", 754, Rarity.MYTHIC, mage.cards.m.MountDoom.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 268, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mountain", 269, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); @@ -565,7 +565,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet { cards.add(new SetCardInfo("Palantir of Orthanc", 247, Rarity.MYTHIC, mage.cards.p.PalantirOfOrthanc.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Palantir of Orthanc", 381, Rarity.MYTHIC, mage.cards.p.PalantirOfOrthanc.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Palantir of Orthanc", 698, Rarity.MYTHIC, mage.cards.p.PalantirOfOrthanc.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Palantir of Orthanc", 749, Rarity.MYTHIC, mage.cards.p.PalantirOfOrthanc.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Palantir of Orthanc", 749, Rarity.MYTHIC, mage.cards.p.PalantirOfOrthanc.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Palantir of Orthanc", 792, Rarity.MYTHIC, mage.cards.p.PalantirOfOrthanc.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Pelargir Survivor", 515, Rarity.COMMON, mage.cards.p.PelargirSurvivor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Pelargir Survivor", 64, Rarity.COMMON, mage.cards.p.PelargirSurvivor.class, NON_FULL_USE_VARIOUS)); @@ -608,7 +608,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet { cards.add(new SetCardInfo("Radagast the Brown", 184, Rarity.MYTHIC, mage.cards.r.RadagastTheBrown.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Radagast the Brown", 365, Rarity.MYTHIC, mage.cards.r.RadagastTheBrown.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Radagast the Brown", 635, Rarity.MYTHIC, mage.cards.r.RadagastTheBrown.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Radagast the Brown", 740, Rarity.MYTHIC, mage.cards.r.RadagastTheBrown.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Radagast the Brown", 740, Rarity.MYTHIC, mage.cards.r.RadagastTheBrown.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Radagast the Brown", 776, Rarity.MYTHIC, mage.cards.r.RadagastTheBrown.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rally at the Hornburg", 142, Rarity.COMMON, mage.cards.r.RallyAtTheHornburg.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rally at the Hornburg", 593, Rarity.COMMON, mage.cards.r.RallyAtTheHornburg.class, NON_FULL_USE_VARIOUS)); @@ -664,7 +664,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet { cards.add(new SetCardInfo("Saruman of Many Colors", 328, Rarity.MYTHIC, mage.cards.s.SarumanOfManyColors.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Saruman of Many Colors", 412, Rarity.MYTHIC, mage.cards.s.SarumanOfManyColors.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Saruman of Many Colors", 674, Rarity.MYTHIC, mage.cards.s.SarumanOfManyColors.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Saruman of Many Colors", 743, Rarity.MYTHIC, mage.cards.s.SarumanOfManyColors.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Saruman of Many Colors", 743, Rarity.MYTHIC, mage.cards.s.SarumanOfManyColors.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Saruman of Many Colors", 820, Rarity.MYTHIC, mage.cards.s.SarumanOfManyColors.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Saruman the White", 518, Rarity.UNCOMMON, mage.cards.s.SarumanTheWhite.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Saruman the White", 67, Rarity.UNCOMMON, mage.cards.s.SarumanTheWhite.class, NON_FULL_USE_VARIOUS)); @@ -678,7 +678,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet { cards.add(new SetCardInfo("Sauron, the Dark Lord", 301, Rarity.MYTHIC, mage.cards.s.SauronTheDarkLord.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sauron, the Dark Lord", 329, Rarity.MYTHIC, mage.cards.s.SauronTheDarkLord.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sauron, the Dark Lord", 675, Rarity.MYTHIC, mage.cards.s.SauronTheDarkLord.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Sauron, the Dark Lord", 744, Rarity.MYTHIC, mage.cards.s.SauronTheDarkLord.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sauron, the Dark Lord", 744, Rarity.MYTHIC, mage.cards.s.SauronTheDarkLord.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Sauron, the Dark Lord", 821, Rarity.MYTHIC, mage.cards.s.SauronTheDarkLord.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sauron, the Lidless Eye", 288, Rarity.MYTHIC, mage.cards.s.SauronTheLidlessEye.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sauron, the Lidless Eye", 396, Rarity.MYTHIC, mage.cards.s.SauronTheLidlessEye.class, NON_FULL_USE_VARIOUS)); @@ -695,7 +695,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet { cards.add(new SetCardInfo("Shadow of the Enemy", 107, Rarity.MYTHIC, mage.cards.s.ShadowOfTheEnemy.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shadow of the Enemy", 424, Rarity.MYTHIC, mage.cards.s.ShadowOfTheEnemy.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shadow of the Enemy", 558, Rarity.MYTHIC, mage.cards.s.ShadowOfTheEnemy.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Shadow of the Enemy", 735, Rarity.MYTHIC, mage.cards.s.ShadowOfTheEnemy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shadow of the Enemy", 735, Rarity.MYTHIC, mage.cards.s.ShadowOfTheEnemy.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Shadowfax, Lord of Horses", 227, Rarity.UNCOMMON, mage.cards.s.ShadowfaxLordOfHorses.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shadowfax, Lord of Horses", 678, Rarity.UNCOMMON, mage.cards.s.ShadowfaxLordOfHorses.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shagrat, Loot Bearer", 228, Rarity.RARE, mage.cards.s.ShagratLootBearer.class, NON_FULL_USE_VARIOUS)); @@ -740,7 +740,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet { cards.add(new SetCardInfo("Spiteful Banditry", 149, Rarity.MYTHIC, mage.cards.s.SpitefulBanditry.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Spiteful Banditry", 439, Rarity.MYTHIC, mage.cards.s.SpitefulBanditry.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Spiteful Banditry", 600, Rarity.MYTHIC, mage.cards.s.SpitefulBanditry.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Spiteful Banditry", 738, Rarity.MYTHIC, mage.cards.s.SpitefulBanditry.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spiteful Banditry", 738, Rarity.MYTHIC, mage.cards.s.SpitefulBanditry.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Stalwarts of Osgiliath", 33, Rarity.COMMON, mage.cards.s.StalwartsOfOsgiliath.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Stalwarts of Osgiliath", 484, Rarity.COMMON, mage.cards.s.StalwartsOfOsgiliath.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Stern Scolding", 522, Rarity.UNCOMMON, mage.cards.s.SternScolding.class, NON_FULL_USE_VARIOUS)); @@ -755,7 +755,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet { cards.add(new SetCardInfo("Storm of Saruman", 413, Rarity.MYTHIC, mage.cards.s.StormOfSaruman.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Storm of Saruman", 523, Rarity.MYTHIC, mage.cards.s.StormOfSaruman.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Storm of Saruman", 72, Rarity.MYTHIC, mage.cards.s.StormOfSaruman.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Storm of Saruman", 733, Rarity.MYTHIC, mage.cards.s.StormOfSaruman.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Storm of Saruman", 733, Rarity.MYTHIC, mage.cards.s.StormOfSaruman.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Strider, Ranger of the North", 232, Rarity.UNCOMMON, mage.cards.s.StriderRangerOfTheNorth.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Strider, Ranger of the North", 683, Rarity.UNCOMMON, mage.cards.s.StriderRangerOfTheNorth.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Surrounded by Orcs", 524, Rarity.COMMON, mage.cards.s.SurroundedByOrcs.class, NON_FULL_USE_VARIOUS)); @@ -793,7 +793,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet { cards.add(new SetCardInfo("The One Ring", 380, Rarity.MYTHIC, mage.cards.t.TheOneRing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The One Ring", 451, Rarity.MYTHIC, mage.cards.t.TheOneRing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The One Ring", 697, Rarity.MYTHIC, mage.cards.t.TheOneRing.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("The One Ring", 748, Rarity.MYTHIC, mage.cards.t.TheOneRing.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The One Ring", 748, Rarity.MYTHIC, mage.cards.t.TheOneRing.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("The One Ring", 791, Rarity.MYTHIC, mage.cards.t.TheOneRing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Ring Goes South", 186, Rarity.RARE, mage.cards.t.TheRingGoesSouth.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Ring Goes South", 366, Rarity.RARE, mage.cards.t.TheRingGoesSouth.class, NON_FULL_USE_VARIOUS)); @@ -807,7 +807,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet { cards.add(new SetCardInfo("The Torment of Gollum", 561, Rarity.COMMON, mage.cards.t.TheTormentOfGollum.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Watcher in the Water", 354, Rarity.MYTHIC, mage.cards.t.TheWatcherInTheWater.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Watcher in the Water", 526, Rarity.MYTHIC, mage.cards.t.TheWatcherInTheWater.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("The Watcher in the Water", 734, Rarity.MYTHIC, mage.cards.t.TheWatcherInTheWater.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Watcher in the Water", 734, Rarity.MYTHIC, mage.cards.t.TheWatcherInTheWater.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("The Watcher in the Water", 75, Rarity.MYTHIC, mage.cards.t.TheWatcherInTheWater.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Watcher in the Water", 765, Rarity.MYTHIC, mage.cards.t.TheWatcherInTheWater.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Theoden, King of Rohan", 233, Rarity.UNCOMMON, mage.cards.t.TheodenKingOfRohan.class, NON_FULL_USE_VARIOUS)); @@ -817,7 +817,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet { cards.add(new SetCardInfo("Tom Bombadil", 234, Rarity.MYTHIC, mage.cards.t.TomBombadil.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Tom Bombadil", 331, Rarity.MYTHIC, mage.cards.t.TomBombadil.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Tom Bombadil", 685, Rarity.MYTHIC, mage.cards.t.TomBombadil.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Tom Bombadil", 745, Rarity.MYTHIC, mage.cards.t.TomBombadil.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tom Bombadil", 745, Rarity.MYTHIC, mage.cards.t.TomBombadil.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Tom Bombadil", 823, Rarity.MYTHIC, mage.cards.t.TomBombadil.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Took Reaper", 35, Rarity.COMMON, mage.cards.t.TookReaper.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Took Reaper", 486, Rarity.COMMON, mage.cards.t.TookReaper.class, NON_FULL_USE_VARIOUS)); @@ -847,7 +847,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet { cards.add(new SetCardInfo("Witch-king of Angmar", 311, Rarity.MYTHIC, mage.cards.w.WitchKingOfAngmar.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Witch-king of Angmar", 423, Rarity.MYTHIC, mage.cards.w.WitchKingOfAngmar.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Witch-king of Angmar", 565, Rarity.MYTHIC, mage.cards.w.WitchKingOfAngmar.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Witch-king of Angmar", 736, Rarity.MYTHIC, mage.cards.w.WitchKingOfAngmar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Witch-king of Angmar", 736, Rarity.MYTHIC, mage.cards.w.WitchKingOfAngmar.class, FULL_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Witch-king of Angmar", 803, Rarity.MYTHIC, mage.cards.w.WitchKingOfAngmar.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Witch-king, Bringer of Ruin", 293, Rarity.RARE, mage.cards.w.WitchKingBringerOfRuin.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Witch-king, Bringer of Ruin", 391, Rarity.RARE, mage.cards.w.WitchKingBringerOfRuin.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage.Sets/src/mage/sets/URLConventionPromos.java b/Mage.Sets/src/mage/sets/URLConventionPromos.java index 429984c3a09..155a5718d2f 100644 --- a/Mage.Sets/src/mage/sets/URLConventionPromos.java +++ b/Mage.Sets/src/mage/sets/URLConventionPromos.java @@ -23,14 +23,13 @@ public class URLConventionPromos extends ExpansionSet { cards.add(new SetCardInfo("Aeronaut Tinkerer", 8, Rarity.COMMON, mage.cards.a.AeronautTinkerer.class)); cards.add(new SetCardInfo("Bloodthrone Vampire", 3, Rarity.RARE, mage.cards.b.BloodthroneVampire.class)); cards.add(new SetCardInfo("Chandra's Fury", 5, Rarity.RARE, mage.cards.c.ChandrasFury.class)); + cards.add(new SetCardInfo("Counterspell", 2, Rarity.RARE, mage.cards.c.Counterspell.class)); + cards.add(new SetCardInfo("Hylda of the Icy Crown", "2025-1", Rarity.MYTHIC, mage.cards.h.HyldaOfTheIcyCrown.class)); + cards.add(new SetCardInfo("Katara, the Fearless", "2025-3", Rarity.RARE, mage.cards.k.KataraTheFearless.class)); cards.add(new SetCardInfo("Kor Skyfisher", 23, Rarity.RARE, mage.cards.k.KorSkyfisher.class)); cards.add(new SetCardInfo("Merfolk Mesmerist", 4, Rarity.RARE, mage.cards.m.MerfolkMesmerist.class)); - // Italian-only printing - //cards.add(new SetCardInfo("Relentless Rats", 9, Rarity.RARE, mage.cards.r.RelentlessRats.class)); - // Japanese-only printing - //cards.add(new SetCardInfo("Shepherd of the Lost", "34*", Rarity.UNCOMMON, mage.cards.s.ShepherdOfTheLost.class)); + cards.add(new SetCardInfo("Shepherd of the Lost", "34*", Rarity.UNCOMMON, mage.cards.s.ShepherdOfTheLost.class)); cards.add(new SetCardInfo("Stealer of Secrets", 7, Rarity.RARE, mage.cards.s.StealerOfSecrets.class)); cards.add(new SetCardInfo("Steward of Valeron", 1, Rarity.RARE, mage.cards.s.StewardOfValeron.class)); - cards.add(new SetCardInfo("Counterspell", 2, Rarity.RARE, mage.cards.c.Counterspell.class)); } } diff --git a/Mage.Sets/src/mage/sets/UrzasSaga.java b/Mage.Sets/src/mage/sets/UrzasSaga.java index e394638101f..eb72c5943ad 100644 --- a/Mage.Sets/src/mage/sets/UrzasSaga.java +++ b/Mage.Sets/src/mage/sets/UrzasSaga.java @@ -352,7 +352,7 @@ public final class UrzasSaga extends ExpansionSet { cards.add(new SetCardInfo("Unworthy Dead", "163s", Rarity.COMMON, mage.cards.u.UnworthyDead.class, RETRO_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Urza's Armor", 313, Rarity.UNCOMMON, mage.cards.u.UrzasArmor.class, RETRO_ART)); cards.add(new SetCardInfo("Vampiric Embrace", 164, Rarity.UNCOMMON, mage.cards.v.VampiricEmbrace.class, RETRO_ART_USE_VARIOUS)); - cards.add(new SetCardInfo("Vampiric Embrace", "164s", Rarity.UNCOMMON, mage.cards.v.VampiricEmbrace.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vampiric Embrace", "164s", Rarity.UNCOMMON, mage.cards.v.VampiricEmbrace.class, RETRO_ART_USE_VARIOUS)); cards.add(new SetCardInfo("Vebulid", 165, Rarity.RARE, mage.cards.v.Vebulid.class, RETRO_ART)); cards.add(new SetCardInfo("Veil of Birds", 106, Rarity.COMMON, mage.cards.v.VeilOfBirds.class, RETRO_ART)); cards.add(new SetCardInfo("Veiled Apparition", 107, Rarity.UNCOMMON, mage.cards.v.VeiledApparition.class, RETRO_ART)); diff --git a/Mage.Sets/src/mage/sets/WizardsPlayNetwork2025.java b/Mage.Sets/src/mage/sets/WizardsPlayNetwork2025.java index 3918977766b..5929c33e2fc 100644 --- a/Mage.Sets/src/mage/sets/WizardsPlayNetwork2025.java +++ b/Mage.Sets/src/mage/sets/WizardsPlayNetwork2025.java @@ -24,9 +24,11 @@ public class WizardsPlayNetwork2025 extends ExpansionSet { cards.add(new SetCardInfo("Despark", 2, Rarity.UNCOMMON, mage.cards.d.Despark.class)); cards.add(new SetCardInfo("Dragon's Hoard", "1p", Rarity.RARE, mage.cards.d.DragonsHoard.class, RETRO_ART)); cards.add(new SetCardInfo("Dragonspeaker Shaman", 3, Rarity.RARE, mage.cards.d.DragonspeakerShaman.class)); + cards.add(new SetCardInfo("Monstrous Rage", 9, Rarity.RARE, mage.cards.m.MonstrousRage.class, RETRO_ART)); cards.add(new SetCardInfo("Palladium Myr", 6, Rarity.RARE, mage.cards.p.PalladiumMyr.class, RETRO_ART)); cards.add(new SetCardInfo("Rishkar's Expertise", 1, Rarity.RARE, mage.cards.r.RishkarsExpertise.class, RETRO_ART)); cards.add(new SetCardInfo("Spectacular Spider-Man", 7, Rarity.RARE, mage.cards.s.SpectacularSpiderMan.class)); + cards.add(new SetCardInfo("Trinket Mage", 8, Rarity.RARE, mage.cards.t.TrinketMage.class, RETRO_ART)); cards.add(new SetCardInfo("Zidane, Tantalus Thief", 5, Rarity.RARE, mage.cards.z.ZidaneTantalusThief.class)); } } diff --git a/Mage.Sets/src/mage/sets/YearOfTheDragon2024.java b/Mage.Sets/src/mage/sets/YearOfTheDragon2024.java new file mode 100644 index 00000000000..a3dd5e0c773 --- /dev/null +++ b/Mage.Sets/src/mage/sets/YearOfTheDragon2024.java @@ -0,0 +1,30 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * https://scryfall.com/sets/pl24 + */ +public class YearOfTheDragon2024 extends ExpansionSet { + + private static final YearOfTheDragon2024 instance = new YearOfTheDragon2024(); + + public static YearOfTheDragon2024 getInstance() { + return instance; + } + + private YearOfTheDragon2024() { + super("Year of the Dragon 2024", "PL24", ExpansionSet.buildDate(2024, 2, 8), SetType.PROMOTIONAL); + this.hasBoosters = false; + this.hasBasicLands = true; + + cards.add(new SetCardInfo("Dragon Tempest", 7, Rarity.RARE, mage.cards.d.DragonTempest.class)); + cards.add(new SetCardInfo("Dragonlord's Servant", 1, Rarity.RARE, mage.cards.d.DragonlordsServant.class)); + cards.add(new SetCardInfo("Korvold, Fae-Cursed King", 6, Rarity.MYTHIC, mage.cards.k.KorvoldFaeCursedKing.class)); + cards.add(new SetCardInfo("Mountain", 5, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Sarkhan Unbroken", 2, Rarity.MYTHIC, mage.cards.s.SarkhanUnbroken.class)); + cards.add(new SetCardInfo("Steel Hellkite", 4, Rarity.RARE, mage.cards.s.SteelHellkite.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/YearOfTheSnake2025.java b/Mage.Sets/src/mage/sets/YearOfTheSnake2025.java new file mode 100644 index 00000000000..1275fd9ecc5 --- /dev/null +++ b/Mage.Sets/src/mage/sets/YearOfTheSnake2025.java @@ -0,0 +1,29 @@ +package mage.sets; + +import mage.cards.ExpansionSet; +import mage.constants.Rarity; +import mage.constants.SetType; + +/** + * https://scryfall.com/sets/pl25 + */ +public class YearOfTheSnake2025 extends ExpansionSet { + + private static final YearOfTheSnake2025 instance = new YearOfTheSnake2025(); + + public static YearOfTheSnake2025 getInstance() { + return instance; + } + + private YearOfTheSnake2025() { + super("Year of the Snake 2025", "PL25", ExpansionSet.buildDate(2025, 2, 14), SetType.PROMOTIONAL); + this.hasBoosters = false; + this.hasBasicLands = true; + + cards.add(new SetCardInfo("Forest", 6, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Kaseto, Orochi Archmage", 5, Rarity.MYTHIC, mage.cards.k.KasetoOrochiArchmage.class)); + cards.add(new SetCardInfo("Lotus Cobra", 3, Rarity.RARE, mage.cards.l.LotusCobra.class)); + cards.add(new SetCardInfo("Sakura-Tribe Elder", 4, Rarity.RARE, mage.cards.s.SakuraTribeElder.class)); + cards.add(new SetCardInfo("Xyris, the Writhing Storm", 1, Rarity.MYTHIC, mage.cards.x.XyrisTheWrithingStorm.class)); + } +} diff --git a/Mage.Sets/src/mage/sets/YearOfTheTiger2022.java b/Mage.Sets/src/mage/sets/YearOfTheTiger2022.java index 48fe95d9167..25672726759 100644 --- a/Mage.Sets/src/mage/sets/YearOfTheTiger2022.java +++ b/Mage.Sets/src/mage/sets/YearOfTheTiger2022.java @@ -21,7 +21,7 @@ public class YearOfTheTiger2022 extends ExpansionSet { this.hasBasicLands = false; cards.add(new SetCardInfo("Herald's Horn", 5, Rarity.RARE, mage.cards.h.HeraldsHorn.class)); - cards.add(new SetCardInfo("Jedit Ojanen", 2, Rarity.RARE, mage.cards.j.JeditOjanen.class)); + cards.add(new SetCardInfo("Jedit Ojanen", 2, Rarity.RARE, mage.cards.j.JeditOjanen.class, FULL_ART)); // cards.add(new SetCardInfo("Snapdax, Apex of the Hunt", 3, Rarity.RARE, mage.cards.s.SnapdaxApexOfTheHunt.class)); cards.add(new SetCardInfo("Temur Sabertooth", 1, Rarity.RARE, mage.cards.t.TemurSabertooth.class)); cards.add(new SetCardInfo("Yuriko, the Tiger's Shadow", 4, Rarity.RARE, mage.cards.y.YurikoTheTigersShadow.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/equipped/GolemSkinGauntletsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/equipped/GolemSkinGauntletsTest.java index f6e16f29cde..f9d4088ddd4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/equipped/GolemSkinGauntletsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/equipped/GolemSkinGauntletsTest.java @@ -2,8 +2,6 @@ package org.mage.test.cards.abilities.equipped; import mage.constants.PhaseStep; import mage.constants.Zone; -import mage.game.permanent.Permanent; -import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -19,27 +17,24 @@ public class GolemSkinGauntletsTest extends CardTestPlayerBase { @Test public void testBoostOnEquip() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6); - // Equipped creature doesn't untap during its controller's untap step. - // Equipped creature has "{T}: This creature deals 2 damage to any target." - // Equip {4) - addCard(Zone.BATTLEFIELD, playerA, "Heavy Arbalest"); - addCard(Zone.BATTLEFIELD, playerA, "Golem-Skin Gauntlets"); - addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard"); + addCard(Zone.BATTLEFIELD, playerA, "Heavy Arbalest"); // Equip {4} + addCard(Zone.BATTLEFIELD, playerA, "Golem-Skin Gauntlets"); // +1/+0 per attached equipment, Equip {2} + addCard(Zone.BATTLEFIELD, playerA, "Shuko"); // +1/+0, Equip {0} + addCard(Zone.BATTLEFIELD, playerA, "Elite Vanguard"); // Creature 2/1 activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip {4}", "Elite Vanguard"); waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip {2}", "Elite Vanguard"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPT("Gauntlets equipped", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Elite Vanguard", 4, 1); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip {0}", "Elite Vanguard"); setStopAt(1, PhaseStep.BEGIN_COMBAT); + setStrictChooseMode(true); execute(); - assertLife(playerA, 20); - assertLife(playerB, 20); - - Permanent eliteVanguard = getPermanent("Elite Vanguard", playerA.getId()); - Assert.assertTrue(eliteVanguard.getAttachments().size() == 2); - Assert.assertEquals(4, eliteVanguard.getPower().getValue()); - Assert.assertEquals(1, eliteVanguard.getToughness().getValue()); + assertPowerToughness(playerA, "Elite Vanguard", 6, 1); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java index 973291e4438..6cc8d660813 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java @@ -147,7 +147,7 @@ public class ManifestTest extends CardTestPlayerBase { runCode("after blink", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> { if (cardAfterBlink == null) { Assert.assertEquals("after blink card must keep in exile", - 1, currentGame.getExile().getAllCardsByRange(currentGame, playerA.getId()).size()); + 1, currentGame.getExile().getCardsInRange(currentGame, playerA.getId()).size()); } else { String realPermanentName = currentGame.getBattlefield().getAllPermanents() .stream() diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/acr/AdrestiaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/acr/AdrestiaTest.java new file mode 100644 index 00000000000..63c3f4618bf --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/acr/AdrestiaTest.java @@ -0,0 +1,58 @@ +package org.mage.test.cards.single.acr; + +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.constants.SubType; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class AdrestiaTest extends CardTestPlayerBase { + + /* + Adrestia + {3} + Legendary Artifact — Vehicle + + Islandwalk (This creature can’t be blocked as long as defending player controls an Island.) + + Whenever Adrestia attacks, if an Assassin crewed it this turn, draw a card. Adrestia becomes an Assassin in addition to its other types until end of turn. + + Crew 1 + */ + private static final String adrestia = "Adrestia"; + /* + Nekrataal + {2}{B}{B} + Creature — Human Assassin + + First strike + + When this creature enters, destroy target nonartifact, nonblack creature. That creature can’t be regenerated. + + 2/1 + */ + private static final String nekrataal = "Nekrataal"; + + @Test + public void testAdrestia() { + setStrictChooseMode(true); + + + addCard(Zone.BATTLEFIELD, playerA, adrestia); + addCard(Zone.BATTLEFIELD, playerA, nekrataal); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Crew"); + setChoice(playerA, nekrataal); + attack(1, playerA, adrestia); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertSubtype(adrestia, SubType.VEHICLE); + assertType(adrestia, CardType.CREATURE, true); + assertType(adrestia, CardType.ARTIFACT, true); + assertSubtype(adrestia, SubType.ASSASSIN); + assertHandCount(playerA, 1); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/big/EsotericDuplicatorTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/big/EsotericDuplicatorTest.java new file mode 100644 index 00000000000..269092e77bb --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/big/EsotericDuplicatorTest.java @@ -0,0 +1,51 @@ +package org.mage.test.cards.single.big; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + + +public class EsotericDuplicatorTest extends CardTestPlayerBase { + + /* + Esoteric Duplicator + {2}{U} + Artifact — Clue + + Whenever you sacrifice this artifact or another artifact, you may pay {2}. If you do, at the beginning of the next end step, create a token that’s a copy of that artifact. + + {2}, Sacrifice this artifact: Draw a card. + */ + private static final String esotericDuplicator = "Esoteric Duplicator"; + + /* + Sculpting Steel + {3} + Artifact + You may have Sculpting Steel enter the battlefield as a copy of any artifact on the battlefield. + */ + private static final String sculptingSteel = "Sculpting Steel"; + + @Test + public void testEsotericDuplicator() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerB, esotericDuplicator); + addCard(Zone.HAND, playerA, sculptingSteel); + addCard(Zone.BATTLEFIELD, playerA, "Island", 7); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sculptingSteel); + setChoice(playerA, true); + setChoice(playerA, esotericDuplicator); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}, Sacrifice"); // Sacrifice duplicator + setChoice(playerA, true); // duplicate + + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertPermanentCount(playerA, esotericDuplicator, 1); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/c18/RavenousSlimeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/c18/RavenousSlimeTest.java new file mode 100644 index 00000000000..aa91658d7e1 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/c18/RavenousSlimeTest.java @@ -0,0 +1,36 @@ + +package org.mage.test.cards.single.c18; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author notgreat + */ +public class RavenousSlimeTest extends CardTestPlayerBase { + + @Test + public void testRavenousSlime() { + addCustomEffect_TargetDestroy(playerA); + addCard(Zone.BATTLEFIELD, playerA, "Ravenous Slime"); + addCard(Zone.BATTLEFIELD, playerB, "Runeclaw Bear"); + addCard(Zone.BATTLEFIELD, playerB, "Centaur Courser"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "target destroy", "Runeclaw Bear"); + checkPT("Ravenous Slime ate Runeclaw Bear", 1, PhaseStep.BEGIN_COMBAT, playerA, "Ravenous Slime", 3, 3); + + attack(1, playerA, "Ravenous Slime"); + block(1, playerB, "Centaur Courser", "Ravenous Slime"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertExileCount(playerB, "Runeclaw Bear", 1); + assertExileCount(playerB, "Centaur Courser", 1); + assertGraveyardCount(playerA, "Ravenous Slime", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/dsk/UnidentifiedHovershipTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dsk/UnidentifiedHovershipTest.java new file mode 100644 index 00000000000..b7ae92e0d6d --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dsk/UnidentifiedHovershipTest.java @@ -0,0 +1,67 @@ +package org.mage.test.cards.single.dsk; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class UnidentifiedHovershipTest extends CardTestPlayerBase { + + /* + Unidentified Hovership + {1}{W}{W} + Artifact - Vehicle + Flying + When this Vehicle enters, exile up to one target creature with toughness 5 or less. + When this Vehicle leaves the battlefield, the exiled card's owner manifests dread. + Crew 1 + 2/2 + */ + private static final String unidentifiedHovership = "Unidentified Hovership"; + + /* + Bear Cub + {1}{G} + Creature - Bear + + 2/2 + */ + private static final String bearCub = "Bear Cub"; + + /* + Naturalize + {1}{G} + Instant + Destroy target artifact or enchantment. + */ + private static final String naturalize = "Naturalize"; + + @Test + public void testUnidentifiedHovership() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, unidentifiedHovership); + addCard(Zone.HAND, playerB, naturalize); + addCard(Zone.BATTLEFIELD, playerB, bearCub); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + addCard(Zone.BATTLEFIELD, playerB, "Forest", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, unidentifiedHovership); + addTarget(playerA, bearCub); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, naturalize, unidentifiedHovership); + setChoice(playerB, "Mountain"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerB, 2 + 1); // forests + face down + assertGraveyardCount(playerA, unidentifiedHovership, 1); + assertGraveyardCount(playerB, 1 + 1); // naturalize + manifest + assertExileCount(playerB, bearCub, 1); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/kld/AnimationModuleTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/kld/AnimationModuleTest.java new file mode 100644 index 00000000000..ba91018396a --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/kld/AnimationModuleTest.java @@ -0,0 +1,80 @@ +package org.mage.test.cards.single.kld; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.target.common.TargetPermanentOrPlayer; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + + +public class AnimationModuleTest extends CardTestPlayerBase { + + /* + Animation Module + {1} + Artifact + + Whenever one or more +1/+1 counters are put on a permanent you control, you may pay {1}. If you do, create a 1/1 colorless Servo artifact creature token. + + {3}, {T}: Choose a counter on target permanent or player. Give that permanent or player another counter of that kind. + */ + private static final String animationModule = "Animation Module"; + + @Test + public void testGivePermanentCounter() { + setStrictChooseMode(true); + + Ability ability = new SimpleActivatedAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), + new ManaCostsImpl<>("") + ); + ability.addTarget(new TargetPermanentOrPlayer(1)); + addCustomCardWithAbility("add counter", playerA, ability); + + addCard(Zone.BATTLEFIELD, playerA, animationModule); + addCard(Zone.BATTLEFIELD, playerA, "Island", 10); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "put", animationModule); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + setChoice(playerA, true); //pay to create servo + + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{3}, {T}: Choose a counter", animationModule); + setChoice(playerA, true); //pay to create servo + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertCounterCount(playerA, animationModule, CounterType.P1P1, 2); + assertPermanentCount(playerA, "Servo Token", 2); + } + + @Test + public void testGivePlayerCounter() { + setStrictChooseMode(true); + + Ability ability = new SimpleActivatedAbility( + new AddCountersTargetEffect(CounterType.ENERGY.createInstance()), + new ManaCostsImpl<>("") + ); + ability.addTarget(new TargetPermanentOrPlayer(1)); + addCustomCardWithAbility("add counter", playerA, ability); + + addCard(Zone.BATTLEFIELD, playerA, animationModule); + addCard(Zone.BATTLEFIELD, playerA, "Island", 10); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "put", playerA); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{3}, {T}: Choose a counter", playerA); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertCounterCount(playerA, CounterType.ENERGY, 2); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/leg/AlAbarasCarpetTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/leg/AlAbarasCarpetTest.java new file mode 100644 index 00000000000..7e548cb5dde --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/leg/AlAbarasCarpetTest.java @@ -0,0 +1,55 @@ +package org.mage.test.cards.single.leg; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class AlAbarasCarpetTest extends CardTestPlayerBase { + + /* + Al-abara's Carpet + {5} + Artifact + + {5}, {T}: Prevent all damage that would be dealt to you this turn by attacking creatures without flying. + */ + private static final String alabarasCarpet = "Al-abara's Carpet"; + + /* + Storm Crow + {1}{U} + Creature — Bird + + Flying (This creature can’t be blocked except by creatures with flying or reach.) + + 1/2 + */ + private static final String stormCrow = "Storm Crow"; + /* + Balduvian Bears + {1}{G} + Creature — Bear + + 2/2 + */ + private static final String balduvianBears = "Balduvian Bears"; + @Test + public void testCarpet() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, alabarasCarpet); + addCard(Zone.BATTLEFIELD, playerA, "Island", 5); + addCard(Zone.BATTLEFIELD, playerB, balduvianBears); + addCard(Zone.BATTLEFIELD, playerB, stormCrow); + + attack(2, playerB, balduvianBears); + attack(2, playerB, stormCrow); + activateAbility(2, PhaseStep.DECLARE_ATTACKERS, playerA, "{5}, {T}"); + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 20 - 1); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mkc/SereneSleuthTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mkc/SereneSleuthTest.java new file mode 100644 index 00000000000..ab7c20aae05 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mkc/SereneSleuthTest.java @@ -0,0 +1,93 @@ +package org.mage.test.cards.single.mkc; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +import static org.junit.Assert.assertTrue; + +/** + * + * @author Jmlundeen + */ +public class SereneSleuthTest extends CardTestPlayerBase { + + /* + Serene Sleuth + {1}{W} + Creature - Human Detective + When Serene Sleuth enters the battlefield, investigate. + At the beginning of combat on your turn, investigate for each goaded creature you control. Then each creature you control is no longer goaded. + 2/2 + */ + private static final String sereneSleuth = "Serene Sleuth"; + + /* + Baeloth Barrityl, Entertainer + {4}{R} + Legendary Creature - Elf Shaman + Creatures your opponents control with power less than Baeloth Barrityl's power are goaded. + Whenever a goaded attacking or blocking creature dies, you create a Treasure token. + Choose a Background + 2/5 + */ + private static final String baelothBarritylEntertainer = "Baeloth Barrityl, Entertainer"; + + /* + Fugitive Wizard + {U} + Creature - Human Wizard + + 1/1 + */ + private static final String fugitiveWizard = "Fugitive Wizard"; + + /* + Cloudshift + {W} + Instant + Exile target creature you control, then return that card to the battlefield under your control. + */ + private static final String cloudshift = "Cloudshift"; + + @Test + public void testSereneSleuth() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerB, baelothBarritylEntertainer); + addCard(Zone.BATTLEFIELD, playerA, fugitiveWizard); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.HAND, playerA, sereneSleuth); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sereneSleuth); + + setStopAt(3, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Clue Token", 1 + 1); + assertTrue("fugitive wizard is not goaded", getPermanent(fugitiveWizard).getGoadingPlayers().isEmpty()); + } + + @Test + public void testSereneSleuthReGoaded() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerB, baelothBarritylEntertainer); + addCard(Zone.BATTLEFIELD, playerA, fugitiveWizard); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerB, "Plains"); + addCard(Zone.HAND, playerA, sereneSleuth); + addCard(Zone.HAND, playerB, cloudshift); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sereneSleuth); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, cloudshift, baelothBarritylEntertainer); + + setStopAt(3, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Clue Token", 1 + 1 + 1); + assertTrue("fugitive wizard is goaded", getPermanent(fugitiveWizard).getGoadingPlayers().isEmpty()); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/one/NahirisSacrificeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/one/NahirisSacrificeTest.java index e69179635a4..24087ff3541 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/one/NahirisSacrificeTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/one/NahirisSacrificeTest.java @@ -2,7 +2,6 @@ package org.mage.test.cards.single.one; import mage.constants.PhaseStep; import mage.constants.Zone; -import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -41,7 +40,6 @@ public class NahirisSacrificeTest extends CardTestPlayerBase { } @Test - @Ignore // Enable after merging #13916 public void testNahirisSacrificePrevented() { setStrictChooseMode(true); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/otc/DuneChanterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/otc/DuneChanterTest.java index 0dc4b879caa..f749bd81cc6 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/otc/DuneChanterTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/otc/DuneChanterTest.java @@ -66,7 +66,7 @@ public class DuneChanterTest extends CardTestPlayerBase { private static void checkExile(String info, Player player, Game game, int count) { int amount = game .getExile() - .getAllCards(game, player.getId()) + .getCardsOwned(game, player.getId()) .stream() .filter(c -> c.getSubtype(game).contains(SubType.DESERT)) .mapToInt(k -> 1) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/AlienSymbiosisTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/AlienSymbiosisTest.java new file mode 100644 index 00000000000..93dc068027d --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/AlienSymbiosisTest.java @@ -0,0 +1,53 @@ +package org.mage.test.cards.single.spm; + +import mage.abilities.keyword.MenaceAbility; +import mage.constants.PhaseStep; +import mage.constants.SubType; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class AlienSymbiosisTest extends CardTestPlayerBase { + + /* + Alien Symbiosis + {1}{B} + Enchantment - Aura + Enchant creature + Enchanted creature gets +1/+1, has menace, and is a Symbiote in addition to its other types. + You may cast this card from your graveyard by discarding a card in addition to paying its other costs. + */ + private static final String alienSymbiosis = "Alien Symbiosis"; + + /* + Bear Cub + {1}{G} + Creature - Bear + + 2/2 + */ + private static final String bearCub = "Bear Cub"; + + @Test + public void testAlienSymbiosisCastFromGrave() { + setStrictChooseMode(true); + + addCard(Zone.GRAVEYARD, playerA, alienSymbiosis); + addCard(Zone.HAND, playerA, "Island"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + addCard(Zone.BATTLEFIELD, playerA, bearCub); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, alienSymbiosis, bearCub); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertPowerToughness(playerA, bearCub, 3, 3); + assertAbilityCount(playerA, bearCub, MenaceAbility.class, 1); + assertSubtype(bearCub, SubType.SYMBIOTE); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/ArachnePsionicWeaverTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/ArachnePsionicWeaverTest.java new file mode 100644 index 00000000000..a210814861c --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/ArachnePsionicWeaverTest.java @@ -0,0 +1,53 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class ArachnePsionicWeaverTest extends CardTestPlayerBase { + + /* + Arachne, Psionic Weaver + {2}{W} + Legendary Creature - Spider Human Hero + Web-slinging {W} + As Arachne enters, look at target opponent's hand, then choose a noncreature card type. + Spells of the chosen type cost {1} more to cast. + 3/3 + */ + private static final String arachnePsionicWeaver = "Arachne, Psionic Weaver"; + + /* + Tormod's Crypt + {0} + Artifact + {tap}, Sacrifice Tormod's Crypt: Exile all cards from target player's graveyard. + */ + private static final String tormodsCrypt = "Tormod's Crypt"; + + @Test + public void testArachnePsionicWeaver() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, arachnePsionicWeaver); + addCard(Zone.HAND, playerA, tormodsCrypt); + addCard(Zone.HAND, playerB, tormodsCrypt); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + addCard(Zone.BATTLEFIELD, playerB, "Plains"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, arachnePsionicWeaver); + setChoice(playerA, CardType.ARTIFACT.toString()); + + checkPlayableAbility("Player A can't cast Tormod's", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast " + tormodsCrypt, false); + + checkPlayableAbility("Player B can cast Tormod's", 2, PhaseStep.PRECOMBAT_MAIN, playerB, "Cast " + tormodsCrypt, true); + setStopAt(2, PhaseStep.PRECOMBAT_MAIN); + execute(); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/BlackCatCunningThiefTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/BlackCatCunningThiefTest.java new file mode 100644 index 00000000000..5af97ba84dc --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/BlackCatCunningThiefTest.java @@ -0,0 +1,65 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class BlackCatCunningThiefTest extends CardTestPlayerBase { + + /* + Black Cat, Cunning Thief + {3}{B}{B} + Legendary Creature - Human Rogue Villain + When Black Cat enters, look at the top nine cards of target opponent's library, exile two of them face down, then put the rest on the bottom of their library in a random order. You may play the exiled cards for as long as they remain exiled. Mana of any type can be spent to cast spells this way. + 2/3 + */ + private static final String blackCatCunningThief = "Black Cat, Cunning Thief"; + + /* + Bear Cub + {1}{G} + Creature - Bear + + 2/2 + */ + private static final String bearCub = "Bear Cub"; + + /* + Fugitive Wizard + {U} + Creature - Human Wizard + + 1/1 + */ + private static final String fugitiveWizard = "Fugitive Wizard"; + + @Test + public void testBlackCatCunningThief() { + setStrictChooseMode(true); + skipInitShuffling(); + + addCard(Zone.HAND, playerA, blackCatCunningThief); + addCard(Zone.LIBRARY, playerB, bearCub); + addCard(Zone.LIBRARY, playerB, fugitiveWizard); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 8); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, blackCatCunningThief); + addTarget(playerA, playerB); + setChoice(playerA, bearCub); + setChoice(playerA, fugitiveWizard); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, bearCub, true); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, fugitiveWizard); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, bearCub, 1); + assertPermanentCount(playerA, fugitiveWizard, 1); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/CarnageCrimsonChaosTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/CarnageCrimsonChaosTest.java new file mode 100644 index 00000000000..0c9cd8dcf45 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/CarnageCrimsonChaosTest.java @@ -0,0 +1,60 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class CarnageCrimsonChaosTest extends CardTestPlayerBase { + + /* + Carnage, Crimson Chaos + {2}{B}{R} + Legendary Creature - Symbiote Villain + Trample + When Carnage enters, return target creature card with mana value 3 or less from your graveyard to the battlefield. It gains "This creature attacks each combat if able" and "When this creature deals combat damage to a player, sacrifice it." + Mayhem {B}{R} + 4/3 + */ + private static final String carnageCrimsonChaos = "Carnage, Crimson Chaos"; + + /* + Concordant Crossroads + {G} + World Enchantment + All creatures have haste. + */ + private static final String concordantCrossroads = "Concordant Crossroads"; + + /* + Bear Cub + {1}{G} + Creature - Bear + + 2/2 + */ + private static final String bearCub = "Bear Cub"; + + @Test + public void testCarnageCrimsonChaos() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, concordantCrossroads); + addCard(Zone.HAND, playerA, carnageCrimsonChaos); + addCard(Zone.GRAVEYARD, playerA, bearCub); + addCard(Zone.BATTLEFIELD, playerA, "Badlands", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, carnageCrimsonChaos); + addTarget(playerA, bearCub); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerA, bearCub, 1); + assertLife(playerB, 20 - 2); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/CheeringCrowdTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/CheeringCrowdTest.java new file mode 100644 index 00000000000..b8a551de14d --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/CheeringCrowdTest.java @@ -0,0 +1,49 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommander4Players; + +/** + * + * @author Jmlundeen + */ +public class CheeringCrowdTest extends CardTestCommander4Players { + + /* + Cheering Crowd + {1}{R/G} + Creature - Human Citizen + At the beginning of each player's first main phase, that player may put a +1/+1 counter on this creature. If they do, they add {C} for each counter on it. + 2/2 + */ + private static final String cheeringCrowd = "Cheering Crowd"; + + @Test + public void testCheeringCrowd() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, cheeringCrowd); + + setChoice(playerA, true); + setChoice(playerB, true); + setChoice(playerC, true); + setChoice(playerD, true); + + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + checkManaPool("PlayerA should have 1 Mana", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "C", 1); + waitStackResolved(2, PhaseStep.PRECOMBAT_MAIN, playerD); + checkManaPool("PlayerD should have 2 Mana", 2, PhaseStep.PRECOMBAT_MAIN, playerD, "C", 2); + waitStackResolved(3, PhaseStep.PRECOMBAT_MAIN, playerC); + checkManaPool("PlayerC should have 3 Mana", 3, PhaseStep.PRECOMBAT_MAIN, playerC, "C", 3); + waitStackResolved(4, PhaseStep.PRECOMBAT_MAIN, playerB); + checkManaPool("PlayerB should have 4 Mana", 4, PhaseStep.PRECOMBAT_MAIN, playerB, "C", 4); + + setStopAt(4, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertCounterCount(playerA, cheeringCrowd, CounterType.P1P1, 4); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/ElectroAssaultingBatteryTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/ElectroAssaultingBatteryTest.java new file mode 100644 index 00000000000..a8a039f54da --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/ElectroAssaultingBatteryTest.java @@ -0,0 +1,65 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class ElectroAssaultingBatteryTest extends CardTestPlayerBase { + + /* + Electro, Assaulting Battery + {1}{R}{R} + Legendary Creature - Human Villain + Flying + You don't lose unspent red mana as steps and phases end. + Whenever you cast an instant or sorcery spell, add {R}. + When Electro leaves the battlefield, you may pay {X}. When you do, he deals X damage to target player. + 2/3 + */ + private static final String electroAssaultingBattery = "Electro, Assaulting Battery"; + + /* + Pyretic Ritual + {1}{R} + Instant + Add {R}{R}{R}. + */ + private static final String pyreticRitual = "Pyretic Ritual"; + + /* + Lightning Bolt + {R} + Instant + Lightning Bolt deals 3 damage to any target. + */ + private static final String lightningBolt = "Lightning Bolt"; + + + @Test + public void testElectroAssaultingBattery() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, electroAssaultingBattery); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerB, "Mountain"); + addCard(Zone.HAND, playerA, pyreticRitual); + addCard(Zone.HAND, playerB, lightningBolt); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, pyreticRitual); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, lightningBolt, electroAssaultingBattery); + setChoice(playerA, true); + setChoiceAmount(playerA, 4); // 3 from ritual + 1 from electro + addTarget(playerA, playerB); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertLife(playerB, 20 - 4); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/GwenStacyTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/GwenStacyTest.java new file mode 100644 index 00000000000..223f35b68c7 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/GwenStacyTest.java @@ -0,0 +1,61 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class GwenStacyTest extends CardTestPlayerBase { + + /* + Gwen Stacy + {1}{R} + Legendary Creature - Human Performer Hero + When Gwen Stacy enters, exile the top card of your library. You may play that card for as long as you control this creature. + {2}{U}{R}{W}: Transform Gwen Stacy. Activate only as a sorcery. + 2/1 + Ghost-Spider + {2}{U}{R}{W} + Legendary Creature - Spider Human Hero + Flying, vigilance, haste + Whenever you play a land from exile or cast a spell from exile, put a +1/+1 counter on Ghost-Spider. + Remove two counters from Ghost-Spider: Exile the top card of your library. You may play that card this turn. + 4/4 + + */ + private static final String gwenStacy = "Gwen Stacy"; + private static final String ghostSpider = "Ghost-Spider"; + + @Test + @Ignore("Enable after transform mdfc rework") + public void testGhostSpider() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, gwenStacy); + addCard(Zone.BATTLEFIELD, playerA, "Plateau", 4); + addCard(Zone.BATTLEFIELD, playerA, "Island"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ghostSpider, true); + addCounters(1, PhaseStep.PRECOMBAT_MAIN, playerA, ghostSpider, CounterType.P1P1, 1); + addCounters(1, PhaseStep.PRECOMBAT_MAIN, playerA, ghostSpider, CounterType.CHARGE, 1); + addCounters(1, PhaseStep.PRECOMBAT_MAIN, playerA, ghostSpider, CounterType.P0P1, 1); + + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Remove two counters"); + setChoiceAmount(playerA, 1); // Charge + setChoiceAmount(playerA, 1); // P0P1 + setChoiceAmount(playerA, 0); // P1P1 + waitStackResolved(1, PhaseStep.POSTCOMBAT_MAIN); + playLand(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Mountain"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPowerToughness(playerA, ghostSpider, 4 + 1 + 1, 4 + 1 + 1); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/GwenomRemorselessTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/GwenomRemorselessTest.java new file mode 100644 index 00000000000..76702d639ff --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/GwenomRemorselessTest.java @@ -0,0 +1,59 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class GwenomRemorselessTest extends CardTestPlayerBase { + + /* + Gwenom, Remorseless + {3}{B}{B} + Legendary Creature - Symbiote Spider Hero + Deathtouch, lifelink + Whenever Gwenom attacks, until end of turn you may look at the top card of your library any time and you may play cards from the top of your library. If you cast a spell this way, pay life equal to its mana value rather than pay its mana cost. + 4/4 + */ + private static final String gwenomRemorseless = "Gwenom, Remorseless"; + + /* + Bear Cub + {1}{G} + Creature - Bear + 2/2 + */ + private static final String bearCub = "Bear Cub"; + + @Test + public void testGwenomRemorseless() { + setStrictChooseMode(true); + skipInitShuffling(); + + addCard(Zone.BATTLEFIELD, playerA, gwenomRemorseless); + addCard(Zone.LIBRARY, playerA, "Island"); + addCard(Zone.LIBRARY, playerA, bearCub, 2); + addCard(Zone.LIBRARY, playerA, "Forest"); + + attack(1, playerA, gwenomRemorseless); + playLand(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Forest"); + waitStackResolved(1, PhaseStep.POSTCOMBAT_MAIN); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, bearCub); + + // effect ends at end of turn + checkPlayableAbility("Can't play top card", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Play Island", false); + + setStopAt(2, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, bearCub, 1); + assertPermanentCount(playerA, gwenomRemorseless, 1); + assertPermanentCount(playerA, "Forest", 1); + assertLife(playerB, 20 - 4); + assertLife(playerA, 20 + 4 - 2); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/HydroManFluidFelonTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/HydroManFluidFelonTest.java new file mode 100644 index 00000000000..8be9cf8912b --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/HydroManFluidFelonTest.java @@ -0,0 +1,55 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class HydroManFluidFelonTest extends CardTestPlayerBase { + + /* + Hydro-Man, Fluid Felon + {U}{U} + Legendary Creature - Elemental Villain + Whenever you cast a blue spell, if Hydro-Man is a creature, he gets +1/+1 until end of turn. + At the beginning of your end step, untap Hydro-Man. Until your next turn, he becomes a land and gains "{T}: Add {U}." + 2/2 + */ + private static final String hydroManFluidFelon = "Hydro-Man, Fluid Felon"; + + /* + Fugitive Wizard + {U} + Creature - Human Wizard + + 1/1 + */ + private static final String fugitiveWizard = "Fugitive Wizard"; + + @Test + public void testHydroManFluidFelon() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, fugitiveWizard); + addCard(Zone.BATTLEFIELD, playerA, hydroManFluidFelon); + addCard(Zone.BATTLEFIELD, playerA, "Island"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, fugitiveWizard, true); + + checkPT("Hydro-Man is boosted", 1, PhaseStep.PRECOMBAT_MAIN, playerA, hydroManFluidFelon, 3, 3); + + attack(1, playerA, hydroManFluidFelon); + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertType(hydroManFluidFelon, CardType.LAND, true); + assertNotType(hydroManFluidFelon, CardType.CREATURE); + assertTapped(hydroManFluidFelon, false); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/InterdimensionalWebWatchTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/InterdimensionalWebWatchTest.java new file mode 100644 index 00000000000..45e97878bfa --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/InterdimensionalWebWatchTest.java @@ -0,0 +1,78 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class InterdimensionalWebWatchTest extends CardTestPlayerBase { + + /* + Interdimensional Web Watch + {4} + Artifact + When this artifact enters, exile the top two cards of your library. Until the end of your next turn, you may play those cards. + {T}: Add two mana in any combination of colors. Spend this mana only to cast spells from exile. + */ + private static final String interdimensionalWebWatch = "Interdimensional Web Watch"; + + /* + Lightning Bolt + {R} + Instant + Lightning Bolt deals 3 damage to any target. + */ + private static final String lightningBolt = "Lightning Bolt"; + + /* + Fugitive Wizard + {U} + Creature - Human Wizard + + 1/1 + */ + private static final String fugitiveWizard = "Fugitive Wizard"; + + /* + Shock + {R} + Instant + Shock deals 2 damage to any target. + */ + private static final String shock = "Shock"; + + @Test + public void testInterdimensionalWebWatch() { + setStrictChooseMode(true); + skipInitShuffling(); + + addCard(Zone.LIBRARY, playerA, lightningBolt); + addCard(Zone.LIBRARY, playerA, fugitiveWizard); + addCard(Zone.HAND, playerA, interdimensionalWebWatch); + addCard(Zone.HAND, playerA, shock); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, interdimensionalWebWatch, true); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add two"); + setChoiceAmount(playerA, 0, 1, 0, 1, 0); // Add {U}{R} + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + checkPlayableAbility("Can't cast shock from hand", 1, PhaseStep.PRECOMBAT_MAIN, playerA, + "Cast " + shock, false); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, lightningBolt, true); + addTarget(playerA, playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, fugitiveWizard); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertLife(playerB, 20 - 3); + assertPermanentCount(playerA, fugitiveWizard, 1); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/JackalGeniusGeneticistTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/JackalGeniusGeneticistTest.java new file mode 100644 index 00000000000..8cabc98289f --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/JackalGeniusGeneticistTest.java @@ -0,0 +1,65 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class JackalGeniusGeneticistTest extends CardTestPlayerBase { + + /* + Jackal, Genius Geneticist + {G}{U} + Legendary Creature - Human Scientist Villain + Trample + Whenever you cast a creature spell with mana value equal to Jackal's power, copy that spell, except the copy isn't legendary. Then put a +1/+1 counter on Jackal. + 1/1 + */ + private static final String jackalGeniusGeneticist = "Jackal, Genius Geneticist"; + + /* + Bear Cub + {1}{G} + Creature - Bear + + 2/2 + */ + private static final String bearCub = "Bear Cub"; + + /* + Ragavan, Nimble Pilferer + {R} + Legendary Creature - Monkey Pirate + Whenever Ragavan, Nimble Pilferer deals combat damage to a player, create a Treasure token and exile the top card of that player's library. Until end of turn, you may cast that card. + Dash {1}{R} + 2/1 + */ + private static final String ragavanNimblePilferer = "Ragavan, Nimble Pilferer"; + + @Test + public void testJackalGeniusGeneticist() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, jackalGeniusGeneticist); + addCard(Zone.BATTLEFIELD, playerA, "Taiga", 3); + addCard(Zone.HAND, playerA, ragavanNimblePilferer); + addCard(Zone.HAND, playerA, bearCub); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ragavanNimblePilferer); + setChoice(playerA, "Cast with no"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bearCub); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, ragavanNimblePilferer, 2); + assertPermanentCount(playerA, bearCub, 2); + assertCounterCount(playerA, jackalGeniusGeneticist, CounterType.P1P1, 2); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/LadyOctopusInspiredInventorTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/LadyOctopusInspiredInventorTest.java new file mode 100644 index 00000000000..77d38c3fb6b --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/LadyOctopusInspiredInventorTest.java @@ -0,0 +1,122 @@ +package org.mage.test.cards.single.spm; + +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.UntapAllControllerEffect; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.common.FilterCreaturePermanent; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class LadyOctopusInspiredInventorTest extends CardTestPlayerBase { + + /* + Lady Octopus, Inspired Inventor + {U} + Legendary Creature - Human Scientist Villain + Whenever you draw your first or second card each turn, put an ingenuity counter on Lady Octopus. + {T}: You may cast an artifact spell from your hand with mana value less than or equal to the number of ingenuity counters on Lady Octopus without paying its mana cost. + */ + private static final String ladyOctopusInspiredInventor = "Lady Octopus, Inspired Inventor"; + + /* + Aether Vial + {1} + Artifact + At the beginning of your upkeep, you may put a charge counter on Aether Vial. + {T}: You may put a creature card with converted mana cost equal to the number of charge counters on ther Vial from your hand onto the battlefield. + */ + private static final String aetherVial = "Aether Vial"; + + /* + Tormod's Crypt + {0} + Artifact + {tap}, Sacrifice Tormod's Crypt: Exile all cards from target player's graveyard. + */ + private static final String tormodsCrypt = "Tormod's Crypt"; + + /* + Howling Mine + {2} + Artifact + At the beginning of each player's draw step, if Howling Mine is untapped, that player draws an additional card. + */ + private static final String howlingMine = "Howling Mine"; + + @Test + public void testLadyOctopusInspiredInventor() { + setStrictChooseMode(true); + + addCustomCardWithAbility("untap all creatures", playerA, new SimpleActivatedAbility( + new UntapAllControllerEffect(new FilterCreaturePermanent()), + new ManaCostsImpl<>("") + )); + addCustomCardWithAbility("draw a card", playerA, new SimpleActivatedAbility( + new DrawCardSourceControllerEffect(1), + new ManaCostsImpl<>("") + )); + addCard(Zone.BATTLEFIELD, playerA, ladyOctopusInspiredInventor); + addCard(Zone.HAND, playerA, aetherVial); + addCard(Zone.HAND, playerA, tormodsCrypt); + addCard(Zone.HAND, playerA, howlingMine); + + activateDrawCardAndUntap(); // Tormod's crypt + activateDrawCardAndUntap(); // Aether Vial + activateDrawCardAndUntap(); // Howling Mine + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertCounterCount(playerA, ladyOctopusInspiredInventor, CounterType.INGENUITY, 2); + assertHandCount(playerA, 3); + } + + private void activateDrawCardAndUntap() { + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: You may cast"); + setChoice(playerA, true); + + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "draw a"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "untap all"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + } + + @Test + public void testLadyOctopusInspiredInventorChoose() { + setStrictChooseMode(true); + + addCustomCardWithAbility("draw a card", playerA, new SimpleActivatedAbility( + new DrawCardSourceControllerEffect(3), + new ManaCostsImpl<>("") + )); + addCard(Zone.BATTLEFIELD, playerA, ladyOctopusInspiredInventor); + addCard(Zone.HAND, playerA, aetherVial); + addCard(Zone.HAND, playerA, tormodsCrypt); + addCard(Zone.HAND, playerA, howlingMine); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "draw "); + setChoice(playerA, "Whenever you draw your first"); // trigger stack + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: You may cast"); + setChoice(playerA, tormodsCrypt); + setChoice(playerA, true); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertCounterCount(playerA, ladyOctopusInspiredInventor, CounterType.INGENUITY, 2); + assertHandCount(playerA, 3 + 2); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/MaximumCarnageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/MaximumCarnageTest.java new file mode 100644 index 00000000000..7807ecc4f90 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/MaximumCarnageTest.java @@ -0,0 +1,69 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommander4Players; + +import static org.junit.Assert.assertTrue; + +/** + * + * @author Jmlundeen + */ +public class MaximumCarnageTest extends CardTestCommander4Players { + + /* + Maximum Carnage + {4}{R} + Enchantment - Saga + (As this Saga enters step and after your draw step, add a lore counter. Sacrifice after III.) + I -- Until your next turn, each creature attacks each combat if able and attacks a player other than you if able. + II -- Add {R}{R}{R}. + III -- This Saga deals 5 damage to each opponent. + */ + private static final String maximumCarnage = "Maximum Carnage"; + + /* + Bear Cub + {1}{G} + Creature - Bear + + 2/2 + */ + private static final String bearCub = "Bear Cub"; + + @Test + public void testMaximumCarnage() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, maximumCarnage); + addCard(Zone.BATTLEFIELD, playerB, bearCub); + addCard(Zone.BATTLEFIELD, playerD, bearCub); + + addTarget(playerD, playerC); // must attack + addTarget(playerB, playerD); // must attack + + setStopAt(4, PhaseStep.END_TURN); + execute(); + } + + @Test + public void testMaximumCarnageCantAttackController() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, maximumCarnage); + addCard(Zone.BATTLEFIELD, playerB, bearCub); + addCard(Zone.BATTLEFIELD, playerD, bearCub); + + addTarget(playerD, playerA); // must attack + + setStopAt(4, PhaseStep.END_TURN); + try { + execute(); + } catch (AssertionError e) { + assertTrue("Shouldn't be able to attack playerA", + e.getMessage().contains("[targetPlayer=PlayerA], but not used")); + } + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/MisterNegativeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/MisterNegativeTest.java new file mode 100644 index 00000000000..029e1298138 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/MisterNegativeTest.java @@ -0,0 +1,62 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Jmlundeen + */ +public class MisterNegativeTest extends CardTestPlayerBase { + + /* + Mister Negative + {5}{W}{B} + Legendary Creature - Human Villain + Vigilance, lifelink + When Mister Negative enters, you may exchange your life total with target opponent. If you lose life this way, draw that many cards. + 5/5 + */ + private static final String misterNegative = "Mister Negative"; + + @Test + public void testMisterNegative() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, misterNegative); + addCard(Zone.BATTLEFIELD, playerA, "Scrubland", 7); + setLife(playerB, 15); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, misterNegative); + addTarget(playerA, playerB); + setChoice(playerA, true); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertHandCount(playerA, 5); + assertLife(playerA, 15); + assertLife(playerB, 20); + } + + @Test + public void testMisterNegativeNoDraw() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, misterNegative); + addCard(Zone.BATTLEFIELD, playerA, "Scrubland", 7); + setLife(playerB, 21); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, misterNegative); + addTarget(playerA, playerB); + setChoice(playerA, true); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertHandCount(playerA, 0); + assertLife(playerA, 21); + assertLife(playerB, 20); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/MultiversalPassageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/MultiversalPassageTest.java new file mode 100644 index 00000000000..c23f342462d --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/MultiversalPassageTest.java @@ -0,0 +1,40 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.PhaseStep; +import mage.constants.SubType; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class MultiversalPassageTest extends CardTestPlayerBase { + + /* + Multiversal Passage + + Land + As this land enters, choose a basic land type. Then you may pay 2 life. If you don't, it enters tapped. + This land is the chosen type. + */ + private static final String multiversalPassage = "Multiversal Passage"; + + @Test + public void testMultiversalPassage() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, multiversalPassage); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, multiversalPassage); + setChoice(playerA, "Swamp"); + setChoice(playerA, true); // untapped + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertSubtype(multiversalPassage, SubType.SWAMP); + assertLife(playerA, 20 - 2); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/OscorpIndustriesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/OscorpIndustriesTest.java new file mode 100644 index 00000000000..364cc480059 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/OscorpIndustriesTest.java @@ -0,0 +1,52 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class OscorpIndustriesTest extends CardTestPlayerBase { + + /* + Oscorp Industries + + Land + This land enters tapped. + When this land enters from a graveyard, you lose 2 life. + {T}: Add {U}, {B}, or {R}. + Mayhem + */ + private static final String oscorpIndustries = "Oscorp Industries"; + + /* + Thought Courier + {1}{U} + Creature - Human Wizard + {tap}: Draw a card, then discard a card. + 1/1 + */ + private static final String thoughtCourier = "Thought Courier"; + + @Test + public void testOscorpIndustries() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, oscorpIndustries); + addCard(Zone.BATTLEFIELD, playerA, thoughtCourier); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Draw"); + setChoice(playerA, oscorpIndustries); + + playLand(1, PhaseStep.POSTCOMBAT_MAIN, playerA, oscorpIndustries + " with Mayhem"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 20 - 2); + assertPermanentCount(playerA, oscorpIndustries, 1); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/ParkerLuckTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/ParkerLuckTest.java new file mode 100644 index 00000000000..0e1bb20db49 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/ParkerLuckTest.java @@ -0,0 +1,79 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommander4Players; + +/** + * + * @author Jmlundeen + */ +public class ParkerLuckTest extends CardTestCommander4Players { + + /* + Parker Luck + {2}{B} + Enchantment + At the beginning of your end step, two target players each reveal the top card of their library. They each lose life equal to the mana value of the card revealed by the other player. Then they each put the card they revealed into their hand. + */ + private static final String parkerLuck = "Parker Luck"; + + /* + Bear Cub + {1}{G} + Creature - Bear + + 2/2 + */ + private static final String bearCub = "Bear Cub"; + + /* + Fugitive Wizard + {U} + Creature - Human Wizard + + 1/1 + */ + private static final String fugitiveWizard = "Fugitive Wizard"; + + @Test + public void testParkerLuck() { + setStrictChooseMode(true); + skipInitShuffling(); + + addCard(Zone.BATTLEFIELD, playerA, parkerLuck); + addCard(Zone.LIBRARY, playerD, bearCub); + addCard(Zone.LIBRARY, playerC, fugitiveWizard); + + addTarget(playerA, playerC); + addTarget(playerA, playerD); + + setStopAt(1, PhaseStep.CLEANUP); + execute(); + + assertLife(playerC, 20 - 2); + assertLife(playerD, 20 - 1); + assertHandCount(playerC, 1); + assertHandCount(playerD, 1); + } + + @Test + public void testParkerLuckOneLibraryEmpty() { + setStrictChooseMode(true); + skipInitShuffling(); + removeAllCardsFromLibrary(playerC); + + addCard(Zone.BATTLEFIELD, playerA, parkerLuck); + addCard(Zone.LIBRARY, playerD, bearCub); + + addTarget(playerA, playerC); + addTarget(playerA, playerD); + + setStopAt(1, PhaseStep.CLEANUP); + execute(); + + assertLife(playerC, 20 - 2); + assertHandCount(playerD, 1); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/PeterParkerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/PeterParkerTest.java new file mode 100644 index 00000000000..38eeace4a37 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/PeterParkerTest.java @@ -0,0 +1,115 @@ +package org.mage.test.cards.single.spm; + +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.TapAllEffect; +import mage.constants.PhaseStep; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class PeterParkerTest extends CardTestPlayerBase { + + /* + Peter Parker + {1}{W} + Legendary Creature - Human Scientist Hero + When Peter Parker enters, create a 2/1 green Spider creature token with reach. + {1}{G}{W}{U}: Transform Peter Parker. Activate only as a sorcery. + Amazing Spider-Man + {1}{G}{W}{U} + Legendary Creature - Spider Human Hero + Vigilance, reach + Each legendary spell you cast that's one or more colors has web-slinging {G}{W}{U}. + 4/4 + */ + private static final String peterParker = "Peter Parker"; + + + /* + Absolute Virtue + {6}{W}{U} + Legendary Creature - Avatar Warrior + This spell can't be countered. + Flying + You have protection from each of your opponents. + 8/8 + */ + private static final String absoluteVirtue = "Absolute Virtue"; + + /* + Adelbert Steiner + {1}{W} + Legendary Creature - Human Knight + Lifelink + Adelbert Steiner gets +1/+1 for each Equipment you control. + 2/1 + */ + private static final String adelbertSteiner = "Adelbert Steiner"; + + /* + Chainer, Nightmare Adept + {2}{B}{R} + Legendary Creature - Human Minion + Discard a card: You may cast a creature card from your graveyard this turn. Activate this ability only once each turn. + Whenever a nontoken creature enters the battlefield under your control, if you didn't cast it from your hand, it gains haste until your next turn. + 3/2 + */ + private static final String chainerNightmareAdept = "Chainer, Nightmare Adept"; + + /* + Balduvian Bears + {1}{G} + Creature - Bear + + 2/2 + */ + private static final String balduvianBears = "Balduvian Bears"; + + @Test + @Ignore("Enable after MDFC rework") + public void testAmazingSpiderMan() { + setStrictChooseMode(true); + + addCustomCardWithAbility("tap all creatures", playerA, new SimpleActivatedAbility( + new TapAllEffect(new FilterCreaturePermanent(SubType.BEAR, "bears")), + new ManaCostsImpl<>("") + )); + + addCard(Zone.HAND, playerA, peterParker); + addCard(Zone.BATTLEFIELD, playerA, chainerNightmareAdept); + addCard(Zone.BATTLEFIELD, playerA, balduvianBears,2); + addCard(Zone.HAND, playerA, adelbertSteiner, 2); + addCard(Zone.GRAVEYARD, playerA, absoluteVirtue); + addCard(Zone.BATTLEFIELD, playerA, "Tropical Island", 8); + addCard(Zone.BATTLEFIELD, playerA, "Tundra", 8); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Amazing Spider-Man", true); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "tap all"); // tap bears, addCard command isn't working to set tapped + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, adelbertSteiner + " with Web-slinging", true); + setChoice(playerA, balduvianBears); // return to hand + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Discard a card"); + setChoice(playerA, adelbertSteiner); // discard + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + checkPlayableAbility("Bear does not have web-slinging", 1, PhaseStep.PRECOMBAT_MAIN, playerA, + "Cast " + balduvianBears + " with Web-slinging", false); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, absoluteVirtue + " with Web-slinging"); + setChoice(playerA, balduvianBears); // return to hand + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/RhinosRampageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/RhinosRampageTest.java new file mode 100644 index 00000000000..c9fdc258521 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/RhinosRampageTest.java @@ -0,0 +1,92 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class RhinosRampageTest extends CardTestPlayerBase { + + /* + Rhino's Rampage + {R/G} + Sorcery + Target creature you control gets +1/+0 until end of turn. It fights target creature an opponent controls. + When excess damage is dealt to the creature an opponent controls this way, destroy up to one target noncreature artifact with mana value 3 or less. + */ + private static final String rhinosRampage = "Rhino's Rampage"; + + /* + Bear Cub + {1}{G} + Creature - Bear + + 2/2 + */ + private static final String bearCub = "Bear Cub"; + + /* + Fugitive Wizard + {U} + Creature - Human Wizard + + 1/1 + */ + private static final String fugitiveWizard = "Fugitive Wizard"; + + /* + Tormod's Crypt + {0} + Artifact + {T}, Sacrifice Tormod's Crypt: Exile all cards from target player's graveyard. + */ + private static final String tormodsCrypt = "Tormod's Crypt"; + + @Test + public void testRhinosRampageExcess() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, rhinosRampage); + addCard(Zone.BATTLEFIELD, playerA, "Forest"); + addCard(Zone.BATTLEFIELD, playerA, bearCub); + addCard(Zone.BATTLEFIELD, playerB, fugitiveWizard); + addCard(Zone.BATTLEFIELD, playerB, tormodsCrypt); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, rhinosRampage); + addTarget(playerA, bearCub); + addTarget(playerA, fugitiveWizard); + addTarget(playerA, tormodsCrypt); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerB, fugitiveWizard, 1); + assertGraveyardCount(playerB, tormodsCrypt, 1); + } + + @Test + public void testRhinosRampageNoExcess() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, rhinosRampage); + addCard(Zone.BATTLEFIELD, playerA, "Forest"); + addCard(Zone.BATTLEFIELD, playerA, fugitiveWizard); + addCard(Zone.BATTLEFIELD, playerB, bearCub); + addCard(Zone.BATTLEFIELD, playerB, tormodsCrypt); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, rhinosRampage); + addTarget(playerA, fugitiveWizard); + addTarget(playerA, bearCub); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, fugitiveWizard, 1); + assertGraveyardCount(playerB, bearCub, 1); + assertPermanentCount(playerB, tormodsCrypt, 1); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/SandmansQuicksandTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/SandmansQuicksandTest.java new file mode 100644 index 00000000000..5f861550f74 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/SandmansQuicksandTest.java @@ -0,0 +1,81 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class SandmansQuicksandTest extends CardTestPlayerBase { + + /* + Sandman's Quicksand + {1}{B}{B} + Sorcery + Mayhem {3}{B} + All creatures get -2/-2 until end of turn. If this spell's mayhem cost was paid, creatures your opponents control get -2/-2 until end of turn instead. + */ + private static final String sandmansQuicksand = "Sandman's Quicksand"; + + /* + Bear Cub + {1}{G} + Creature - Bear + + 2/2 + */ + private static final String bearCub = "Bear Cub"; + + /* + Thought Courier + {1}{U} + Creature - Human Wizard + {tap}: Draw a card, then discard a card. + 1/1 + */ + private static final String thoughtCourier = "Thought Courier"; + + @Test + public void testSandmansQuicksand() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, sandmansQuicksand); + addCard(Zone.BATTLEFIELD, playerA, bearCub); + addCard(Zone.BATTLEFIELD, playerB, bearCub); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sandmansQuicksand); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, bearCub, 1); + assertGraveyardCount(playerB, bearCub, 1); + } + + @Test + public void testSandmansQuicksandMayhem() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, sandmansQuicksand); + addCard(Zone.BATTLEFIELD, playerA, bearCub); + addCard(Zone.BATTLEFIELD, playerA, thoughtCourier); + addCard(Zone.BATTLEFIELD, playerB, bearCub); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Draw"); + setChoice(playerA, sandmansQuicksand); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, sandmansQuicksand + " with Mayhem"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, bearCub, 0); + assertGraveyardCount(playerB, bearCub, 1); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/ScarletSpiderBenReillyTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/ScarletSpiderBenReillyTest.java new file mode 100644 index 00000000000..c2e7afe3c97 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/ScarletSpiderBenReillyTest.java @@ -0,0 +1,65 @@ +package org.mage.test.cards.single.spm; + +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.TapAllEffect; +import mage.constants.PhaseStep; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.common.FilterCreaturePermanent; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class ScarletSpiderBenReillyTest extends CardTestPlayerBase { + + /* + Scarlet Spider, Ben Reilly + {1}{R}{G} + Legendary Creature - Spider Human Hero + Web-slinging {R}{G} + Trample + Sensational Save -- If Scarlet Spider was cast using web-slinging, he enters with X +1/+1 counters on him, where X is the mana value of the returned creature. + 4/3 + */ + private static final String scarletSpiderBenReilly = "Scarlet Spider, Ben Reilly"; + + /* + Bear Cub + {1}{G} + Creature - Bear + + 2/2 + */ + private static final String bearCub = "Bear Cub"; + + @Test + public void testScarletSpiderBenReilly() { + setStrictChooseMode(true); + + addCustomCardWithAbility("tap all creatures", playerA, new SimpleActivatedAbility( + new TapAllEffect(new FilterCreaturePermanent(SubType.BEAR, "bears")), + new ManaCostsImpl<>("") + )); + + addCard(Zone.HAND, playerA, scarletSpiderBenReilly); + addCard(Zone.BATTLEFIELD, playerA, bearCub, 1); + addCard(Zone.BATTLEFIELD, playerA, "Taiga", 3); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "tap all"); // tap bears, addCard command isn't working to set tapped + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, scarletSpiderBenReilly + " with Web-slinging"); + setChoice(playerA, bearCub); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertCounterCount(playerA, scarletSpiderBenReilly, CounterType.P1P1, 2); + assertHandCount(playerA, bearCub, 1); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/ShadowOfTheGoblinTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/ShadowOfTheGoblinTest.java new file mode 100644 index 00000000000..50312827c11 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/ShadowOfTheGoblinTest.java @@ -0,0 +1,123 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class ShadowOfTheGoblinTest extends CardTestPlayerBase { + + /* + Shadow of the Goblin + {1}{R} + Enchantment + Unreliable Visions -- At the beginning of your first main phase, discard a card. If you do, draw a card. + Undying Vengeance -- Whenever you play a land or cast a spell from anywhere other than your hand, this enchantment deals 1 damage to each opponent. + */ + private static final String shadowOfTheGoblin = "Shadow of the Goblin"; + + /* + Party Thrasher + {1}{R} + Creature - Lizard Wizard + Noncreature spells you cast from exile have convoke. + At the beginning of your precombat main phase, you may discard a card. If you do, exile the top two cards of your library, then choose one of them. You may play that card this turn. + 1/4 + */ + private static final String partyThrasher = "Party Thrasher"; + + /* + Misthollow Griffin + {2}{U}{U} + Creature - Griffin + Flying + You may cast Misthollow Griffin from exile. + 3/3 + */ + private static final String misthollowGriffin = "Misthollow Griffin"; + + /* + Glarb, Calamity's Augur + {B}{G}{U} + Legendary Creature - Frog Wizard Noble + Deathtouch + You may look at the top card of your library any time. + You may play lands and cast spells with mana value 4 or greater from the top of your library. + {T}: Surveil 2. + 2/4 + */ + private static final String glarbCalamitysAugur = "Glarb, Calamity's Augur"; + + @Test + public void testShadowOfTheGoblinFromHand() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, shadowOfTheGoblin); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.HAND, playerA, "Mountain"); + addCard(Zone.HAND, playerA, partyThrasher); + + setChoice(playerA, false); // shadow trigger + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mountain"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, partyThrasher); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertLife(playerB, 20); // no trigger + } + + @Test + public void testShadowOfTheGoblinFromExile() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, shadowOfTheGoblin); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + addCard(Zone.BATTLEFIELD, playerA, partyThrasher); + addCard(Zone.HAND, playerA, "Mountain"); + addCard(Zone.EXILED, playerA, misthollowGriffin); + + setChoice(playerA, "Unreliable Visions"); + setChoice(playerA, true); // discard + setChoice(playerA, "Mountain", 2); + + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mountain"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, misthollowGriffin); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertLife(playerB, 20 - 1 - 1); // land + spell from exile + } + + @Test + public void testShadowOfTheGoblinFromLibrary() { + setStrictChooseMode(true); + skipInitShuffling(); + + addCard(Zone.BATTLEFIELD, playerA, shadowOfTheGoblin); + addCard(Zone.BATTLEFIELD, playerA, glarbCalamitysAugur); + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + addCard(Zone.LIBRARY, playerA, misthollowGriffin); + addCard(Zone.LIBRARY, playerA, "Island"); + + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Island"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, misthollowGriffin); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertLife(playerB, 20 - 1 - 1); // land + spell from library + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/SpiderPunkTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/SpiderPunkTest.java new file mode 100644 index 00000000000..ee04a8fb83c --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/SpiderPunkTest.java @@ -0,0 +1,91 @@ +package org.mage.test.cards.single.spm; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.keyword.RiotAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.target.TargetPlayer; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class SpiderPunkTest extends CardTestPlayerBase { + + /* + Spider-Punk + {1}{R} + Legendary Creature - Spider Human Hero + Riot + Other Spiders you control have riot. + Spells and abilities can't be countered. + Damage can't be prevented. + 2/1 + */ + private static final String spiderPunk = "Spider-Punk"; + + /* + Counterspell + {U}{U} + Instant + Counter target spell. + */ + private static final String counterspell = "Counterspell"; + + /* + Disallow + {1}{U}{U} + Instant + Counter target spell, activated ability, or triggered ability. + */ + private static final String disallow = "Disallow"; + + /* + Giant Spider + {3}{G} + Creature - Spider + Reach (This creature can block creatures with flying.) + 2/4 + */ + private static final String giantSpider = "Giant Spider"; + + + @Test + public void testSpiderPunk() { + setStrictChooseMode(true); + + Ability ability = new SimpleActivatedAbility(new DamageTargetEffect(2), new TapSourceCost()); + ability.addTarget(new TargetPlayer(1)); + addCustomCardWithAbility("{T} deal damage", playerA, ability); + addCard(Zone.BATTLEFIELD, playerA, spiderPunk); + setChoice(playerA, "No"); // Haste - Spider-Punk + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + addCard(Zone.BATTLEFIELD, playerB, "Island", 5); + addCard(Zone.HAND, playerA, giantSpider); + addCard(Zone.HAND, playerB, counterspell); + addCard(Zone.HAND, playerB, disallow); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, giantSpider); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, counterspell, giantSpider); + + setChoice(playerA, "No"); // Haste + + attack(1, playerA, giantSpider); + attack(1, playerA, spiderPunk); + + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: {this} deals 2 damage", playerB); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, disallow); + addTarget(playerB, "stack ability"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerB, 20 - 2 - 2 - 2); + assertAbilityCount(playerA, giantSpider, RiotAbility.class, 1); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/SpiderVerseTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/SpiderVerseTest.java new file mode 100644 index 00000000000..91bdd0de6e5 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/SpiderVerseTest.java @@ -0,0 +1,81 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommander4Players; + +/** + * + * @author Jmlundeen + */ +public class SpiderVerseTest extends CardTestCommander4Players { + + /* + Spider-Verse + {3}{R}{R} + Enchantment + The "legend rule" doesn't apply to Spiders you control. + Whenever you cast a spell from anywhere other than your hand, you may copy it. If you do, you may choose new targets for the copy. If the copy is a permanent spell, it gains haste. Do this only once each turn. + */ + private static final String spiderVerse = "Spider-Verse"; + + /* + Lightning Bolt + {R} + Instant + Lightning Bolt deals 3 damage to any target. + */ + private static final String lightningBolt = "Lightning Bolt"; + + /* + Bear Cub + {1}{G} + Creature - Bear + + 2/2 + */ + private static final String bearCub = "Bear Cub"; + + /* + Ashiok, Nightmare Muse + {3}{U}{B} + Legendary Planeswalker - Ashiok + + +1: Create a 2/3 blue and black Nightmare creature token with "Whenever this creature attacks or blocks, each opponent exiles the top two cards of their library." + -3: Return target nonland permanent to its owner's hand, then that player exiles a card from their hand. + -7: You may cast up to three face-up cards your opponents own from exile without paying their mana costs. + */ + private static final String ashiokNightmareMuse = "Ashiok, Nightmare Muse"; + + @Test + public void testSpiderVerse() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, spiderVerse); + addCard(Zone.BATTLEFIELD, playerA, ashiokNightmareMuse); + addCard(Zone.EXILED, playerB, lightningBolt); + addCard(Zone.EXILED, playerB, bearCub); + + addCounters(1, PhaseStep.PRECOMBAT_MAIN, playerA, ashiokNightmareMuse, CounterType.LOYALTY, 9); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "-7"); + setChoice(playerA, lightningBolt); + setChoice(playerA, true); + addTarget(playerA, playerD); + setChoice(playerA, false, 2); + setChoice(playerA, true, 2); // use spider-verse / new target + addTarget(playerA, playerB); + + activateAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "-7"); + setChoice(playerA, true, 2); // ashiok + spider-verse + attack(5, playerA, bearCub, playerC); // copy has haste + + setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerB, 20 - 3); + assertLife(playerD, 20 - 3); + assertLife(playerC, 20 - 2); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/SuperiorSpiderManTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/SuperiorSpiderManTest.java new file mode 100644 index 00000000000..df31e046e53 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/SuperiorSpiderManTest.java @@ -0,0 +1,57 @@ +package org.mage.test.cards.single.spm; + +import mage.abilities.keyword.LifelinkAbility; +import mage.constants.PhaseStep; +import mage.constants.SubType; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class SuperiorSpiderManTest extends CardTestPlayerBase { + + /* + Superior Spider-Man + {2}{U}{B} + Legendary Creature - Spider Human Hero + Mind Swap -- You may have Superior Spider- Man enter as a copy of any creature card in a graveyard, except his name is Superior Spider-Man and he's a 4/4 Spider Human Hero in addition to his other types. When you do, exile that card. + 4/4 + */ + private static final String superiorSpiderMan = "Superior Spider-Man"; + + /* + Adelbert Steiner + {1}{W} + Legendary Creature - Human Knight + Lifelink + Adelbert Steiner gets +1/+1 for each Equipment you control. + 2/1 + */ + private static final String adelbertSteiner = "Adelbert Steiner"; + + @Test + public void testSuperiorSpiderMan() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, superiorSpiderMan); + addCard(Zone.GRAVEYARD, playerA, adelbertSteiner); + addCard(Zone.BATTLEFIELD, playerA, "Underground Sea", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, superiorSpiderMan); + setChoice(playerA, true); + setChoice(playerA, adelbertSteiner); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertExileCount(playerA, 1); + assertSubtype(superiorSpiderMan, SubType.KNIGHT); + assertSubtype(superiorSpiderMan, SubType.SPIDER); + assertSubtype(superiorSpiderMan, SubType.HERO); + assertSubtype(superiorSpiderMan, SubType.HUMAN); + assertAbility(playerA, superiorSpiderMan, LifelinkAbility.getInstance(), true); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/TerrificTeamUpTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/TerrificTeamUpTest.java new file mode 100644 index 00000000000..706d34f7b84 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/TerrificTeamUpTest.java @@ -0,0 +1,61 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.filter.Filter; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class TerrificTeamUpTest extends CardTestPlayerBase { + + /* + Terrific Team-Up + {3}{G} + Instant + This spell costs {2} less to cast if you control a permanent with mana value 4 or greater. + One or two target creatures you control each get +1/+0 until end of turn. They each deal damage equal to their power to target creature an opponent controls. + */ + private static final String terrificTeamUp = "Terrific Team-Up"; + + /* + Bear Cub + {1}{G} + Creature - Bear + + 2/2 + */ + private static final String bearCub = "Bear Cub"; + + /* + Sea Monster + {4}{U}{U} + Creature - Serpent + Sea Monster can't attack unless defending player controls an Island. + 6/6 + */ + private static final String seaMonster = "Sea Monster"; + + @Test + public void testTerrificTeamUp() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, bearCub, 2); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + addCard(Zone.BATTLEFIELD, playerB, seaMonster); + addCard(Zone.HAND, playerA, terrificTeamUp); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, terrificTeamUp); + addTarget(playerA, bearCub, 2); + addTarget(playerA, seaMonster); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertGraveyardCount(playerB, seaMonster, 1); + assertPowerToughness(playerA, bearCub, 3, 2, Filter.ComparisonScope.All); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/TheCloneSagaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/TheCloneSagaTest.java new file mode 100644 index 00000000000..9e0af202b8a --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/TheCloneSagaTest.java @@ -0,0 +1,64 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.player.TestPlayer; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class TheCloneSagaTest extends CardTestPlayerBase { + + /* + The Clone Saga + {3}{U} + Enchantment - Saga + (As this Saga enters step, add a lore counter. Sacrifice after III.) + I -- Surveil 3. + II -- When you next cast a creature spell this turn, copy it, except the copy isn't legendary. + III -- Choose a card name. Whenever a creature with the chosen name deals combat damage to a player this turn, draw a card. + */ + private static final String theCloneSaga = "The Clone Saga"; + + /* + Ragavan, Nimble Pilferer + {R} + Legendary Creature - Monkey Pirate + Whenever Ragavan, Nimble Pilferer deals combat damage to a player, create a Treasure token and exile the top card of that player's library. Until end of turn, you may cast that card. + Dash {1}{R} + 2/1 + */ + private static final String ragavanNimblePilferer = "Ragavan, Nimble Pilferer"; + + @Test + public void testTheCloneSaga() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, theCloneSaga); + addCard(Zone.HAND, playerA, ragavanNimblePilferer); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + + addTarget(playerA, TestPlayer.TARGET_SKIP); + setChoice(playerA, "Mountain", 2); + + setChoice(playerA, ragavanNimblePilferer); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, ragavanNimblePilferer); + setChoice(playerA, "Cast with no alternative cost"); + + attack(3, playerA, ragavanNimblePilferer); + attack(3, playerA, ragavanNimblePilferer); + setChoice(playerA, "Whenever a creature with the chosen name", 2); + setChoice(playerA, "Whenever {this} deals"); + + setStopAt(3, PhaseStep.END_TURN); + execute(); + + assertLife(playerB, 20 - 2 - 2); + assertHandCount(playerA, 1 + 1 + 1); // 1 draw + 2 triggers + assertPermanentCount(playerA, "Treasure Token", 2); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/TheDeathOfGwenStacyTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/TheDeathOfGwenStacyTest.java new file mode 100644 index 00000000000..a2d52b5b82f --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/TheDeathOfGwenStacyTest.java @@ -0,0 +1,48 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.player.TestPlayer; +import org.mage.test.serverside.base.CardTestCommander4Players; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class TheDeathOfGwenStacyTest extends CardTestCommander4Players { + + /* + The Death of Gwen Stacy + {2}{B} + Enchantment - Saga + (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.) + I -- Destroy target creature. + II -- Each player may discard a card. Each player who doesn't loses 3 life. + III -- Exile any number of target players' graveyards. + */ + private static final String theDeathOfGwenStacy = "The Death of Gwen Stacy"; + + @Test + public void testTheDeathOfGwenStacy() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, theDeathOfGwenStacy); + addCard(Zone.HAND, playerC, "Mountain"); + addCard(Zone.HAND, playerB, "Mountain"); + + addTarget(playerA, "Mountain"); + addTarget(playerD, TestPlayer.TARGET_SKIP); + addTarget(playerC, "Mountain"); + addTarget(playerB, "Mountain"); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + assertLife(playerC, 20); + assertLife(playerD, 20 - 3); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/TheSpotLivingPortalTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/TheSpotLivingPortalTest.java new file mode 100644 index 00000000000..f798aaffffe --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/TheSpotLivingPortalTest.java @@ -0,0 +1,115 @@ +package org.mage.test.cards.single.spm; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.player.TestPlayer; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author Jmlundeen + */ +public class TheSpotLivingPortalTest extends CardTestPlayerBase { + + /* + The Spot, Living Portal + {3}{W}{B} + Legendary Creature - Human Scientist Villain + When The Spot enters, exile up to one target nonland permanent and up to one target nonland permanent card from a graveyard. + When The Spot dies, put him on the bottom of his owner's library. If you do, return the exiled cards to their owners' hands. + 4/4 + */ + private static final String theSpotLivingPortal = "The Spot, Living Portal"; + + /* + Bear Cub + {1}{G} + Creature - Bear + + 2/2 + */ + private static final String bearCub = "Bear Cub"; + + /* + Fugitive Wizard + {U} + Creature - Human Wizard + + 1/1 + */ + private static final String fugitiveWizard = "Fugitive Wizard"; + + @Test + public void testTheSpotLivingPortal() { + setStrictChooseMode(true); + + addCustomEffect_TargetDestroy(playerB); + addCard(Zone.HAND, playerA, theSpotLivingPortal); + addCard(Zone.GRAVEYARD, playerA, bearCub); + addCard(Zone.BATTLEFIELD, playerA, "Scrubland", 5); + addCard(Zone.BATTLEFIELD, playerB, fugitiveWizard); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, theSpotLivingPortal); + addTarget(playerA, bearCub); + addTarget(playerA, fugitiveWizard); + + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "target destroy"); + addTarget(playerB, theSpotLivingPortal); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertHandCount(playerA, bearCub, 1); + assertHandCount(playerB, fugitiveWizard, 1); + assertLibraryCount(playerA, theSpotLivingPortal, 1); + } + + @Test + public void testOnlyGraveyard() { + setStrictChooseMode(true); + + addCustomEffect_TargetDestroy(playerB); + addCard(Zone.HAND, playerA, theSpotLivingPortal); + addCard(Zone.GRAVEYARD, playerA, bearCub); + addCard(Zone.BATTLEFIELD, playerA, "Scrubland", 5); + addCard(Zone.BATTLEFIELD, playerB, fugitiveWizard); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, theSpotLivingPortal); + addTarget(playerA, TestPlayer.TARGET_SKIP); + addTarget(playerA, bearCub); + + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "target destroy"); + addTarget(playerB, theSpotLivingPortal); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertHandCount(playerA, bearCub, 1); + assertLibraryCount(playerA, theSpotLivingPortal, 1); + } + + @Test + public void testOnlyBattlefield() { + setStrictChooseMode(true); + + addCustomEffect_TargetDestroy(playerB); + addCard(Zone.HAND, playerA, theSpotLivingPortal); + addCard(Zone.GRAVEYARD, playerA, bearCub); + addCard(Zone.BATTLEFIELD, playerA, "Scrubland", 5); + addCard(Zone.BATTLEFIELD, playerB, fugitiveWizard); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, theSpotLivingPortal); + addTarget(playerA, fugitiveWizard); + addTarget(playerA, TestPlayer.TARGET_SKIP); + + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "target destroy"); + addTarget(playerB, theSpotLivingPortal); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertHandCount(playerB, fugitiveWizard, 1); + assertLibraryCount(playerA, theSpotLivingPortal, 1); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/tdc/ParapetThrasherTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/tdc/ParapetThrasherTest.java new file mode 100644 index 00000000000..fa6a6233b77 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/tdc/ParapetThrasherTest.java @@ -0,0 +1,89 @@ +package org.mage.test.cards.single.tdc; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommander4Players; + +/** + * + * @author Jmlundeen + */ +public class ParapetThrasherTest extends CardTestCommander4Players { + + /* + Parapet Thrasher + {2}{R}{R} + Creature - Dragon + Flying + Whenever one or more Dragons you control deal combat damage to an opponent, choose one that hasn't been chosen this turn -- + * Destroy target artifact that opponent controls. + * This creature deals 4 damage to each other opponent. + * Exile the top card of your library. You may play it this turn. + 4/3 + */ + private static final String parapetThrasher = "Parapet Thrasher"; + + /* + Fountain of Youth + {0} + Artifact + {2}, {tap}: You gain 1 life. + */ + private static final String fountainOfYouth = "Fountain of Youth"; + + @Test + public void testParapetThrasherMode1() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, parapetThrasher); + addCard(Zone.BATTLEFIELD, playerB, fountainOfYouth); + + attack(1, playerA, parapetThrasher, playerB); + setModeChoice(playerA, "1"); + addTarget(playerA, fountainOfYouth); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerB, fountainOfYouth, 1); + } + + @Test + public void testParapetThrasherMode2() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, parapetThrasher); + + attack(1, playerA, parapetThrasher); + setModeChoice(playerA, "2"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertLife(playerB, 20 - 4); + assertLife(playerC, 20 - 4); + assertLife(playerD, 20 - 4); + } + + @Test + public void testParapetThrasherMode3() { + setStrictChooseMode(true); + skipInitShuffling(); + + addCard(Zone.BATTLEFIELD, playerA, parapetThrasher); + addCard(Zone.LIBRARY, playerA, "Island"); + addCard(Zone.LIBRARY, playerA, "Mountain"); + + attack(1, playerA, parapetThrasher); + setModeChoice(playerA, "3"); + + playLand(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Island"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertExileCount(playerA, 0); + assertPermanentCount(playerA, "Island", 1); + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/war/PriceOfBetrayalTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/war/PriceOfBetrayalTest.java new file mode 100644 index 00000000000..f800e0f1c8c --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/war/PriceOfBetrayalTest.java @@ -0,0 +1,86 @@ +package org.mage.test.cards.single.war; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.target.TargetPlayer; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +import static org.junit.Assert.assertEquals; + +/** + * + * @author Jmlundeen + */ +public class PriceOfBetrayalTest extends CardTestPlayerBase { + + /* + Price of Betrayal + {B} + Sorcery + Remove up to five counters from target artifact, creature, planeswalker, or opponent. + */ + private static final String priceOfBetrayal = "Price of Betrayal"; + + /* + Bear Cub + {1}{G} + Creature - Bear + + 2/2 + */ + private static final String bearCub = "Bear Cub"; + + @Test + public void testPriceOfBetrayalPermanent() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, priceOfBetrayal); + addCard(Zone.BATTLEFIELD, playerA, bearCub); + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + + addCounters(1, PhaseStep.PRECOMBAT_MAIN, playerA, bearCub, CounterType.P1P1, 3); + addCounters(1, PhaseStep.PRECOMBAT_MAIN, playerA, bearCub, CounterType.CHARGE, 1); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, priceOfBetrayal, bearCub); + setChoiceAmount(playerA, 1); // charge counter + setChoiceAmount(playerA, 3); // P1P1 counter + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPowerToughness(playerA, bearCub, 2, 2); + } + + @Test + public void testPriceOfBetrayalPlayer() { + setStrictChooseMode(true); + + Ability ability = new SimpleActivatedAbility( + new AddCountersTargetEffect(CounterType.ENERGY.createInstance(5)), + new ManaCostsImpl<>("") + ); + ability.addTarget(new TargetPlayer(1)); + addCustomCardWithAbility("add counter", playerA, ability); + + addCard(Zone.BATTLEFIELD, playerA, "Swamp"); + addCard(Zone.HAND, playerA, priceOfBetrayal); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "target player gets", playerB); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, priceOfBetrayal, playerB); + setChoiceAmount(playerA, 5); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertEquals("Player should have no counters", 0, playerA.getCountersCount(CounterType.ENERGY)); + } +} \ No newline at end of file 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 index a835773568d..558d9e8969c 100644 --- 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 @@ -19,21 +19,29 @@ public class CrackInTimeTest extends CardTestPlayerBase { addCard(Zone.HAND, playerA, "Crack in Time"); // {3}{W} addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 1); + addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears", 1); - // exile + // exile on etb 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); + checkExileCount("on etb Balduvian", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Balduvian Bears", 1); + checkExileCount("on etb Grizzly", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Grizzly Bears", 0); + + // exile on next precombat main + addTarget(playerA, "Grizzly Bears"); // exile on etb + checkExileCount("on main Balduvian", 3, PhaseStep.BEGIN_COMBAT, playerB, "Balduvian Bears", 1); + checkExileCount("on main Grizzly", 3, PhaseStep.BEGIN_COMBAT, playerB, "Grizzly 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); + activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "target destroy", "Crack in Time"); setStrictChooseMode(true); - setStopAt(1, PhaseStep.END_TURN); + setStopAt(3, PhaseStep.END_TURN); execute(); + + assertExileCount(playerB, "Balduvian Bears", 0); + assertPermanentCount(playerB, "Balduvian Bears", 1); + assertExileCount(playerB, "Grizzly Bears", 0); + assertPermanentCount(playerB, "Grizzly Bears", 1); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/woe/GracefulTakedownTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/woe/GracefulTakedownTest.java index ad185845a26..19ceebb0ee3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/woe/GracefulTakedownTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/woe/GracefulTakedownTest.java @@ -4,6 +4,7 @@ import mage.constants.PhaseStep; import mage.constants.Zone; import org.junit.Assert; import org.junit.Test; +import org.mage.test.player.TestPlayer; import org.mage.test.serverside.base.CardTestPlayerBase; /** @@ -38,7 +39,8 @@ public class GracefulTakedownTest extends CardTestPlayerBase { addCard(Zone.HAND, playerA, takedown); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, takedown); - addTarget(playerA, cub); + addTarget(playerA, TestPlayer.TARGET_SKIP); // enchanted + addTarget(playerA, cub); // one other addTarget(playerA, ancient); setStopAt(1, PhaseStep.END_COMBAT); @@ -62,7 +64,8 @@ public class GracefulTakedownTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, strength, piker, true); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, takedown); - addTarget(playerA, cub + "^" + piker); + addTarget(playerA, piker); // enchanted + addTarget(playerA, cub); // one other addTarget(playerA, ancient); setStopAt(1, PhaseStep.END_COMBAT); diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/TokenImagesTest.java b/Mage.Tests/src/test/java/org/mage/test/serverside/TokenImagesTest.java index 8f645ad543f..8e27afb2ff5 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/TokenImagesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/TokenImagesTest.java @@ -888,7 +888,7 @@ public class TokenImagesTest extends CardTestPlayerBase { // check face down card in exile runCode("on face down", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> { - Card card = currentGame.getExile().getAllCards(currentGame, playerA.getId()).get(0); + Card card = currentGame.getExile().getCardsOwned(currentGame, playerA.getId()).get(0); GameView gameView = getGameView(playerA); CardView controllerCardView = gameView.getExile() .stream() @@ -941,7 +941,7 @@ public class TokenImagesTest extends CardTestPlayerBase { // check face down card runCode("on face down", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> { - Card card = currentGame.getExile().getAllCards(currentGame, playerA.getId()).get(0); + Card card = currentGame.getExile().getCardsOwned(currentGame, playerA.getId()).get(0); GameView gameView = getGameView(playerA); CardView controllerCardView = gameView.getExile() .stream() diff --git a/Mage.Verify/src/main/java/mage/verify/mtgjson/MtgJsonCard.java b/Mage.Verify/src/main/java/mage/verify/mtgjson/MtgJsonCard.java index 2adafa6e7d0..b257c3ef6b0 100644 --- a/Mage.Verify/src/main/java/mage/verify/mtgjson/MtgJsonCard.java +++ b/Mage.Verify/src/main/java/mage/verify/mtgjson/MtgJsonCard.java @@ -43,6 +43,8 @@ public final class MtgJsonCard { public String frameVersion; public List printings; // set codes with that card public boolean isFunny; + public List promoTypes; + public boolean isTextless; @Override public String toString() { diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java index ebebe6c5903..9bb9ed2c176 100644 --- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java +++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java @@ -170,12 +170,14 @@ public class VerifyCardDataTest { skipListAddName(SKIP_LIST_TYPE, "UNH", "Old Fogey"); // uses summon word as a joke card skipListAddName(SKIP_LIST_TYPE, "UND", "Old Fogey"); skipListAddName(SKIP_LIST_TYPE, "UST", "capital offense"); // uses "instant" instead "Instant" as a joke card + skipListAddName(SKIP_LIST_TYPE, "SPM", "Superior Foes of Spider-Man"); // temporary // subtype // skipListAddName(SKIP_LIST_SUBTYPE, set, cardName); skipListAddName(SKIP_LIST_SUBTYPE, "UGL", "Miss Demeanor"); // uses multiple types as a joke card: Lady, of, Proper, Etiquette skipListAddName(SKIP_LIST_SUBTYPE, "UGL", "Elvish Impersonators"); // subtype is "Elves" pun skipListAddName(SKIP_LIST_SUBTYPE, "UND", "Elvish Impersonators"); + skipListAddName(SKIP_LIST_SUBTYPE, "SPM", "Superior Foes of Spider-Man"); // temporary // number // skipListAddName(SKIP_LIST_NUMBER, set, cardName); @@ -183,6 +185,7 @@ public class VerifyCardDataTest { // rarity // skipListAddName(SKIP_LIST_RARITY, set, cardName); skipListAddName(SKIP_LIST_RARITY, "CMR", "The Prismatic Piper"); // Collation is not yet set up for CMR https://www.lethe.xyz/mtg/collation/cmr.html + skipListAddName(SKIP_LIST_RARITY, "SPM", "Gwenom, Remorseless"); // temporary // missing abilities // skipListAddName(SKIP_LIST_MISSING_ABILITIES, set, cardName); @@ -697,32 +700,6 @@ public class VerifyCardDataTest { // String code = MtgJsonService.xMageToMtgJsonCodes.getOrDefault(set.getCode(), set.getCode()) + " - " + jsonCard.getNameAsFull() + " - " + jsonCard.number; // foundedJsonCards.add(code); // -// // CHECK: only lands can use full art in current version; -// // Another cards must be in text render mode as normal, example: https://scryfall.com/card/sld/76/athreos-god-of-passage -// // TODO: add support textless cards like https://scryfall.com/card/sch/12/thalia-and-the-gitrog-monster -// boolean isLand = card.getRarity().equals(Rarity.LAND); -// if (card.isFullArt() && !isLand) { -// errorsList.add("Error: only lands can use full art setting: " -// + set.getCode() + " - " + set.getName() + " - " + card.getName() + " - " + card.getCardNumber()); -// } -// -// // CHECK: must use full art setting -// if (jsonCard.isFullArt && isLand && !card.isFullArt()) { -// errorsList.add("Error: card must use full art setting: " -// + set.getCode() + " - " + set.getName() + " - " + card.getName() + " - " + card.getCardNumber()); -// } -// -// // CHECK: must not use full art setting -// if (!jsonCard.isFullArt && card.isFullArt()) { -// errorsList.add("Error: card must NOT use full art setting: " -// + set.getCode() + " - " + set.getName() + " - " + card.getName() + " - " + card.getCardNumber()); -// } - - // CHECK: must use retro frame setting - if ((jsonCard.frameVersion.equals("1993") || jsonCard.frameVersion.equals("1997")) && !card.isRetroFrame()) { - errorsList.add("Error: card must use retro art setting: " - + set.getCode() + " - " + set.getName() + " - " + card.getName() + " - " + card.getCardNumber()); - } } } @@ -766,6 +743,63 @@ public class VerifyCardDataTest { } } + @Test + @Ignore + public void test_checkWrongFullArtAndRetro() { + Collection errorsList = new ArrayList<>(); + Collection xmageSets = Sets.getInstance().values(); + + // CHECK: wrong card numbers + for (ExpansionSet set : xmageSets) { + if (skipListHaveName(SKIP_LIST_WRONG_CARD_NUMBERS, set.getCode())) { + continue; + } + + for (ExpansionSet.SetCardInfo card : set.getSetCardInfo()) { + MtgJsonCard jsonCard = MtgJsonService.cardFromSet(set.getCode(), card.getName(), card.getCardNumber()); + if (jsonCard == null) { + continue; + } + + // CHECK: poster promoType and/or textless must use full art setting + if (((jsonCard.promoTypes != null && jsonCard.promoTypes.contains("poster")) || jsonCard.isTextless) && !card.isFullArt()) { + errorsList.add("Error: card must use full art setting: " + + set.getCode() + " - " + set.getName() + " - " + card.getName() + " - " + card.getCardNumber()); + } + + // CHECK: full art lands must use full art setting + boolean isLand = card.getRarity().equals(Rarity.LAND); + if (isLand && jsonCard.isFullArt && !card.isFullArt()) { + errorsList.add("Error: card must use full art lands setting: " + + set.getCode() + " - " + set.getName() + " - " + card.getName() + " - " + card.getCardNumber()); + } + + // CHECK: non-full art lands must not use full art setting + if (isLand && !jsonCard.isFullArt && card.isFullArt()) { + errorsList.add("Error: card must NOT use full art lands setting: " + + set.getCode() + " - " + set.getName() + " - " + card.getName() + " - " + card.getCardNumber()); + } + + // CHECK: must use retro frame setting + if ((jsonCard.frameVersion.equals("1993") || jsonCard.frameVersion.equals("1997")) && !card.isRetroFrame()) { + errorsList.add("Error: card must use retro art setting: " + + set.getCode() + " - " + set.getName() + " - " + card.getName() + " - " + card.getCardNumber()); + } + + // CHECK: must not use retro frame setting + if ((!(jsonCard.frameVersion.equals("1993") || jsonCard.frameVersion.equals("1997"))) && card.isRetroFrame()) { + errorsList.add("Error: card must NOT use retro art setting: " + + set.getCode() + " - " + set.getName() + " - " + card.getName() + " - " + card.getCardNumber()); + } + } + } + + printMessages(errorsList); + if (errorsList.size() > 0) { + Assert.fail("Found wrong cards data in sets, errors: " + errorsList.size()); + } + } + @Test @Ignore // TODO: enable after all missing cards and settings fixes public void test_checkMissingScryfallSettingsAndCardNumbers() { diff --git a/Mage/src/main/java/mage/MageIdentifier.java b/Mage/src/main/java/mage/MageIdentifier.java index 3cca48c6e40..08224c3a446 100644 --- a/Mage/src/main/java/mage/MageIdentifier.java +++ b/Mage/src/main/java/mage/MageIdentifier.java @@ -38,6 +38,7 @@ public enum MageIdentifier { CoramTheUndertakerWatcher, ThundermanDragonWatcher, LockeTreasureHunterWatcher, + TheFourthDoctorWatcher, // ----------------------------// // alternate casts // @@ -85,7 +86,10 @@ public enum MageIdentifier { WickerfolkIndomitableAlternateCast, UriangerAugureltAlternateCast, ValgavothTerrorEaterAlternateCast, - LightstallInquisitorAlternateCast; + LightstallInquisitorAlternateCast, + UndeadSprinterAlternateCast, + GwenomRemorselessAlternateCast, + AlienSymbiosisAlternateCast; /** * Additional text if there is need to differentiate two very similar effects diff --git a/Mage/src/main/java/mage/MageObjectReference.java b/Mage/src/main/java/mage/MageObjectReference.java index dd18a605c13..6fe3eff9337 100644 --- a/Mage/src/main/java/mage/MageObjectReference.java +++ b/Mage/src/main/java/mage/MageObjectReference.java @@ -65,7 +65,7 @@ public class MageObjectReference implements Comparable, Ser @Deprecated // cause of many bugs, see issue #10479 public MageObjectReference(Ability source, int modifier) { this.sourceId = source.getSourceId(); - this.zoneChangeCounter = source.getSourceObjectZoneChangeCounter() + modifier; + this.zoneChangeCounter = source.getStackMomentSourceZCC() + modifier; } /** @@ -162,8 +162,8 @@ public class MageObjectReference implements Comparable, Ser if (source == null || !source.getSourceId().equals(sourceId)) { return false; } - return zoneChangeCounter * source.getSourceObjectZoneChangeCounter() == 0 - || zoneChangeCounter == source.getSourceObjectZoneChangeCounter(); + return zoneChangeCounter * source.getStackMomentSourceZCC() == 0 + || zoneChangeCounter == source.getStackMomentSourceZCC(); } public Permanent getPermanent(Game game) { diff --git a/Mage/src/main/java/mage/abilities/Ability.java b/Mage/src/main/java/mage/abilities/Ability.java index a834cf8be68..c05feea8548 100644 --- a/Mage/src/main/java/mage/abilities/Ability.java +++ b/Mage/src/main/java/mage/abilities/Ability.java @@ -516,11 +516,12 @@ public interface Ability extends Controllable, Serializable { */ void initSourceObjectZoneChangeCounter(Game game, boolean force); - // TODO: it's activating time of ability, not current object's zcc, see #13737, - // in most use cases you must use game.getState().getZoneChangeCounter or input.getObject().getZoneChangeCounter(game) - // only ability related logic can use it (example: delayed triggers) - @Deprecated - int getSourceObjectZoneChangeCounter(); + /** + * Returns the internally stored Source Object ZCC value, which is set at the time this ability was put on the stack. + * For static abilities or trigger conditions, you probably want to use + * game.getState().getZoneChangeCounter or input.getObject().getZoneChangeCounter(game) instead + */ + int getStackMomentSourceZCC(); /** * Finds the source object (Permanent, StackObject, Card, etc.) as long as its zcc has not changed, otherwise null diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index 479fe01ef6a..afe43785d56 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -1658,8 +1658,8 @@ public abstract class AbilityImpl implements Ability { @Override public MageObject getSourceObjectIfItStillExists(Game game) { - if (getSourceObjectZoneChangeCounter() == 0 - || getSourceObjectZoneChangeCounter() == getCurrentSourceObjectZoneChangeCounter(game)) { + if (getStackMomentSourceZCC() == 0 + || getStackMomentSourceZCC() == getCurrentSourceObjectZoneChangeCounter(game)) { // exists or lki from battlefield return game.getObject(getSourceId()); } @@ -1688,7 +1688,7 @@ public abstract class AbilityImpl implements Ability { public Permanent getSourcePermanentOrLKI(Game game) { Permanent permanent = getSourcePermanentIfItStillExists(game); if (permanent == null) { - permanent = (Permanent) game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD, getSourceObjectZoneChangeCounter()); + permanent = (Permanent) game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD, getStackMomentSourceZCC()); } return permanent; } @@ -1720,7 +1720,7 @@ public abstract class AbilityImpl implements Ability { } @Override - public int getSourceObjectZoneChangeCounter() { + public int getStackMomentSourceZCC() { return sourceObjectZoneChangeCounter; } diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbilities.java b/Mage/src/main/java/mage/abilities/TriggeredAbilities.java index bb683bce3ea..1e745ea6294 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbilities.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbilities.java @@ -241,7 +241,7 @@ public class TriggeredAbilities extends LinkedHashMap && game.getLKI().get(Zone.BATTLEFIELD) != null && game.getLKI().get(Zone.BATTLEFIELD).containsKey(ability.getSourceId())) { // need to check if object was face down for dies and destroy events because the ability triggers in the new zone, zone counter -1 is used - Permanent permanent = (Permanent) game.getLastKnownInformation(ability.getSourceId(), Zone.BATTLEFIELD, ability.getSourceObjectZoneChangeCounter() - 1); + Permanent permanent = (Permanent) game.getLastKnownInformation(ability.getSourceId(), Zone.BATTLEFIELD, ability.getStackMomentSourceZCC() - 1); if (permanent != null) { if (permanent.isFaceDown(game) && !isGainedAbility(ability, permanent) // the face down creature got the ability from an effect => so it should work diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java b/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java index b3e20a2d92b..171669368c5 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java @@ -333,7 +333,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge } } if (replaceRuleText && triggerPhrase != null) { - superRule = superRule.replaceFirst("^((?:you may )?sacrifice |(put|remove) [^ ]+ [^ ]+ counters? (on|from) |return |transform |untap |regenerate |attach )?\\{this\\}", "$1it"); + superRule = superRule.replaceFirst("^((?:you may )?sacrifice |(put|remove) [^ ]+ [^ ]+ counters? (on|from) |return |transform |untap |regenerate |attach |exile )?\\{this\\}", "$1it"); } sb.append(superRule); if (triggerLimitEachTurn != Integer.MAX_VALUE) { diff --git a/Mage/src/main/java/mage/abilities/common/DrawNthOrNthCardTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DrawNthOrNthCardTriggeredAbility.java new file mode 100644 index 00000000000..84f4f210934 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/DrawNthOrNthCardTriggeredAbility.java @@ -0,0 +1,151 @@ +package mage.abilities.common; + +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.dynamicvalue.common.CardsDrawnThisTurnDynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.constants.TargetController; +import mage.constants.WatcherScope; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.util.CardUtil; +import mage.watchers.Watcher; + +import java.util.*; + +/** + * @author TheElk801 + */ +public class DrawNthOrNthCardTriggeredAbility extends TriggeredAbilityImpl { + + private static final Hint hint = new ValueHint( + "Cards drawn this turn", CardsDrawnThisTurnDynamicValue.instance + ); + private final TargetController targetController; + private final int firstCardNumber; + private final int secondCardNumber; + + public DrawNthOrNthCardTriggeredAbility(Effect effect) { + this(effect, false); + } + + public DrawNthOrNthCardTriggeredAbility(Effect effect, boolean optional) { + this(effect, optional, 1); + } + + public DrawNthOrNthCardTriggeredAbility(Effect effect, boolean optional, int firstCardNumber) { + this(effect, optional, TargetController.YOU, firstCardNumber); + } + + public DrawNthOrNthCardTriggeredAbility(Effect effect, boolean optional, TargetController targetController, int firstCardNumber) { + this(Zone.BATTLEFIELD, effect, optional, targetController, firstCardNumber, 2); + } + + public DrawNthOrNthCardTriggeredAbility(Zone zone, Effect effect, boolean optional, TargetController targetController, int firstCardNumber, int secondCardNumber) { + super(zone, effect, optional); + this.targetController = targetController; + this.firstCardNumber = firstCardNumber; + this.secondCardNumber = secondCardNumber; + if (targetController == TargetController.YOU) { + this.addHint(hint); + } + setTriggerPhrase(generateTriggerPhrase()); + this.addWatcher(new DrawNthOrNthCardWatcher()); + } + + protected DrawNthOrNthCardTriggeredAbility(final DrawNthOrNthCardTriggeredAbility ability) { + super(ability); + this.targetController = ability.targetController; + this.firstCardNumber = ability.firstCardNumber; + this.secondCardNumber = ability.secondCardNumber; + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DREW_CARD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + switch (targetController) { + case YOU: + if (!isControlledBy(event.getPlayerId())) { + return false; + } + break; + case ACTIVE: + if (!game.isActivePlayer(event.getPlayerId())) { + return false; + } + break; + case OPPONENT: + if (!game.getOpponents(getControllerId()).contains(event.getPlayerId())) { + return false; + } + break; + case ANY: + // Doesn't matter who + break; + default: + throw new IllegalArgumentException("TargetController " + targetController + " not supported"); + } + int drawnCards = DrawNthOrNthCardWatcher.checkEvent(event.getPlayerId(), event.getId(), game) + 1; + return drawnCards == firstCardNumber || drawnCards == secondCardNumber; + } + + public String generateTriggerPhrase() { + String numberText = CardUtil.numberToOrdinalText(firstCardNumber) + " or " + CardUtil.numberToOrdinalText(secondCardNumber); + switch (targetController) { + case YOU: + return "Whenever you draw your " + numberText + " card each turn, "; + case ACTIVE: + return "Whenever a player draws their " + numberText + " card during their turn, "; + case OPPONENT: + return "Whenever an opponent draws their " + numberText + " card each turn, "; + case ANY: + return "Whenever a player draws their " + numberText + " card each turn, "; + default: + throw new IllegalArgumentException("TargetController " + targetController + " not supported"); + } + } + + @Override + public DrawNthOrNthCardTriggeredAbility copy() { + return new DrawNthOrNthCardTriggeredAbility(this); + } +} + +class DrawNthOrNthCardWatcher extends Watcher { + + private final Map> playerDrawEventMap = new HashMap<>(); + + DrawNthOrNthCardWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.DREW_CARD) { + playerDrawEventMap + .computeIfAbsent(event.getPlayerId(), x -> new ArrayList<>()) + .add(event.getId()); + } + } + + @Override + public void reset() { + super.reset(); + playerDrawEventMap.clear(); + } + + static int checkEvent(UUID playerId, UUID eventId, Game game) { + return game + .getState() + .getWatcher(DrawNthOrNthCardWatcher.class) + .playerDrawEventMap + .getOrDefault(playerId, Collections.emptyList()) + .indexOf(eventId); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAbility.java b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAbility.java index 231e5dd9a1d..e15928b8f81 100644 --- a/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAbility.java +++ b/Mage/src/main/java/mage/abilities/common/EntersBattlefieldAbility.java @@ -77,7 +77,7 @@ public class EntersBattlefieldAbility extends StaticAbility { @Override public String getRule() { if (abilityRule != null && !abilityRule.isEmpty()) { - return abilityRule; + return addRulePrefix(abilityRule); } String superRule = super.getRule(); String rule = (optional ? "you may have " : "") + "{this} enter" + (optional ? "" : "s") diff --git a/Mage/src/main/java/mage/abilities/common/EscapesWithAbility.java b/Mage/src/main/java/mage/abilities/common/EscapesWithAbility.java index 095d277702a..6ac1bdcefb7 100644 --- a/Mage/src/main/java/mage/abilities/common/EscapesWithAbility.java +++ b/Mage/src/main/java/mage/abilities/common/EscapesWithAbility.java @@ -100,7 +100,7 @@ class EscapesWithEffect extends OneShotEffect { SpellAbility spellAbility = (SpellAbility) getValue(EntersBattlefieldEffect.SOURCE_CAST_SPELL_ABILITY); if (!(spellAbility instanceof EscapeAbility) || !spellAbility.getSourceId().equals(source.getSourceId()) - || permanent.getZoneChangeCounter(game) != spellAbility.getSourceObjectZoneChangeCounter()) { + || permanent.getZoneChangeCounter(game) != spellAbility.getStackMomentSourceZCC()) { return false; } diff --git a/Mage/src/main/java/mage/abilities/common/PayMoreToCastAsThoughtItHadFlashAbility.java b/Mage/src/main/java/mage/abilities/common/PayMoreToCastAsThoughtItHadFlashAbility.java index f27112aba7f..9da52a09526 100644 --- a/Mage/src/main/java/mage/abilities/common/PayMoreToCastAsThoughtItHadFlashAbility.java +++ b/Mage/src/main/java/mage/abilities/common/PayMoreToCastAsThoughtItHadFlashAbility.java @@ -59,9 +59,9 @@ public class PayMoreToCastAsThoughtItHadFlashAbility extends SpellAbility { return rule; } if (costsToAdd instanceof ManaCosts) { - return "You may cast {this} as though it had flash if you pay " + costsToAdd.getText() + " more to cast it. (You may cast it any time you could cast an instant.)"; + return "You may cast this spell as though it had flash if you pay " + costsToAdd.getText() + " more to cast it. (You may cast it any time you could cast an instant.)"; } else { - return "You may cast {this} as though it had flash by " + costsToAdd.getText() + " in addition to paying its other costs."; + return "You may cast this spell as though it had flash by " + costsToAdd.getText() + " in addition to paying its other costs."; } } diff --git a/Mage/src/main/java/mage/abilities/common/WhileSearchingPlayFromLibraryAbility.java b/Mage/src/main/java/mage/abilities/common/WhileSearchingPlayFromLibraryAbility.java index 193f731548c..7cdf9572337 100644 --- a/Mage/src/main/java/mage/abilities/common/WhileSearchingPlayFromLibraryAbility.java +++ b/Mage/src/main/java/mage/abilities/common/WhileSearchingPlayFromLibraryAbility.java @@ -30,7 +30,7 @@ public class WhileSearchingPlayFromLibraryAbility extends StaticAbility implemen @Override public String getRule() { - return "While you're searching your library, you may cast {this} from your library."; + return "While you're searching your library, you may cast this card from your library."; } @Override diff --git a/Mage/src/main/java/mage/abilities/common/delayed/AddCounterNextSpellDelayedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/delayed/AddCounterNextSpellDelayedTriggeredAbility.java index f06e2b3eb62..bf9f8c9db26 100644 --- a/Mage/src/main/java/mage/abilities/common/delayed/AddCounterNextSpellDelayedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/delayed/AddCounterNextSpellDelayedTriggeredAbility.java @@ -1,7 +1,6 @@ package mage.abilities.common.delayed; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.effects.ReplacementEffectImpl; import mage.constants.Duration; import mage.constants.Outcome; @@ -18,9 +17,7 @@ import mage.util.CardUtil; /** * @author TheElk801 */ -public class AddCounterNextSpellDelayedTriggeredAbility extends DelayedTriggeredAbility { - - private final FilterSpell filter; +public class AddCounterNextSpellDelayedTriggeredAbility extends CastNextSpellDelayedTriggeredAbility { public AddCounterNextSpellDelayedTriggeredAbility() { this(StaticFilters.FILTER_SPELL_A_CREATURE); @@ -31,38 +28,17 @@ public class AddCounterNextSpellDelayedTriggeredAbility extends DelayedTriggered } public AddCounterNextSpellDelayedTriggeredAbility(int amount, FilterSpell filter) { - super(new AddCounterNextSpellEffect(amount), Duration.EndOfTurn, true, false); - this.filter = filter; - this.setTriggerPhrase("When you next cast " + filter.getMessage() + " this turn, "); + super(new AddCounterNextSpellEffect(amount), filter, false); } private AddCounterNextSpellDelayedTriggeredAbility(final AddCounterNextSpellDelayedTriggeredAbility ability) { super(ability); - this.filter = ability.filter; } @Override public AddCounterNextSpellDelayedTriggeredAbility copy() { return new AddCounterNextSpellDelayedTriggeredAbility(this); } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.SPELL_CAST; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (!isControlledBy(event.getPlayerId())) { - return false; - } - Spell spell = game.getSpell(event.getTargetId()); - if (spell == null || !filter.match(spell, getControllerId(), this, game)) { - return false; - } - this.getEffects().setValue("spellCast", spell); - return true; - } } class AddCounterNextSpellEffect extends ReplacementEffectImpl { @@ -72,7 +48,8 @@ class AddCounterNextSpellEffect extends ReplacementEffectImpl { AddCounterNextSpellEffect(int amount) { super(Duration.EndOfStep, Outcome.BoostCreature); this.amount = amount; - staticText = "that creature enters with " + CardUtil.numberToText(amount, "an") + " additional +1/+1 counter" + (amount > 1 ? "s" : "") + " on it"; + staticText = "that creature enters with " + CardUtil.numberToText(amount, "an") + + " additional +1/+1 counter" + (amount > 1 ? "s" : "") + " on it"; } private AddCounterNextSpellEffect(AddCounterNextSpellEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/common/delayed/CastNextSpellDelayedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/delayed/CastNextSpellDelayedTriggeredAbility.java new file mode 100644 index 00000000000..e50cc9cd733 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/delayed/CastNextSpellDelayedTriggeredAbility.java @@ -0,0 +1,59 @@ +package mage.abilities.common.delayed; + +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.constants.Duration; +import mage.filter.FilterSpell; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.stack.Spell; +import mage.target.targetpointer.FixedTarget; + +/** + * @author TheElk801 + */ +public class CastNextSpellDelayedTriggeredAbility extends DelayedTriggeredAbility { + + private final FilterSpell filter; + private final boolean setTargetPointer; + + public CastNextSpellDelayedTriggeredAbility(Effect effect, FilterSpell filter, boolean setTargetPointer) { + super(effect, Duration.EndOfTurn, true, false); + this.filter = filter; + this.setTriggerPhrase("When you next cast " + filter.getMessage() + " this turn, "); + this.setTargetPointer = setTargetPointer; + } + + protected CastNextSpellDelayedTriggeredAbility(final CastNextSpellDelayedTriggeredAbility ability) { + super(ability); + this.filter = ability.filter; + this.setTargetPointer = ability.setTargetPointer; + } + + @Override + public CastNextSpellDelayedTriggeredAbility copy() { + return new CastNextSpellDelayedTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (!isControlledBy(event.getPlayerId())) { + return false; + } + Spell spell = game.getSpell(event.getTargetId()); + if (spell == null || !filter.match(spell, getControllerId(), this, game)) { + return false; + } + this.getEffects().setValue("spellCast", spell); + if (setTargetPointer) { + this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId())); + } + return true; + } + +} diff --git a/Mage/src/main/java/mage/abilities/common/delayed/CopyNextSpellDelayedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/delayed/CopyNextSpellDelayedTriggeredAbility.java index 0029c04e488..569de98f0d4 100644 --- a/Mage/src/main/java/mage/abilities/common/delayed/CopyNextSpellDelayedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/delayed/CopyNextSpellDelayedTriggeredAbility.java @@ -1,75 +1,32 @@ package mage.abilities.common.delayed; -import mage.abilities.DelayedTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.CopyTargetStackObjectEffect; -import mage.constants.Duration; import mage.filter.FilterSpell; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.stack.Spell; -import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; /** * @author TheElk801 */ -public class CopyNextSpellDelayedTriggeredAbility extends DelayedTriggeredAbility { - - private final FilterSpell filter; - private final String rule; +public class CopyNextSpellDelayedTriggeredAbility extends CastNextSpellDelayedTriggeredAbility { public CopyNextSpellDelayedTriggeredAbility() { - this(StaticFilters.FILTER_SPELL_INSTANT_OR_SORCERY); + this(StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY); } public CopyNextSpellDelayedTriggeredAbility(FilterSpell filter) { - this(filter, new CopyTargetStackObjectEffect(true), null); + this(filter, 1); } - public CopyNextSpellDelayedTriggeredAbility(FilterSpell filter, Effect effect, String rule) { - super(effect, Duration.EndOfTurn); - this.filter = filter; - this.rule = rule; + public CopyNextSpellDelayedTriggeredAbility(FilterSpell filter, int amount) { + super(new CopyTargetStackObjectEffect(false, true, true, amount, null), filter, true); } protected CopyNextSpellDelayedTriggeredAbility(final CopyNextSpellDelayedTriggeredAbility ability) { super(ability); - this.filter = ability.filter; - this.rule = ability.rule; } @Override public CopyNextSpellDelayedTriggeredAbility copy() { return new CopyNextSpellDelayedTriggeredAbility(this); } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.SPELL_CAST; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (!isControlledBy(event.getPlayerId())) { - return false; - } - Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell == null || !filter.match(spell, getControllerId(), this, game)) { - return false; - } - this.getEffects().setValue("spellCast", spell); - this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId())); - return true; - } - - @Override - public String getRule() { - if (rule != null && !rule.isEmpty()) { - return rule; - } - return "When you next cast " + CardUtil.addArticle(filter.getMessage()) + " this turn, " - + "copy that spell. You may choose new targets for the copy."; - } } 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 33afa3d86df..c1a47c44bc5 100644 --- a/Mage/src/main/java/mage/abilities/common/delayed/OnLeaveReturnExiledAbility.java +++ b/Mage/src/main/java/mage/abilities/common/delayed/OnLeaveReturnExiledAbility.java @@ -103,7 +103,7 @@ class ReturnExiledPermanentsEffect extends OneShotEffect { } private ExileZone getExileIfPossible(final Game game, final Ability source) { - UUID exileZone = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileZone = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); if (exileZone != null) { ExileZone exile = game.getExile().getExileZone(exileZone); diff --git a/Mage/src/main/java/mage/abilities/common/delayed/ReflexiveTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/delayed/ReflexiveTriggeredAbility.java index 621a1ab677e..2c4cc9afc07 100644 --- a/Mage/src/main/java/mage/abilities/common/delayed/ReflexiveTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/delayed/ReflexiveTriggeredAbility.java @@ -8,8 +8,6 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.util.CardUtil; -import java.util.Locale; - /** * @author TheElk801 */ diff --git a/Mage/src/main/java/mage/abilities/common/delayed/UntilYourNextTurnDelayedTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/delayed/UntilYourNextTurnDelayedTriggeredAbility.java index 86c873bdf00..cf51f1f5b3f 100644 --- a/Mage/src/main/java/mage/abilities/common/delayed/UntilYourNextTurnDelayedTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/delayed/UntilYourNextTurnDelayedTriggeredAbility.java @@ -111,8 +111,8 @@ public class UntilYourNextTurnDelayedTriggeredAbility extends DelayedTriggeredAb } @Override - public int getSourceObjectZoneChangeCounter() { - return ability.getSourceObjectZoneChangeCounter(); + public int getStackMomentSourceZCC() { + return ability.getStackMomentSourceZCC(); } @Override diff --git a/Mage/src/main/java/mage/abilities/condition/common/CastAnotherSpellThisTurnCondition.java b/Mage/src/main/java/mage/abilities/condition/common/CastAnotherSpellThisTurnCondition.java index 10ee9bdd27f..d2368d42dba 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/CastAnotherSpellThisTurnCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/CastAnotherSpellThisTurnCondition.java @@ -30,7 +30,7 @@ public enum CastAnotherSpellThisTurnCondition implements Condition { return spells != null && spells .stream() .filter(Objects::nonNull) - .anyMatch(spell -> !spell.getSourceId().equals(source.getSourceId()) || spell.getZoneChangeCounter(game) != source.getSourceObjectZoneChangeCounter()); + .anyMatch(spell -> !spell.getSourceId().equals(source.getSourceId()) || spell.getZoneChangeCounter(game) != source.getStackMomentSourceZCC()); } public Hint getHint() { diff --git a/Mage/src/main/java/mage/abilities/condition/common/SourceDealtDamageCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceDealtDamageCondition.java index 5512e058588..694e7bb5d06 100644 --- a/Mage/src/main/java/mage/abilities/condition/common/SourceDealtDamageCondition.java +++ b/Mage/src/main/java/mage/abilities/condition/common/SourceDealtDamageCondition.java @@ -22,7 +22,7 @@ public class SourceDealtDamageCondition implements Condition { @Override public boolean apply(Game game, Ability source) { DamageDoneWatcher watcher = game.getState().getWatcher(DamageDoneWatcher.class); - return watcher != null && watcher.damageDoneBy(source.getSourceId(), source.getSourceObjectZoneChangeCounter(), game) >= value; + return watcher != null && watcher.damageDoneBy(source.getSourceId(), source.getStackMomentSourceZCC(), game) >= value; } @Override diff --git a/Mage/src/main/java/mage/abilities/condition/common/SourceModifiedCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceModifiedCondition.java new file mode 100644 index 00000000000..e030d7052ea --- /dev/null +++ b/Mage/src/main/java/mage/abilities/condition/common/SourceModifiedCondition.java @@ -0,0 +1,28 @@ +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.filter.predicate.permanent.ModifiedPredicate; +import mage.game.Game; + +import java.util.Optional; + +/** + * @author TheElk801 + */ +public enum SourceModifiedCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + return Optional + .ofNullable(source.getSourcePermanentIfItStillExists(game)) + .filter(permanent -> ModifiedPredicate.instance.apply(permanent, game)) + .isPresent(); + } + + @Override + public String toString() { + return "it's modified"; + } +} diff --git a/Mage/src/main/java/mage/abilities/condition/common/WebSlingingCondition.java b/Mage/src/main/java/mage/abilities/condition/common/WebSlingingCondition.java new file mode 100644 index 00000000000..e47a5184363 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/condition/common/WebSlingingCondition.java @@ -0,0 +1,30 @@ +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.abilities.keyword.WebSlingingAbility; +import mage.game.Game; +import mage.util.CardUtil; + +/** + * @author TheElk801 + */ +public enum WebSlingingCondition implements Condition { + THEY("they were"), + THIS("{this}"); + private final String message; + + WebSlingingCondition(String message) { + this.message = message; + } + + @Override + public boolean apply(Game game, Ability source) { + return CardUtil.checkSourceCostsTagExists(game, source, WebSlingingAbility.WEB_SLINGING_ACTIVATION_VALUE_KEY); + } + + @Override + public String toString() { + return message + " cast using web-slinging"; + } +} diff --git a/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java b/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java index 62dd2fade0a..783461fe4fc 100644 --- a/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java +++ b/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java @@ -203,7 +203,7 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter } private String getActivatedKey(Ability source) { - return getActivatedKey(this.getOriginalId(), source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + return getActivatedKey(this.getOriginalId(), source.getSourceId(), source.getStackMomentSourceZCC()); } private static String getActivatedKey(UUID alternativeCostOriginalId, UUID sourceId, int sourceZCC) { @@ -217,7 +217,7 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter } private String getDynamicCostActivatedKey(Ability source) { - return getDynamicCostActivatedKey(this.getOriginalId(), source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + return getDynamicCostActivatedKey(this.getOriginalId(), source.getSourceId(), source.getStackMomentSourceZCC()); } private static String getDynamicCostActivatedKey(UUID alternativeCostOriginalId, UUID sourceId, int sourceZCC) { @@ -243,7 +243,7 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter String key = getActivatedKey( alternativeCostOriginalId, source.getSourceId(), - source.getSourceObjectZoneChangeCounter() + (searchPrevZCC ? -1 : 0) + source.getStackMomentSourceZCC() + (searchPrevZCC ? -1 : 0) ); Boolean status = (Boolean) game.getState().getValue(key); return status != null && status; @@ -284,7 +284,8 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter } StringBuilder sb = new StringBuilder(); if (condition != null) { - sb.append(condition.toString()); + sb.append("if "); + sb.append(condition); if (alternateCosts.size() > 1) { sb.append(", rather than pay this spell's mana cost, "); } else { @@ -293,8 +294,6 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter } else { sb.append("You may "); } - int numberCosts = 0; - String remarkText = ""; sb.append(CardUtil.concatWithAnd(alternateCosts .stream() .map(cost -> cost.getCost() instanceof ManaCost @@ -308,9 +307,6 @@ public class AlternativeCostSourceAbility extends StaticAbility implements Alter sb.append("cast this spell without paying its mana cost"); } sb.append('.'); - if (numberCosts == 1 && remarkText != null) { - sb.append(' ').append(remarkText); - } return sb.toString(); } diff --git a/Mage/src/main/java/mage/abilities/costs/common/PutSourceOnBottomOwnerLibraryCost.java b/Mage/src/main/java/mage/abilities/costs/common/PutSourceOnBottomOwnerLibraryCost.java index 417b1f0e296..00a6689d5fa 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/PutSourceOnBottomOwnerLibraryCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/PutSourceOnBottomOwnerLibraryCost.java @@ -1,7 +1,6 @@ package mage.abilities.costs.common; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.costs.Cost; import mage.abilities.costs.CostImpl; @@ -10,6 +9,8 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import java.util.UUID; + /** * * @author LevelX2 @@ -27,7 +28,7 @@ public class PutSourceOnBottomOwnerLibraryCost extends CostImpl { @Override public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { Player player = game.getPlayer(controllerId); - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); + Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); if (player != null && sourcePermanent != null) { paid = true; player.putCardsOnBottomOfLibrary(new CardsImpl(sourcePermanent), game, ability, false); @@ -37,7 +38,7 @@ public class PutSourceOnBottomOwnerLibraryCost extends CostImpl { @Override public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { - return game.getPermanent(source.getSourceId()) != null; + return game.getPermanentOrLKIBattlefield(source.getSourceId()) != null; } @Override diff --git a/Mage/src/main/java/mage/abilities/costs/common/RemoveCountersSourceCost.java b/Mage/src/main/java/mage/abilities/costs/common/RemoveCountersSourceCost.java index 5fc6f56077c..2c5b8ac5552 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/RemoveCountersSourceCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/RemoveCountersSourceCost.java @@ -3,19 +3,15 @@ package mage.abilities.costs.common; import mage.abilities.Ability; import mage.abilities.costs.Cost; import mage.abilities.costs.CostImpl; -import mage.choices.Choice; -import mage.choices.ChoiceImpl; +import mage.constants.MultiAmountType; import mage.constants.Outcome; import mage.counters.Counter; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.util.RandomUtil; +import mage.util.CardUtil; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.UUID; +import java.util.*; /** * @author BetaSteward_at_googlemail.com @@ -31,6 +27,12 @@ public class RemoveCountersSourceCost extends CostImpl { this.text = "remove a counter from {this}"; } + public RemoveCountersSourceCost(int amount) { + this.amount = amount; + this.name = ""; + this.text = "remove " + CardUtil.numberToText(amount) + " counters from {this}"; + } + public RemoveCountersSourceCost(Counter counter) { this.amount = counter.getCount(); this.name = counter.getName(); @@ -56,8 +58,8 @@ public class RemoveCountersSourceCost extends CostImpl { .getCounters(game) .values() .stream() - .map(Counter::getCount) - .anyMatch(i -> i >= amount); + .mapToInt(Counter::getCount) + .sum() >= amount; } else { // specific counter return permanent.getCounters(game).getCount(name) >= amount; @@ -71,36 +73,22 @@ public class RemoveCountersSourceCost extends CostImpl { if (player == null || permanent == null) { return paid; } - String toRemove; if (name.isEmpty()) { - Set toChoose = new LinkedHashSet<>(permanent.getCounters(game).keySet()); - switch (toChoose.size()) { - case 0: - return paid; - case 1: - toRemove = RandomUtil.randomFromCollection(toChoose); - break; - case 2: - Iterator iterator = toChoose.iterator(); - String choice1 = iterator.next(); - String choice2 = iterator.next(); - toRemove = player.chooseUse( - Outcome.UnboostCreature, "Choose a type of counter to remove", - null, choice1, choice2, source, game - ) ? choice1 : choice2; - break; - default: - Choice choice = new ChoiceImpl(true); - choice.setChoices(toChoose); - choice.setMessage("Choose a type of counter to remove"); - player.choose(Outcome.UnboostCreature, choice, game); - toRemove = choice.getChoice(); + List toChoose = new ArrayList<>(permanent.getCounters(game).keySet()); + if (toChoose.isEmpty()) { + return paid; + } else { + List counterList = player.getMultiAmount(Outcome.UnboostCreature, toChoose, 0, amount, amount, MultiAmountType.REMOVE_COUNTERS, game); + for (int i = 0; i < toChoose.size(); i++) { + int amountToRemove = counterList.get(i); + if (amountToRemove > 0) { + permanent.removeCounters(toChoose.get(i), amountToRemove, source, game); + } + } + paid = true; } - } else { - toRemove = name; - } - if (permanent.getCounters(game).getCount(toRemove) >= amount) { - permanent.removeCounters(toRemove, amount, source, game); + } else if (permanent.getCounters(game).getCount(name) >= amount){ + permanent.removeCounters(name, amount, source, game); this.paid = true; } return paid; diff --git a/Mage/src/main/java/mage/abilities/costs/common/SacrificeAttachmentCost.java b/Mage/src/main/java/mage/abilities/costs/common/SacrificeAttachmentCost.java index 1764caf2bba..81ff57ddd63 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/SacrificeAttachmentCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/SacrificeAttachmentCost.java @@ -6,6 +6,7 @@ import mage.abilities.costs.SacrificeCost; import mage.abilities.costs.UseAttachedCost; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; import java.util.UUID; @@ -52,7 +53,12 @@ public class SacrificeAttachmentCost extends UseAttachedCost implements Sacrific if (!super.canPay(ability, source, controllerId, game)) { return false; } - return game.getPermanent(source.getSourceId()).canBeSacrificed(); + Player controller = game.getPlayer(controllerId); + Permanent permanent = mageObjectReference.getPermanent(game); + if (controller == null || permanent == null) { + return false; + } + return controller.canPaySacrificeCost(permanent, source, controllerId, game); } @Override diff --git a/Mage/src/main/java/mage/abilities/costs/common/SacrificeXTargetCost.java b/Mage/src/main/java/mage/abilities/costs/common/SacrificeXTargetCost.java index 4a3ae6f865f..804db8e45ac 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/SacrificeXTargetCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/SacrificeXTargetCost.java @@ -7,8 +7,12 @@ import mage.abilities.costs.VariableCostImpl; import mage.abilities.costs.VariableCostType; import mage.filter.FilterPermanent; import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.common.TargetSacrifice; +import java.util.UUID; + /** * @author LevelX2 */ @@ -39,6 +43,26 @@ public class SacrificeXTargetCost extends VariableCostImpl implements SacrificeC this.minValue = cost.minValue; } + @Override + public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { + int canSacAmount = getValidSacAmount(source, controllerId, game); + return canSacAmount >= minValue; + } + + private int getValidSacAmount(Ability source, UUID controllerId, Game game) { + Player controller = game.getPlayer(controllerId); + if (controller == null) { + return -1; + } + int canSacAmount = 0; + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, controllerId, game)) { + if (controller.canPaySacrificeCost(permanent, source, controllerId, game)) { + canSacAmount++; + } + } + return canSacAmount; + } + @Override public SacrificeXTargetCost copy() { return new SacrificeXTargetCost(this); @@ -51,7 +75,7 @@ public class SacrificeXTargetCost extends VariableCostImpl implements SacrificeC @Override public int getMaxValue(Ability source, Game game) { - return game.getBattlefield().count(filter, source.getControllerId(), source, game); + return getValidSacAmount(source, source.getControllerId(), game); } @Override diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttachedPermanentPowerCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttachedPermanentPowerCount.java index 17a38a50c19..4550bc49500 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttachedPermanentPowerCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttachedPermanentPowerCount.java @@ -18,7 +18,7 @@ public enum AttachedPermanentPowerCount implements DynamicValue { public int calculate(Game game, Ability sourceAbility, Effect effect) { Permanent attachmentPermanent = game.getPermanent(sourceAbility.getSourceId()); if (attachmentPermanent == null) { - attachmentPermanent = (Permanent) game.getLastKnownInformation(sourceAbility.getSourceId(), Zone.BATTLEFIELD, sourceAbility.getSourceObjectZoneChangeCounter()); + attachmentPermanent = (Permanent) game.getLastKnownInformation(sourceAbility.getSourceId(), Zone.BATTLEFIELD, sourceAbility.getStackMomentSourceZCC()); } if (attachmentPermanent == null || attachmentPermanent.getAttachedTo() == null) { return 0; diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttachedPermanentToughnessCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttachedPermanentToughnessCount.java index 35543b26778..080de1d6c09 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttachedPermanentToughnessCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/AttachedPermanentToughnessCount.java @@ -17,7 +17,7 @@ public enum AttachedPermanentToughnessCount implements DynamicValue { public int calculate(Game game, Ability sourceAbility, Effect effect) { Permanent attachmentPermanent = game.getPermanent(sourceAbility.getSourceId()); if (attachmentPermanent == null) { - attachmentPermanent = (Permanent) game.getLastKnownInformation(sourceAbility.getSourceId(), Zone.BATTLEFIELD, sourceAbility.getSourceObjectZoneChangeCounter()); + attachmentPermanent = (Permanent) game.getLastKnownInformation(sourceAbility.getSourceId(), Zone.BATTLEFIELD, sourceAbility.getStackMomentSourceZCC()); } if (attachmentPermanent == null || attachmentPermanent.getAttachedTo() == null) { return 0; diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardsInExileCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardsInExileCount.java index 2a6b6a1bfd7..0ae8141f665 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardsInExileCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardsInExileCount.java @@ -74,8 +74,8 @@ public enum CardsInExileCount implements DynamicValue { return playerIds.stream() .map(game::getPlayer) .filter(Objects::nonNull) - .map(player -> game.getExile().getAllCards(game, player.getId())) + .map(player -> game.getExile().getCardsOwned(game, player.getId())) .flatMap(Collection::stream) .filter(Objects::nonNull); } -} \ No newline at end of file +} diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/InstantSorceryExileGraveyardCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/InstantSorceryExileGraveyardCount.java index ee7c48b8b20..b8de0842556 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/InstantSorceryExileGraveyardCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/InstantSorceryExileGraveyardCount.java @@ -3,7 +3,6 @@ package mage.abilities.dynamicvalue.common; import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; -import mage.cards.Card; import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; @@ -17,18 +16,11 @@ public enum InstantSorceryExileGraveyardCount implements DynamicValue { @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { Player player = game.getPlayer(sourceAbility.getControllerId()); - if (player != null) { - int exileCount = 0; - for (Card exiledCard : game.getExile().getAllCards(game)) { - if (exiledCard.getOwnerId().equals(player.getId()) && exiledCard.isInstantOrSorcery(game)) { - exileCount++; - } - } - return player.getGraveyard().count( - StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, game - ) + exileCount; + if (player == null) { + return 0; } - return 0; + return game.getExile().getCardsOwned(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, player.getId(), sourceAbility, game).size() + + player.getGraveyard().count(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, player.getId(), sourceAbility, game); } @Override diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/TotalCardsExiledOwnedManaValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/TotalCardsExiledOwnedManaValue.java index 400155b7708..8f70b4b7543 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/TotalCardsExiledOwnedManaValue.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/TotalCardsExiledOwnedManaValue.java @@ -26,7 +26,7 @@ public enum TotalCardsExiledOwnedManaValue implements DynamicValue { @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { int totalCMC = 0; - List cards = game.getExile().getAllCards( + List cards = game.getExile().getCardsOwned( game, sourceAbility.getControllerId() ); diff --git a/Mage/src/main/java/mage/abilities/effects/PreventDamageAndRemoveCountersEffect.java b/Mage/src/main/java/mage/abilities/effects/PreventDamageAndRemoveCountersEffect.java index c9faecee202..d204f04b445 100644 --- a/Mage/src/main/java/mage/abilities/effects/PreventDamageAndRemoveCountersEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/PreventDamageAndRemoveCountersEffect.java @@ -71,7 +71,7 @@ public class PreventDamageAndRemoveCountersEffect extends PreventionEffectImpl { if (permanent == null || watcher == null || damageAmount <= 0) { return false; } - MageObjectReference mor = new MageObjectReference(source.getId(), source.getSourceObjectZoneChangeCounter(), game); + MageObjectReference mor = new MageObjectReference(source.getId(), source.getStackMomentSourceZCC(), game); int beforeCount = permanent.getCounters(game).getCount(CounterType.P1P1); if (thatMany) { // Remove them. @@ -110,7 +110,7 @@ public class PreventDamageAndRemoveCountersEffect extends PreventionEffectImpl { if (whileHasCounter && !permanent.getCounters(game).containsKey(CounterType.P1P1)) { // If the last counter has already be removed for the same batch of prevention, we still want to prevent the damage. PreventDamageAndRemoveCountersWatcher watcher = game.getState().getWatcher(PreventDamageAndRemoveCountersWatcher.class); - MageObjectReference mor = new MageObjectReference(source.getId(), source.getSourceObjectZoneChangeCounter(), game); + MageObjectReference mor = new MageObjectReference(source.getId(), source.getStackMomentSourceZCC(), game); return watcher != null && watcher.hadMORCounterRemovedThisBatch(mor); } return true; @@ -149,4 +149,4 @@ class PreventDamageAndRemoveCountersWatcher extends Watcher { void addMOR(MageObjectReference mor) { morRemovedCounterThisDamageBatch.add(mor); } -} \ No newline at end of file +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/CanBlockAsThoughtItHadShadowEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CanBlockAsThoughtItHadShadowEffect.java index 42dc538fab4..9fba0b76c05 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CanBlockAsThoughtItHadShadowEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CanBlockAsThoughtItHadShadowEffect.java @@ -13,7 +13,7 @@ public class CanBlockAsThoughtItHadShadowEffect extends AsThoughEffectImpl { public CanBlockAsThoughtItHadShadowEffect(Duration duration) { super(AsThoughEffectType.BLOCK_SHADOW, duration, Outcome.Benefit); - staticText = "{this} can block creatures with shadow as though {this} had shadow"; + staticText = "{this} can block creatures with shadow as though it had shadow"; } protected CanBlockAsThoughtItHadShadowEffect(final CanBlockAsThoughtItHadShadowEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java index eaadf209ab4..f464355448e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CopyEffect.java @@ -86,7 +86,7 @@ public class CopyEffect extends ContinuousEffectImpl { return false; } // As long as the permanent is still in the short living LKI continue to copy to get triggered abilities to TriggeredAbilities for dies events. - permanent = (Permanent) game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD, source.getSourceObjectZoneChangeCounter()); + permanent = (Permanent) game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD, source.getStackMomentSourceZCC()); if (permanent == null) { discard(); return false; diff --git a/Mage/src/main/java/mage/abilities/effects/common/CopyTargetStackObjectEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CopyTargetStackObjectEffect.java index b414b90f8b5..cfd72deb1a2 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CopyTargetStackObjectEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CopyTargetStackObjectEffect.java @@ -7,6 +7,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; import mage.game.stack.StackObject; +import mage.util.CardUtil; import mage.util.functions.StackObjectCopyApplier; /** @@ -96,8 +97,10 @@ public class CopyTargetStackObjectEffect extends OneShotEffect { if (staticText != null && !staticText.isEmpty()) { return staticText; } - return "copy " + - getTargetPointer().describeTargets(mode.getTargets(), objectName) + - (chooseTargets ? ". You may choose new targets for the copy" : ""); + String amountText = (amount == 1) ? "" : ((amount == 2) ? " twice" : " " + CardUtil.numberToText(amount) + " times"); + String chooseText = chooseTargets ? ". You may choose new targets for the cop" + ((amount == 1) ? "y" : "ies") : ""; + return "copy " + + getTargetPointer().describeTargets(mode.getTargets(), objectName) + + amountText + chooseText; } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java index 204befa2900..6f02329d0b3 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java @@ -176,8 +176,8 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { } // can target card or permanent - Card copyFrom; - CopyApplier applier = new EmptyCopyApplier(); + Card copyFrom = null; + CopyApplier applier = null; if (permanent != null) { // handle copies of copies Permanent copyFromPermanent = permanent; @@ -196,9 +196,17 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { } } } + // check if permanent was copying, but copy effect is no longer active + if (applier == null) { + if (permanent.isCopy() && permanent.getCopyFrom() instanceof Permanent) { + copyFromPermanent = (Permanent) permanent.getCopyFrom(); + } + applier = new EmptyCopyApplier(); + } copyFrom = copyFromPermanent; } else { copyFrom = game.getCard(getTargetPointer().getFirst(game, source)); + applier = new EmptyCopyApplier(); } if (copyFrom == null) { @@ -406,41 +414,29 @@ public class CreateTokenCopyTargetEffect extends OneShotEffect { } public void sacrificeTokensCreatedAtNextEndStep(Game game, Ability source) { - this.removeTokensCreatedAtEndOf(game, source, PhaseStep.END_TURN, false); + this.removeTokensCreatedAt(game, source, false, PhaseStep.END_TURN, TargetController.ANY); } public void exileTokensCreatedAtNextEndStep(Game game, Ability source) { - this.removeTokensCreatedAtEndOf(game, source, PhaseStep.END_TURN, true); + this.removeTokensCreatedAt(game, source, true, PhaseStep.END_TURN, TargetController.ANY); } - public void sacrificeTokensCreatedAtEndOfCombat(Game game, Ability source) { - this.removeTokensCreatedAtEndOf(game, source, PhaseStep.END_COMBAT, false); - } - - public void exileTokensCreatedAtEndOfCombat(Game game, Ability source) { - this.removeTokensCreatedAtEndOf(game, source, PhaseStep.END_COMBAT, true); - } - - private void removeTokensCreatedAtEndOf(Game game, Ability source, PhaseStep phaseStepToExileCards, boolean exile) { - Effect effect; - if (exile) { - effect = new ExileTargetEffect(null, "", Zone.BATTLEFIELD).setText("exile the token copies"); - } else { - effect = new SacrificeTargetEffect("sacrifice the token copies", source.getControllerId()); - } + public void removeTokensCreatedAt(Game game, Ability source, boolean exile, PhaseStep phaseStep, TargetController targetController) { + Effect effect = exile + ? new ExileTargetEffect(null, "", Zone.BATTLEFIELD).setText("exile the token copies") + : new SacrificeTargetEffect("sacrifice the token copies", source.getControllerId()); effect.setTargetPointer(new FixedTargets(new ArrayList<>(addedTokenPermanents), game)); DelayedTriggeredAbility exileAbility; - - switch (phaseStepToExileCards) { + switch (phaseStep) { case END_TURN: - exileAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); + exileAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect, targetController); break; case END_COMBAT: exileAbility = new AtTheEndOfCombatDelayedTriggeredAbility(effect); break; default: - return; + throw new UnsupportedOperationException("Unsupported PhaseStep in CreateTokenCopyTargetEffect::removeTokensCreatedAt"); } game.addDelayedTriggeredAbility(exileAbility, source); diff --git a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenEffect.java index 7d05603eeda..51b21230ae7 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenEffect.java @@ -1,24 +1,26 @@ package mage.abilities.effects.common; import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; +import mage.constants.PhaseStep; +import mage.constants.TargetController; import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.Token; -import mage.target.targetpointer.FixedTarget; +import mage.target.targetpointer.FixedTargets; import mage.util.CardUtil; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.UUID; +import java.util.*; +import java.util.stream.Collectors; /** * @author BetaSteward_at_googlemail.com @@ -121,37 +123,34 @@ public class CreateTokenEffect extends OneShotEffect { return lastAddedTokenIds; } - public void sacrificeTokensCreatedAtNextEndStep(Game game, Ability source) { - for (UUID tokenId : this.getLastAddedTokenIds()) { - Permanent tokenPermanent = game.getPermanent(tokenId); - if (tokenPermanent != null) { - SacrificeTargetEffect sacrificeEffect = new SacrificeTargetEffect(); - sacrificeEffect.setTargetPointer(new FixedTarget(tokenPermanent, game)); - game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect), source); - } - } - } - public void exileTokensCreatedAtNextEndStep(Game game, Ability source) { - for (UUID tokenId : this.getLastAddedTokenIds()) { - Permanent tokenPermanent = game.getPermanent(tokenId); - if (tokenPermanent != null) { - ExileTargetEffect exileEffect = new ExileTargetEffect(null, "", Zone.BATTLEFIELD); - exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game)); - game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect), source); - } - } + removeTokensCreatedAt(game, source, true, PhaseStep.END_TURN, TargetController.ANY); } - public void exileTokensCreatedAtEndOfCombat(Game game, Ability source) { - for (UUID tokenId : this.getLastAddedTokenIds()) { - Permanent tokenPermanent = game.getPermanent(tokenId); - if (tokenPermanent != null) { - ExileTargetEffect exileEffect = new ExileTargetEffect(null, "", Zone.BATTLEFIELD); - exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game)); - game.addDelayedTriggeredAbility(new AtTheEndOfCombatDelayedTriggeredAbility(exileEffect), source); - } + public void removeTokensCreatedAt(Game game, Ability source, boolean exile, PhaseStep phaseStep, TargetController targetController) { + List permanents = this.getLastAddedTokenIds() + .stream() + .map(game::getPermanent) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + Effect effect = exile + ? new ExileTargetEffect(null, "", Zone.BATTLEFIELD).setText("exile those tokens") + : new SacrificeTargetEffect("sacrifice those tokens", source.getControllerId()); + effect.setTargetPointer(new FixedTargets(permanents, game)); + + DelayedTriggeredAbility exileAbility; + switch (phaseStep) { + case END_TURN: + exileAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect, targetController); + break; + case END_COMBAT: + exileAbility = new AtTheEndOfCombatDelayedTriggeredAbility(effect); + break; + default: + throw new UnsupportedOperationException("Unsupported PhaseStep in CreateTokenEffect::removeTokensCreatedAt"); } + + game.addDelayedTriggeredAbility(exileAbility, source); } /** diff --git a/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java b/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java index 1118d48eb50..9d6edca166b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java @@ -19,6 +19,7 @@ public class DoIfCostPaid extends OneShotEffect { protected final Effects executingEffects; protected final Effects otherwiseEffects; + protected String otherwiseText = "If you don't"; protected final Cost cost; private final String chooseUseText; private final boolean optional; @@ -79,6 +80,11 @@ public class DoIfCostPaid extends OneShotEffect { return this; } + public DoIfCostPaid setOtherwiseText(String otherwiseText) { + this.otherwiseText = otherwiseText; + return this; + } + /** * Allow to add additional info in pay dialog, so user can split it in diff use cases to remember by right click * Example: ignore untap payment for already untapped permanent like Mana Vault @@ -178,7 +184,7 @@ public class DoIfCostPaid extends OneShotEffect { return (optional ? "you may " : "") + CardUtil.addCostVerb(cost.getText()) + "." + (!executingEffects.isEmpty() ? " If you do, " + executingEffects.getText(mode) : "") - + (!otherwiseEffects.isEmpty() ? " If you don't, " + otherwiseEffects.getText(mode) : ""); + + (!otherwiseEffects.isEmpty() ? " " + otherwiseText + ", " + otherwiseEffects.getText(mode) : ""); } @Override diff --git a/Mage/src/main/java/mage/abilities/effects/common/EntersBattlefieldUnderControlOfOpponentOfChoiceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/EntersBattlefieldUnderControlOfOpponentOfChoiceEffect.java index 643864d3466..52462ed7b1d 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/EntersBattlefieldUnderControlOfOpponentOfChoiceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/EntersBattlefieldUnderControlOfOpponentOfChoiceEffect.java @@ -59,7 +59,7 @@ public class EntersBattlefieldUnderControlOfOpponentOfChoiceEffect extends OneSh Duration.Custom, true, opponent.getId() ); continuousEffect.setTargetPointer(new FixedTarget( - source.getSourceId(), source.getSourceObjectZoneChangeCounter() + source.getSourceId(), source.getStackMomentSourceZCC() )); game.addEffect(continuousEffect, source); return true; diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect.java index 7763a00ee8c..176717e55eb 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect.java @@ -73,7 +73,7 @@ public class ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect extends OneShotEf return false; } // move card to exile - UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); MageObject sourceObject = source.getSourceObject(game); String exileName = sourceObject == null ? "" : sourceObject.getIdName(); for (Card card : cards.getCards(game)) { @@ -101,4 +101,4 @@ public class ExileFaceDownYouMayPlayAsLongAsExiledTargetEffect extends OneShotEf } return true; } -} \ No newline at end of file +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileGraveyardAllTargetPlayerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileGraveyardAllTargetPlayerEffect.java index b96ba40fabc..6ae0335d31b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileGraveyardAllTargetPlayerEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileGraveyardAllTargetPlayerEffect.java @@ -1,13 +1,19 @@ package mage.abilities.effects.common; import mage.abilities.Ability; +import mage.abilities.Mode; import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** * @author LevelX2 */ @@ -22,7 +28,6 @@ public class ExileGraveyardAllTargetPlayerEffect extends OneShotEffect { public ExileGraveyardAllTargetPlayerEffect(boolean toUniqueExile) { super(Outcome.Exile); this.toUniqueExile = toUniqueExile; - staticText = "exile target player's graveyard"; } private ExileGraveyardAllTargetPlayerEffect(final ExileGraveyardAllTargetPlayerEffect effect) { @@ -38,14 +43,39 @@ public class ExileGraveyardAllTargetPlayerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Player targetPlayer = game.getPlayer(this.getTargetPointer().getFirst(game, source)); - if (targetPlayer == null || controller == null) { + if (controller == null) { return false; } + Set cardsToExile = new HashSet<>(); + + for (UUID playerId : this.getTargetPointer().getTargets(game, source)) { + Player targetPlayer = game.getPlayer(playerId); + if (targetPlayer == null) { + continue; + } + cardsToExile.addAll(targetPlayer.getGraveyard().getCards(game)); + } return toUniqueExile ? controller.moveCardsToExile( - targetPlayer.getGraveyard().getCards(game), source, game, true, + cardsToExile, source, game, true, CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source) - ) : controller.moveCards(targetPlayer.getGraveyard(), Zone.EXILED, source, game); + ) : controller.moveCards(cardsToExile, Zone.EXILED, source, game); + } + + @Override + public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } + StringBuilder sb = new StringBuilder("exile "); + sb.append(getTargetPointer().describeTargets(mode.getTargets(), "target player")); + if (sb.toString().toLowerCase().endsWith("player") || sb.toString().toLowerCase().endsWith("opponent")) { + if (getTargetPointer().isPlural(mode.getTargets())) { + sb.append("s' graveyards"); + } else { + sb.append("'s graveyard"); + } + } + return sb.toString(); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileSourceEffect.java index de5e9aba55a..69a78aae6de 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileSourceEffect.java @@ -1,6 +1,5 @@ package mage.abilities.effects.common; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; @@ -59,7 +58,7 @@ public class ExileSourceEffect extends OneShotEffect { UUID exileZoneId = null; String exileZoneName = ""; if (toUniqueExileZone) { - exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); exileZoneName = card.getName(); } return controller.moveCardsToExile(card, source, game, true, exileZoneId, exileZoneName); diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java index edc5433bba3..1d88dd2a854 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileTargetEffect.java @@ -111,7 +111,7 @@ public class ExileTargetEffect extends OneShotEffect { } if (toSourceExileZone) { MageObject sourceObject = source.getSourceObject(game); - exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); if (sourceObject != null) { exileZone = sourceObject.getIdName(); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileTargetForSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileTargetForSourceEffect.java index 1b16ffdf372..11ece3e1bf3 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ExileTargetForSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileTargetForSourceEffect.java @@ -78,7 +78,7 @@ public class ExileTargetForSourceEffect extends OneShotEffect { } } - UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); // it can target permanents on battlefield, so use objects first Set cardsToMove = objectsToMove.stream() diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileTopCardPlayUntilExileAnotherEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileTopCardPlayUntilExileAnotherEffect.java new file mode 100644 index 00000000000..3a0e3e29eed --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileTopCardPlayUntilExileAnotherEffect.java @@ -0,0 +1,147 @@ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.constants.AsThoughEffectType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.ExileZone; +import mage.game.Game; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +import java.util.Set; +import java.util.UUID; + +/** + * @author Susucr + */ +public class ExileTopCardPlayUntilExileAnotherEffect extends OneShotEffect { + + + public ExileTopCardPlayUntilExileAnotherEffect() { + this(false, "that card"); + } + + public ExileTopCardPlayUntilExileAnotherEffect(boolean withInterveningIf) { + this(withInterveningIf, "that card"); + } + + public ExileTopCardPlayUntilExileAnotherEffect(String cardDescriptor) { + this(false, cardDescriptor); + } + + public ExileTopCardPlayUntilExileAnotherEffect(boolean withInterveningIf, String cardDescriptor) { + super(Outcome.DrawCard); + staticText = makeText(withInterveningIf, cardDescriptor); + } + + + private ExileTopCardPlayUntilExileAnotherEffect(final ExileTopCardPlayUntilExileAnotherEffect effect) { + super(effect); + } + + @Override + public OneShotEffect copy() { + return new ExileTopCardPlayUntilExileAnotherEffect(); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null || !controller.getLibrary().hasCards()) { + return false; + } + Card card = controller.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + UUID exileId = CardUtil.getExileZoneId(game, source); + String exileName = CardUtil.getSourceIdName(game, source); + controller.moveCardsToExile(card, source, game, true, exileId, exileName); + game.processAction(); + if (!Zone.EXILED.equals(game.getState().getZone(card.getId()))) { + return true; + } + // Allow the card to be played until it leaves that exile zone. + ContinuousEffect effect = new ExileTopCardPlayEffect(exileId); + effect.setTargetPointer(new FixedTarget(card.getMainCard(), game)); + game.addEffect(effect, source); + // Clean the exile Zone from other cards, that can no longer be played. + ExileZone exileZone = game.getExile().getExileZone(exileId); + if (exileZone == null) { + return true; + } + Set inExileZone = exileZone.getCards(game); + for (Card cardInExile : inExileZone) { + if (cardInExile.getMainCard().getId().equals(card.getMainCard().getId())) { + continue; + } + game.getExile().moveToMainExileZone(cardInExile, game); + } + return true; + } + + private String makeText(boolean withInterveningIf, String cardDescriptor) { + StringBuilder sb = new StringBuilder("exile the top card of your library. "); + if (withInterveningIf) { + sb.append("If you do, you may play "); + } else { + sb.append("You may play "); + } + sb.append(cardDescriptor); + sb.append(" until you exile another card with {this}."); + return sb.toString(); + } +} + +class ExileTopCardPlayEffect extends AsThoughEffectImpl { + + private final UUID exileId; + + ExileTopCardPlayEffect(UUID exileId) { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit); + this.exileId = exileId; + } + + private ExileTopCardPlayEffect(final ExileTopCardPlayEffect effect) { + super(effect); + this.exileId = effect.exileId; + } + + @Override + public ExileTopCardPlayEffect copy() { + return new ExileTopCardPlayEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + Card mainTargetCard = game.getCard(getTargetPointer().getFirst(game, source)); + if (mainTargetCard == null) { + this.discard(); + return false; + } + ExileZone exileZone = game.getExile().getExileZone(exileId); + if (exileZone == null || !exileZone.contains(mainTargetCard.getId())) { + // Clean the Continuous effect if the target card is no longer in the exile zone + this.discard(); + return false; + } + Card objectCard = game.getCard(sourceId); + if (objectCard == null) { + return false; + } + return mainTargetCard.getId().equals(objectCard.getMainCard().getId()) // using main card to work with split/mdfc/adventures + && affectedControllerId.equals(source.getControllerId()); + } +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/effects/common/RemoveUpToAmountCountersEffect.java b/Mage/src/main/java/mage/abilities/effects/common/RemoveUpToAmountCountersEffect.java new file mode 100644 index 00000000000..e69d9cce542 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/RemoveUpToAmountCountersEffect.java @@ -0,0 +1,86 @@ +package mage.abilities.effects.common; + +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.MultiAmountType; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.util.CardUtil; + +import java.util.ArrayList; +import java.util.List; + +public class RemoveUpToAmountCountersEffect extends OneShotEffect { + + final DynamicValue amount; + + public RemoveUpToAmountCountersEffect(int amount) { + super(Outcome.Neutral); + this.amount = StaticValue.get(amount); + } + + public RemoveUpToAmountCountersEffect(DynamicValue amount) { + super(Outcome.Neutral); + this.amount = amount; + } + + private RemoveUpToAmountCountersEffect(final RemoveUpToAmountCountersEffect effect) { + super(effect); + this.amount = effect.amount; + } + + @Override + public RemoveUpToAmountCountersEffect copy() { + return new RemoveUpToAmountCountersEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + int max = this.amount.calculate(game, source, this); + // from permanent + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent != null) { + List toChoose = new ArrayList<>(permanent.getCounters(game).keySet()); + List counterList = controller.getMultiAmount(Outcome.UnboostCreature, toChoose, 0, 0, max, MultiAmountType.REMOVE_COUNTERS, game); + for (int i = 0; i < toChoose.size(); i++) { + int amountToRemove = counterList.get(i); + if (amountToRemove > 0) { + permanent.removeCounters(toChoose.get(i), amountToRemove, source, game); + } + } + return true; + } + + // from player + Player player = game.getPlayer(source.getFirstTarget()); + if (player != null) { + List toChoose = new ArrayList<>(player.getCountersAsCopy().keySet()); + List counterList = controller.getMultiAmount(Outcome.Neutral, toChoose, 0, 0, max, MultiAmountType.REMOVE_COUNTERS, game); + for (int i = 0; i < toChoose.size(); i++) { + int amountToRemove = counterList.get(i); + if (amountToRemove > 0) { + player.loseCounters(toChoose.get(i), amountToRemove, source, game); + } + } + return true; + } + return false; + } + + @Override + public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } + return "remove up to " + CardUtil.numberToText(this.amount.toString()) + " counters from " + getTargetPointer().describeTargets(mode.getTargets(), "that permanent"); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/ReturnFromExileForSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ReturnFromExileForSourceEffect.java index 0bcb084d1a0..267012e2c57 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ReturnFromExileForSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ReturnFromExileForSourceEffect.java @@ -66,7 +66,7 @@ public class ReturnFromExileForSourceEffect extends OneShotEffect { if (permanentLeftBattlefield != null) { exileId = CardUtil.getExileZoneId(game, source.getSourceId(), permanentLeftBattlefield.getZoneChangeCounter(game)); } else { - exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); } ExileZone exile = game.getExile().getExileZone(exileId); diff --git a/Mage/src/main/java/mage/abilities/effects/common/ReturnToBattlefieldUnderYourControlSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ReturnToBattlefieldUnderYourControlSourceEffect.java index 7bc72ac3174..15dc5590c6f 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ReturnToBattlefieldUnderYourControlSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ReturnToBattlefieldUnderYourControlSourceEffect.java @@ -38,7 +38,7 @@ public class ReturnToBattlefieldUnderYourControlSourceEffect extends OneShotEffe if (controller == null) { return false; } - UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC()); ExileZone exileZone = game.getExile().getExileZone(exileZoneId); if (exileZone != null && exileZone.contains(source.getSourceId())) { Card card = game.getCard(source.getSourceId()); diff --git a/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandSourceEffect.java index 29eaf85c47b..509ee503062 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/ReturnToHandSourceEffect.java @@ -60,7 +60,7 @@ public class ReturnToHandSourceEffect extends OneShotEffect { } MageObject mageObject; if (returnFromNextZone - && game.getState().getZoneChangeCounter(source.getSourceId()) == source.getSourceObjectZoneChangeCounter() + 1) { + && game.getState().getZoneChangeCounter(source.getSourceId()) == source.getStackMomentSourceZCC() + 1) { mageObject = game.getObject(source); } else { mageObject = source.getSourceObjectIfItStillExists(game); diff --git a/Mage/src/main/java/mage/abilities/effects/common/SacrificeSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SacrificeSourceEffect.java index c74c2318ea4..b6ab6a28099 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/SacrificeSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/SacrificeSourceEffect.java @@ -41,7 +41,7 @@ public class SacrificeSourceEffect extends OneShotEffect { if (sourceObject == null) { // Check if the effect was installed by the spell the source was cast by (e.g. Necromancy), if not don't sacrifice the permanent if (game.getState().getZone(source.getSourceId()).equals(Zone.BATTLEFIELD) - && source.getSourceObjectZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(source.getSourceId())) { + && source.getStackMomentSourceZCC() + 1 == game.getState().getZoneChangeCounter(source.getSourceId())) { sourceObject = game.getPermanent(source.getSourceId()); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/SacrificeSourceUnlessPaysEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SacrificeSourceUnlessPaysEffect.java index cc0039f6a44..eb1cc799df9 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/SacrificeSourceUnlessPaysEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/SacrificeSourceUnlessPaysEffect.java @@ -76,7 +76,7 @@ public class SacrificeSourceUnlessPaysEffect extends OneShotEffect { } game.informPlayers(player.getLogName() + " chooses not to " + logMessage + " to prevent sacrifice effect"); - if (source.getSourceObjectZoneChangeCounter() == game.getState().getZoneChangeCounter(source.getSourceId()) + if (source.getStackMomentSourceZCC() == game.getState().getZoneChangeCounter(source.getSourceId()) && game.getState().getZone(source.getSourceId()) == Zone.BATTLEFIELD) { sourcePermanent.sacrifice(source, game); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/TargetsDamageTargetsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/TargetsDamageTargetsEffect.java new file mode 100644 index 00000000000..cad1c21c75b --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/TargetsDamageTargetsEffect.java @@ -0,0 +1,105 @@ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.Target; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * Used for [target creatures] deal damage equal to their power to [target creature] + *
set targets using tags .setTargetTag(1) + *
set the first target tag for creatures dealing damage + *
set the second target tag for additional creatures, not required (Friendly Rivalry) + *
set the third target tag for creatures receiving damage + * + * @author Jmlundeen + */ +public class TargetsDamageTargetsEffect extends OneShotEffect { + + private final boolean describeDamagingTargets; + + public TargetsDamageTargetsEffect(boolean describeDamagingTargets) { + super(Outcome.Benefit); + this.describeDamagingTargets = describeDamagingTargets; + } + + private TargetsDamageTargetsEffect(final TargetsDamageTargetsEffect effect) { + super(effect); + this.describeDamagingTargets = effect.describeDamagingTargets; + } + + @Override + public TargetsDamageTargetsEffect copy() { + return new TargetsDamageTargetsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (source.getTargets().size() < 2) { + return false; + } + + Target damageTarget = source.getTargets().getByTag(1); + Target additionalDamageTarget = source.getTargets().getByTag(2); + Target destTarget = source.getTargets().getByTag(3); + + List damagingPermanents = new ArrayList<>(); + List receivingPermanents = new ArrayList<>(); + for (UUID id : damageTarget.getTargets()) { + Permanent permanent = game.getPermanent(id); + if (permanent != null) { + damagingPermanents.add(permanent); + } + } + if (additionalDamageTarget != null) { + for (UUID id : additionalDamageTarget.getTargets()) { + Permanent permanent = game.getPermanent(id); + if (permanent != null) { + damagingPermanents.add(permanent); + } + } + } + for (UUID id : destTarget.getTargets()) { + Permanent permanent = game.getPermanent(id); + if (permanent != null) { + receivingPermanents.add(permanent); + } + } + + if (receivingPermanents.isEmpty() || damagingPermanents.isEmpty()) { + return false; + } + for (Permanent receivingPermanent : receivingPermanents) { + for (Permanent damagingPermanent: damagingPermanents) { + receivingPermanent.damage(damagingPermanent.getPower().getValue(), damagingPermanent.getId(), source, game, false, true); + } + } + return true; + } + + @Override + public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } + StringBuilder sb = new StringBuilder(); + if (describeDamagingTargets) { + sb.append(mode.getTargets().getByTag(1).getDescription()); + if (mode.getTargets().getByTag(2) != null) { + sb.append(" and ").append(mode.getTargets().getByTag(2).getDescription()); + } + } else { + sb.append("they"); + } + sb.append(" each deal damage equal to their power to "); + sb.append(mode.getTargets().getByTag(3).getDescription()); + return sb.toString(); + } +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/effects/common/WishEffect.java b/Mage/src/main/java/mage/abilities/effects/common/WishEffect.java index 9d76b9a82f3..b47a6302c96 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/WishEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/WishEffect.java @@ -99,7 +99,7 @@ public class WishEffect extends OneShotEffect { return false; } Cards cards = controller.getSideboard(); - List exile = game.getExile().getAllCards(game); + List exile = game.getExile().getCardsOwned(game, controller.getId()); boolean noTargets = cards.isEmpty() && (!alsoFromExile || exile.isEmpty()); if (noTargets) { game.informPlayer(controller, "You have no cards outside the game" + (alsoFromExile ? " or in exile" : "") + '.'); diff --git a/Mage/src/main/java/mage/abilities/effects/common/asthought/MayCastFromGraveyardAsAdventureEffect.java b/Mage/src/main/java/mage/abilities/effects/common/asthought/MayCastFromGraveyardAsAdventureEffect.java index 78b3c80b95c..7ffec5a4206 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/asthought/MayCastFromGraveyardAsAdventureEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/asthought/MayCastFromGraveyardAsAdventureEffect.java @@ -49,6 +49,6 @@ public class MayCastFromGraveyardAsAdventureEffect extends AsThoughEffectImpl { return sourceCard != null && game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD - && source.getSourceObjectZoneChangeCounter() == sourceCard.getZoneChangeCounter(game); + && source.getStackMomentSourceZCC() == sourceCard.getZoneChangeCounter(game); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddBasicLandTypeAllLandsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddBasicLandTypeAllLandsEffect.java index fb92836067d..c5fdd8277fb 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddBasicLandTypeAllLandsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddBasicLandTypeAllLandsEffect.java @@ -20,6 +20,7 @@ public class AddBasicLandTypeAllLandsEffect extends ContinuousEffectImpl { this.subType = subType; this.staticText = "Each land is " + subType.getIndefiniteArticle() + " " + subType.getDescription() + " in addition to its other land types"; + this.dependendToTypes.add(DependencyType.BecomeNonbasicLand); switch (subType) { case PLAINS: this.dependencyTypes.add(DependencyType.BecomePlains); diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCreatureSubTypeAllMultiZoneEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCreatureSubTypeAllMultiZoneEffect.java index 8367e9ceab7..5b73ab7e313 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCreatureSubTypeAllMultiZoneEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/AddCreatureSubTypeAllMultiZoneEffect.java @@ -98,7 +98,7 @@ public class AddCreatureSubTypeAllMultiZoneEffect extends ContinuousEffectImpl { } } // in Exile - for (Card card : game.getState().getExile().getAllCards(game, controllerId)) { + for (Card card : game.getState().getExile().getCardsOwned(game, controllerId)) { if (filterCard.match(card, controllerId, source, game) && !card.hasSubtype(subType, game)) { game.getState().getCreateMageObjectAttribute(card, game).getSubtype().add(subType); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesAllBasicsControlledEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesAllBasicsControlledEffect.java index b5991f27b3a..1e96acf7468 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesAllBasicsControlledEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesAllBasicsControlledEffect.java @@ -8,9 +8,6 @@ import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; -import java.util.ArrayList; -import java.util.List; - /** * @author TheElk801 */ @@ -27,6 +24,7 @@ public class BecomesAllBasicsControlledEffect extends ContinuousEffectImpl { public BecomesAllBasicsControlledEffect() { super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Detriment); this.staticText = "Lands you control are every basic land type in addition to their other types"; + dependendToTypes.add(DependencyType.BecomeNonbasicLand); dependencyTypes.add(DependencyType.BecomeMountain); dependencyTypes.add(DependencyType.BecomeForest); dependencyTypes.add(DependencyType.BecomeSwamp); diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureTargetEffect.java index 4b4a8e3f362..2ada47074ad 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesCreatureTargetEffect.java @@ -7,6 +7,7 @@ import mage.constants.*; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.Token; +import mage.util.CardUtil; import java.util.UUID; @@ -194,8 +195,11 @@ public class BecomesCreatureTargetEffect extends ContinuousEffectImpl { " lose all their abilities and" : " loses all abilities and"); } - sb.append(getTargetPointer().isPlural(mode.getTargets()) ? " become " : " becomes a "); - sb.append(token.getDescription()); + if (getTargetPointer().isPlural(mode.getTargets())) { + sb.append(" become ").append(token.getDescription()); + } else { + sb.append(" becomes ").append(CardUtil.addArticle(token.getDescription())); + } if (!durationRuleAtStart && !duration.toString().isEmpty()) { sb.append(' ').append(duration.toString()); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostSourceWhileControlsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostSourceWhileControlsEffect.java index f77dbf313c0..381b597a82a 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostSourceWhileControlsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BoostSourceWhileControlsEffect.java @@ -29,8 +29,7 @@ public class BoostSourceWhileControlsEffect extends WhileConditionContinuousEffe staticText = "{this} gets " + CardUtil.getBoostCountAsStr(power, toughness) + " as long as you control " - + (filterDescription.startsWith("an ") ? "" : "a ") - + filterDescription; + + CardUtil.addArticle(filterDescription); } protected BoostSourceWhileControlsEffect(final BoostSourceWhileControlsEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java index 76afce20dce..2ef681f85e1 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityAttachedEffect.java @@ -24,6 +24,7 @@ public class GainAbilityAttachedEffect extends ContinuousEffectImpl { protected boolean independentEffect; protected String targetObjectName; protected boolean doesntRemoveItself = false; + protected boolean useQuotes = false; public GainAbilityAttachedEffect(Ability ability, AttachmentType attachmentType) { this(ability, attachmentType, Duration.WhileOnBattlefield); @@ -68,6 +69,7 @@ public class GainAbilityAttachedEffect extends ContinuousEffectImpl { this.independentEffect = effect.independentEffect; this.targetObjectName = effect.targetObjectName; this.doesntRemoveItself = effect.doesntRemoveItself; + this.useQuotes = effect.useQuotes; } @Override @@ -130,20 +132,27 @@ public class GainAbilityAttachedEffect extends ContinuousEffectImpl { return this; } + public GainAbilityAttachedEffect withQuotes(boolean useQuotes) { + this.useQuotes = useQuotes; + return this; + } + @Override public String getText(Mode mode) { if (staticText != null && !staticText.isEmpty()) { return staticText; } StringBuilder sb = new StringBuilder(); - sb.append(attachmentType.verb().toLowerCase()); - sb.append(" " + targetObjectName + " "); + if (attachmentType != null) { + sb.append(attachmentType.verb().toLowerCase()); + sb.append(" " + targetObjectName + " "); + } if (duration == Duration.WhileOnBattlefield) { sb.append("has "); } else { sb.append("gains "); } - boolean quotes = ability instanceof SimpleActivatedAbility + boolean quotes = useQuotes || ability instanceof SimpleActivatedAbility || ability instanceof TriggeredAbility || ability instanceof LoyaltyAbility || ability instanceof ManaAbility diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledSpellsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledSpellsEffect.java index 7cef0dc05df..2947f24523e 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledSpellsEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainAbilityControlledSpellsEffect.java @@ -44,7 +44,7 @@ public class GainAbilityControlledSpellsEffect extends ContinuousEffectImpl { return false; } - for (Card card : game.getExile().getAllCardsByRange(game, source.getControllerId())) { + for (Card card : game.getExile().getCardsInRange(game, source.getControllerId())) { if (filter.match(card, player.getId(), source, game)) { game.getState().addOtherAbility(card, ability); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/NextSpellCastHasAbilityEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/NextSpellCastHasAbilityEffect.java index df157da49ef..8c14cf6353b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/NextSpellCastHasAbilityEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/NextSpellCastHasAbilityEffect.java @@ -93,7 +93,7 @@ public class NextSpellCastHasAbilityEffect extends ContinuousEffectImpl { discard(); // only one use return false; } - for (Card card : game.getExile().getAllCardsByRange(game, playerId)) { + for (Card card : game.getExile().getCardsInRange(game, playerId)) { if (filter.match(card, playerId, source, game)) { game.getState().addOtherAbility(card, ability); } diff --git a/Mage/src/main/java/mage/abilities/effects/common/counter/AddCountersSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/counter/AddCountersSourceEffect.java index 358be527915..26fb56a60c8 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/counter/AddCountersSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/counter/AddCountersSourceEffect.java @@ -110,8 +110,8 @@ public class AddCountersSourceEffect extends OneShotEffect { return false; } - if ((source.getSourceObjectZoneChangeCounter() == 0 // from static ability - || source.getSourceObjectZoneChangeCounter() == permanent.getZoneChangeCounter(game))) { // prevent to add counters to later source objects + if ((source.getStackMomentSourceZCC() == 0 // from static ability + || source.getStackMomentSourceZCC() == permanent.getZoneChangeCounter(game))) { // prevent to add counters to later source objects Counter newCounter = counter.copy(); int countersToAdd = amount.calculate(game, source, this); if (amount instanceof StaticValue || countersToAdd > 0) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/counter/DoubleCounterOnEachPermanentEffect.java b/Mage/src/main/java/mage/abilities/effects/common/counter/DoubleCounterOnEachPermanentEffect.java index cf71cb93fdd..29abc2c89fc 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/counter/DoubleCounterOnEachPermanentEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/counter/DoubleCounterOnEachPermanentEffect.java @@ -4,12 +4,14 @@ package mage.abilities.effects.common.counter; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; +import mage.counters.Counter; import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import java.util.List; +import java.util.stream.Collectors; /** * @author Susucr @@ -23,7 +25,9 @@ public class DoubleCounterOnEachPermanentEffect extends OneShotEffect { super(Outcome.BoostCreature); this.counterType = counterType; this.filter = filter.copy(); - this.staticText = "double the number of " + counterType.getName() + " counters on each " + filter.getMessage(); + this.staticText = "double the number of " + + (counterType != null ? (counterType.getName() + " counters") : "each kind of counter") + + " on each " + filter.getMessage(); } private DoubleCounterOnEachPermanentEffect(final DoubleCounterOnEachPermanentEffect effect) { @@ -39,8 +43,19 @@ public class DoubleCounterOnEachPermanentEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - List permanents = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source, game); - for (Permanent permanent : permanents) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source, game)) { + if (counterType == null) { + List counters = permanent + .getCounters(game) + .values() + .stream() + .map(Counter::copy) + .collect(Collectors.toList()); + for (Counter counter : counters) { + permanent.addCounters(counter, source, game); + } + continue; + } int existingCounters = permanent.getCounters(game).getCount(counterType); if (existingCounters > 0) { permanent.addCounters(counterType.createInstance(existingCounters), source.getControllerId(), source, game); diff --git a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryPutOntoBattlefieldTappedRestInHandEffect.java b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryPutOntoBattlefieldTappedRestInHandEffect.java index 3788fb5c54b..2b0801bf373 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryPutOntoBattlefieldTappedRestInHandEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/search/SearchLibraryPutOntoBattlefieldTappedRestInHandEffect.java @@ -26,7 +26,7 @@ public class SearchLibraryPutOntoBattlefieldTappedRestInHandEffect extends Searc super(target, Outcome.PutLandInPlay); staticText = "search your library for " + target.getDescription() + ", reveal those cards, put " + CardUtil.numberToText(numToBattlefield) + " onto the battlefield tapped and the other into your hand, then shuffle"; - this.filter = new FilterCard((numToBattlefield > 1 ? "cards" : "card") + "to put on the battlefield tapped"); + this.filter = new FilterCard((numToBattlefield > 1 ? "cards" : "card") + " to put on the battlefield tapped"); this.numToBattlefield = numToBattlefield; } diff --git a/Mage/src/main/java/mage/abilities/keyword/CraftAbility.java b/Mage/src/main/java/mage/abilities/keyword/CraftAbility.java index 3ba9690fb4d..70f36cad717 100644 --- a/Mage/src/main/java/mage/abilities/keyword/CraftAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/CraftAbility.java @@ -169,7 +169,7 @@ class CraftEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); Card card = game.getCard(source.getSourceId()); - if (player == null || card == null || card.getZoneChangeCounter(game) != source.getSourceObjectZoneChangeCounter() + 1) { + if (player == null || card == null || card.getZoneChangeCounter(game) != source.getStackMomentSourceZCC() + 1) { return false; } game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + source.getSourceId(), Boolean.TRUE); diff --git a/Mage/src/main/java/mage/abilities/keyword/EscapeAbility.java b/Mage/src/main/java/mage/abilities/keyword/EscapeAbility.java index aa370c2649d..d841f172422 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EscapeAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EscapeAbility.java @@ -101,7 +101,7 @@ public class EscapeAbility extends SpellAbility { @Override public boolean activate(Game game, Set allowedIdentifiers, boolean noMana) { if (super.activate(game, allowedIdentifiers, noMana)) { - game.getState().setValue(CASTED_WITH_ESCAPE_KEY + getSourceId().toString() + (getSourceObjectZoneChangeCounter() + 1), Boolean.TRUE); + game.getState().setValue(CASTED_WITH_ESCAPE_KEY + getSourceId().toString() + (getStackMomentSourceZCC() + 1), Boolean.TRUE); return true; } return false; diff --git a/Mage/src/main/java/mage/abilities/keyword/JumpStartAbility.java b/Mage/src/main/java/mage/abilities/keyword/JumpStartAbility.java index 12270fcb6d5..d30d6a8a665 100644 --- a/Mage/src/main/java/mage/abilities/keyword/JumpStartAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/JumpStartAbility.java @@ -126,7 +126,7 @@ class JumpStartReplacementEffect extends ReplacementEffectImpl { if (event.getTargetId().equals(source.getSourceId()) && ((ZoneChangeEvent) event).getFromZone() == Zone.STACK && ((ZoneChangeEvent) event).getToZone() != Zone.EXILED) { - return game.getState().getZoneChangeCounter(source.getSourceId()) == source.getSourceObjectZoneChangeCounter() + 1; + return game.getState().getZoneChangeCounter(source.getSourceId()) == source.getStackMomentSourceZCC() + 1; } return false; diff --git a/Mage/src/main/java/mage/abilities/keyword/MadnessAbility.java b/Mage/src/main/java/mage/abilities/keyword/MadnessAbility.java index 7297698bac1..d4e5dc7d20c 100644 --- a/Mage/src/main/java/mage/abilities/keyword/MadnessAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/MadnessAbility.java @@ -280,7 +280,7 @@ enum MadnessCondition implements Condition { @Override public boolean apply(Game game, Ability source) { - MageObject madnessSpell = game.getLastKnownInformation(source.getSourceId(), Zone.STACK, source.getSourceObjectZoneChangeCounter() - 1); + MageObject madnessSpell = game.getLastKnownInformation(source.getSourceId(), Zone.STACK, source.getStackMomentSourceZCC() - 1); if (!(madnessSpell instanceof Spell)) { return false; } diff --git a/Mage/src/main/java/mage/abilities/keyword/MayhemLandAbility.java b/Mage/src/main/java/mage/abilities/keyword/MayhemLandAbility.java new file mode 100644 index 00000000000..14c5d962023 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/keyword/MayhemLandAbility.java @@ -0,0 +1,60 @@ +package mage.abilities.keyword; + +import mage.MageIdentifier; +import mage.abilities.PlayLandAbility; +import mage.cards.Card; +import mage.constants.Zone; +import mage.game.Game; + +import java.util.Set; +import java.util.UUID; + +public class MayhemLandAbility extends PlayLandAbility { + + private final String rule; + + public MayhemLandAbility(Card card) { + super(card.getName()); + this.zone = Zone.GRAVEYARD; + this.newId(); + this.name += " with Mayhem"; + this.addWatcher(new MayhemWatcher()); + this.setRuleAtTheTop(true); + this.rule = "Mayhem " + + " (You may play this card from your graveyard if you discarded it this turn. " + + "Timing rules still apply.)"; + } + + protected MayhemLandAbility(final MayhemLandAbility ability) { + super(ability); + this.rule = ability.rule; + } + + @Override + public ActivationStatus canActivate(UUID playerId, Game game) { + if (!Zone.GRAVEYARD.match(game.getState().getZone(getSourceId())) + || !MayhemWatcher.checkCard(getSourceId(), game)) { + return ActivationStatus.getFalse(); + } + return super.canActivate(playerId, game); + } + + @Override + public boolean activate(Game game, Set allowedIdentifiers, boolean noMana) { + if (!super.activate(game, allowedIdentifiers, noMana)) { + return false; + } + this.setCostsTag(MayhemAbility.MAYHEM_ACTIVATION_VALUE_KEY, null); + return true; + } + + @Override + public MayhemLandAbility copy() { + return new MayhemLandAbility(this); + } + + @Override + public String getRule() { + return rule; + } + } \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/keyword/MobilizeAbility.java b/Mage/src/main/java/mage/abilities/keyword/MobilizeAbility.java index 41878053fd8..9549ead76dc 100644 --- a/Mage/src/main/java/mage/abilities/keyword/MobilizeAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/MobilizeAbility.java @@ -7,6 +7,8 @@ import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.constants.Outcome; +import mage.constants.PhaseStep; +import mage.constants.TargetController; import mage.game.Game; import mage.game.permanent.token.RedWarriorToken; import mage.util.CardUtil; @@ -34,7 +36,6 @@ public class MobilizeAbility extends AttacksTriggeredAbility { } private static String makeText(DynamicValue amount) { - StringBuilder sb = new StringBuilder(); String message; String numToText; boolean plural; @@ -77,7 +78,7 @@ class MobilizeEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { CreateTokenEffect effect = new CreateTokenEffect(new RedWarriorToken(), this.count, true, true); effect.apply(game, source); - effect.sacrificeTokensCreatedAtNextEndStep(game, source); + effect.removeTokensCreatedAt(game, source, false, PhaseStep.END_TURN, TargetController.ANY); return true; } } diff --git a/Mage/src/main/java/mage/abilities/keyword/UnearthAbility.java b/Mage/src/main/java/mage/abilities/keyword/UnearthAbility.java index 7333596bb50..c5a4e2ecbca 100644 --- a/Mage/src/main/java/mage/abilities/keyword/UnearthAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/UnearthAbility.java @@ -87,7 +87,7 @@ class UnearthDelayedTriggeredAbility extends DelayedTriggeredAbility { } // The delayed trigger source is the card in the graveyard. // So we need to exile the zcc + 1 permanent - MageObjectReference object = new MageObjectReference(getSourceId(), getSourceObjectZoneChangeCounter() + 1, game); + MageObjectReference object = new MageObjectReference(getSourceId(), getStackMomentSourceZCC() + 1, game); Permanent permanent = object.getPermanent(game); if (permanent == null || !permanent.isPhasedIn()) { // Triggers, but do nothing. @@ -131,7 +131,7 @@ class UnearthLeavesBattlefieldEffect extends ReplacementEffectImpl { ZoneChangeEvent zEvent = (ZoneChangeEvent) event; if (zEvent.getFromZone() == Zone.BATTLEFIELD && zEvent.getToZone() != Zone.EXILED) { // Only move it to exile if it was this instance that was moved to battlefield with unearth - return source.getSourceObjectZoneChangeCounter() == game.getState().getZoneChangeCounter(source.getSourceId()); + return source.getStackMomentSourceZCC() == game.getState().getZoneChangeCounter(source.getSourceId()); } } return false; diff --git a/Mage/src/main/java/mage/abilities/keyword/WarpAbility.java b/Mage/src/main/java/mage/abilities/keyword/WarpAbility.java index 10b18f10d98..34ec5882c66 100644 --- a/Mage/src/main/java/mage/abilities/keyword/WarpAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/WarpAbility.java @@ -169,7 +169,7 @@ class WarpExileEffect extends OneShotEffect { player.moveCardsToExile( permanent, source, game, true, CardUtil.getExileZoneId(WarpAbility.makeWarpString(player.getId()), game), - "Warped by " + player.getLogName() + "Warped by " + player.getName() ); CardUtil.makeCardPlayable( game, source, permanent.getMainCard(), true, diff --git a/Mage/src/main/java/mage/abilities/meta/OrTriggeredAbility.java b/Mage/src/main/java/mage/abilities/meta/OrTriggeredAbility.java index 55d98a00b13..7c5d37b484f 100644 --- a/Mage/src/main/java/mage/abilities/meta/OrTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/meta/OrTriggeredAbility.java @@ -90,12 +90,14 @@ public class OrTriggeredAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { boolean toRet = false; for (TriggeredAbility ability : triggeredAbilities) { - for (Effect e : getEffects()) { //Add effects to the sub-abilities so that they can set target pointers + for (Effect e : getEffects()) { // Add effects to the sub-abilities so that they can set target pointers ability.addEffect(e); } + ability.getTargets().addAll(this.getTargets()); // AtStepTriggeredAbility automatically sets target pointer if it can't find any targets if (ability.checkEventType(event, game) && ability.checkTrigger(event, game) && ability.checkTriggerCondition(game)) { toRet = true; } + ability.getTargets().clear(); ability.getEffects().clear(); //Remove afterwards, ensures that they remain synced even with copying } return toRet; diff --git a/Mage/src/main/java/mage/cards/ExpansionSet.java b/Mage/src/main/java/mage/cards/ExpansionSet.java index 90867e8ef2b..88d5a506841 100644 --- a/Mage/src/main/java/mage/cards/ExpansionSet.java +++ b/Mage/src/main/java/mage/cards/ExpansionSet.java @@ -145,7 +145,8 @@ public abstract class ExpansionSet implements Serializable { return this.graphicInfo != null && this.graphicInfo.getFrameStyle() != null && (this.graphicInfo.getFrameStyle() == FrameStyle.RETRO - || this.graphicInfo.getFrameStyle() == FrameStyle.LEA_ORIGINAL_DUAL_LAND_ART_BASIC); + || this.graphicInfo.getFrameStyle() == FrameStyle.LEA_ORIGINAL_DUAL_LAND_ART_BASIC + || this.graphicInfo.getFrameStyle() == FrameStyle.UGL_FULL_ART_BASIC); } } diff --git a/Mage/src/main/java/mage/cards/repository/TokenRepository.java b/Mage/src/main/java/mage/cards/repository/TokenRepository.java index 65d80a97c93..819e2b20335 100644 --- a/Mage/src/main/java/mage/cards/repository/TokenRepository.java +++ b/Mage/src/main/java/mage/cards/repository/TokenRepository.java @@ -271,6 +271,7 @@ public enum TokenRepository { res.add(createXmageToken(XMAGE_IMAGE_NAME_COPY, 11, "https://api.scryfall.com/cards/tacr/1/en?format=image")); res.add(createXmageToken(XMAGE_IMAGE_NAME_COPY, 12, "https://api.scryfall.com/cards/tpip/1/en?format=image")); res.add(createXmageToken(XMAGE_IMAGE_NAME_COPY, 13, "https://api.scryfall.com/cards/teoc/1/en?format=image")); + res.add(createXmageToken(XMAGE_IMAGE_NAME_COPY, 14, "https://api.scryfall.com/cards/tspm/1/en?format=image")); // City's Blessing // https://scryfall.com/search?q=type%3Atoken+include%3Aextras+unique%3Aprints+City%27s+Blessing+&unique=cards&as=grid&order=name diff --git a/Mage/src/main/java/mage/choices/ChoiceCreatureType.java b/Mage/src/main/java/mage/choices/ChoiceCreatureType.java index 733bb37ca78..7a71402bd62 100644 --- a/Mage/src/main/java/mage/choices/ChoiceCreatureType.java +++ b/Mage/src/main/java/mage/choices/ChoiceCreatureType.java @@ -79,7 +79,7 @@ public class ChoiceCreatureType extends ChoiceImpl { }); // exile - game.getExile().getAllCards(game, playerId).forEach(card -> { + game.getExile().getCardsOwned(game, playerId).forEach(card -> { list.addAll(card.getSubtype(game).stream().map(SubType::toString).collect(Collectors.toList())); }); }); diff --git a/Mage/src/main/java/mage/constants/MultiAmountType.java b/Mage/src/main/java/mage/constants/MultiAmountType.java index 398ca7a0fd8..1755424a611 100644 --- a/Mage/src/main/java/mage/constants/MultiAmountType.java +++ b/Mage/src/main/java/mage/constants/MultiAmountType.java @@ -15,6 +15,7 @@ public class MultiAmountType { public static final MultiAmountType P1P1 = new MultiAmountType("Add +1/+1 counters", "Distribute +1/+1 counters among creatures"); public static final MultiAmountType COUNTERS = new MultiAmountType("Choose counters", "Move counters"); + public static final MultiAmountType REMOVE_COUNTERS = new MultiAmountType("Choose counters", "Remove counters"); public static final MultiAmountType CHEAT_LANDS = new MultiAmountType("Choose lands", "Add lands to your battlefield", true); private final String title; diff --git a/Mage/src/main/java/mage/constants/SubType.java b/Mage/src/main/java/mage/constants/SubType.java index 4e6b7ca361d..50f81cc87e8 100644 --- a/Mage/src/main/java/mage/constants/SubType.java +++ b/Mage/src/main/java/mage/constants/SubType.java @@ -234,6 +234,7 @@ public enum SubType { ILLUSION("Illusion", SubTypeSet.CreatureType), IMP("Imp", SubTypeSet.CreatureType), INCARNATION("Incarnation", SubTypeSet.CreatureType), + INFINITY("Infinity", SubTypeSet.ArtifactType), INKLING("Inkling", SubTypeSet.CreatureType), INQUISITOR("Inquisitor", SubTypeSet.CreatureType), INSECT("Insect", SubTypeSet.CreatureType), @@ -402,6 +403,7 @@ public enum SubType { SQUIRREL("Squirrel", SubTypeSet.CreatureType), STARFISH("Starfish", SubTypeSet.CreatureType), STARSHIP("Starship", SubTypeSet.CreatureType, true), // Star Wars + STONE("Stone", SubTypeSet.ArtifactType), SULLUSTAN("Sullustan", SubTypeSet.CreatureType, true), // Star Wars SURRAKAR("Surrakar", SubTypeSet.CreatureType), SURVIVOR("Survivor", SubTypeSet.CreatureType), diff --git a/Mage/src/main/java/mage/counters/CounterType.java b/Mage/src/main/java/mage/counters/CounterType.java index 62104d11a95..c774348bd19 100644 --- a/Mage/src/main/java/mage/counters/CounterType.java +++ b/Mage/src/main/java/mage/counters/CounterType.java @@ -86,6 +86,7 @@ public enum CounterType { FELLOWSHIP("fellowship"), FETCH("fetch"), FILIBUSTER("filibuster"), + FILM("film"), FINALITY("finality"), FIRE("fire"), FIRST_STRIKE("first strike"), diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/ChosenCardTypePredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/ChosenCardTypePredicate.java new file mode 100644 index 00000000000..ab1e4ab1ec1 --- /dev/null +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/ChosenCardTypePredicate.java @@ -0,0 +1,36 @@ +package mage.filter.predicate.mageobject; + +import mage.MageObject; +import mage.constants.CardType; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; + +/** + * + * @author jmlundeen + */ +public enum ChosenCardTypePredicate implements ObjectSourcePlayerPredicate { + TRUE(true), FALSE(false); + + private final boolean value; + + ChosenCardTypePredicate(boolean value) { + this.value = value; + } + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + Object savedType = game.getState().getValue(input.getSourceId() + "_type"); + if (!(savedType instanceof String)) { + return false; + } + CardType cardType = CardType.fromString((String) savedType); + return input.getObject().getCardType(game).contains(cardType) == value; + } + + @Override + public String toString() { + return "Chosen card type"; + } +} diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/ManaValueCompareToCountersSourceCountPredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/ManaValueCompareToCountersSourceCountPredicate.java new file mode 100644 index 00000000000..721cb9bbf99 --- /dev/null +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/ManaValueCompareToCountersSourceCountPredicate.java @@ -0,0 +1,39 @@ +package mage.filter.predicate.mageobject; + +import mage.MageObject; +import mage.constants.ComparisonType; +import mage.counters.CounterType; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; + +import java.util.Optional; + +/** + * @author jmlundeen + */ +public class ManaValueCompareToCountersSourceCountPredicate implements ObjectSourcePlayerPredicate { + + private final CounterType counterType; + private final ComparisonType comparisonType; + + public ManaValueCompareToCountersSourceCountPredicate(CounterType counterType, ComparisonType comparisonType) { + this.counterType = counterType; + this.comparisonType = comparisonType; + } + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + int counterCount = Optional + .ofNullable(input.getSource().getSourcePermanentOrLKI(game)) + .map(permanent -> permanent.getCounters(game)) + .map(counters -> counters.getCount(counterType)) + .orElse(-1); // always false + return ComparisonType.compare(input.getObject().getManaValue(), comparisonType, counterCount); + } + + @Override + public String toString() { + return "mana value " + comparisonType.toString() + " to the number of " + counterType.getName() + " counters on {this}"; + } +} diff --git a/Mage/src/main/java/mage/filter/predicate/permanent/AttachedToAttachedPredicate.java b/Mage/src/main/java/mage/filter/predicate/permanent/AttachedToAttachedPredicate.java new file mode 100644 index 00000000000..13ebc21e42d --- /dev/null +++ b/Mage/src/main/java/mage/filter/predicate/permanent/AttachedToAttachedPredicate.java @@ -0,0 +1,30 @@ +package mage.filter.predicate.permanent; + +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.Optional; + +/** + * @author TheElk801 + */ +public enum AttachedToAttachedPredicate implements ObjectSourcePlayerPredicate { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + return Optional + .ofNullable(input.getSource().getSourcePermanentOrLKI(game)) + .map(Permanent::getAttachedTo) + .filter(input.getObject()::isAttachedTo) + .isPresent(); + } + + @Override + public String toString() { + return "attached to attached permanent"; + } + +} diff --git a/Mage/src/main/java/mage/filter/predicate/permanent/SaddledSourceThisTurnPredicate.java b/Mage/src/main/java/mage/filter/predicate/permanent/SaddledSourceThisTurnPredicate.java index f150d8244ea..77ed2d89118 100644 --- a/Mage/src/main/java/mage/filter/predicate/permanent/SaddledSourceThisTurnPredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/permanent/SaddledSourceThisTurnPredicate.java @@ -18,7 +18,7 @@ public enum SaddledSourceThisTurnPredicate implements ObjectSourcePlayerPredicat @Override public boolean apply(ObjectSourcePlayer input, Game game) { // for delayed triggers must use starting zcc (when delayed trigger created) - MageObjectReference startingMor = new MageObjectReference(input.getSourceId(), input.getSource().getSourceObjectZoneChangeCounter(), game); + MageObjectReference startingMor = new MageObjectReference(input.getSourceId(), input.getSource().getStackMomentSourceZCC(), game); return SaddledMountWatcher.checkIfSaddledThisTurn(input.getObject(), startingMor, game); } diff --git a/Mage/src/main/java/mage/game/Exile.java b/Mage/src/main/java/mage/game/Exile.java index 7d8b7bd5660..4342443b321 100644 --- a/Mage/src/main/java/mage/game/Exile.java +++ b/Mage/src/main/java/mage/game/Exile.java @@ -1,5 +1,6 @@ package mage.game; +import mage.abilities.Ability; import mage.cards.Card; import mage.filter.FilterCard; import mage.util.Copyable; @@ -61,24 +62,33 @@ public class Exile implements Serializable, Copyable { return null; } + /** + * Returns all cards in exile matching the filter. Use only for test framework. + * For card effects, instead use a method that checks owner or range of influence. + */ + @Deprecated public List getCards(FilterCard filter, Game game) { List allCards = getAllCards(game); return allCards.stream().filter(card -> filter.match(card, game)).collect(Collectors.toList()); } - @Deprecated // TODO: must use related request due game range like getAllCardsByRange + /** + * Returns all cards in exile. Use only for test framework. + * For card effects, instead use a method that checks owner or range of influence. + */ + @Deprecated public List getAllCards(Game game) { - return getAllCards(game, null); + return getCardsOwned(game, null); } /** - * Return exiled cards owned by a specific player. Use it in effects to find all cards in range. + * Returns all cards in exile owned by the specified player */ - public List getAllCards(Game game, UUID fromPlayerId) { + public List getCardsOwned(Game game, UUID ownerId) { List res = new ArrayList<>(); for (ExileZone exile : exileZones.values()) { for (Card card : exile.getCards(game)) { - if (fromPlayerId == null || card.isOwnedBy(fromPlayerId)) { + if (ownerId == null || card.isOwnedBy(ownerId)) { res.add(card); } } @@ -86,14 +96,37 @@ public class Exile implements Serializable, Copyable { return res; } - public List getAllCardsByRange(Game game, UUID controllerId) { + /** + * Returns all cards in exile matching the filter, owned by the specified player + */ + public Set getCardsOwned(FilterCard filter, UUID playerId, Ability source, Game game) { + return getCardsOwned(game, playerId) + .stream() + .filter(card -> filter.match(card, playerId, source, game)) + .collect(Collectors.toCollection(LinkedHashSet::new)); + } + + /** + * Returns all cards in exile in range of the specified player + */ + public List getCardsInRange(Game game, UUID controllerId) { List res = new ArrayList<>(); for (UUID playerId : game.getState().getPlayersInRange(controllerId, game)) { - res.addAll(getAllCards(game, playerId)); + res.addAll(getCardsOwned(game, playerId)); } return res; } + /** + * Returns all cards in exile matching the filter, in range of the specified player + */ + public Set getCardsInRange(FilterCard filter, UUID playerId, Ability source, Game game) { + return getCardsInRange(game, playerId) + .stream() + .filter(card -> filter.match(card, playerId, source, game)) + .collect(Collectors.toCollection(LinkedHashSet::new)); + } + public boolean removeCard(Card card) { for (ExileZone exile : exileZones.values()) { if (exile.contains(card.getId())) { diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index 978171210d6..62a2b2f17cd 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -2264,7 +2264,7 @@ public abstract class GameImpl implements Game { // // There are two possibility for the zcc: // 1/ the source is an Ability with a valid (not 0) zcc, and we must use the same. - int zcc = source.getSourceObjectZoneChangeCounter(); + int zcc = source.getStackMomentSourceZCC(); if (zcc == 0) { // 2/ the source has not a valid zcc (it is most likely a StaticAbility instantiated at beginning of game) // we use the source objects's zcc diff --git a/Mage/src/main/java/mage/game/command/emblems/KayaTheInexorableEmblem.java b/Mage/src/main/java/mage/game/command/emblems/KayaTheInexorableEmblem.java index 0dacb1e09cb..940744eddde 100644 --- a/Mage/src/main/java/mage/game/command/emblems/KayaTheInexorableEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/KayaTheInexorableEmblem.java @@ -48,7 +48,6 @@ public class KayaTheInexorableEmblem extends Emblem { class KayaTheInexorableEmblemEffect extends OneShotEffect { - private static final FilterCard filter = new FilterOwnedCard(); private static final FilterCard filter2 = new FilterCard(); private static final Set choices = new LinkedHashSet<>(); @@ -94,7 +93,7 @@ class KayaTheInexorableEmblemEffect extends OneShotEffect { cards.addAll(player.getGraveyard()); break; case "Exile": - cards.addAllCards(game.getExile().getCards(filter, game)); + cards.addAllCards(game.getExile().getCardsOwned(game, player.getId())); break; } return CardUtil.castSpellWithAttributesForFree(player, source, game, cards, filter2); diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index 6a74a11ad23..c4a812dc4cb 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -1537,7 +1537,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { @Override public boolean canBlock(UUID attackerId, Game game) { - if (tapped && game.getState().getContinuousEffects().asThough(this.getId(), AsThoughEffectType.BLOCK_TAPPED, null, this.getControllerId(), game).isEmpty() || isBattle(game) || isSuspected()) { + if (tapped && game.getState().getContinuousEffects().asThough(this.getId(), AsThoughEffectType.BLOCK_TAPPED, null, this.getControllerId(), game).isEmpty() || isBattle(game) || !isCreature(game) || isSuspected()) { return false; } Permanent attacker = game.getPermanent(attackerId); diff --git a/Mage/src/main/java/mage/game/permanent/token/BananaToken.java b/Mage/src/main/java/mage/game/permanent/token/BananaToken.java index ea238a15cfe..10c37377784 100644 --- a/Mage/src/main/java/mage/game/permanent/token/BananaToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/BananaToken.java @@ -13,7 +13,7 @@ import mage.constants.CardType; public final class BananaToken extends TokenImpl { public BananaToken() { - super("Banana", "colorless artifact token named Banana with \"{T}, Sacrifice this artifact: Add {R} or {G}. You gain 2 life.\""); + super("Banana", "colorless artifact token named Banana with \"{T}, Sacrifice this token: Add {R} or {G}. You gain 2 life.\""); cardType.add(CardType.ARTIFACT); // {T}, Sacrifice this artifact: Add {R} or {G}. You gain 2 life. @@ -22,7 +22,7 @@ public final class BananaToken extends TokenImpl { ability.addEffect(new GainLifeEffect(2)); this.addAbility(ability); ability = new GreenManaAbility(); - ability.addCost(new SacrificeSourceCost().setText("sacrifice this artifact")); + ability.addCost(new SacrificeSourceCost().setText("sacrifice this token")); ability.addEffect(new GainLifeEffect(2)); this.addAbility(ability); } diff --git a/Mage/src/main/java/mage/game/permanent/token/HumanCitizenToken.java b/Mage/src/main/java/mage/game/permanent/token/HumanCitizenToken.java new file mode 100644 index 00000000000..dd094f16380 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/HumanCitizenToken.java @@ -0,0 +1,31 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author LoneFox + */ +public final class HumanCitizenToken extends TokenImpl { + + public HumanCitizenToken() { + super("Human Token", "1/1 green and white Human Citizen creature token"); + cardType.add(CardType.CREATURE); + color.setGreen(true); + color.setWhite(true); + subtype.add(SubType.HUMAN); + subtype.add(SubType.CITIZEN); + power = new MageInt(1); + toughness = new MageInt(1); + } + + private HumanCitizenToken(final HumanCitizenToken token) { + super(token); + } + + @Override + public HumanCitizenToken copy() { + return new HumanCitizenToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/IllusionVillainToken.java b/Mage/src/main/java/mage/game/permanent/token/IllusionVillainToken.java new file mode 100644 index 00000000000..9862f81e717 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/IllusionVillainToken.java @@ -0,0 +1,30 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class IllusionVillainToken extends TokenImpl { + + public IllusionVillainToken() { + super("Illusion Token", "3/3 blue Illusion Villain creature token"); + cardType.add(CardType.CREATURE); + color.setBlue(true); + + subtype.add(SubType.ILLUSION); + subtype.add(SubType.VILLAIN); + power = new MageInt(3); + toughness = new MageInt(3); + } + + private IllusionVillainToken(final IllusionVillainToken token) { + super(token); + } + + public IllusionVillainToken copy() { + return new IllusionVillainToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/permanent/token/RobotFlyingToken.java b/Mage/src/main/java/mage/game/permanent/token/RobotFlyingToken.java new file mode 100644 index 00000000000..43350acd33a --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/RobotFlyingToken.java @@ -0,0 +1,31 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.FlyingAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * @author TheElk801 + */ +public final class RobotFlyingToken extends TokenImpl { + + public RobotFlyingToken() { + super("Robot Token", "1/1 colorless Robot artifact creature tokens with flying"); + cardType.add(CardType.ARTIFACT); + cardType.add(CardType.CREATURE); + subtype.add(SubType.ROBOT); + power = new MageInt(1); + toughness = new MageInt(1); + + addAbility(FlyingAbility.getInstance()); + } + + private RobotFlyingToken(final RobotFlyingToken token) { + super(token); + } + + public RobotFlyingToken copy() { + return new RobotFlyingToken(this); + } +} diff --git a/Mage/src/main/java/mage/game/stack/StackAbility.java b/Mage/src/main/java/mage/game/stack/StackAbility.java index a6f80c416c1..66211b60147 100644 --- a/Mage/src/main/java/mage/game/stack/StackAbility.java +++ b/Mage/src/main/java/mage/game/stack/StackAbility.java @@ -678,8 +678,8 @@ public class StackAbility extends StackObjectImpl implements Ability { ability.initSourceObjectZoneChangeCounter(game, force); } @Override - public int getSourceObjectZoneChangeCounter() { - return ability.getSourceObjectZoneChangeCounter(); + public int getStackMomentSourceZCC() { + return ability.getStackMomentSourceZCC(); } @Override diff --git a/Mage/src/main/java/mage/target/TargetCard.java b/Mage/src/main/java/mage/target/TargetCard.java index af7b05fc114..7824cfb22da 100644 --- a/Mage/src/main/java/mage/target/TargetCard.java +++ b/Mage/src/main/java/mage/target/TargetCard.java @@ -134,7 +134,7 @@ public class TargetCard extends TargetObject { protected static Set getAllPossibleTargetInExile(Game game, Player player, UUID sourceControllerId, Ability source, FilterCard filter, boolean isNotTarget) { Set possibleTargets = new HashSet<>(); UUID sourceId = source != null ? source.getSourceId() : null; - for (Card card : game.getExile().getAllCardsByRange(game, sourceControllerId)) { + for (Card card : game.getExile().getCardsInRange(game, sourceControllerId)) { if (filter.match(card, sourceControllerId, source, game)) { possibleTargets.add(card.getId()); } diff --git a/Mage/src/main/java/mage/target/TargetSource.java b/Mage/src/main/java/mage/target/TargetSource.java index 5ffbc7639f2..f1f7bc61194 100644 --- a/Mage/src/main/java/mage/target/TargetSource.java +++ b/Mage/src/main/java/mage/target/TargetSource.java @@ -115,7 +115,7 @@ public class TargetSource extends TargetObject { } } } - for (Card card : game.getExile().getAllCards(game)) { + for (Card card : game.getExile().getCardsInRange(game, sourceControllerId)) { if (filter.match(card, sourceControllerId, source, game)) { possibleTargets.add(card.getId()); } diff --git a/Mage/src/main/java/mage/target/common/TargetCardInExile.java b/Mage/src/main/java/mage/target/common/TargetCardInExile.java index 292e958cb1c..4cdfde920eb 100644 --- a/Mage/src/main/java/mage/target/common/TargetCardInExile.java +++ b/Mage/src/main/java/mage/target/common/TargetCardInExile.java @@ -52,7 +52,7 @@ public class TargetCardInExile extends TargetCard { Set possibleTargets = new HashSet<>(); if (zoneId == null) { // no specific exile zone - for (Card card : game.getExile().getAllCardsByRange(game, sourceControllerId)) { + for (Card card : game.getExile().getCardsInRange(game, sourceControllerId)) { if (filter.match(card, sourceControllerId, source, game)) { possibleTargets.add(card.getId()); } diff --git a/Mage/src/main/java/mage/target/common/TargetPermanentOrSuspendedCard.java b/Mage/src/main/java/mage/target/common/TargetPermanentOrSuspendedCard.java index 144b3aa29b3..a18a5ba90f9 100644 --- a/Mage/src/main/java/mage/target/common/TargetPermanentOrSuspendedCard.java +++ b/Mage/src/main/java/mage/target/common/TargetPermanentOrSuspendedCard.java @@ -66,7 +66,7 @@ public class TargetPermanentOrSuspendedCard extends TargetImpl { possibleTargets.add(permanent.getId()); } } - for (Card card : game.getExile().getAllCards(game)) { + for (Card card : game.getExile().getCardsInRange(game, sourceControllerId)) { if (filter.match(card, sourceControllerId, source, game)) { possibleTargets.add(card.getId()); } diff --git a/Mage/src/main/java/mage/target/common/TargetTriggeredAbility.java b/Mage/src/main/java/mage/target/common/TargetTriggeredAbility.java deleted file mode 100644 index b771bb39943..00000000000 --- a/Mage/src/main/java/mage/target/common/TargetTriggeredAbility.java +++ /dev/null @@ -1,96 +0,0 @@ -package mage.target.common; - -import mage.abilities.Ability; -import mage.constants.Zone; -import mage.filter.Filter; -import mage.filter.FilterStackObject; -import mage.game.Game; -import mage.game.stack.StackObject; -import mage.target.TargetObject; - -import java.util.Set; -import java.util.UUID; -import java.util.stream.Collectors; - -/** - * @author Styxo - */ -public class TargetTriggeredAbility extends TargetObject { - - protected final FilterStackObject filter; - - public TargetTriggeredAbility(FilterStackObject filter) { - this.minNumberOfTargets = 1; - this.maxNumberOfTargets = 1; - this.zone = Zone.STACK; - this.targetName = filter.getMessage(); - this.filter = filter; - } - - protected TargetTriggeredAbility(final TargetTriggeredAbility target) { - super(target); - this.filter = target.filter.copy(); - } - - @Override - public boolean canTarget(UUID id, Ability source, Game game) { - // 114.4. A spell or ability on the stack is an illegal target for itself. - if (source != null && source.getSourceId().equals(id)) { - return false; - } - - StackObject stackObject = game.getStack().getStackObject(id); - return isTriggeredAbility(stackObject) - && source != null - && filter.match(stackObject, source.getControllerId(), source, game); - } - - @Override - public boolean canChoose(UUID sourceControllerId, Ability source, Game game) { - for (StackObject stackObject : game.getStack()) { - if (isTriggeredAbility(stackObject) - && filter.match(stackObject, sourceControllerId, source, game)) { - return true; - } - } - return false; - } - - @Override - public boolean canChoose(UUID sourceControllerId, Game game) { - return game.getStack() - .stream() - .anyMatch(TargetTriggeredAbility::isTriggeredAbility); - } - - @Override - public Set possibleTargets(UUID sourceControllerId, Ability source, Game game) { - return game.getStack().stream() - .filter(TargetTriggeredAbility::isTriggeredAbility) - .map(stackObject -> stackObject.getStackAbility().getId()) - .filter(this::notContains) - .collect(Collectors.toSet()); - } - - @Override - public TargetTriggeredAbility copy() { - return new TargetTriggeredAbility(this); - } - - @Override - public Filter getFilter() { - return filter; - } - - static boolean isTriggeredAbility(StackObject stackObject) { - if (stackObject == null) { - return false; - } - if (stackObject instanceof Ability) { - Ability ability = (Ability) stackObject; - return ability.getAbilityType().isTriggeredAbility(); - } - return false; - } - -} diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index abc4f0bbe53..65a79575a4b 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -615,7 +615,7 @@ public final class CardUtil { } public static UUID getExileZoneId(Game game, Ability source, int offset) { - return getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter() + offset); + return getExileZoneId(game, source.getSourceId(), source.getStackMomentSourceZCC() + offset); } public static UUID getExileZoneId(Game game, UUID objectId, int zoneChangeCounter) { @@ -758,7 +758,7 @@ public final class CardUtil { MageObject sourceObject = game.getObject(source); if (sourceObject != null) { title = sourceObject.getIdName() - + " [" + source.getSourceObjectZoneChangeCounter() + "]" + + " [" + source.getStackMomentSourceZCC() + "]" + (textSuffix == null ? "" : " " + textSuffix); } else { title = textSuffix == null ? "" : textSuffix; @@ -1812,7 +1812,7 @@ public final class CardUtil { */ public static int getActualSourceObjectZoneChangeCounter(Game game, Ability source) { // current object zcc, find from source object (it can be permanent or spell on stack) - int zcc = source.getSourceObjectZoneChangeCounter(); + int zcc = source.getStackMomentSourceZCC(); if (zcc == 0) { // if ability is not activated yet then use current object's zcc (example: triggered etb ability checking the kicker conditional) zcc = game.getState().getZoneChangeCounter(source.getSourceId()); diff --git a/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java b/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java index d97285e86dc..259a0b6588d 100644 --- a/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java @@ -86,7 +86,7 @@ public class SpellsCastWatcher extends Watcher { .map(Spell::getCard) .map(Card::getMainCard) .anyMatch(card -> card.getId().equals(source.getSourceId()) - && card.getZoneChangeCounter(game) == source.getSourceObjectZoneChangeCounter())) { + && card.getZoneChangeCounter(game) == source.getStackMomentSourceZCC())) { return entry.getKey(); } } diff --git a/Mage/src/main/resources/tokens-database.txt b/Mage/src/main/resources/tokens-database.txt index b7dab103f95..9875e23e7fc 100644 --- a/Mage/src/main/resources/tokens-database.txt +++ b/Mage/src/main/resources/tokens-database.txt @@ -2871,6 +2871,14 @@ |Generate|TOK:EOC|Shapeshifter|||ShapeshifterDeathtouchToken| |Generate|TOK:EOC|Thopter|||ThopterColorlessToken| +#SPM +|Generate|TOK:SPM|Food|||FoodToken| +|Generate|TOK:SPM|Human|||HumanCitizenToken| +|Generate|TOK:SPM|Illusion|||IllusionVillainToken| +|Generate|TOK:SPM|Robot|||RobotFlyingToken| +|Generate|TOK:SPM|Spider|||Spider21Token| +|Generate|TOK:SPM|Treasure|||TreasureToken| + # JVC |Generate|TOK:JVC|Elemental Shaman|||ElementalShamanToken| @@ -2943,3 +2951,9 @@ # PL23 |Generate|TOK:PL23|Food|||FoodToken| + +# PL24 +|Generate|TOK:PL24|Dragon|||DragonToken| + +# PL25 +|Generate|TOK:PL25|Snake|||SnakeToken| \ No newline at end of file diff --git a/Utils/cardInfo.tmpl b/Utils/cardInfo.tmpl new file mode 100644 index 00000000000..5b04c6a01b1 --- /dev/null +++ b/Utils/cardInfo.tmpl @@ -0,0 +1,7 @@ + /* + [=$cardName=] + [=$manaCost=] + [=$typeLine=] + [=$abilities=][= $powerToughness ? "\n $powerToughness" : "" =] + */ + private static final String [=$classNameLower=] = "[=$cardName=]"; \ No newline at end of file diff --git a/Utils/cardTest.tmpl b/Utils/cardTest.tmpl new file mode 100644 index 00000000000..e6cab7e6c31 --- /dev/null +++ b/Utils/cardTest.tmpl @@ -0,0 +1,18 @@ +package org.mage.test.cards.single.[=$setCode=]; + +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author [=$author=] + */ +public class [=$className=]Test extends CardTestPlayerBase { + +[=$cardInfo=] + + @Test + public void test[=$className=]() { + setStrictChooseMode(true); + } +} \ No newline at end of file diff --git a/Utils/gen-card-test.pl b/Utils/gen-card-test.pl new file mode 100644 index 00000000000..1817ed3a63f --- /dev/null +++ b/Utils/gen-card-test.pl @@ -0,0 +1,198 @@ +#!/usr/bin/perl -w + +#author: North/Jmlundeen +=begin comment + +To use this script you can call it with perl ./gen-card-test.pl "Storm Crow" "Lightning Bolt" +The first argument (Storm Crow) is the main card to generate a test class for. +The cards after (Lighting Bolt) will place card info and create a variable inside the test class. +You can add as many additional cards as you like. + +You can also call the script without arguments and it will prompt you for card names +=cut + + +use Text::Template; +use strict; +use File::Path qw(make_path); + +my $authorFile = 'author.txt'; +my $dataFile = 'mtg-cards-data.txt'; +my $setsFile = 'mtg-sets-data.txt'; +my $knownSetsFile = 'known-sets.txt'; +my $keywordsFile = 'keywords.txt'; + +my %cards; +my %sets; +my %knownSets; +my %keywords; + +sub toCamelCase { + my $string = $_[0]; + $string =~ s/\b([\w']+)\b/ucfirst($1)/ge; + $string =~ s/[-,\s\':.!\/]//g; + $string; +} + +sub fixCost { + my $string = $_[0]; + $string =~ s/{([2BUGRW])([2BUGRW])}/{$1\/$2}/g; + $string; +} + +sub generateCardInfo { + my ($cardName, $infoTemplate) = @_; + + if (!exists $cards{$cardName}) { + warn "Card name doesn't exist: $cardName (skipping)\n"; + return ""; + } + + my %vars; + $vars{'classNameLower'} = lcfirst(toCamelCase($cardName)); + my @card; + + foreach my $setName (keys %{$cards{$cardName}}) { + @card = @{(values(%{$cards{$cardName}{$setName}}))[0]}; + last; # Just get the first one for additional cards + } + + $vars{'cardName'} = $card[0]; + $vars{'manaCost'} = $card[4]; + $vars{'typeLine'} = $card[5]; + + my $cardAbilities = $card[8]; + my @abilities = split(/\$/, $cardAbilities); + my $abilitiesFormatted = join("\n ", @abilities); + $vars{'abilities'} = $abilitiesFormatted; + if ($card[6]) { + $vars{'powerToughness'} = "$card[6]/$card[7]" + } + + return $infoTemplate->fill_in(HASH => \%vars); +} + +my $author; +if (-e $authorFile) { + open(DATA, $authorFile) || die "can't open $authorFile : $!"; + $author = ; + chomp $author; + close(DATA); +} else { + $author = 'anonymous'; +} + +open(DATA, $dataFile) || die "can't open $dataFile : $!"; +while (my $line = ) { + my @data = split('\\|', $line); + $cards{$data[0]}{$data[1]}{$data[2]} = \@data; +} +close(DATA); + +open(DATA, $setsFile) || die "can't open $setsFile : $!"; +while (my $line = ) { + my @data = split('\\|', $line); + $sets{$data[0]} = $data[1]; +} +close(DATA); + +open(DATA, $knownSetsFile) || die "can't open $knownSetsFile : $!"; +while (my $line = ) { + my @data = split('\\|', $line); + $knownSets{$data[0]} = $data[1]; +} +close(DATA); + +open(DATA, $keywordsFile) || die "can't open $keywordsFile : $!"; +while (my $line = ) { + my @data = split('\\|', $line); + $keywords{toCamelCase($data[0])} = $data[1]; +} +close(DATA); + +# Get card names from arguments +my @cardNames = @ARGV; +if (@cardNames == 0) { + print 'Enter a card name: '; + my $input = ; + chomp $input; + push @cardNames, $input; + + # Prompt for additional cards + print 'Enter additional card names (one per line, empty line to finish): '; + while (my $additionalCard = ) { + chomp $additionalCard; + last if $additionalCard eq ''; # Empty line ends input + push @cardNames, $additionalCard; + } +} + +# Main card is the first one +my $mainCardName = $cardNames[0]; +my @additionalCards = @cardNames[1..$#cardNames]; + +if (!exists $cards{$mainCardName}) { + my $possible; + foreach $possible (sort keys(%cards)) { + if ($possible =~ m/$mainCardName/img && $mainCardName =~ m/..../) { + print("Did you mean $possible?\n"); + } + } + die "Card name doesn't exist: $mainCardName\n"; +} + +my $cardTemplate = 'cardTest.tmpl'; +my $cardInfoTemplate = 'cardInfo.tmpl'; +my $originalName = $mainCardName; +my $setCode; + +# Generate lines to corresponding sets +my %vars; +$vars{'className'} = toCamelCase($mainCardName); +$vars{'classNameLower'} = lcfirst(toCamelCase($mainCardName)); +$vars{'cardNameFirstLetter'} = lc substr($mainCardName, 0, 1); + +foreach my $setName (keys %{$cards{$originalName}}) { + $setCode = lc($sets{$setName}); +} + +# Check if card is already implemented +my $fileName = "../Mage.Tests/src/test/java/org/mage/test/cards/single/" . $setCode . "/" . toCamelCase($mainCardName) . "Test.java"; +if (-e $fileName) { + die "$mainCardName is already implemented.\n$fileName\n"; +} + +# Create directory if it doesn't exist +my $dir = "../Mage.Tests/src/test/java/org/mage/test/cards/single/" . $setCode; +make_path($dir) unless -d $dir; + +# Generate the card templates +my $result; +my $template = Text::Template->new(TYPE => 'FILE', SOURCE => $cardTemplate, DELIMITERS => [ '[=', '=]' ]); +my $infoTemplate = Text::Template->new(TYPE => 'FILE', SOURCE => $cardInfoTemplate, DELIMITERS => [ '[=', '=]' ]); + +$vars{'author'} = $author; +$vars{'setCode'} = $setCode; + +# Generate main card info +my $allCardInfo = generateCardInfo($mainCardName, $infoTemplate); + +# Generate additional card info templates +foreach my $additionalCard (@additionalCards) { + my $additionalInfo = generateCardInfo($additionalCard, $infoTemplate); + if (defined $additionalInfo) { + $allCardInfo .= "\n\n" . $additionalInfo; + } +} + +$vars{'cardInfo'} = $allCardInfo; +$result = $template->fill_in(HASH => \%vars); + +open CARD, "> $fileName"; +print CARD $result; +close CARD; + +print "$fileName\n"; +if (@additionalCards > 0) { + print "Additional cards included: " . join(", ", @additionalCards) . "\n"; +} \ No newline at end of file diff --git a/Utils/gen-list-unimplemented-cards-for-set.pl b/Utils/gen-list-unimplemented-cards-for-set.pl index 3d8d2849a3c..560f76812f1 100755 --- a/Utils/gen-list-unimplemented-cards-for-set.pl +++ b/Utils/gen-list-unimplemented-cards-for-set.pl @@ -1,13 +1,14 @@ #!/usr/bin/perl -w #author: North - +use Text::Template; use strict; use Scalar::Util qw(looks_like_number); my $dataFile = "mtg-cards-data.txt"; my $setsFile = "mtg-sets-data.txt"; my $knownSetsFile = "known-sets.txt"; +my $templateFile = "issue_tracker.tmpl"; my %sets; my %knownSets; @@ -16,12 +17,13 @@ my @setCards; open (DATA, $knownSetsFile) || die "can't open $knownSetsFile"; while(my $line = ) { + chomp $line; my @data = split('\\|', $line); $knownSets{$data[0]} = $data[1]; - #print ("$data[0] ===> $data[1]\n"); } close(DATA); +my @basicLands = ("Plains", "Island", "Swamp", "Mountain", "Forest"); # gets the set name my $setName = $ARGV[0]; @@ -29,7 +31,6 @@ if(!$setName) { print 'Enter a set name: '; $setName = ; chomp $setName; - $setName = $setName; } while (!defined ($knownSets{$setName})) @@ -49,13 +50,12 @@ while (!defined ($knownSets{$setName})) print 'Enter a set name: '; $setName = ; - $setName = $setName; chomp $setName; } - open (DATA, $dataFile) || die "can't open $dataFile"; while(my $line = ) { + chomp $line; my @data = split('\\|', $line); if ($data[1] eq $setName) { push(@setCards, \@data); @@ -65,70 +65,97 @@ close(DATA); open (DATA, $setsFile) || die "can't open $setsFile"; while(my $line = ) { + chomp $line; my @data = split('\\|', $line); $sets{$data[0]}= $data[1]; } close(DATA); - sub cardSort { - if (!looks_like_number(@{$a}[2])) { return -1; } - if (!looks_like_number(@{$b}[2])) { return 1; } - if (@{$a}[2] < @{$b}[2]) { return -1; } - elsif (@{$a}[2] == @{$b}[2]) { return 0;} - elsif (@{$a}[2] > @{$b}[2]) { return 1; } + if (!looks_like_number(@{$a}[2])) { return -1; } + if (!looks_like_number(@{$b}[2])) { return 1; } + if (@{$a}[2] < @{$b}[2]) { return -1; } + elsif (@{$a}[2] == @{$b}[2]) { return 0;} + elsif (@{$a}[2] > @{$b}[2]) { return 1; } } sub toCamelCase { my $string = $_[0]; $string =~ s/\b([\w']+)\b/ucfirst($1)/ge; - $string =~ s/[-,\s\']//g; + $string =~ s/[-,\s\'\.!@#*\(\)]//g; $string; } -# TODO: check for basic lands with ending 1,2,3,4,5 ... +# Check which cards are implemented my %cardNames; -my $toPrint = ''; -my $setAbbr = $sets{$setName}; -foreach my $card (sort cardSort @setCards) { - my $className = toCamelCase(@{$card}[0]); +my %seenCards; # Track which card names we've already processed +my @implementedCards; +my @unimplementedCards; +my $previousCollectorNumber = -1; +my %vars; - $cardNames {@{$card}[0]} = 1; +my $setAbbr = $sets{$setName}; + +foreach my $card (sort cardSort @setCards) { + my $className = toCamelCase(@{$card}[0]); + if ($className ~~ @basicLands) { + next; + } + + my $cardName = @{$card}[0]; + my $collectorNumber = @{$card}[2]; + + # Skip if we've already processed this card name or is the back face of a card + if (exists $seenCards{$cardName} or $previousCollectorNumber == $collectorNumber) { + $seenCards{$cardName} = 1; + next; + } + $seenCards{$cardName} = 1; + $previousCollectorNumber = $collectorNumber; my $currentFileName = "../Mage.Sets/src/mage/cards/" . lc(substr($className, 0, 1)) . "/" . $className . ".java"; - if(! -e $currentFileName) { - $cardNames {@{$card}[0]} = 0; - if ($toPrint) { - $toPrint .= "\n"; - } - my $cardName = @{$card}[0]; - $cardName =~ s/ /+/g; - $toPrint .= "@{$card}[2]|[@{$card}[0]](https://scryfall.com/search?q=!\"$cardName\" e:$setAbbr)"; - } -} + my $cardNameForUrl = $cardName; + $cardNameForUrl =~ s/ //g; + my $cardEntry = "- [ ] In progress -- [$cardName](https://scryfall.com/search?q=!\"$cardNameForUrl\" e:$setAbbr)"; -open CARD, "> " . lc($sets{$setName}) ."_unimplemented.txt"; -print CARD $toPrint; -close CARD; - - -print ("Unimplemented cards are here: " . lc($sets{$setName}) ."_unimplemented.txt\n"); - -open ISSUE_TRACKER, "> " . lc($sets{$setName}) ."_issue_tracker.txt"; -print ISSUE_TRACKER "# Cards in set:\n"; - - -my $cn; -foreach $cn (sort keys (%cardNames)) -{ - my $x_or_not = "[ ]"; - if ($cardNames {$cn} == 1) - { - $x_or_not = "[x]"; + if(-e $currentFileName) { + # Card is implemented + $cardNames{$cardName} = 1; + my $implementedEntry = "- [x] Done -- [$cardName](https://scryfall.com/search?q=!\"$cardNameForUrl\" e:$setAbbr)"; + push(@implementedCards, $implementedEntry); + } else { + # Card is not implemented + $cardNames{$cardName} = 0; + push(@unimplementedCards, $cardEntry); } - my $cn2 = $cn; - $cn2 =~ s/ /+/g; - print ISSUE_TRACKER "- $x_or_not [$cn](https://scryfall.com/search?q=!\"$cn2\" e:$setAbbr)\n"; } -close ISSUE_TRACKER; -print ("Tracking Issue text for a new Github issue (similar to https://github.com/magefree/mage/issues/2215): " . lc($setAbbr) ."_issue_tracker.txt\n"); + +# Build the unimplemented URL for Scryfall +my $unimplementedUrl = "https://scryfall.com/search?q="; +my @unimplementedNames; +foreach my $cardName (sort keys %cardNames) { + if ($cardNames{$cardName} == 0) { + my $urlCardName = $cardName; + $urlCardName =~ s/ //g; + $urlCardName =~ s/"/\\"/g; # Escape quotes + push(@unimplementedNames, "!\"$urlCardName\""); + } +} +$unimplementedUrl .= join("or", @unimplementedNames) . "&unique=cards"; + +# Read template file +my $template = Text::Template->new(TYPE => 'FILE', SOURCE => $templateFile, DELIMITERS => [ '[=', '=]' ]); +$vars{'unimplemented'} = join("\n", @unimplementedCards); +$vars{'implemented'} = join("\n", @implementedCards); +$vars{'setName'} = $setName; +$vars{'unimplementedUrl'} = $unimplementedUrl; +my $result = $template->fill_in(HASH => \%vars); +# Write the final issue tracker file +my $outputFile = lc($sets{$setName}) . "_issue_tracker.txt"; +open(OUTPUT, "> $outputFile") || die "can't open $outputFile for writing"; +print OUTPUT $result; +close(OUTPUT); + +print "Issue tracker generated: $outputFile\n"; +print "Implemented cards: " . scalar(@implementedCards) . "\n"; +print "Unimplemented cards: " . scalar(@unimplementedCards) . "\n"; \ No newline at end of file diff --git a/Utils/issue_tracker.tmpl b/Utils/issue_tracker.tmpl new file mode 100644 index 00000000000..17b7b196da2 --- /dev/null +++ b/Utils/issue_tracker.tmpl @@ -0,0 +1,18 @@ +This checklist is here to help manage the implementation of [=$setName=]. If a card is marked as being in progress then someone is working on it. + +If you're new to implementing cards then you likely don't have permission to check things off. This is totally fine! We still appreciate your contributions so just leave a comment to let us know that you're working on it. + +Don't worry about moving things from in progress to completed either, there's a script that handles that, and don't worry about fixing text issues as those are usually handled when the set is done. + +[All Sets](https://github.com/magefree/mage/wiki/Set-implementation-list) + +# Unimplemented Cards + +[=$unimplemented=] + +[Scryfall gallery of everything currently unimplemented]([=$unimplementedUrl=]) +# Implemented Cards +
Click to expand + +[=$implemented=] +
diff --git a/Utils/mtg-cards-data.txt b/Utils/mtg-cards-data.txt index e2c195fa7b8..96a670e932f 100644 --- a/Utils/mtg-cards-data.txt +++ b/Utils/mtg-cards-data.txt @@ -59833,71 +59833,198 @@ Aang, Master of Elements|Avatar: The Last Airbender|363|M||Legendary Creature - Hakoda, Selfless Commander|Avatar: The Last Airbender|366|R|{3}{W}|Legendary Creature - Human Warrior Ally|3|5|Vigilance$You may look at the top card of your library any time.$You may cast Ally spells from the top of your library.$Sacrifice Hakoda: Creatures you control get +0/+5 and gain indestructible until end of turn.| Sokka, Bold Boomeranger|Avatar: The Last Airbender|383|R|{U}{R}|Legendary Creature - Human Warrior Ally|1|1|When Sokka enters, discard up to two cards, then draw that many cards.$Whenever you cast an artifact or Lesson spell, put a +1/+1 counter on Sokka.| Anti-Venom, Horrifying Healer|Marvel's Spider-Man|1|M|{W}{W}{W}{W}{W}|Legendary Creature - Symbiote Hero|5|5|When Anti-Venom enters, if he was cast, return target creature card from your graveyard to the battlefield.$If damage would be dealt to Anti-Venom, prevent that damage and put that many +1/+1 counters on him.| +Arachne, Psionic Weaver|Marvel's Spider-Man|2|R|{2}{W}|Legendary Creature - Spider Human Hero|3|3|Web-slinging {W}$As Arachne enters, look at target opponent's hand, then choose a noncreature card type.$Spells of the chosen type cost {1} more to cast.| Aunt May|Marvel's Spider-Man|3|U|{W}|Legendary Creature - Human Citizen|0|2|Whenever another creature you control enters, you gain 1 life. If it's a Spider, put a +1/+1 counter on it.| +City Pigeon|Marvel's Spider-Man|4|C|{W}|Creature - Bird|1|1|Flying$When this creature leaves the battlefield, create a Food token.| +Costume Closet|Marvel's Spider-Man|5|U|{1}{W}|Artifact|||This artifact enters with two +1/+1 counters on it.${T}: Move a +1/+1 counter from this artifact onto target creature you control. Activate only as a sorcery.$Whenever a modified creature you control leaves the battlefield, put a +1/+1 counter on this artifact.| Daily Bugle Reporters|Marvel's Spider-Man|6|C|{3}{W}|Creature - Human Citizen|2|3|When this creature enters, choose one --$* Puff Piece -- Put a +1/+1 counter on each of up to two target creatures.$* Investigative Journalism -- Return target creature card with mana value 2 or less from your graveyard to your hand.| +Flash Thompson, Spider-Fan|Marvel's Spider-Man|7|U|{1}{W}|Legendary Creature - Human Citizen|2|2|Flash$When Flash Thompson enters, choose one or both--$* Heckle -- Tap target creature.$* Hero Worship -- Untap target creature.| +Friendly Neighborhood|Marvel's Spider-Man|8|R|{3}{W}|Enchantment - Aura|||Enchant land$When this Aura enters, create three 1/1 green and white Human Citizen creature tokens.$Enchanted land has "{1}, {T}: Target creature gets +1/+1 until end of turn for each creature you control. Activate only as a sorcery."| Origin of Spider-Man|Marvel's Spider-Man|9|R|{1}{W}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Create a 2/1 green Spider creature token with reach.$II -- Put a +1/+1 counter on target creature you control. It becomes a legendary Spider Hero in addition to its other types.$III -- Target creature you control gains double strike until end of turn.| Peter Parker|Marvel's Spider-Man|10|M|{1}{W}|Legendary Creature - Human Scientist Hero|0|1|When Peter Parker enters, create a 2/1 green Spider creature token with reach.${1}{G}{W}{U}: Transform Peter Parker. Activate only as a sorcery.| Amazing Spider-Man|Marvel's Spider-Man|10|M|{1}{G}{W}{U}|Legendary Creature - Spider Human Hero|4|4|Vigilance, reach$Each legendary spell you cast that's one or more colors has web-slinging {G}{W}{U}.| +Rent Is Due|Marvel's Spider-Man|11|R|{W}|Enchantment|||At the beginning of your end step, you may tap two untapped creatures and/or Treasures you control. If you do, draw a card. Otherwise, sacrifice this enchantment.| Selfless Police Captain|Marvel's Spider-Man|12|C|{1}{W}|Creature - Human Detective|1|1|This creature enters with a +1/+1 counter on it.$When this creature leaves the battlefield, put its +1/+1 counters on target creature you control.| +Silver Sable, Mercenary Leader|Marvel's Spider-Man|13|U|{2}{W}|Legendary Creature - Human Mercenary Hero|2|3|When Silver Sable enters, put a +1/+1 counter on another target creature.$Whenever Silver Sable attacks, target modified creature you control gains lifelink until end of turn.| Spectacular Spider-Man|Marvel's Spider-Man|14|R|{1}{W}|Legendary Creature - Spider Human Hero|3|2|Flash${1}: Spectacular Spider-Man gains flying until end of turn.${1}, Sacrifice Spectacular Spider-Man: Creatures you control gain hexproof and indestructible until end of turn.| Spectacular Tactics|Marvel's Spider-Man|15|C|{1}{W}|Instant|||Choose one --$* Put a +1/+1 counter on target creature you control. It gains hexproof until end of turn.$* Destroy target creature with power 4 or greater.| Spider-Man, Web-Slinger|Marvel's Spider-Man|16|C|{2}{W}|Legendary Creature - Spider Human Hero|3|3|Web-slinging {W}| +Spider-UK|Marvel's Spider-Man|17|U|{3}{W}|Legendary Creature - Spider Human Hero|3|4|Web-slinging {2}{W}$At the beginning of your end step, if two or more creatures entered the battlefield under your control this turn, you draw a card and gain 2 life.| Starling, Aerial Ally|Marvel's Spider-Man|18|C|{4}{W}|Legendary Creature - Human Hero|3|4|Flying$When Starling enters, another target creature you control gains flying until end of turn.| +Sudden Strike|Marvel's Spider-Man|19|U|{1}{W}|Instant|||Destroy target attacking or blocking creature.| Thwip!|Marvel's Spider-Man|20|C|{W}|Instant|||Target creature gets +2/+2 and gains flying until end of turn. If it's a Spider, you gain 2 life.| Web Up|Marvel's Spider-Man|21|C|{2}{W}|Enchantment|||When this enchantment enters, exile target nonland permanent an opponent controls until this enchantment leaves the battlefield.| +Web-Shooters|Marvel's Spider-Man|22|U|{1}{W}|Artifact - Equipment|||Equipped creature gets +1/+1 and has reach and "Whenever this creature attacks, tap target creature an opponent controls."$Equip {2}| Wild Pack Squad|Marvel's Spider-Man|23|C|{2}{W}|Creature - Human Mercenary|2|3|At the beginning of combat on your turn, up to one target creature gains first strike and vigilance until end of turn.| +With Great Power...|Marvel's Spider-Man|24|R|{3}{W}|Enchantment - Aura|||Enchant creature you control$Enchanted creature gets +2/+2 for each Aura and Equipment attached to it.$All damage that would be dealt to you is dealt to enchanted creature instead.| +Amazing Acrobatics|Marvel's Spider-Man|25|C|{1}{U}{U}|Instant|||Choose one or both --$* Counter target spell.$* Tap one or two target creatures.| Beetle, Legacy Criminal|Marvel's Spider-Man|26|C|{3}{U}|Legendary Creature - Human Rogue Villain|3|3|Flying${1}{U}, Exile this card from your graveyard: Put a +1/+1 counter on target creature. It gains flying until end of turn. Activate only as a sorcery.| +Chameleon, Master of Disguise|Marvel's Spider-Man|27|U|{3}{U}|Legendary Creature - Human Shapeshifter Villain|2|3|You may have Chameleon enter as a copy of a creature you control, except his name is Chameleon, Master of Disguise.$Mayhem {2}{U}| +The Clone Saga|Marvel's Spider-Man|28|R|{3}{U}|Enchantment - Saga|||(As this Saga enters step, add a lore counter. Sacrifice after III.)$I -- Surveil 3.$II -- When you next cast a creature spell this turn, copy it, except the copy isn't legendary.$III -- Choose a card name. Whenever a creature with the chosen name deals combat damage to a player this turn, draw a card.| Doc Ock, Sinister Scientist|Marvel's Spider-Man|29|C|{4}{U}|Legendary Creature - Human Scientist Villain|4|5|As long as there are eight or more cards in your graveyard, Doc Ock has base power and toughness 8/8.$As long as you control another Villain, Doc Ock has hexproof.| Doc Ock's Henchmen|Marvel's Spider-Man|30|C|{2}{U}|Creature - Human Villain|2|1|Flash$Whenever this creature attacks, it connives.| Flying Octobot|Marvel's Spider-Man|31|U|{1}{U}|Artifact Creature - Robot Villain|1|1|Flying$Whenever another Villain you control enters, put a +1/+1 counter on this creature. This ability triggers only once each turn.| +Hide on the Ceiling|Marvel's Spider-Man|32|R|{X}{U}|Instant|||Exile X target artifacts and/or creatures. Return the exiled cards to the battlefield under their owners' control at the beginning of the next end step.| +Hydro-Man, Fluid Felon|Marvel's Spider-Man|33|R|{U}{U}|Legendary Creature - Elemental Villain|2|2|Whenever you cast a blue spell, if Hydro-Man is a creature, he gets +1/+1 until end of turn.$At the beginning of your end step, untap Hydro-Man. Until your next turn, he becomes a land and gains "{T}: Add {U}."| +Impostor Syndrome|Marvel's Spider-Man|34|M|{4}{U}{U}|Enchantment|||Whenever a nontoken creature you control deals combat damage to a player, create a token that's a copy of it, except it isn't legendary.| +Lady Octopus, Inspired Inventor|Marvel's Spider-Man|35|R|{U}|Legendary Creature - Human Scientist Villain|0|2|Whenever you draw your first or second card each turn, put an ingenuity counter on Lady Octopus.${T}: You may cast an artifact spell from your hand with mana value less than or equal to the number of ingenuity counters on Lady Octopus without paying its mana cost.| +Madame Web, Clairvoyant|Marvel's Spider-Man|36|U|{4}{U}{U}|Legendary Creature - Mutant Advisor|4|4|You may look at the top card of your library any time.$You may cast Spider spells and noncreature spells from the top of your library.$Whenever you attack, you may mill a card.| +Mysterio, Master of Illusion|Marvel's Spider-Man|37|R|{3}{U}|Legendary Creature - Human Villain|3|3|When Mysterio enters, create a 3/3 blue Illusion Villain creature token for each nontoken Villain you control. Exile those tokens when Mysterio leaves the battlefield.| +Mysterio's Phantasm|Marvel's Spider-Man|38|C|{1}{U}|Creature - Illusion Villain|1|3|Flying, vigilance$Whenever this creature attacks, mill a card.| +Norman Osborn|Marvel's Spider-Man|39|M|{1}{U}|Legendary Creature - Human Scientist Villain|1|1|Norman Osborn can't be blocked.$Whenever Norman Osborn deals combat damage to a player, he connives.${1}{U}{B}{R}: Transform Norman Osborn. Activate only as a sorcery.| +Green Goblin|Marvel's Spider-Man|39|M|{1}{U}{B}{R}|Legendary Creature - Goblin Human Villain|3|3|Flying, menace$Spells you cast from your graveyard cost {2} less to cast.$Goblin Formula -- Each nonland card in your graveyard has mayhem. The mayhem cost is equal to its mana cost.| Oscorp Research Team|Marvel's Spider-Man|40|C|{3}{U}|Creature - Human Scientist|1|5|{6}{U}: Draw two cards.| +Robotics Mastery|Marvel's Spider-Man|41|U|{4}{U}|Enchantment - Aura|||Flash$Enchant creature$When this Aura enters, create two 1/1 colorless Robot artifact creature tokens with flying.$Enchanted creature gets +2/+2.| +School Daze|Marvel's Spider-Man|42|U|{3}{U}{U}|Instant|||Choose one --$* Do Homework -- Draw three cards.$* Fight Crime -- Counter target spell. Draw a card.| +Secret Identity|Marvel's Spider-Man|43|U|{U}|Instant|||Choose one --$* Conceal -- Until end of turn, target creature you control becomes a Citizen with base power and toughness 1/1 and gains hexproof.$* Reveal -- Until end of turn, target creature you control becomes a Hero with base power and toughness 3/4 and gains flying and vigilance.| Spider-Byte, Web Warden|Marvel's Spider-Man|44|U|{2}{U}|Legendary Creature - Spider Avatar Hero|2|2|When Spider-Byte enters, return up to one target nonland permanent to its owner's hand.| +Spider-Man No More|Marvel's Spider-Man|45|C|{1}{U}|Enchantment - Aura|||Enchant creature$Enchanted creature is a Citizen with base power and toughness 1/1. It has defender and loses all other abilities.| +Spider-Sense|Marvel's Spider-Man|46|R|{1}{U}|Instant|||Web-slinging {U}$Counter target instant spell, sorcery spell, or triggered ability.| Unstable Experiment|Marvel's Spider-Man|47|C|{1}{U}|Instant|||Target player draws a card, then up to one target creature you control connives.| Whoosh!|Marvel's Spider-Man|48|C|{1}{U}|Instant|||Kicker {1}{U}$Return target nonland permanent to its owner's hand. If this spell was kicked, draw a card.| +Agent Venom|Marvel's Spider-Man|49|R|{2}{B}|Legendary Creature - Symbiote Soldier Hero|2|3|Flash$Menace$Whenever another nontoken creature you control dies, you draw a card and lose 1 life.| +Alien Symbiosis|Marvel's Spider-Man|50|U|{1}{B}|Enchantment - Aura|||Enchant creature$Enchanted creature gets +1/+1, has menace, and is a Symbiote in addition to its other types.$You may cast this card from your graveyard by discarding a card in addition to paying its other costs.| +Behold the Sinister Six!|Marvel's Spider-Man|51|M|{6}{B}|Sorcery|||Return up to six target creature cards with different names from your graveyard to the battlefield.| +Black Cat, Cunning Thief|Marvel's Spider-Man|52|R|{3}{B}{B}|Legendary Creature - Human Rogue Villain|2|3|When Black Cat enters, look at the top nine cards of target opponent's library, exile two of them face down, then put the rest on the bottom of their library in a random order. You may play the exiled cards for as long as they remain exiled. Mana of any type can be spent to cast spells this way.| +Common Crook|Marvel's Spider-Man|53|C|{1}{B}|Creature - Human Rogue Villain|2|2|When this creature dies, create a Treasure token.| +The Death of Gwen Stacy|Marvel's Spider-Man|54|R|{2}{B}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Destroy target creature.$II -- Each player may discard a card. Each player who doesn't loses 3 life.$III -- Exile any number of target players' graveyards.| +Eddie Brock|Marvel's Spider-Man|55|M|{2}{B}|Legendary Creature - Human Hero Villain|5|5|When Eddie Brock enters, return target creature card with mana value 1 or less from your graveyard to the battlefield.${3}{B}{R}{G}: Transform Eddie Brock. Activate only as a sorcery.| +Venom, Lethal Protector|Marvel's Spider-Man|55|M|{3}{B}{R}{G}|Legendary Creature - Symbiote Hero Villain|5|5|Menace, trample, haste$Whenever Venom attacks, you may sacrifice another creature. If you do, draw X cards, then you may put a permanent card with mana value X or less from your hand onto the battlefield, where X is the sacrificed creature's mana value.| +Gwenom, Remorseless|Marvel's Spider-Man|56|M|{3}{B}{B}|Legendary Creature - Symbiote Spider Hero|4|4|Deathtouch, lifelink$Whenever Gwenom attacks, until end of turn you may look at the top card of your library any time and you may play cards from the top of your library. If you cast a spell this way, pay life equal to its mana value rather than pay its mana cost.| +Inner Demons Gangsters|Marvel's Spider-Man|57|C|{3}{B}|Creature - Human Rogue Villain|3|4|Discard a card: This creature gets +1/+0 and gains menace until end of turn. Activate only as a sorcery.| Merciless Enforcers|Marvel's Spider-Man|58|C|{1}{B}|Creature - Human Mercenary Villain|2|1|Lifelink${3}{B}: This creature deals 1 damage to each opponent.| +Morlun, Devourer of Spiders|Marvel's Spider-Man|59|R|{X}{B}{B}|Legendary Creature - Vampire Villain|2|1|Lifelink$Morlun enters with X+1/+1 counters on him.$When Morlun enters, he deals X damage to target opponent.| +Parker Luck|Marvel's Spider-Man|60|R|{2}{B}|Enchantment|||At the beginning of your end step, two target players each reveal the top card of their library. They each lose life equal to the mana value of the card revealed by the other player. Then they each put the card they revealed into their hand.| +Prison Break|Marvel's Spider-Man|61|U|{4}{B}|Sorcery|||Return target creature card from your graveyard to the battlefield with a +1/+1 counter on it.$Mayhem {3}{B}| Risky Research|Marvel's Spider-Man|62|C|{2}{B}|Sorcery|||Surveil 2, then draw two cards. You lose 2 life.| +Sandman's Quicksand|Marvel's Spider-Man|63|U|{1}{B}{B}|Sorcery|||Mayhem {3}{B}$All creatures get -2/-2 until end of turn. If this spell's mayhem cost was paid, creatures your opponents control get -2/-2 until end of turn instead.| Scorpion, Seething Striker|Marvel's Spider-Man|64|U|{3}{B}|Legendary Creature - Scorpion Human Villain|3|3|Deathtouch$At the beginning of your end step, if a creature died this turn, target creature you control connives.| Scorpion's Sting|Marvel's Spider-Man|65|C|{1}{B}|Instant|||Target creature gets -3/-3 until end of turn.| +The Soul Stone|Marvel's Spider-Man|66|M|{1}{B}|Legendary Artifact - Infinity Stone|||Indestructible${T}: Add {B}.${6}{B}, {T}, Exile a creature you control: Harness The Soul Stone.$ -- At the beginning of your upkeep, return target creature card from your graveyard to the battlefield.| Spider-Man Noir|Marvel's Spider-Man|67|U|{4}{B}|Legendary Creature - Spider Human Hero|4|4|Menace$Whenever a creature you control attacks alone, put a +1/+1 counter on it. Then surveil X, where X is the number of counters on it.| +The Spot's Portal|Marvel's Spider-Man|68|U|{2}{B}|Instant|||Put target creature on the bottom of its owner's library. You lose 2 life unless you control a Villain.| +Swarm, Being of Bees|Marvel's Spider-Man|69|C|{2}{B}|Legendary Creature - Insect Villain|2|2|Flash$Flying$Mayhem {B}| Tombstone, Career Criminal|Marvel's Spider-Man|70|U|{2}{B}|Legendary Creature - Human Villain|2|2|When Tombstone enters, return target Villain card from your graveyard to your hand.$Villain spells you cast cost {1} less to cast.| Venom, Evil Unleashed|Marvel's Spider-Man|71|C|{4}{B}|Legendary Creature - Symbiote Villain|4|5|Deathtouch${2}{B}, Exile this card from your graveyard: Put two +1/+1 counters on target creature. It gains deathtouch until end of turn. Activate only as a sorcery.| +Venomized Cat|Marvel's Spider-Man|72|C|{2}{B}|Creature - Symbiote Cat Villain|2|3|Deathtouch$When this creature enters, mill two cards.| Venom's Hunger|Marvel's Spider-Man|73|C|{4}{B}|Sorcery|||This spell costs {2} less to cast if you control a Villain.$Destroy target creature. You gain 2 life.| +Villainous Wrath|Marvel's Spider-Man|74|R|{3}{B}{B}|Sorcery|||Target opponent loses life equal to the number of creatures they control. Then destroy all creatures.| Angry Rabble|Marvel's Spider-Man|75|C|{1}{R}|Creature - Human Citizen|2|2|Trample$Whenever you cast a spell with mana value 4 or greater, this creature deals 1 damage to each opponent.${5}{R}: Put two +1/+1 counters on this creature. Activate only as a sorcery.| +Electro, Assaulting Battery|Marvel's Spider-Man|76|R|{1}{R}{R}|Legendary Creature - Human Villain|2|3|Flying$You don't lose unspent red mana as steps and phases end.$Whenever you cast an instant or sorcery spell, add {R}.$When Electro leaves the battlefield, you may pay {X}. When you do, he deals X damage to target player.| Electro's Bolt|Marvel's Spider-Man|77|C|{2}{R}|Sorcery|||Electro's Bolt deals 4 damage to target creature.$Mayhem {1}{R}| Gwen Stacy|Marvel's Spider-Man|78|M|{1}{R}|Legendary Creature - Human Performer Hero|2|1|When Gwen Stacy enters, exile the top card of your library. You may play that card for as long as you control this creature.${2}{U}{R}{W}: Transform Gwen Stacy. Activate only as a sorcery.| -Ghost-Spider|Marvel's Spider-Man|78|M|{2}{U}{R}{W}|Legendary Creature - Spider Human Hero|4|4|Flying, vigilance, haste$Whenever you play a land from exile or cast a spell from exile, put a +1/+1 counter on Ghost-Spider.$Remove two counters from Ghost- Spider: Exile the top card of your library. You may play that card this turn.| +Ghost-Spider|Marvel's Spider-Man|78|M|{2}{U}{R}{W}|Legendary Creature - Spider Human Hero|4|4|Flying, vigilance, haste$Whenever you play a land from exile or cast a spell from exile, put a +1/+1 counter on Ghost-Spider.$Remove two counters from Ghost-Spider: Exile the top card of your library. You may play that card this turn.| +Heroes' Hangout|Marvel's Spider-Man|79|U|{R}|Sorcery|||Choose one --$* Date Night -- Exile the top two cards of your library. Choose one of them. Until the end of your next turn, you may play that card.$* Patrol Night -- One or two target creatures each get +1/+0 and gain first strike until end of turn.| +Hobgoblin, Mantled Marauder|Marvel's Spider-Man|80|U|{1}{R}|Legendary Creature - Goblin Human Villain|1|2|Flying, haste$Whenever you discard a card, Hobgoblin gets +2/+0 until end of turn.| +J. Jonah Jameson|Marvel's Spider-Man|81|R|{2}{R}|Legendary Creature - Human Citizen|2|2|When J. Jonah Jameson enters, suspect up to one target creature.$Whenever a creature you control with menace attacks, create a Treasure token.| Masked Meower|Marvel's Spider-Man|82|C|{R}|Creature - Spider Cat Hero|1|1|Haste$Discard a card, Sacrifice this creature: Draw a card.| +Maximum Carnage|Marvel's Spider-Man|83|R|{4}{R}|Enchantment - Saga|||(As this Saga enters step and after your draw step, add a lore counter. Sacrifice after III.)$I -- Until your next turn, each creature attacks each combat if able and attacks a player other than you if able.$II -- Add {R}{R}{R}.$III -- This Saga deals 5 damage to each opponent.| +Molten Man, Inferno Incarnate|Marvel's Spider-Man|84|U|{2}{R}|Legendary Creature - Elemental Villain|0|0|When Molten Man enters, search your library$for a basic Mountain card, put it onto the$battlefield tapped, then shuffle.$Molten Man gets +1/+1 for each Mountain$you control.$When Molten Man leaves the battlefield,$sacrifice a land.| +Raging Goblinoids|Marvel's Spider-Man|85|U|{4}{R}|Creature - Goblin Berserker Villain|5|4|Haste$Mayhem {2}{R}| Romantic Rendezvous|Marvel's Spider-Man|86|C|{1}{R}|Sorcery|||Discard a card, then draw two cards.| +Shadow of the Goblin|Marvel's Spider-Man|87|R|{1}{R}|Enchantment|||Unreliable Visions -- At the beginning of your first main phase, discard a card. If you do, draw a card.$Undying Vengeance -- Whenever you play a land or cast a spell from anywhere other than your hand, this enchantment deals 1 damage to each opponent.| Shock|Marvel's Spider-Man|88|C|{R}|Instant|||Shock deals 2 damage to any target.| Shocker, Unshakable|Marvel's Spider-Man|89|U|{4}{R}{R}|Legendary Creature - Human Rogue Villain|5|5|During your turn, Shocker has first strike.$Vibro-Shock Gauntlets -- When Shocker enters, he deals 2 damage to target creature and 2 damage to that creature's controller.| Spider-Gwen, Free Spirit|Marvel's Spider-Man|90|C|{2}{R}|Legendary Creature - Spider Human Hero|2|3|Reach$Whenever Spider-Gwen becomes tapped, you may discard a card. If you do, draw a card.| Spider-Islanders|Marvel's Spider-Man|91|C|{3}{R}|Creature - Spider Horror Citizen|4|3|Mayhem {1}{R}| Spider-Punk|Marvel's Spider-Man|92|R|{1}{R}|Legendary Creature - Spider Human Hero|2|1|Riot$Other Spiders you control have riot.$Spells and abilities can't be countered.$Damage can't be prevented.| +Spider-Verse|Marvel's Spider-Man|93|M|{3}{R}{R}|Enchantment|||The "legend rule" doesn't apply to Spiders you control.$Whenever you cast a spell from anywhere other than your hand, you may copy it. If you do, you may choose new targets for the copy. If the copy is a permanent spell, it gains haste. Do this only once each turn.| +Spinneret and Spiderling|Marvel's Spider-Man|94|R|{R}|Legendary Creature - Spider Human Hero|1|2|Whenever you attack with two or more Spiders, put a +1/+1 counter on Spinneret and Spiderling.$Whenever Spinneret and Spiderling deals 4 or more damage, exile the top card of your library. Until the end of your next turn, you may play that card.| Stegron the Dinosaur Man|Marvel's Spider-Man|95|C|{4}{R}|Legendary Creature - Dinosaur Villain|5|4|Menace$Dinosaur Formula -- {1}{R}, Discard this card: Until end of turn, target creature you control gets +3/+1 and becomes a Dinosaur in addition to its other types.| +Superior Foes of Spider-Man|Marvel's Spider-Man|96|U|{2}{R}|Creature Human Rogue Villain|3|3|Trample$Whenever you cast a spell with mana value 4 or greater, you may exile the top card of your library. If you do, you may play that card until you exile another card with this creature.| Taxi Driver|Marvel's Spider-Man|97|C|{1}{R}|Creature - Human Pilot|3|1|{1}, {T}: Target creature gains haste until end of turn.| +Wisecrack|Marvel's Spider-Man|98|U|{2}{R}|Instant|||Target creature deals damage equal to its power to itself. If that creature is attacking, Wisecrack deals 2 damage to that creature's controller.| +Damage Control Crew|Marvel's Spider-Man|99|U|{3}{G}|Creature - Human Citizen|3|3|When this creature enters, choose one --$* Repair -- Return target card with mana value 4 or greater from your graveyard to your hand.$* Impound -- Exile target artifact or enchantment.| +Ezekiel Sims, Spider-Totem|Marvel's Spider-Man|100|U|{4}{G}|Legendary Creature - Spider Human Advisor|3|5|Reach$At the beginning of combat on your turn, target Spider you control gets +2/+2 until end of turn.| Grow Extra Arms|Marvel's Spider-Man|101|C|{1}{G}|Instant|||This spell costs {1} less to cast if it targets a Spider.$Target creature gets +4/+4 until end of turn.| Guy in the Chair|Marvel's Spider-Man|102|C|{2}{G}|Creature - Human Advisor|2|3|{T}: Add one mana of any color.$Web Support -- {2}{G}, {T}: Put a +1/+1 counter on target Spider. Activate only as a sorcery.| Kapow!|Marvel's Spider-Man|103|C|{2}{G}|Sorcery|||Put a +1/+1 counter on target creature you control. It fights target creature an opponent controls.| Kraven's Cats|Marvel's Spider-Man|104|C|{1}{G}|Creature - Cat Villain|2|2|{2}{G}: This creature gets +2/+2 until end of turn. Activate only once each turn.| Kraven's Last Hunt|Marvel's Spider-Man|105|R|{3}{G}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Mill five cards. When you do, this Saga deals damage equal to the greatest power among creature cards in your graveyard to target creature.$II -- Target creature you control gets +2/+2 until end of turn.$III -- Return target creature card from your graveyard to your hand.| +Lizard, Connors's Curse|Marvel's Spider-Man|106|R|{2}{G}{G}|Legendary Creature - Lizard Villain|5|5|Trample$Lizard Formula -- When Lizard, Connors's Curse enters, up to one other target creature loses all abilities and becomes a green Lizard creature with base power and toughness 4/4.| Lurking Lizards|Marvel's Spider-Man|107|C|{1}{G}|Creature - Lizard Villain|1|3|Trample$Whenever you cast a spell with mana value 4 or greater, put a +1/+1 counter on this creature.| Miles Morales|Marvel's Spider-Man|108|M|{1}{G}|Legendary Creature - Human Citizen Hero|1|2|When Miles Morales enters, put a +1/+1 counter on each of up to two target creatures.${3}{R}{G}{W}: Transform Miles Morales. Activate only as a sorcery.| Ultimate Spider-Man|Marvel's Spider-Man|108|M|{3}{R}{G}{W}|Legendary Creature - Spider Human Hero|4|3|First strike, haste$Camouflage -- {2}: Put a +1/+1 counter on Ultimate Spider-Man. He gains hexproof and becomes colorless until end of turn.$Whenever you attack, double the number of each kind of counter on each Spider and legendary creature you control.| +Pictures of Spider-Man|Marvel's Spider-Man|109|U|{2}{G}|Artifact|||When this artifact enters, look at the top five cards of your library. You may reveal up to two creature cards from among them and put them into your hand. Put the rest on the bottom of your library in a random order.${1}, {T}, Sacrifice this artifact: Create a Treasure token.| +Professional Wrestler|Marvel's Spider-Man|110|C|{3}{G}|Creature - Human Warrior Performer|4|4|When this creature enters, create a Treasure token.$This creature can't be blocked by more than one creature.| +Radioactive Spider|Marvel's Spider-Man|111|R|{G}|Creature - Spider|1|1|Reach, deathtouch$Fateful Bite -- {2}, Sacrifice this creature: Search your library for a Spider Hero card, reveal it, put it into your hand, then shuffle. Activate only as a sorcery.| +Sandman, Shifting Scoundrel|Marvel's Spider-Man|112|R|{1}{G}{G}|Legendary Creature - Sand Elemental Villain|*|*|Sandman's power and toughness are each equal to the number of lands you control.$Sandman can't be blocked by creatures with power 2 or less.${3}{G}{G}: Return this card and target land card from your graveyard to the battlefield tapped.| Scout the City|Marvel's Spider-Man|113|C|{1}{G}|Sorcery|||Choose one --$* Look Around -- Mill three cards. You may put a permanent card from among them into your hand. You gain 3 life.$* Bring Down -- Destroy target creature with flying.| Spider-Ham, Peter Porker|Marvel's Spider-Man|114|R|{1}{G}|Legendary Creature - Spider Boar Hero|2|2|When Spider-Ham enters, create a Food token.$Animal May-Ham -- Other Spiders, Boars, Bats, Bears, Birds, Cats, Dogs, Frogs, Jackals, Lizards, Mice, Otters, Rabbits, Raccoons, Rats, Squirrels, Turtles, and Wolves you control get +1/+1.| Spider-Man, Brooklyn Visionary|Marvel's Spider-Man|115|C|{4}{G}|Legendary Creature - Spider Human Hero|4|3|Web-slinging {2}{G}$When Spider-Man enters, search your library for a basic land card, put it onto the battlefield tapped, then shuffle.| Spider-Rex, Daring Dino|Marvel's Spider-Man|116|C|{4}{G}{G}|Legendary Creature - Spider Dinosaur Hero|6|6|Reach, trample$Ward {2}| +Spiders-Man, Heroic Horde|Marvel's Spider-Man|117|U|{1}{G}|Legendary Creature - Spider Hero|2|3|Web-slinging {4}{G}{G}$When Spiders-Man enters, if they were cast using web-slinging, you gain 3 life and create two 2/1 green Spider creature tokens with reach.| +Strength of Will|Marvel's Spider-Man|118|R|{1}{G}|Instant|||Until end of turn, target creature you control gains indestructible and "Whenever this creature is dealt damage, put that many +1/+1 counters on it."| +Supportive Parents|Marvel's Spider-Man|119|U|{2}{G}|Creature - Human Citizen|3|3|Tap two untapped creatures you control: Add one mana of any color.| +Terrific Team-Up|Marvel's Spider-Man|120|U|{3}{G}|Instant|||This spell costs {2} less to cast if you control a permanent with mana value 4 or greater.$One or two target creatures you control each get +1/+0 until end of turn. They each deal damage equal to their power to target creature an opponent controls.| +Wall Crawl|Marvel's Spider-Man|121|U|{3}{G}|Enchantment|||When this enchantment enters, create a 2/1 green Spider creature token with reach, then you gain 1 life for each Spider you control.$Spiders you control get +1/+1 and can't be blocked by creatures with defender.| +Web of Life and Destiny|Marvel's Spider-Man|122|M|{6}{G}{G}|Enchantment|||Convoke$At the beginning of combat on your turn, look at the top five cards of your library. You may put a creature card from among them onto the battlefield. Put the rest on the bottom of your library in a random order.| +Arana, Heart of the Spider|Marvel's Spider-Man|123|R|{1}{R}{W}|Legendary Creature - Spider Human Hero|3|3|Whenever you attack, put a +1/+1 counter on target attacking creature.$Whenever a modified creature you control deals combat damage to a player, exile the top card of your library. You may play that card this turn.| +Biorganic Carapace|Marvel's Spider-Man|124|R|{2}{W}{U}|Artifact - Equipment|||When this Equipment enters, attach it to target creature you control.$Equipped creature gets +2/+2 and has "Whenever this creature deals combat damage to a player, draw a card for each modified creature you control."$Equip {2}| +Carnage, Crimson Chaos|Marvel's Spider-Man|125|R|{2}{B}{R}|Legendary Creature - Symbiote Villain|4|3|Trample$When Carnage enters, return target creature card with mana value 3 or less from your graveyard to the battlefield. It gains "This creature attacks each combat if able" and "When this creature deals combat damage to a player, sacrifice it."$Mayhem {B}{R}| +Cheering Crowd|Marvel's Spider-Man|126|R|{1}{R/G}|Creature - Human Citizen|2|2|At the beginning of each player's first main phase, that player may put a +1/+1 counter on this creature. If they do, they add {C} for each counter on it.| +Cosmic Spider-Man|Marvel's Spider-Man|127|M|{W}{U}{B}{R}{G}|Legendary Creature - Spider Human Hero|5|5|Flying, first strike, trample, lifelink, haste$At the beginning of combat on your turn, other Spiders you control gain flying, first strike, trample, lifelink, and haste until end of turn.| Doctor Octopus, Master Planner|Marvel's Spider-Man|128|M|{5}{U}{B}|Legendary Creature - Human Scientist Villain|4|8|Other Villains you control get +2/+2.$Your maximum hand size is eight.$At the beginning of your end step, if you have fewer than eight cards in hand, draw cards equal to the difference.| +Gallant Citizen|Marvel's Spider-Man|129|C|{G/W}{G/W}|Creature - Human Citizen|1|1|When this creature enters, draw a card.| Green Goblin, Revenant|Marvel's Spider-Man|130|U|{3}{B}{R}|Legendary Creature - Goblin Human Villain|3|3|Flying, deathtouch$Whenever Green Goblin attacks, discard a card. Then draw a card for each card you've discarded this turn.| +Jackal, Genius Geneticist|Marvel's Spider-Man|131|R|{G}{U}|Legendary Creature - Human Scientist Villain|1|1|Trample$Whenever you cast a creature spell with mana value equal to Jackal's power, copy that spell, except the copy isn't legendary. Then put a +1/+1 counter on Jackal.| +Kraven, Proud Predator|Marvel's Spider-Man|132|U|{1}{R}{G}|Legendary Creature - Human Warrior Villain|*|4|Vigilance$Top of the Food Chain -- Kraven's power is equal to the greatest mana value among permanents you control.| +Kraven the Hunter|Marvel's Spider-Man|133|R|{1}{B}{G}|Legendary Creature - Human Warrior Villain|4|3|Trample$Whenever a creature an opponent controls with the greatest power among creatures that player controls dies, draw a card and put a +1/+1 counter on Kraven the Hunter.| +Mary Jane Watson|Marvel's Spider-Man|134|R|{1}{G/W}|Legendary Creature - Human Performer|2|2|Whenever a Spider you control enters, draw a card. This ability triggers only once each turn.| +Mister Negative|Marvel's Spider-Man|135|M|{5}{W}{B}|Legendary Creature - Human Villain|5|5|Vigilance, lifelink$Darkforce Inversion -- When Mister Negative enters, you may exchange your life total with target opponent. If you lose life this way, draw that many cards.| +Mob Lookout|Marvel's Spider-Man|136|C|{1}{U/B}|Creature - Human Rogue Villain|0|3|When this creature enters, target creature you control connives.| +Morbius the Living Vampire|Marvel's Spider-Man|137|U|{2}{U}{B}|Legendary Creature - Vampire Scientist Villain|3|1|Flying, vigilance, lifelink${U}{B}, Exile this card from your graveyard: Look at the top three cards of your library. Put one of them into your hand and the rest on the bottom of your library in any order.| +Prowler, Clawed Thief|Marvel's Spider-Man|138|U|{1}{U}{B}|Legendary Creature - Human Rogue Villain|2|3|Menace$Whenever another Villain you control enters, Prowler connives.| +Pumpkin Bombardment|Marvel's Spider-Man|139|C|{B/R}|Sorcery|||As an additional cost to cast this spell, discard a card or pay {2}.$Pumpkin Bombardment deals 3 damage to target creature.| +Rhino, Barreling Brute|Marvel's Spider-Man|140|U|{3}{R}{R}{G}{G}|Legendary Creature - Human Villain|6|7|Vigilance, trample, haste$Whenever Rhino attacks, if you've cast a spell with mana value 4 or greater this turn, draw a card.| +Rhino's Rampage|Marvel's Spider-Man|141|U|{R/G}|Sorcery|||Target creature you control gets +1/+0 until end of turn. It fights target creature an opponent controls. When excess damage is dealt to the creature an opponent controls this way, destroy up to one target noncreature artifact with mana value 3 or less.| +Scarlet Spider, Ben Reilly|Marvel's Spider-Man|142|R|{1}{R}{G}|Legendary Creature - Spider Human Hero|4|3|Web-slinging {R}{G}$Trample$Sensational Save -- If Scarlet Spider was cast using web-slinging, he enters with X +1/+1 counters on him, where X is the mana value of the returned creature.| +Scarlet Spider, Kaine|Marvel's Spider-Man|143|U|{B}{R}|Legendary Creature - Spider Human Hero|2|1|Menace$When Scarlet Spider enters, you may discard a card. If you do, put a +1/+1 counter on him.$Mayhem {B/R}| +Shriek, Treblemaker|Marvel's Spider-Man|144|U|{2}{B/R}|Legendary Creature - Mutant Villain|2|3|At the beginning of your first main phase, you may discard a card. When you do, target creature can't block this turn.$Sonic Blast -- Whenever a creature an opponent controls dies, Shriek deals 1 damage to that player.| +Silk, Web Weaver|Marvel's Spider-Man|145|R|{2}{G}{W}|Legendary Creature - Spider Human Hero|3|5|Web-slinging {1}{G}{W}$Whenever you cast a creature spell, create a 1/1 green and white Human Citizen creature token.${3}{G}{W}: Creatures you control get +2/+2 and gain vigilance until end of turn.| +Skyward Spider|Marvel's Spider-Man|146|C|{W/U}{W/U}|Creature - Spider Human Hero|2|2|Ward {2}$This creature has flying as long as it's modified.| SP//dr, Piloted by Peni|Marvel's Spider-Man|147|U|{3}{W}{U}|Legendary Artifact Creature - Spider Hero|4|4|Vigilance$When SP//dr enters, put a +1/+1 counter on target creature.$Whenever a modified creature you control deals combat damage to a player, draw a card.| +Spider Manifestation|Marvel's Spider-Man|148|C|{1}{R/G}|Creature - Spider Avatar|2|2|Reach${T}: Add {R} or {G}.$Whenever you cast a spell with mana value 4 or greater, untap this creature.| +Spider-Girl, Legacy Hero|Marvel's Spider-Man|149|U|{G}{W}|Legendary Creature - Spider Human Hero|2|2|During your turn, Spider-Girl has flying.$When Spider-Girl leaves the battlefield, create a 1/1 green and white Human Citizen creature token.| Spider-Man 2099|Marvel's Spider-Man|150|R|{U}{R}|Legendary Creature - Spider Human Hero|2|3|From the Future -- You can't cast Spider-Man 2099 during your first, second, or third turns of the game.$Double strike, vigilance$At the beginning of your end step, if you've played a land or cast a spell this turn from anywhere other than your hand, Spider-Man 2099 deals damage equal to his power to any target.| +Spider-Man India|Marvel's Spider-Man|151|U|{3}{G}{W}|Legendary Creature - Spider Human Hero|4|4|Web-slinging {1}{G}{W}$Pavitr's Seva -- Whenever you cast a creature spell, put a +1/+1 counter on target creature you control. It gains flying until end of turn.| +Spider-Woman, Stunning Savior|Marvel's Spider-Man|152|R|{1}{W/U}|Legendary Creature - Spider Human Hero|2|2|Flying$Venom Blast -- Artifacts and creatures your opponents control enter tapped.| +The Spot, Living Portal|Marvel's Spider-Man|153|R|{3}{W}{B}|Legendary Creature - Human Scientist Villain|4|4|When The Spot enters, exile up to one target nonland permanent and up to one target nonland permanent card from a graveyard.$When The Spot dies, put him on the bottom of his owner's library. If you do, return the exiled cards to their owners' hands.| +Sun-Spider, Nimble Webber|Marvel's Spider-Man|154|U|{3}{W/U}|Legendary Creature - Spider Human Hero|3|2|During your turn, Sun-Spider has flying.$When Sun-Spider enters, search your library for an Aura or Equipment card, reveal it, put it into your hand, then shuffle.| +Superior Spider-Man|Marvel's Spider-Man|155|R|{2}{U}{B}|Legendary Creature - Spider Human Hero|4|4|Mind Swap -- You may have Superior Spider- Man enter as a copy of any creature card in a graveyard, except his name is Superior Spider-Man and he's a 4/4 Spider Human Hero in addition to his other types. When you do, exile that card.| Symbiote Spider-Man|Marvel's Spider-Man|156|R|{2}{U/B}|Legendary Creature - Symbiote Spider Hero|2|4|Whenever this creature deals combat damage to a player, look at that many cards from the top of your library. Put one of them into your hand and the rest into your graveyard.$Find New Host -- {2}{U/B}, Exile this card from your graveyard: Put a +1/+1 counter on target creature you control. It gains this card's other abilities. Activate only as a sorcery.| +Ultimate Green Goblin|Marvel's Spider-Man|157|R|{1}{B/R}{B/R}|Legendary Creature - Goblin Villain|5|4|At the beginning of your upkeep, discard a card, then create a Treasure token.$Mayhem {2}{B/R}| +Vulture, Scheming Scavenger|Marvel's Spider-Man|158|U|{5}{U/B}|Legendary Creature - Human Artificer Villain|4|6|Flying$Whenever Vulture attacks, other Villains you control gain flying until end of turn.| Web-Warriors|Marvel's Spider-Man|159|U|{4}{G/W}|Creature - Spider Hero|4|3|When this creature enters, put a +1/+1 counter on each other creature you control.| +Wraith, Vicious Vigilante|Marvel's Spider-Man|160|U|{1}{W}{U}|Legendary Creature - Human Detective Hero|1|1|Double strike$Fear Gas -- Wraith can't be blocked.| +Bagel and Schmear|Marvel's Spider-Man|161|C|{1}|Artifact - Food|||Share -- {W}, {T}, Sacrifice this artifact: Put a +1/+1 counter on up to one target creature. Draw a card. Activate only as a sorcery.$Nosh -- {2}, {T}, Sacrifice this artifact: You gain 3 life and draw a card.| +Doc Ock's Tentacles|Marvel's Spider-Man|162|R|{1}|Artifact - Equipment|||Whenever a creature you control with mana value 5 or greater enters, you may attach this Equipment to it.$Equipped creature gets +4/+4.$Equip {5}| Eerie Gravestone|Marvel's Spider-Man|163|C|{2}|Artifact|||When this artifact enters, draw a card.${1}{B}, Sacrifice this artifact: Mill four cards. You may put a creature card from among them into your hand.| +Hot Dog Cart|Marvel's Spider-Man|164|C|{3}|Artifact|||When this artifact enters, create a Food token.${T}: Add one mana of any color.| +Interdimensional Web Watch|Marvel's Spider-Man|165|R|{4}|Artifact|||When this artifact enters, exile the top two cards of your library. Until the end of your next turn, you may play those cards.${T}: Add two mana in any combination of colors. Spend this mana only to cast spells from exile.| Iron Spider, Stark Upgrade|Marvel's Spider-Man|166|R|{3}|Legendary Artifact Creature - Spider Hero|2|3|Vigilance${T}: Put a +1/+1 counter on each artifact creature and/or Vehicle you control.${2}, Remove two +1/+1 counters from among artifacts you control: Draw a card.| +Living Brain, Mechanical Marvel|Marvel's Spider-Man|167|U|{4}|Legendary Artifact Creature - Robot Villain|3|3|At the beginning of combat on your turn, target non-Equipment artifact you control becomes an artifact creature with base power and toughness 3/3 until end of turn. Untap it.| Mechanical Mobster|Marvel's Spider-Man|168|C|{3}|Artifact Creature - Human Robot Villain|2|1|When this creature enters, exile up to one target card from a graveyard. Target creature you control connives.| +News Helicopter|Marvel's Spider-Man|169|C|{3}|Artifact Creature - Construct|1|1|Flying$When this creature enters, create a 1/1 green and white Human Citizen creature token.| +Passenger Ferry|Marvel's Spider-Man|170|C|{3}|Artifact - Vehicle|4|3|Whenever this Vehicle attacks, you may pay {U}. When you do, another target attacking creature can't be blocked this turn.$Crew 2| +Peter Parker's Camera|Marvel's Spider-Man|171|R|{1}|Artifact|||This artifact enters with three film counters on it.${2}, {T}, Remove a film counter from this artifact: Copy target activated or triggered ability you control. You may choose new targets for the copy.| +Rocket-Powered Goblin Glider|Marvel's Spider-Man|172|R|{3}|Artifact - Equipment|||When this Equipment enters, if it was cast from your graveyard, attach it to target creature you control.$Equipped creature gets +2/+0 and has flying and haste.$Equip {2}$Mayhem {2}| Spider-Bot|Marvel's Spider-Man|173|C|{2}|Artifact Creature - Spider Robot Scout|2|1|Reach$When this creature enters, you may search your library for a basic land card, reveal it, then shuffle and put that card on top.| +Spider-Mobile|Marvel's Spider-Man|174|U|{3}|Artifact - Vehicle|3|3|Trample$Whenever this Vehicle attacks or blocks, it gets +1/+1 until end of turn for each Spider you control.$Crew 2| +Spider-Slayer, Hatred Honed|Marvel's Spider-Man|175|U|{2}|Legendary Artifact Creature - Human Villain|2|1|Whenever Spider-Slayer deals damage to a Spider, destroy that creature.${6}, Exile this card from your graveyard: Create two tapped 1/1 colorless Robot artifact creature tokens with flying.| +Spider-Suit|Marvel's Spider-Man|176|U|{1}|Artifact - Equipment|||Equipped creature gets +2/+2 and is a Spider Hero in addition to its other types.$Equip {3}| +Steel Wrecking Ball|Marvel's Spider-Man|177|C|{5}|Artifact|||When this artifact enters, it deals 5 damage to target creature.${1}{R}, Discard this card: Destroy target artifact.| +Subway Train|Marvel's Spider-Man|178|C|{2}|Artifact - Vehicle|3|1|When this Vehicle enters, you may pay {G}. If you do, search your library for a basic land card, reveal it, put it into your hand, then shuffle.$Crew 2| +Daily Bugle Building|Marvel's Spider-Man|179|U||Land|||{T}: Add {C}.${1}, {T}: Add one mana of any color.$Smear Campaign -- {1}, {T}: Target legendary creature gains menace until end of turn. Activate only as a sorcery.| Multiversal Passage|Marvel's Spider-Man|180|R||Land|||As this land enters, choose a basic land type. Then you may pay 2 life. If you don't, it enters tapped.$This land is the chosen type.| +Ominous Asylum|Marvel's Spider-Man|181|C||Land|||This land enters tapped.${T}: Add {B} or {R}.${4}, {T}: Surveil 1.| +Oscorp Industries|Marvel's Spider-Man|182|R||Land|||This land enters tapped.$When this land enters from a graveyard, you lose 2 life.${T}: Add {U}, {B}, or {R}.$Mayhem| +Savage Mansion|Marvel's Spider-Man|183|C||Land|||This land enters tapped.${T}: Add {R} or {G}.${4}, {T}: Surveil 1.| +Sinister Hideout|Marvel's Spider-Man|184|C||Land|||This land enters tapped.${T}: Add {U} or {B}.${4}, {T}: Surveil 1.| +Suburban Sanctuary|Marvel's Spider-Man|185|C||Land|||This land enters tapped.${T}: Add {G} or {W}.${4}, {T}: Surveil 1.| +University Campus|Marvel's Spider-Man|186|C||Land|||This land enters tapped.${T}: Add {W} or {U}.${4}, {T}: Surveil 1.| +Urban Retreat|Marvel's Spider-Man|187|R||Land|||This land enters tapped.${T}: Add {G}, {W} , or {U}.${2}, Return a tapped creature you control to its owner's hand: Put this card from your hand onto the battlefield. Activate only as a sorcery.| +Vibrant Cityscape|Marvel's Spider-Man|188|C||Land|||{T}, Sacrifice this land: Search your library for a basic land card, put it onto the battlefield tapped, then shuffle.| Plains|Marvel's Spider-Man|189|C||Basic Land - Plains|||({T}: Add {W}.)| Island|Marvel's Spider-Man|190|C||Basic Land - Island|||({T}: Add {U}.)| Swamp|Marvel's Spider-Man|191|C||Basic Land - Swamp|||({T}: Add {B}.)| @@ -59912,7 +60039,7 @@ Miles Morales|Marvel's Spider-Man|200|M|{1}{G}|Legendary Creature - Human Citize Ultimate Spider-Man|Marvel's Spider-Man|200|M|{3}{R}{G}{W}|Legendary Creature - Spider Human Hero|4|3|First strike, haste$Camouflage -- {2}: Put a +1/+1 counter on Ultimate Spider-Man. He gains hexproof and becomes colorless until end of turn.$Whenever you attack, double the number of each kind of counter on each Spider and legendary creature you control.| Spider-Ham, Peter Porker|Marvel's Spider-Man|201|R|{1}{G}|Legendary Creature - Spider Boar Hero|2|2|When Spider-Ham enters, create a Food token.$Animal May-Ham -- Other Spiders, Boars, Bats, Bears, Birds, Cats, Dogs, Frogs, Jackals, Lizards, Mice, Otters, Rabbits, Raccoons, Rats, Squirrels, Turtles, and Wolves you control get +1/+1.| Gwen Stacy|Marvel's Spider-Man|202|M|{1}{R}|Legendary Creature - Human Performer Hero|2|1|When Gwen Stacy enters, exile the top card of your library. You may play that card for as long as you control this creature.${2}{U}{R}{W}: Transform Gwen Stacy. Activate only as a sorcery.| -Ghost-Spider|Marvel's Spider-Man|202|M|{2}{U}{R}{W}|Legendary Creature - Spider Human Hero|4|4|Flying, vigilance, haste$Whenever you play a land from exile or cast a spell from exile, put a +1/+1 counter on Ghost-Spider.$Remove two counters from Ghost- Spider: Exile the top card of your library. You may play that card this turn.| +Ghost-Spider|Marvel's Spider-Man|202|M|{2}{U}{R}{W}|Legendary Creature - Spider Human Hero|4|4|Flying, vigilance, haste$Whenever you play a land from exile or cast a spell from exile, put a +1/+1 counter on Ghost-Spider.$Remove two counters from Ghost-Spider: Exile the top card of your library. You may play that card this turn.| Web-Warriors|Marvel's Spider-Man|203|U|{4}{G/W}|Creature - Spider Hero|4|3|When this creature enters, put a +1/+1 counter on each other creature you control.| Spider-Man Noir|Marvel's Spider-Man|204|U|{4}{B}|Legendary Creature - Spider Human Hero|4|4|Menace$Whenever a creature you control attacks alone, put a +1/+1 counter on it. Then surveil X, where X is the number of counters on it.| Spider-Man 2099|Marvel's Spider-Man|205|R|{U}{R}|Legendary Creature - Spider Human Hero|2|3|From the Future -- You can't cast Spider-Man 2099 during your first, second, or third turns of the game.$Double strike, vigilance$At the beginning of your end step, if you've played a land or cast a spell this turn from anywhere other than your hand, Spider-Man 2099 deals damage equal to his power to any target.| @@ -59921,21 +60048,90 @@ Spider-Punk|Marvel's Spider-Man|207|R|{1}{R}|Legendary Creature - Spider Human H Peter Parker|Marvel's Spider-Man|208|M|{1}{W}|Legendary Creature - Human Scientist Hero|0|1|When Peter Parker enters, create a 2/1 green Spider creature token with reach.${1}{G}{W}{U}: Transform Peter Parker. Activate only as a sorcery.| Amazing Spider-Man|Marvel's Spider-Man|208|M|{1}{G}{W}{U}|Legendary Creature - Spider Human Hero|4|4|Vigilance, reach$Each legendary spell you cast that's one or more colors has web-slinging {G}{W}{U}.| Gwen Stacy|Marvel's Spider-Man|209|M|{1}{R}|Legendary Creature - Human Performer Hero|2|1|When Gwen Stacy enters, exile the top card of your library. You may play that card for as long as you control this creature.${2}{U}{R}{W}: Transform Gwen Stacy. Activate only as a sorcery.| -Ghost-Spider|Marvel's Spider-Man|209|M|{2}{U}{R}{W}|Legendary Creature - Spider Human Hero|4|4|Flying, vigilance, haste$Whenever you play a land from exile or cast a spell from exile, put a +1/+1 counter on Ghost-Spider.$Remove two counters from Ghost- Spider: Exile the top card of your library. You may play that card this turn.| +Ghost-Spider|Marvel's Spider-Man|209|M|{2}{U}{R}{W}|Legendary Creature - Spider Human Hero|4|4|Flying, vigilance, haste$Whenever you play a land from exile or cast a spell from exile, put a +1/+1 counter on Ghost-Spider.$Remove two counters from Ghost-Spider: Exile the top card of your library. You may play that card this turn.| Spider-Punk|Marvel's Spider-Man|210|R|{1}{R}|Legendary Creature - Spider Human Hero|2|1|Riot$Other Spiders you control have riot.$Spells and abilities can't be countered.$Damage can't be prevented.| Miles Morales|Marvel's Spider-Man|211|M|{1}{G}|Legendary Creature - Human Citizen Hero|1|2|When Miles Morales enters, put a +1/+1 counter on each of up to two target creatures.${3}{R}{G}{W}: Transform Miles Morales. Activate only as a sorcery.| Ultimate Spider-Man|Marvel's Spider-Man|211|M|{3}{R}{G}{W}|Legendary Creature - Spider Human Hero|4|3|First strike, haste$Camouflage -- {2}: Put a +1/+1 counter on Ultimate Spider-Man. He gains hexproof and becomes colorless until end of turn.$Whenever you attack, double the number of each kind of counter on each Spider and legendary creature you control.| +Radioactive Spider|Marvel's Spider-Man|212|R|{G}|Creature - Spider|1|1|Reach, deathtouch$Fateful Bite -- {2}, Sacrifice this creature: Search your library for a Spider Hero card, reveal it, put it into your hand, then shuffle. Activate only as a sorcery.| +Arana, Heart of the Spider|Marvel's Spider-Man|213|R|{1}{R}{W}|Legendary Creature - Spider Human Hero|3|3|Whenever you attack, put a +1/+1 counter on target attacking creature.$Whenever a modified creature you control deals combat damage to a player, exile the top card of your library. You may play that card this turn.| +Scarlet Spider, Ben Reilly|Marvel's Spider-Man|214|R|{1}{R}{G}|Legendary Creature - Spider Human Hero|4|3|Web-slinging {R}{G}$Trample$Sensational Save -- If Scarlet Spider was cast using web-slinging, he enters with X +1/+1 counters on him, where X is the mana value of the returned creature.| +Silk, Web Weaver|Marvel's Spider-Man|215|R|{2}{G}{W}|Legendary Creature - Spider Human Hero|3|5|Web-slinging {1}{G}{W}$Whenever you cast a creature spell, create a 1/1 green and white Human Citizen creature token.${3}{G}{W}: Creatures you control get +2/+2 and gain vigilance until end of turn.| Spider-Man 2099|Marvel's Spider-Man|216|R|{U}{R}|Legendary Creature - Spider Human Hero|2|3|From the Future -- You can't cast Spider-Man 2099 during your first, second, or third turns of the game.$Double strike, vigilance$At the beginning of your end step, if you've played a land or cast a spell this turn from anywhere other than your hand, Spider-Man 2099 deals damage equal to his power to any target.| Symbiote Spider-Man|Marvel's Spider-Man|217|R|{2}{U/B}|Legendary Creature - Symbiote Spider Hero|2|4|Whenever this creature deals combat damage to a player, look at that many cards from the top of your library. Put one of them into your hand and the rest into your graveyard.$Find New Host -- {2}{U/B}, Exile this card from your graveyard: Put a +1/+1 counter on target creature you control. It gains this card's other abilities. Activate only as a sorcery.| Origin of Spider-Man|Marvel's Spider-Man|218|R|{1}{W}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Create a 2/1 green Spider creature token with reach.$II -- Put a +1/+1 counter on target creature you control. It becomes a legendary Spider Hero in addition to its other types.$III -- Target creature you control gains double strike until end of turn.| +The Clone Saga|Marvel's Spider-Man|219|R|{3}{U}|Enchantment - Saga|||(As this Saga enters step, add a lore counter. Sacrifice after III.)$I -- Surveil 3.$II -- When you next cast a creature spell this turn, copy it, except the copy isn't legendary.$III -- Choose a card name. Whenever a creature with the chosen name deals combat damage to a player this turn, draw a card.| +Norman Osborn|Marvel's Spider-Man|220|M|{1}{U}|Legendary Creature - Human Scientist Villain|1|1|Norman Osborn can't be blocked.$Whenever Norman Osborn deals combat damage to a player, he connives.${1}{U}{B}{R}: Transform Norman Osborn. Activate only as a sorcery.| +Green Goblin|Marvel's Spider-Man|220|M|{1}{U}{B}{R}|Legendary Creature - Goblin Human Villain|3|3|Flying, menace$Spells you cast from your graveyard cost {2} less to cast.$Goblin Formula -- Each nonland card in your graveyard has mayhem. The mayhem cost is equal to its mana cost.| +Behold the Sinister Six!|Marvel's Spider-Man|221|M|{6}{B}|Sorcery|||Return up to six target creature cards with different names from your graveyard to the battlefield.| +Black Cat, Cunning Thief|Marvel's Spider-Man|222|R|{3}{B}{B}|Legendary Creature - Human Rogue Villain|2|3|When Black Cat enters, look at the top nine cards of target opponent's library, exile two of them face down, then put the rest on the bottom of their library in a random order. You may play the exiled cards for as long as they remain exiled. Mana of any type can be spent to cast spells this way.| +The Death of Gwen Stacy|Marvel's Spider-Man|223|R|{2}{B}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Destroy target creature.$II -- Each player may discard a card. Each player who doesn't loses 3 life.$III -- Exile any number of target players' graveyards.| +Eddie Brock|Marvel's Spider-Man|224|M|{2}{B}|Legendary Creature - Human Hero Villain|5|5|When Eddie Brock enters, return target creature card with mana value 1 or less from your graveyard to the battlefield.${3}{B}{R}{G}: Transform Eddie Brock. Activate only as a sorcery.| +Venom, Lethal Protector|Marvel's Spider-Man|224|M|{3}{B}{R}{G}|Legendary Creature - Symbiote Hero Villain|5|5|Menace, trample, haste$Whenever Venom attacks, you may sacrifice another creature. If you do, draw X cards, then you may put a permanent card with mana value X or less from your hand onto the battlefield, where X is the sacrificed creature's mana value.| +Maximum Carnage|Marvel's Spider-Man|225|R|{4}{R}|Enchantment - Saga|||(As this Saga enters step and after your draw step, add a lore counter. Sacrifice after III.)$I -- Until your next turn, each creature attacks each combat if able and attacks a player other than you if able.$II -- Add {R}{R}{R}.$III -- This Saga deals 5 damage to each opponent.| Kraven's Last Hunt|Marvel's Spider-Man|226|R|{3}{G}|Enchantment - Saga|||(As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)$I -- Mill five cards. When you do, this Saga deals damage equal to the greatest power among creature cards in your graveyard to target creature.$II -- Target creature you control gets +2/+2 until end of turn.$III -- Return target creature card from your graveyard to your hand.| +Carnage, Crimson Chaos|Marvel's Spider-Man|227|R|{2}{B}{R}|Legendary Creature - Symbiote Villain|4|3|Trample$When Carnage enters, return target creature card with mana value 3 or less from your graveyard to the battlefield. It gains "This creature attacks each combat if able" and "When this creature deals combat damage to a player, sacrifice it."$Mayhem {B}{R}| Doctor Octopus, Master Planner|Marvel's Spider-Man|228|M|{5}{U}{B}|Legendary Creature - Human Scientist Villain|4|8|Other Villains you control get +2/+2.$Your maximum hand size is eight.$At the beginning of your end step, if you have fewer than eight cards in hand, draw cards equal to the difference.| +Mary Jane Watson|Marvel's Spider-Man|229|R|{1}{G/W}|Legendary Creature - Human Performer|2|2|Whenever a Spider you control enters, draw a card. This ability triggers only once each turn.| +Spider-Woman, Stunning Savior|Marvel's Spider-Man|230|R|{1}{W/U}|Legendary Creature - Spider Human Hero|2|2|Flying$Venom Blast -- Artifacts and creatures your opponents control enter tapped.| +The Spot, Living Portal|Marvel's Spider-Man|231|R|{3}{W}{B}|Legendary Creature - Human Scientist Villain|4|4|When The Spot enters, exile up to one target nonland permanent and up to one target nonland permanent card from a graveyard.$When The Spot dies, put him on the bottom of his owner's library. If you do, return the exiled cards to their owners' hands.| Peter Parker|Marvel's Spider-Man|232|M|{1}{W}|Legendary Creature - Human Scientist Hero|0|1|When Peter Parker enters, create a 2/1 green Spider creature token with reach.${1}{G}{W}{U}: Transform Peter Parker. Activate only as a sorcery.| Amazing Spider-Man|Marvel's Spider-Man|232|M|{1}{G}{W}{U}|Legendary Creature - Spider Human Hero|4|4|Vigilance, reach$Each legendary spell you cast that's one or more colors has web-slinging {G}{W}{U}.| +Eddie Brock|Marvel's Spider-Man|233|M|{2}{B}|Legendary Creature - Human Hero Villain|5|5|When Eddie Brock enters, return target creature card with mana value 1 or less from your graveyard to the battlefield.${3}{B}{R}{G}: Transform Eddie Brock. Activate only as a sorcery.| +Venom, Lethal Protector|Marvel's Spider-Man|233|M|{3}{B}{R}{G}|Legendary Creature - Symbiote Hero Villain|5|5|Menace, trample, haste$Whenever Venom attacks, you may sacrifice another creature. If you do, draw X cards, then you may put a permanent card with mana value X or less from your hand onto the battlefield, where X is the sacrificed creature's mana value.| Miles Morales|Marvel's Spider-Man|234|M|{1}{G}|Legendary Creature - Human Citizen Hero|1|2|When Miles Morales enters, put a +1/+1 counter on each of up to two target creatures.${3}{R}{G}{W}: Transform Miles Morales. Activate only as a sorcery.| Ultimate Spider-Man|Marvel's Spider-Man|234|M|{3}{R}{G}{W}|Legendary Creature - Spider Human Hero|4|3|First strike, haste$Camouflage -- {2}: Put a +1/+1 counter on Ultimate Spider-Man. He gains hexproof and becomes colorless until end of turn.$Whenever you attack, double the number of each kind of counter on each Spider and legendary creature you control.| +Spectacular Spider-Man|Marvel's Spider-Man|235|R|{1}{W}|Legendary Creature - Spider Human Hero|3|2|Flash${1}: Spectacular Spider-Man gains flying until end of turn.${1}, Sacrifice Spectacular Spider-Man: Creatures you control gain hexproof and indestructible until end of turn.| +Spectacular Spider-Man|Marvel's Spider-Man|236|R|{1}{W}|Legendary Creature - Spider Human Hero|3|2|Flash${1}: Spectacular Spider-Man gains flying until end of turn.${1}, Sacrifice Spectacular Spider-Man: Creatures you control gain hexproof and indestructible until end of turn.| +Spectacular Spider-Man|Marvel's Spider-Man|237|R|{1}{W}|Legendary Creature - Spider Human Hero|3|2|Flash${1}: Spectacular Spider-Man gains flying until end of turn.${1}, Sacrifice Spectacular Spider-Man: Creatures you control gain hexproof and indestructible until end of turn.| +Spectacular Spider-Man|Marvel's Spider-Man|238|R|{1}{W}|Legendary Creature - Spider Human Hero|3|2|Flash${1}: Spectacular Spider-Man gains flying until end of turn.${1}, Sacrifice Spectacular Spider-Man: Creatures you control gain hexproof and indestructible until end of turn.| +Spectacular Spider-Man|Marvel's Spider-Man|239|R|{1}{W}|Legendary Creature - Spider Human Hero|3|2|Flash${1}: Spectacular Spider-Man gains flying until end of turn.${1}, Sacrifice Spectacular Spider-Man: Creatures you control gain hexproof and indestructible until end of turn.| +Spectacular Spider-Man|Marvel's Spider-Man|240|R|{1}{W}|Legendary Creature - Spider Human Hero|3|2|Flash${1}: Spectacular Spider-Man gains flying until end of turn.${1}, Sacrifice Spectacular Spider-Man: Creatures you control gain hexproof and indestructible until end of turn.| +Spectacular Spider-Man|Marvel's Spider-Man|241|R|{1}{W}|Legendary Creature - Spider Human Hero|3|2|Flash${1}: Spectacular Spider-Man gains flying until end of turn.${1}, Sacrifice Spectacular Spider-Man: Creatures you control gain hexproof and indestructible until end of turn.| +The Soul Stone|Marvel's Spider-Man|242|M|{1}{B}|Legendary Artifact - Infinity Stone|||Indestructible${T}: Add {B}.${6}{B}, {T}, Exile a creature you control: Harness The Soul Stone.$ -- At the beginning of your upkeep, return target creature card from your graveyard to the battlefield.| +The Soul Stone|Marvel's Spider-Man|243|M|{1}{B}|Legendary Artifact - Infinity Stone|||Indestructible${T}: Add {B}.${6}{B}, {T}, Exile a creature you control: Harness The Soul Stone.$ -- At the beginning of your upkeep, return target creature card from your graveyard to the battlefield.| Anti-Venom, Horrifying Healer|Marvel's Spider-Man|244|M|{W}{W}{W}{W}{W}|Legendary Creature - Symbiote Hero|5|5|When Anti-Venom enters, if he was cast, return target creature card from your graveyard to the battlefield.$If damage would be dealt to Anti-Venom, prevent that damage and put that many +1/+1 counters on him.| +Arachne, Psionic Weaver|Marvel's Spider-Man|245|R|{2}{W}|Legendary Creature - Spider Human Hero|3|3|Web-slinging {W}$As Arachne enters, look at target opponent's hand, then choose a noncreature card type.$Spells of the chosen type cost {1} more to cast.| +Friendly Neighborhood|Marvel's Spider-Man|246|R|{3}{W}|Enchantment - Aura|||Enchant land$When this Aura enters, create three 1/1 green and white Human Citizen creature tokens.$Enchanted land has "{1}, {T}: Target creature gets +1/+1 until end of turn for each creature you control. Activate only as a sorcery."| +Rent Is Due|Marvel's Spider-Man|247|R|{W}|Enchantment|||At the beginning of your end step, you may tap two untapped creatures and/or Treasures you control. If you do, draw a card. Otherwise, sacrifice this enchantment.| +With Great Power...|Marvel's Spider-Man|248|R|{3}{W}|Enchantment - Aura|||Enchant creature you control$Enchanted creature gets +2/+2 for each Aura and Equipment attached to it.$All damage that would be dealt to you is dealt to enchanted creature instead.| +Hide on the Ceiling|Marvel's Spider-Man|249|R|{X}{U}|Instant|||Exile X target artifacts and/or creatures. Return the exiled cards to the battlefield under their owners' control at the beginning of the next end step.| +Hydro-Man, Fluid Felon|Marvel's Spider-Man|250|R|{U}{U}|Legendary Creature - Elemental Villain|2|2|Whenever you cast a blue spell, if Hydro-Man is a creature, he gets +1/+1 until end of turn.$At the beginning of your end step, untap Hydro-Man. Until your next turn, he becomes a land and gains "{T}: Add {U}."| +Impostor Syndrome|Marvel's Spider-Man|251|M|{4}{U}{U}|Enchantment|||Whenever a nontoken creature you control deals combat damage to a player, create a token that's a copy of it, except it isn't legendary.| +Lady Octopus, Inspired Inventor|Marvel's Spider-Man|252|R|{U}|Legendary Creature - Human Scientist Villain|0|2|Whenever you draw your first or second card each turn, put an ingenuity counter on Lady Octopus.${T}: You may cast an artifact spell from your hand with mana value less than or equal to the number of ingenuity counters on Lady Octopus without paying its mana cost.| +Mysterio, Master of Illusion|Marvel's Spider-Man|253|R|{3}{U}|Legendary Creature - Human Villain|3|3|When Mysterio enters, create a 3/3 blue Illusion Villain creature token for each nontoken Villain you control. Exile those tokens when Mysterio leaves the battlefield.| +Spider-Sense|Marvel's Spider-Man|254|R|{1}{U}|Instant|||Web-slinging {U}$Counter target instant spell, sorcery spell, or triggered ability.| +Agent Venom|Marvel's Spider-Man|255|R|{2}{B}|Legendary Creature - Symbiote Soldier Hero|2|3|Flash$Menace$Whenever another nontoken creature you control dies, you draw a card and lose 1 life.| +Gwenom, Remorseless|Marvel's Spider-Man|256|M|{3}{B}{B}|Legendary Creature - Symbiote Spider Hero|4|4|Deathtouch, lifelink$Whenever Gwenom attacks, until end of turn you may look at the top card of your library any time and you may play cards from the top of your library. If you cast a spell this way, pay life equal to its mana value rather than pay its mana cost.| +Morlun, Devourer of Spiders|Marvel's Spider-Man|257|R|{X}{B}{B}|Legendary Creature - Vampire Villain|2|1|Lifelink$Morlun enters with X+1/+1 counters on him.$When Morlun enters, he deals X damage to target opponent.| +Parker Luck|Marvel's Spider-Man|258|R|{2}{B}|Enchantment|||At the beginning of your end step, two target players each reveal the top card of their library. They each lose life equal to the mana value of the card revealed by the other player. Then they each put the card they revealed into their hand.| +Villainous Wrath|Marvel's Spider-Man|259|R|{3}{B}{B}|Sorcery|||Target opponent loses life equal to the number of creatures they control. Then destroy all creatures.| +Electro, Assaulting Battery|Marvel's Spider-Man|260|R|{1}{R}{R}|Legendary Creature - Human Villain|2|3|Flying$You don't lose unspent red mana as steps and phases end.$Whenever you cast an instant or sorcery spell, add {R}.$When Electro leaves the battlefield, you may pay {X}. When you do, he deals X damage to target player.| +J. Jonah Jameson|Marvel's Spider-Man|261|R|{2}{R}|Legendary Creature - Human Citizen|2|2|When J. Jonah Jameson enters, suspect up to one target creature.$Whenever a creature you control with menace attacks, create a Treasure token.| +Shadow of the Goblin|Marvel's Spider-Man|262|R|{1}{R}|Enchantment|||Unreliable Visions -- At the beginning of your first main phase, discard a card. If you do, draw a card.$Undying Vengeance -- Whenever you play a land or cast a spell from anywhere other than your hand, this enchantment deals 1 damage to each opponent.| +Spider-Verse|Marvel's Spider-Man|263|M|{3}{R}{R}|Enchantment|||The "legend rule" doesn't apply to Spiders you control.$Whenever you cast a spell from anywhere other than your hand, you may copy it. If you do, you may choose new targets for the copy. If the copy is a permanent spell, it gains haste. Do this only once each turn.| +Spinneret and Spiderling|Marvel's Spider-Man|264|R|{R}|Legendary Creature - Spider Human Hero|1|2|Whenever you attack with two or more Spiders, put a +1/+1 counter on Spinneret and Spiderling.$Whenever Spinneret and Spiderling deals 4 or more damage, exile the top card of your library. Until the end of your next turn, you may play that card.| +Lizard, Connors's Curse|Marvel's Spider-Man|265|R|{2}{G}{G}|Legendary Creature - Lizard Villain|5|5|Trample$Lizard Formula -- When Lizard, Connors's Curse enters, up to one other target creature loses all abilities and becomes a green Lizard creature with base power and toughness 4/4.| +Sandman, Shifting Scoundrel|Marvel's Spider-Man|266|R|{1}{G}{G}|Legendary Creature - Sand Elemental Villain|*|*|Sandman's power and toughness are each equal to the number of lands you control.$Sandman can't be blocked by creatures with power 2 or less.${3}{G}{G}: Return this card and target land card from your graveyard to the battlefield tapped.| +Strength of Will|Marvel's Spider-Man|267|R|{1}{G}|Instant|||Until end of turn, target creature you control gains indestructible and "Whenever this creature is dealt damage, put that many +1/+1 counters on it."| +Web of Life and Destiny|Marvel's Spider-Man|268|M|{6}{G}{G}|Enchantment|||Convoke$At the beginning of combat on your turn, look at the top five cards of your library. You may put a creature card from among them onto the battlefield. Put the rest on the bottom of your library in a random order.| +Biorganic Carapace|Marvel's Spider-Man|269|R|{2}{W}{U}|Artifact - Equipment|||When this Equipment enters, attach it to target creature you control.$Equipped creature gets +2/+2 and has "Whenever this creature deals combat damage to a player, draw a card for each modified creature you control."$Equip {2}| +Cheering Crowd|Marvel's Spider-Man|270|R|{1}{R/G}|Creature - Human Citizen|2|2|At the beginning of each player's first main phase, that player may put a +1/+1 counter on this creature. If they do, they add {C} for each counter on it.| +Cosmic Spider-Man|Marvel's Spider-Man|271|M|{W}{U}{B}{R}{G}|Legendary Creature - Spider Human Hero|5|5|Flying, first strike, trample, lifelink, haste$At the beginning of combat on your turn, other Spiders you control gain flying, first strike, trample, lifelink, and haste until end of turn.| +Jackal, Genius Geneticist|Marvel's Spider-Man|272|R|{G}{U}|Legendary Creature - Human Scientist Villain|1|1|Trample$Whenever you cast a creature spell with mana value equal to Jackal's power, copy that spell, except the copy isn't legendary. Then put a +1/+1 counter on Jackal.| +Kraven the Hunter|Marvel's Spider-Man|273|R|{1}{B}{G}|Legendary Creature - Human Warrior Villain|4|3|Trample$Whenever a creature an opponent controls with the greatest power among creatures that player controls dies, draw a card and put a +1/+1 counter on Kraven the Hunter.| +Mister Negative|Marvel's Spider-Man|274|M|{5}{W}{B}|Legendary Creature - Human Villain|5|5|Vigilance, lifelink$Darkforce Inversion -- When Mister Negative enters, you may exchange your life total with target opponent. If you lose life this way, draw that many cards.| +Superior Spider-Man|Marvel's Spider-Man|275|R|{2}{U}{B}|Legendary Creature - Spider Human Hero|4|4|Mind Swap -- You may have Superior Spider- Man enter as a copy of any creature card in a graveyard, except his name is Superior Spider-Man and he's a 4/4 Spider Human Hero in addition to his other types. When you do, exile that card.| +Ultimate Green Goblin|Marvel's Spider-Man|276|R|{1}{B/R}{B/R}|Legendary Creature - Goblin Villain|5|4|At the beginning of your upkeep, discard a card, then create a Treasure token.$Mayhem {2}{B/R}| +Doc Ock's Tentacles|Marvel's Spider-Man|277|R|{1}|Artifact - Equipment|||Whenever a creature you control with mana value 5 or greater enters, you may attach this Equipment to it.$Equipped creature gets +4/+4.$Equip {5}| +Interdimensional Web Watch|Marvel's Spider-Man|278|R|{4}|Artifact|||When this artifact enters, exile the top two cards of your library. Until the end of your next turn, you may play those cards.${T}: Add two mana in any combination of colors. Spend this mana only to cast spells from exile.| Iron Spider, Stark Upgrade|Marvel's Spider-Man|279|R|{3}|Legendary Artifact Creature - Spider Hero|2|3|Vigilance${T}: Put a +1/+1 counter on each artifact creature and/or Vehicle you control.${2}, Remove two +1/+1 counters from among artifacts you control: Draw a card.| +Peter Parker's Camera|Marvel's Spider-Man|280|R|{1}|Artifact|||This artifact enters with three film counters on it.${2}, {T}, Remove a film counter from this artifact: Copy target activated or triggered ability you control. You may choose new targets for the copy.| +Rocket-Powered Goblin Glider|Marvel's Spider-Man|281|R|{3}|Artifact - Equipment|||When this Equipment enters, if it was cast from your graveyard, attach it to target creature you control.$Equipped creature gets +2/+0 and has flying and haste.$Equip {2}$Mayhem {2}| +Oscorp Industries|Marvel's Spider-Man|282|R||Land|||This land enters tapped.$When this land enters from a graveyard, you lose 2 life.${T}: Add {U}, {B}, or {R}.$Mayhem| +Urban Retreat|Marvel's Spider-Man|283|R||Land|||This land enters tapped.${T}: Add {G}, {W} , or {U}.${2}, Return a tapped creature you control to its owner's hand: Put this card from your hand onto the battlefield. Activate only as a sorcery.| +Spider-Sense|Marvel's Spider-Man|284|R|{1}{U}|Instant|||Web-slinging {U}$Counter target instant spell, sorcery spell, or triggered ability.| +Radioactive Spider|Marvel's Spider-Man|285|R|{G}|Creature - Spider|1|1|Reach, deathtouch$Fateful Bite -- {2}, Sacrifice this creature: Search your library for a Spider Hero card, reveal it, put it into your hand, then shuffle. Activate only as a sorcery.| +Gwenom, Remorseless|Marvel's Spider-Man|286|M|{3}{B}{B}|Legendary Creature - Symbiote Spider Hero|4|4|Deathtouch, lifelink$Whenever Gwenom attacks, until end of turn you may look at the top card of your library any time and you may play cards from the top of your library. If you cast a spell this way, pay life equal to its mana value rather than pay its mana cost.| Force of Negation|Avatar: The Last Airbender Eternal|13|M|{1}{U}{U}|Instant|||If it's not your turn, you may exile a blue card from your hand rather than pay this spell's mana cost.$Counter target noncreature spell. If that spell is countered this way, exile it instead of putting it into its owner's graveyard.| The Great Henge|Avatar: The Last Airbender Eternal|41|M|{7}{G}{G}|Legendary Artifact|||This spell costs {X} less to cast, where X is the greatest power among creatures you control.${T}: Add {G}{G}. You gain 2 life.$Whenever a nontoken creature you control enters, put a +1/+1 counter on it and draw a card.| Aang, Airbending Master|Avatar: The Last Airbender Eternal|74|M|{4}{W}|Legendary Creature - Human Avatar Ally|4|4|When Aang enters, airbend another target creature.$Whenever one or more creatures you control leave the battlefield without dying, you get an experience counter.$At the beginning of your upkeep, create a 1/1 white Ally creature token for each experience counter you have.| diff --git a/Utils/print-card-info.pl b/Utils/print-card-info.pl new file mode 100644 index 00000000000..1c79be89324 --- /dev/null +++ b/Utils/print-card-info.pl @@ -0,0 +1,97 @@ +#!/usr/bin/perl -w + +#author: Jmlundeen + +use Text::Template; +use strict; +use utf8; +use open ':std', ':encoding(UTF-8)'; + +my $dataFile = 'mtg-cards-data.txt'; +my $setsFile = 'mtg-sets-data.txt'; +my $cardInfoTemplate = 'cardInfo.tmpl'; + +my %cards; +my %sets; + +sub toCamelCase { + my $string = $_[0]; + $string =~ s/\b([\w']+)\b/ucfirst($1)/ge; + $string =~ s/[-,\s\':.!\/]//g; + $string; +} + +sub printCardInfo { + my ($cardName, $infoTemplate) = @_; + + if (!exists $cards{$cardName}) { + print "Card name doesn't exist: $cardName\n\n"; + return; + } + + my %vars; + $vars{'classNameLower'} = lcfirst(toCamelCase($cardName)); + my @card; + + foreach my $setName (keys %{$cards{$cardName}}) { + @card = @{(values(%{$cards{$cardName}{$setName}}))[0]}; + last; # Just get the first one + } + + $vars{'cardName'} = $card[0]; + $vars{'manaCost'} = $card[4]; + $vars{'typeLine'} = $card[5]; + + # Combine power and toughness if they exist + my $powerToughness = ""; + if ($card[6] && $card[7] && $card[6] ne "" && $card[7] ne "") { + $powerToughness = "$card[6]/$card[7]"; + } + $vars{'powerToughness'} = $powerToughness; + + my $cardAbilities = $card[8]; + my @abilities = split(/\$/, $cardAbilities); + my $abilitiesFormatted = join("\n ", @abilities); + $vars{'abilities'} = $abilitiesFormatted; + + my $result = $infoTemplate->fill_in(HASH => \%vars); + print "$result\n\n"; +} + +# Load data files +open(DATA, $dataFile) || die "can't open $dataFile : $!"; +while (my $line = ) { + my @data = split('\\|', $line); + $cards{$data[0]}{$data[1]}{$data[2]} = \@data; +} +close(DATA); + +open(DATA, $setsFile) || die "can't open $setsFile : $!"; +while (my $line = ) { + my @data = split('\\|', $line); + $sets{$data[0]} = $data[1]; +} +close(DATA); + +# Get card names from arguments +my @cardNames = @ARGV; +if (@cardNames == 0) { + print 'Enter card names (one per line, empty line to finish): '; + while (my $input = ) { + chomp $input; + last if $input eq ''; + push @cardNames, $input; + } +} + +if (@cardNames == 0) { + die "No card names provided.\n"; +} + +# Load template +my $infoTemplate = Text::Template->new(TYPE => 'FILE', SOURCE => $cardInfoTemplate, DELIMITERS => [ '[=', '=]' ]); + +# Print card info for each card +foreach my $cardName (@cardNames) { + printCardInfo($cardName, $infoTemplate); +} \ No newline at end of file