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/ScryfallImageSupportTokens.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java index 96db19de51a..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"); 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/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/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/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/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/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/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/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/BiorganicCarapace.java b/Mage.Sets/src/mage/cards/b/BiorganicCarapace.java index 42a052657e6..388deedc3c8 100644 --- a/Mage.Sets/src/mage/cards/b/BiorganicCarapace.java +++ b/Mage.Sets/src/mage/cards/b/BiorganicCarapace.java @@ -2,26 +2,24 @@ package mage.cards.b; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageTriggeredAbility; -import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.EntersBattlefieldAttachToTarget; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.continuous.BoostEquippedEffect; import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; import mage.abilities.keyword.EquipAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.permanent.ModifiedPredicate; -import mage.target.common.TargetControlledCreaturePermanent; import java.util.UUID; /** - * * @author Jmlundeen */ public final class BiorganicCarapace extends CardImpl { @@ -34,13 +32,11 @@ public final class BiorganicCarapace extends CardImpl { public BiorganicCarapace(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{W}{U}"); - + this.subtype.add(SubType.EQUIPMENT); // When this Equipment enters, attach it to target creature you control. - Ability ability = new EntersBattlefieldAbility(new AttachEffect(Outcome.BoostCreature)); - ability.addTarget(new TargetControlledCreaturePermanent()); - this.addAbility(ability); + 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); 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/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/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/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/CelestialDawn.java b/Mage.Sets/src/mage/cards/c/CelestialDawn.java index 123db6fa2e1..a7a7db29db7 100644 --- a/Mage.Sets/src/mage/cards/c/CelestialDawn.java +++ b/Mage.Sets/src/mage/cards/c/CelestialDawn.java @@ -128,7 +128,7 @@ class CelestialDawnToWhiteEffect extends ContinuousEffectImpl { } } // Exile - for (Card card : game.getExile().getAllCardsByRange(game, controller.getId())) { + 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/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/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/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/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/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/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/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/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/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/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/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 2cd68f54296..bbc688657cf 100644 --- a/Mage.Sets/src/mage/cards/d/DuneChanter.java +++ b/Mage.Sets/src/mage/cards/d/DuneChanter.java @@ -115,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); } @@ -174,5 +174,3 @@ class DuneChanterEffect extends OneShotEffect { return true; } } - - 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/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/EncroachingMycosynth.java b/Mage.Sets/src/mage/cards/e/EncroachingMycosynth.java index 0e88261963a..ec5af61315b 100644 --- a/Mage.Sets/src/mage/cards/e/EncroachingMycosynth.java +++ b/Mage.Sets/src/mage/cards/e/EncroachingMycosynth.java @@ -80,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/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/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/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 9143c5fb65e..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.getStackMomentSourceZCC() + ")"); - 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.getStackMomentSourceZCC() == ability.getStackMomentSourceZCC()) { - 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/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/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/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/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/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/GwenStacy.java b/Mage.Sets/src/mage/cards/g/GwenStacy.java index d5076e6e817..cfb9cbbfc5e 100644 --- a/Mage.Sets/src/mage/cards/g/GwenStacy.java +++ b/Mage.Sets/src/mage/cards/g/GwenStacy.java @@ -53,7 +53,7 @@ public final class GwenStacy extends ModalDoubleFacedCard { // 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(CounterType.P1P1.createInstance(2)))); + new RemoveCountersSourceCost(2))); } private GwenStacy(final GwenStacy card) { 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/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/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/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/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/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/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/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/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/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/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/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/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/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/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 index ead24418699..42aef22cfab 100644 --- a/Mage.Sets/src/mage/cards/m/MaximumCarnage.java +++ b/Mage.Sets/src/mage/cards/m/MaximumCarnage.java @@ -2,7 +2,6 @@ package mage.cards.m; import mage.Mana; import mage.abilities.Ability; -import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SagaAbility; import mage.abilities.effects.RestrictionEffect; import mage.abilities.effects.common.DamagePlayersEffect; @@ -11,7 +10,6 @@ import mage.abilities.effects.mana.AddManaToManaPoolSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; -import mage.filter.StaticFilters; import mage.filter.common.FilterOpponentsCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; @@ -19,14 +17,13 @@ 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.) @@ -39,9 +36,11 @@ public final class MaximumCarnage extends CardImpl { // 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) { @@ -53,6 +52,7 @@ public final class MaximumCarnage extends CardImpl { return new MaximumCarnage(this); } } + class MaximumCarnageEffect extends RestrictionEffect { MaximumCarnageEffect() { 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/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/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/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/MorbiusTheLivingVampire.java b/Mage.Sets/src/mage/cards/m/MorbiusTheLivingVampire.java index 40c76fd98d9..0f8f8c2baf5 100644 --- a/Mage.Sets/src/mage/cards/m/MorbiusTheLivingVampire.java +++ b/Mage.Sets/src/mage/cards/m/MorbiusTheLivingVampire.java @@ -11,10 +11,7 @@ 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.PutCards; -import mage.constants.SubType; -import mage.constants.SuperType; +import mage.constants.*; import java.util.UUID; @@ -44,7 +41,7 @@ public final class MorbiusTheLivingVampire extends CardImpl { 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(new LookLibraryAndPickControllerEffect(3, 1, PutCards.HAND, PutCards.BOTTOM_ANY), + 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); 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/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/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 index 797c25db21c..c35bd9937e7 100644 --- a/Mage.Sets/src/mage/cards/n/NewsHelicopter.java +++ b/Mage.Sets/src/mage/cards/n/NewsHelicopter.java @@ -8,7 +8,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.game.permanent.token.CitizenGreenWhiteToken; +import mage.game.permanent.token.HumanCitizenToken; import java.util.UUID; @@ -28,7 +28,7 @@ public final class NewsHelicopter extends CardImpl { 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 CitizenGreenWhiteToken()))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new HumanCitizenToken()))); } private NewsHelicopter(final NewsHelicopter card) { 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/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/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/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 index 05522c22f56..12be2293b5b 100644 --- a/Mage.Sets/src/mage/cards/p/PeterParker.java +++ b/Mage.Sets/src/mage/cards/p/PeterParker.java @@ -106,7 +106,7 @@ class AmazingSpiderManEffect extends ContinuousEffectImpl { controller.getLibrary().getCards(game).stream() .filter(c -> filter.match(c, game)) .forEach(cardsToGainAbility::add); - game.getExile().getAllCardsByRange(game, controller.getId()).stream() + game.getExile().getCardsInRange(game, controller.getId()).stream() .filter(c -> filter.match(c, game)) .forEach(cardsToGainAbility::add); game.getCommanderCardsFromCommandZone(controller, CommanderCardType.ANY).stream() @@ -127,4 +127,4 @@ class AmazingSpiderManEffect extends ContinuousEffectImpl { } return true; } -} \ No newline at end of file +} 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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/SpiderGirlLegacyHero.java b/Mage.Sets/src/mage/cards/s/SpiderGirlLegacyHero.java index fe380dcf605..9a541d82cc0 100644 --- a/Mage.Sets/src/mage/cards/s/SpiderGirlLegacyHero.java +++ b/Mage.Sets/src/mage/cards/s/SpiderGirlLegacyHero.java @@ -14,7 +14,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; import mage.constants.SuperType; -import mage.game.permanent.token.CitizenGreenWhiteToken; +import mage.game.permanent.token.HumanCitizenToken; import java.util.UUID; @@ -40,7 +40,7 @@ public final class SpiderGirlLegacyHero extends CardImpl { ))); // When Spider-Girl leaves the battlefield, create a 1/1 green and white Human Citizen creature token. - this.addAbility(new LeavesBattlefieldTriggeredAbility(new CreateTokenEffect(new CitizenGreenWhiteToken()))); + this.addAbility(new LeavesBattlefieldTriggeredAbility(new CreateTokenEffect(new HumanCitizenToken()))); } private SpiderGirlLegacyHero(final SpiderGirlLegacyHero card) { 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/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/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/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/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/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/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 index b6f244f7644..d53ae570f21 100644 --- a/Mage.Sets/src/mage/cards/t/TheCloneSaga.java +++ b/Mage.Sets/src/mage/cards/t/TheCloneSaga.java @@ -2,7 +2,7 @@ package mage.cards.t; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.SagaAbility; -import mage.abilities.common.delayed.CopyNextSpellDelayedTriggeredAbility; +import mage.abilities.common.delayed.CastNextSpellDelayedTriggeredAbility; import mage.abilities.effects.common.ChooseACardNameEffect; import mage.abilities.effects.common.CopyTargetStackObjectEffect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; @@ -43,10 +43,10 @@ public final class TheCloneSaga extends CardImpl { 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 CopyNextSpellDelayedTriggeredAbility( - StaticFilters.FILTER_SPELL_A_CREATURE, - new CopyTargetStackObjectEffect(false, false, false, 1, new RemoveTypeCopyApplier(SuperType.LEGENDARY)), - "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)); @@ -105,4 +105,4 @@ class TheCloneSagaDelayedTrigger extends DelayedTriggeredAbility { public String getRule() { return "Whenever a creature with the chosen name deals combat damage to a player this turn, draw a card."; } -} \ No newline at end of file +} 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/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/TheSoulStone.java b/Mage.Sets/src/mage/cards/t/TheSoulStone.java new file mode 100644 index 00000000000..252ea24aafa --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheSoulStone.java @@ -0,0 +1,67 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.ExileTargetCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.HarnessSourceEffect; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; +import mage.abilities.effects.common.continuous.GainHarnessedAbilitySourceEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.abilities.mana.BlackManaAbility; +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.filter.StaticFilters; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.common.TargetControlledPermanent; + +import java.util.UUID; + +/** + * + * @author Jmlundeen + */ +public final class TheSoulStone extends CardImpl { + + public TheSoulStone(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.INFINITY); + this.subtype.add(SubType.STONE); + + // Indestructible + this.addAbility(IndestructibleAbility.getInstance()); + + // {T}: Add {B}. + this.addAbility(new BlackManaAbility()); + + // {6}{B}, {T}, Exile a creature you control: Harness The Soul Stone. + Ability ability = new SimpleActivatedAbility(new HarnessSourceEffect(), new ManaCostsImpl<>("{6}{B}")); + ability.addCost(new TapSourceCost()); + ability.addCost(new ExileTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_A_CREATURE))); + this.addAbility(ability); + + // ∞ -- At the beginning of your upkeep, return target creature card from your graveyard to the battlefield. + Ability soulStoneAbility = new BeginningOfUpkeepTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect()); + soulStoneAbility.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); + this.addAbility(new SimpleStaticAbility( + new GainHarnessedAbilitySourceEffect(soulStoneAbility)) + ); + } + + private TheSoulStone(final TheSoulStone card) { + super(card); + } + + @Override + public TheSoulStone copy() { + return new TheSoulStone(this); + } +} 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/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 1abb7c6fbc6..545a5244eec 100644 --- a/Mage.Sets/src/mage/cards/t/ThranPowerSuit.java +++ b/Mage.Sets/src/mage/cards/t/ThranPowerSuit.java @@ -35,7 +35,7 @@ public final class ThranPowerSuit extends CardImpl { filter.add(AttachedToAttachedPredicate.instance); } - private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, 2); + private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, 1); public ThranPowerSuit(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); 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/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/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/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/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/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/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/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/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/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/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/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/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 fafcc856776..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)); 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/MarvelsSpiderMan.java b/Mage.Sets/src/mage/sets/MarvelsSpiderMan.java index 5c6ad9766d7..d4d03eff0c6 100644 --- a/Mage.Sets/src/mage/sets/MarvelsSpiderMan.java +++ b/Mage.Sets/src/mage/sets/MarvelsSpiderMan.java @@ -1,7 +1,6 @@ package mage.sets; import mage.cards.ExpansionSet; -import mage.cards.s.SilkWebWeaver; import mage.constants.Rarity; import mage.constants.SetType; @@ -25,6 +24,8 @@ public final class MarvelsSpiderMan extends ExpansionSet { this.blockName = "Marvel's Spider-Man"; // for sorting in GUI this.hasBasicLands = true; + this.enablePlayBooster(Integer.MAX_VALUE); + 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)); @@ -74,7 +75,7 @@ public final class MarvelsSpiderMan extends ExpansionSet { 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", 193, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_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)); @@ -125,7 +126,7 @@ public final class MarvelsSpiderMan extends ExpansionSet { 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, NON_FULL_USE_VARIOUS)); + 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)); @@ -140,7 +141,7 @@ public final class MarvelsSpiderMan extends ExpansionSet { 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", 192, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Mountain", 197, Rarity.LAND, mage.cards.basiclands.Mountain.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)); @@ -165,7 +166,7 @@ public final class MarvelsSpiderMan extends ExpansionSet { 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", 189, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_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)); @@ -204,8 +205,8 @@ public final class MarvelsSpiderMan extends ExpansionSet { 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("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("Silk, Web Weaver", 145, Rarity.RARE, mage.cards.s.SilkWebWeaver.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Silk, Web Weaver", 215, Rarity.RARE, mage.cards.s.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)); @@ -256,20 +257,33 @@ public final class MarvelsSpiderMan extends ExpansionSet { 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", 191, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_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("The Clone Saga", 219, Rarity.RARE, mage.cards.t.TheCloneSaga.class, NON_FULL_USE_VARIOUS)); + 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 Soul Stone", 242, Rarity.MYTHIC, mage.cards.t.TheSoulStone.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Soul Stone", 243, Rarity.MYTHIC, mage.cards.t.TheSoulStone.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Soul Stone", 66, Rarity.MYTHIC, mage.cards.t.TheSoulStone.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)); @@ -289,6 +303,7 @@ public final class MarvelsSpiderMan extends ExpansionSet { 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)); diff --git a/Mage.Sets/src/mage/sets/SecretLairDrop.java b/Mage.Sets/src/mage/sets/SecretLairDrop.java index 108133868e1..b9fff33cee5 100644 --- a/Mage.Sets/src/mage/sets/SecretLairDrop.java +++ b/Mage.Sets/src/mage/sets/SecretLairDrop.java @@ -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)); @@ -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)); @@ -1971,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)); 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/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.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/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/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/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/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/TheSoulStoneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/TheSoulStoneTest.java new file mode 100644 index 00000000000..c61b8fc1655 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/spm/TheSoulStoneTest.java @@ -0,0 +1,121 @@ +package org.mage.test.cards.single.spm; + +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; +import mage.cards.Card; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertTrue; + +/** + * + * @author Jmlundeen + */ +public class TheSoulStoneTest extends CardTestPlayerBase { + + /* + The Soul Stone + {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. + */ + private static final String theSoulStone = "The Soul Stone"; + + /* + Bear Cub + {1}{G} + Creature - Bear + + 2/2 + */ + private static final String bearCub = "Bear Cub"; + + /* + Teferi's Time Twist + {1}{U} + Instant + Exile target permanent you control. Return that card to the battlefield under its owner's control at the beginning of the next end step. If it enters the battlefield as a creature, it enters with an additional +1/+1 counter on it. + */ + private static final String teferisTimeTwist = "Teferi's Time Twist"; + + @Test + public void testTheSoulStone() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, theSoulStone); + addCard(Zone.BATTLEFIELD, playerA, bearCub); + addCard(Zone.GRAVEYARD, playerA, bearCub); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 7); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{6}{B}, {T}"); + setChoice(playerA, bearCub); // exile as cost + addTarget(playerA, bearCub); // return to battlefield + + setStopAt(3, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, bearCub, 1); + assertExileCount(playerA, bearCub, 1); + // doesn't have ability if blinked + assertAbilityCount(playerA, theSoulStone, BeginningOfUpkeepTriggeredAbility.class, 1); + } + + @Test + public void testBlinkSoulStone() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, theSoulStone); + addCard(Zone.BATTLEFIELD, playerA, bearCub); + addCard(Zone.GRAVEYARD, playerA, bearCub); + addCard(Zone.HAND, playerA, teferisTimeTwist); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 7); + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{6}{B}, {T}"); + setChoice(playerA, bearCub); + + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, teferisTimeTwist, theSoulStone); + + setStopAt(3, PhaseStep.END_TURN); + execute(); + + assertExileCount(playerA, bearCub, 1); + assertPermanentCount(playerA, bearCub, 0); + assertGraveyardCount(playerA, bearCub, 1); + // doesn't have ability if blinked + assertAbilityCount(playerA, theSoulStone, BeginningOfUpkeepTriggeredAbility.class, 0); + } + + @Test + public void testSoulStoneHasNoInfinityAbility() { + setStrictChooseMode(true); + removeAllCardsFromLibrary(playerA); + + addCard(Zone.HAND, playerA, theSoulStone); + addCard(Zone.LIBRARY, playerA, theSoulStone); + addCard(Zone.GRAVEYARD, playerA, theSoulStone); + addCard(Zone.EXILED, playerA, theSoulStone); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + + List cards = new ArrayList<>(getHandCards(playerA)); + cards.addAll(getLibraryCards(playerA)); + cards.addAll(getGraveCards(playerA)); + cards.addAll(getExiledCards(playerA)); + + cards.forEach(card -> { + if (card.getName().equals(theSoulStone)) { + assertTrue("Should not have Infinity ability", !card.getAbilities(currentGame).containsClass(BeginningOfUpkeepTriggeredAbility.class)); + } + }); + } +} \ 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/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/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/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java index 18485b9f8c6..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); diff --git a/Mage/src/main/java/mage/MageIdentifier.java b/Mage/src/main/java/mage/MageIdentifier.java index db65ff23de3..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 // 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/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/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/condition/common/SourceHarnessedCondition.java b/Mage/src/main/java/mage/abilities/condition/common/SourceHarnessedCondition.java new file mode 100644 index 00000000000..7d5de8a4545 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/condition/common/SourceHarnessedCondition.java @@ -0,0 +1,25 @@ +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * @author Jmlundee + */ + +public enum SourceHarnessedCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getSourceId()); + return permanent != null && permanent.isHarnessed(); + } + + @Override + public String toString() { + return "{this} is harnessed"; + } +} diff --git a/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java b/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java index ad9f3fbae43..783461fe4fc 100644 --- a/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java +++ b/Mage/src/main/java/mage/abilities/costs/AlternativeCostSourceAbility.java @@ -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/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/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/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 ca0a32efa39..6f02329d0b3 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CreateTokenCopyTargetEffect.java @@ -414,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/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/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/HarnessSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/HarnessSourceEffect.java new file mode 100644 index 00000000000..a47aa31f40b --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/HarnessSourceEffect.java @@ -0,0 +1,37 @@ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * @author Jmlundeen + */ +public class HarnessSourceEffect extends OneShotEffect { + + public HarnessSourceEffect() { + super(Outcome.AIDontUseIt); + staticText = "Harness {this}. (Once harnessed, its ∞ ability is active.)"; + } + + protected HarnessSourceEffect(final HarnessSourceEffect effect) { + super(effect); + } + + @Override + public HarnessSourceEffect copy() { + return new HarnessSourceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent == null) { + return false; + } + permanent.setHarnessed(true); + return true; + } +} 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/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/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/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/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/GainHarnessedAbilitySourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainHarnessedAbilitySourceEffect.java new file mode 100644 index 00000000000..157a05bdb5b --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/GainHarnessedAbilitySourceEffect.java @@ -0,0 +1,58 @@ +package mage.abilities.effects.common.continuous; + +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.Effect; +import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; +import mage.constants.SubLayer; +import mage.game.Game; +import mage.game.permanent.Permanent; + +/** + * @author Jmlundeen + */ +public class GainHarnessedAbilitySourceEffect extends ContinuousEffectImpl { + + private final Ability ability; + + public GainHarnessedAbilitySourceEffect(Effect effect) { + this(new SimpleStaticAbility(effect)); + } + + public GainHarnessedAbilitySourceEffect(Ability ability) { + super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + staticText = ability.getRule(); + this.ability = ability; + this.ability.setRuleVisible(false); + generateGainAbilityDependencies(ability, null); + } + + private GainHarnessedAbilitySourceEffect(final GainHarnessedAbilitySourceEffect effect) { + super(effect); + this.ability = effect.ability; + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent == null || !permanent.isHarnessed()) { + return false; + } + permanent.addAbility(ability, source.getSourceId(), game); + return true; + } + + @Override + public GainHarnessedAbilitySourceEffect copy() { + return new GainHarnessedAbilitySourceEffect(this); + } + + @Override + public String getText(Mode mode) { + return "∞ — " + super.getText(mode); + } +} 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/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/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/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/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/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/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/Permanent.java b/Mage/src/main/java/mage/game/permanent/Permanent.java index 73075f8f663..3b88aff182b 100644 --- a/Mage/src/main/java/mage/game/permanent/Permanent.java +++ b/Mage/src/main/java/mage/game/permanent/Permanent.java @@ -470,6 +470,10 @@ public interface Permanent extends Card, Controllable { boolean solve(Game game, Ability source); + boolean isHarnessed(); + + void setHarnessed(boolean value); + @Override Permanent copy(); diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index 6a74a11ad23..23422c76a64 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -72,6 +72,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { protected boolean monstrous; protected boolean renowned; protected boolean suspected; + protected boolean harnessed = false; protected boolean manifested = false; protected boolean cloaked = false; protected boolean morphed = false; @@ -176,6 +177,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { this.monstrous = permanent.monstrous; this.renowned = permanent.renowned; this.suspected = permanent.suspected; + this.harnessed = permanent.harnessed; this.ringBearerFlag = permanent.ringBearerFlag; this.classLevel = permanent.classLevel; this.goadingPlayers.addAll(permanent.goadingPlayers); @@ -1537,7 +1539,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); @@ -2004,6 +2006,16 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { return true; } + @Override + public boolean isHarnessed() { + return this.harnessed; + } + + @Override + public void setHarnessed(boolean value) { + this.harnessed = value; + } + @Override public boolean fight(Permanent fightTarget, Ability source, Game game) { return this.fight(fightTarget, source, game, true); 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/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/resources/tokens-database.txt b/Mage/src/main/resources/tokens-database.txt index d3d050e1e5d..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|