diff --git a/Mage.Sets/src/mage/cards/a/AjanisAid.java b/Mage.Sets/src/mage/cards/a/AjanisAid.java index d3869aadd9a..2d2fd726353 100644 --- a/Mage.Sets/src/mage/cards/a/AjanisAid.java +++ b/Mage.Sets/src/mage/cards/a/AjanisAid.java @@ -11,6 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.FilterCard; +import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.NamePredicate; @@ -22,6 +23,7 @@ import java.util.UUID; public final class AjanisAid extends CardImpl { private static final FilterCard filter = new FilterCard("Ajani, Valiant Protector"); + private static final FilterPermanent filterPrevent = new FilterCreaturePermanent("creature of your choice"); static { filter.add(new NamePredicate("Ajani, Valiant Protector")); @@ -35,7 +37,7 @@ public final class AjanisAid extends CardImpl { this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryGraveyardPutInHandEffect(filter), true)); // Sacrifice Ajani's Aid: Prevent all combat damage a creature of your choice would deal this turn. - Effect effect = new PreventDamageByChosenSourceEffect(new FilterCreaturePermanent("creature of your choice"), true); + Effect effect = new PreventDamageByChosenSourceEffect(filterPrevent, true); this.addAbility(new SimpleActivatedAbility(effect, new SacrificeSourceCost())); } diff --git a/Mage.Sets/src/mage/cards/b/BoneMask.java b/Mage.Sets/src/mage/cards/b/BoneMask.java index 5a18b1de750..41e19ca7aab 100644 --- a/Mage.Sets/src/mage/cards/b/BoneMask.java +++ b/Mage.Sets/src/mage/cards/b/BoneMask.java @@ -15,7 +15,7 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; -import mage.target.TargetSource; +import mage.target.Target; import java.util.Set; import java.util.UUID; @@ -53,7 +53,7 @@ public final class BoneMask extends CardImpl { enum BoneMaskEffectPreventionApplier implements PreventNextDamageFromChosenSourceEffect.ApplierOnPrevention { instance; - public boolean apply(PreventionEffectData data, TargetSource targetSource, GameEvent event, Ability source, Game game) { + public boolean apply(PreventionEffectData data, Target target, GameEvent event, Ability source, Game game) { if (data == null || data.getPreventedDamage() <= 0) { return false; } diff --git a/Mage.Sets/src/mage/cards/b/BurrentonForgeTender.java b/Mage.Sets/src/mage/cards/b/BurrentonForgeTender.java index 330537dd312..70c6d353548 100644 --- a/Mage.Sets/src/mage/cards/b/BurrentonForgeTender.java +++ b/Mage.Sets/src/mage/cards/b/BurrentonForgeTender.java @@ -11,7 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; import java.util.UUID; @@ -21,10 +21,10 @@ import java.util.UUID; */ public final class BurrentonForgeTender extends CardImpl { - private static final FilterObject filterObject = new FilterObject("a red"); + private static final FilterSource filter = new FilterSource("a red source"); static { - filterObject.add(new ColorPredicate(ObjectColor.RED)); + filter.add(new ColorPredicate(ObjectColor.RED)); } public BurrentonForgeTender(UUID ownerId, CardSetInfo setInfo) { @@ -38,7 +38,7 @@ public final class BurrentonForgeTender extends CardImpl { this.addAbility(ProtectionAbility.from(ObjectColor.RED)); // Sacrifice Burrenton Forge-Tender: Prevent all damage a red source of your choice would deal this turn. - this.addAbility(new SimpleActivatedAbility(new PreventDamageByChosenSourceEffect(filterObject), new SacrificeSourceCost())); + this.addAbility(new SimpleActivatedAbility(new PreventDamageByChosenSourceEffect(filter), new SacrificeSourceCost())); } diff --git a/Mage.Sets/src/mage/cards/c/CircleOfProtectionArtifacts.java b/Mage.Sets/src/mage/cards/c/CircleOfProtectionArtifacts.java index 8e81511bd6a..31a98658b80 100644 --- a/Mage.Sets/src/mage/cards/c/CircleOfProtectionArtifacts.java +++ b/Mage.Sets/src/mage/cards/c/CircleOfProtectionArtifacts.java @@ -9,7 +9,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import java.util.UUID; @@ -18,7 +18,7 @@ import java.util.UUID; */ public final class CircleOfProtectionArtifacts extends CardImpl { - private static final FilterObject filter = new FilterObject("artifact source"); + private static final FilterSource filter = new FilterSource("artifact source"); static { filter.add(CardType.ARTIFACT.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/c/CircleOfProtectionBlack.java b/Mage.Sets/src/mage/cards/c/CircleOfProtectionBlack.java index 5fd760f7cd6..617a182a435 100644 --- a/Mage.Sets/src/mage/cards/c/CircleOfProtectionBlack.java +++ b/Mage.Sets/src/mage/cards/c/CircleOfProtectionBlack.java @@ -10,7 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; import java.util.UUID; @@ -20,7 +20,7 @@ import java.util.UUID; */ public final class CircleOfProtectionBlack extends CardImpl { - private static final FilterObject filter = new FilterObject("black source"); + private static final FilterSource filter = new FilterSource("black source"); static { filter.add(new ColorPredicate(ObjectColor.BLACK)); diff --git a/Mage.Sets/src/mage/cards/c/CircleOfProtectionBlue.java b/Mage.Sets/src/mage/cards/c/CircleOfProtectionBlue.java index cccfb199a99..185317f8683 100644 --- a/Mage.Sets/src/mage/cards/c/CircleOfProtectionBlue.java +++ b/Mage.Sets/src/mage/cards/c/CircleOfProtectionBlue.java @@ -10,7 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; import java.util.UUID; @@ -20,7 +20,7 @@ import java.util.UUID; */ public final class CircleOfProtectionBlue extends CardImpl { - private static final FilterObject filter = new FilterObject("blue source"); + private static final FilterSource filter = new FilterSource("blue source"); static { filter.add(new ColorPredicate(ObjectColor.BLUE)); diff --git a/Mage.Sets/src/mage/cards/c/CircleOfProtectionGreen.java b/Mage.Sets/src/mage/cards/c/CircleOfProtectionGreen.java index 50c9b2e1228..7585afa504d 100644 --- a/Mage.Sets/src/mage/cards/c/CircleOfProtectionGreen.java +++ b/Mage.Sets/src/mage/cards/c/CircleOfProtectionGreen.java @@ -10,7 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; import java.util.UUID; @@ -20,7 +20,7 @@ import java.util.UUID; */ public final class CircleOfProtectionGreen extends CardImpl { - private static final FilterObject filter = new FilterObject("green source"); + private static final FilterSource filter = new FilterSource("green source"); static { filter.add(new ColorPredicate(ObjectColor.GREEN)); diff --git a/Mage.Sets/src/mage/cards/c/CircleOfProtectionRed.java b/Mage.Sets/src/mage/cards/c/CircleOfProtectionRed.java index c8b52163eed..84cc319d464 100644 --- a/Mage.Sets/src/mage/cards/c/CircleOfProtectionRed.java +++ b/Mage.Sets/src/mage/cards/c/CircleOfProtectionRed.java @@ -10,7 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; import java.util.UUID; @@ -20,7 +20,7 @@ import java.util.UUID; */ public final class CircleOfProtectionRed extends CardImpl { - private static final FilterObject filter = new FilterObject("red source"); + private static final FilterSource filter = new FilterSource("red source"); static { filter.add(new ColorPredicate(ObjectColor.RED)); diff --git a/Mage.Sets/src/mage/cards/c/CircleOfProtectionShadow.java b/Mage.Sets/src/mage/cards/c/CircleOfProtectionShadow.java index aaa4e0e067b..eea2a46f828 100644 --- a/Mage.Sets/src/mage/cards/c/CircleOfProtectionShadow.java +++ b/Mage.Sets/src/mage/cards/c/CircleOfProtectionShadow.java @@ -10,6 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; +import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.AbilityPredicate; @@ -20,7 +21,7 @@ import java.util.UUID; */ public final class CircleOfProtectionShadow extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature of your choice with shadow"); + private static final FilterPermanent filter = new FilterCreaturePermanent("a creature of your choice with shadow"); static { filter.add(new AbilityPredicate(ShadowAbility.class)); diff --git a/Mage.Sets/src/mage/cards/c/CircleOfProtectionWhite.java b/Mage.Sets/src/mage/cards/c/CircleOfProtectionWhite.java index 36420aa2994..18226add18b 100644 --- a/Mage.Sets/src/mage/cards/c/CircleOfProtectionWhite.java +++ b/Mage.Sets/src/mage/cards/c/CircleOfProtectionWhite.java @@ -10,7 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; import java.util.UUID; @@ -20,7 +20,7 @@ import java.util.UUID; */ public final class CircleOfProtectionWhite extends CardImpl { - private static final FilterObject filter = new FilterObject("white source"); + private static final FilterSource filter = new FilterSource("white source"); static { filter.add(new ColorPredicate(ObjectColor.WHITE)); diff --git a/Mage.Sets/src/mage/cards/c/ConsulateSurveillance.java b/Mage.Sets/src/mage/cards/c/ConsulateSurveillance.java index c578435ea36..04fc763a31f 100644 --- a/Mage.Sets/src/mage/cards/c/ConsulateSurveillance.java +++ b/Mage.Sets/src/mage/cards/c/ConsulateSurveillance.java @@ -1,7 +1,6 @@ package mage.cards.c; -import java.util.UUID; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.PayEnergyCost; @@ -11,24 +10,23 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; -import mage.filter.FilterObject; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class ConsulateSurveillance extends CardImpl { public ConsulateSurveillance(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{W}"); + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}"); // When Consulate Surveillance enters the battlefield, you get {E}{E}{E}{E}. this.addAbility(new EntersBattlefieldTriggeredAbility(new GetEnergyCountersControllerEffect(4))); // Pay {E}{E}: Prevent all damage that would be dealt to you this turn by a source of your choice. this.addAbility(new SimpleActivatedAbility( - new PreventAllDamageFromChosenSourceToYouEffect(Duration.EndOfTurn, new FilterObject("source"), false), + new PreventAllDamageFromChosenSourceToYouEffect(Duration.EndOfTurn), new PayEnergyCost(2))); } diff --git a/Mage.Sets/src/mage/cards/d/DarkSphere.java b/Mage.Sets/src/mage/cards/d/DarkSphere.java index 64fb3666bb5..e084e635a1a 100644 --- a/Mage.Sets/src/mage/cards/d/DarkSphere.java +++ b/Mage.Sets/src/mage/cards/d/DarkSphere.java @@ -1,35 +1,32 @@ package mage.cards.d; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.PreventNextDamageFromChosenSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.game.Game; import mage.game.events.DamageEvent; import mage.game.events.GameEvent; import mage.players.Player; -import mage.target.TargetSource; + +import java.util.UUID; /** - * * @author ThomasLerner */ public final class DarkSphere extends CardImpl { public DarkSphere(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{0}"); - + // {tap}, Sacrifice Dark Sphere: The next time a source of your choice would deal damage to you this turn, prevent half that damage, rounded down. Ability ability = new SimpleActivatedAbility(new DarkSpherePreventionEffect(), new TapSourceCost()); @@ -48,19 +45,17 @@ public final class DarkSphere extends CardImpl { } -class DarkSpherePreventionEffect extends ReplacementEffectImpl { +class DarkSpherePreventionEffect extends PreventNextDamageFromChosenSourceEffect { + + private static final FilterSource filter = new FilterSource("source"); - private final TargetSource targetSource; - public DarkSpherePreventionEffect() { - super(Duration.OneUse, Outcome.RedirectDamage); + super(Duration.EndOfTurn, true, filter); this.staticText = "The next time a source of your choice would deal damage to you this turn, prevent half that damage, rounded down"; - this.targetSource = new TargetSource(new FilterObject("source of your choice")); } - + private DarkSpherePreventionEffect(final DarkSpherePreventionEffect effect) { super(effect); - this.targetSource = effect.targetSource.copy(); } @Override @@ -68,40 +63,24 @@ class DarkSpherePreventionEffect extends ReplacementEffectImpl { return new DarkSpherePreventionEffect(this); } - @Override - public void init(Ability source, Game game) { - super.init(source, game); - this.targetSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); - } - @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = game.getObject(source); DamageEvent damageEvent = (DamageEvent) event; - if (controller != null) { - controller.damage((int) Math.ceil(damageEvent.getAmount() / 2.0), damageEvent.getSourceId(), source, game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), damageEvent.getAppliedEffects()); - StringBuilder sb = new StringBuilder(sourceObject != null ? sourceObject.getLogName() : ""); - sb.append(": ").append(damageEvent.getAmount() / 2).append(" damage prevented"); - sb.append(" from ").append(controller.getLogName()); - game.informPlayers(sb.toString()); - discard(); // only one use - return true; + int damage = damageEvent.getAmount(); + if (controller == null || damage <= 0) { + return false; } - return false; + controller.damage( + (int) Math.ceil(damage / 2.0), damageEvent.getSourceId(), source, game, + damageEvent.isCombatDamage(), damageEvent.isPreventable(), damageEvent.getAppliedEffects() + ); + StringBuilder sb = new StringBuilder(sourceObject != null ? sourceObject.getLogName() : ""); + sb.append(": ").append(damage / 2).append(" damage prevented"); + sb.append(" from ").append(controller.getLogName()); + game.informPlayers(sb.toString()); + discard(); // only one use + return true; } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGE_PLAYER; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getSourceId().equals(targetSource.getFirstTarget()) && event.getTargetId().equals(source.getControllerId())) { - return true; - } - return false; - } - } diff --git a/Mage.Sets/src/mage/cards/d/DeflectingPalm.java b/Mage.Sets/src/mage/cards/d/DeflectingPalm.java index 0f9c4328d70..8d8ed56608d 100644 --- a/Mage.Sets/src/mage/cards/d/DeflectingPalm.java +++ b/Mage.Sets/src/mage/cards/d/DeflectingPalm.java @@ -10,7 +10,7 @@ import mage.constants.Duration; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; -import mage.target.TargetSource; +import mage.target.Target; import java.util.UUID; @@ -44,12 +44,12 @@ public final class DeflectingPalm extends CardImpl { enum DeflectingPalmPreventionApplier implements PreventNextDamageFromChosenSourceEffect.ApplierOnPrevention { instance; - public boolean apply(PreventionEffectData data, TargetSource targetSource, GameEvent event, Ability source, Game game) { + public boolean apply(PreventionEffectData data, Target target, GameEvent event, Ability source, Game game) { if (data == null || data.getPreventedDamage() <= 0) { return false; } int prevented = data.getPreventedDamage(); - UUID objectControllerId = game.getControllerId(targetSource.getFirstTarget()); + UUID objectControllerId = game.getControllerId(target.getFirstTarget()); Player objectController = game.getPlayer(objectControllerId); if (objectController == null) { return false; diff --git a/Mage.Sets/src/mage/cards/d/DesperateGambit.java b/Mage.Sets/src/mage/cards/d/DesperateGambit.java index 1931ccdc0e3..a33067ba290 100644 --- a/Mage.Sets/src/mage/cards/d/DesperateGambit.java +++ b/Mage.Sets/src/mage/cards/d/DesperateGambit.java @@ -1,10 +1,6 @@ package mage.cards.d; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.PreventionEffectImpl; @@ -14,24 +10,28 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; +import mage.filter.FilterSource; import mage.game.Game; import mage.game.events.DamageEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.stack.StackObject; -import mage.filter.FilterObject; import mage.players.Player; import mage.target.TargetSource; import mage.util.CardUtil; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + /** - * * @author L_J */ public final class DesperateGambit extends CardImpl { public DesperateGambit(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{R}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}"); // Choose a source you control and flip a coin. If you win the flip, the next time that source would deal damage this turn, it deals double that damage instead. If you lose the flip, the next time it would deal damage this turn, prevent that damage. this.getSpellAbility().addEffect(new DesperateGambitEffect()); @@ -48,7 +48,7 @@ public final class DesperateGambit extends CardImpl { } class DesperateGambitEffect extends PreventionEffectImpl { - + private final TargetSource target; private boolean wonFlip; @@ -122,10 +122,14 @@ class DesperateGambitEffect extends PreventionEffectImpl { } } +/** + * TODO: should not be needed with the proper {@link FilterSource}. + * make sure it works properly with 108.4a if you do + */ class TargetControlledSource extends TargetSource { public TargetControlledSource() { - super(1, 1, new FilterObject("source you control")); + super(1, 1, new FilterSource("source you control")); } private TargetControlledSource(final TargetControlledSource target) { @@ -135,16 +139,16 @@ class TargetControlledSource extends TargetSource { @Override public boolean canChoose(UUID sourceControllerId, Game game) { int count = 0; - for (StackObject stackObject: game.getStack()) { - if (game.getState().getPlayersInRange(sourceControllerId, game).contains(stackObject.getControllerId()) - && Objects.equals(stackObject.getControllerId(), sourceControllerId)) { + for (StackObject stackObject : game.getStack()) { + if (game.getState().getPlayersInRange(sourceControllerId, game).contains(stackObject.getControllerId()) + && Objects.equals(stackObject.getControllerId(), sourceControllerId)) { count++; if (count >= this.minNumberOfTargets) { return true; } } } - for (Permanent permanent: game.getBattlefield().getActivePermanents(sourceControllerId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(sourceControllerId, game)) { if (Objects.equals(permanent.getControllerId(), sourceControllerId)) { count++; if (count >= this.minNumberOfTargets) { @@ -177,13 +181,13 @@ class TargetControlledSource extends TargetSource { @Override public Set possibleTargets(UUID sourceControllerId, Game game) { Set possibleTargets = new HashSet<>(); - for (StackObject stackObject: game.getStack()) { - if (game.getState().getPlayersInRange(sourceControllerId, game).contains(stackObject.getControllerId()) - && Objects.equals(stackObject.getControllerId(), sourceControllerId)) { + for (StackObject stackObject : game.getStack()) { + if (game.getState().getPlayersInRange(sourceControllerId, game).contains(stackObject.getControllerId()) + && Objects.equals(stackObject.getControllerId(), sourceControllerId)) { possibleTargets.add(stackObject.getId()); } } - for (Permanent permanent: game.getBattlefield().getActivePermanents(sourceControllerId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(sourceControllerId, game)) { if (Objects.equals(permanent.getControllerId(), sourceControllerId)) { possibleTargets.add(permanent.getId()); } diff --git a/Mage.Sets/src/mage/cards/g/GreaterRealmOfPreservation.java b/Mage.Sets/src/mage/cards/g/GreaterRealmOfPreservation.java index 8ebb09bec90..c3b9ef78ae7 100644 --- a/Mage.Sets/src/mage/cards/g/GreaterRealmOfPreservation.java +++ b/Mage.Sets/src/mage/cards/g/GreaterRealmOfPreservation.java @@ -10,7 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; @@ -21,7 +21,7 @@ import java.util.UUID; */ public final class GreaterRealmOfPreservation extends CardImpl { - private static final FilterObject filter = new FilterObject("black or red source"); + private static final FilterSource filter = new FilterSource("black or red source"); static { filter.add(Predicates.or(new ColorPredicate(ObjectColor.BLACK), new ColorPredicate(ObjectColor.RED))); diff --git a/Mage.Sets/src/mage/cards/j/JadeMonolith.java b/Mage.Sets/src/mage/cards/j/JadeMonolith.java index 9812521fe1c..9d9bb4581e5 100644 --- a/Mage.Sets/src/mage/cards/j/JadeMonolith.java +++ b/Mage.Sets/src/mage/cards/j/JadeMonolith.java @@ -1,7 +1,6 @@ package mage.cards.j; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -12,8 +11,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.game.Game; import mage.game.events.DamageEvent; import mage.game.events.GameEvent; @@ -23,14 +21,15 @@ import mage.players.Player; import mage.target.TargetSource; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author Quercitron */ public final class JadeMonolith extends CardImpl { public JadeMonolith(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); // {1}: The next time a source of your choice would deal damage to target creature this turn, that source deals that damage to you instead. Ability ability = new SimpleActivatedAbility(new JadeMonolithRedirectionEffect(), new GenericManaCost(1)); @@ -51,13 +50,13 @@ public final class JadeMonolith extends CardImpl { class JadeMonolithRedirectionEffect extends ReplacementEffectImpl { private final TargetSource targetSource; - + public JadeMonolithRedirectionEffect() { super(Duration.OneUse, Outcome.RedirectDamage); this.staticText = "The next time a source of your choice would deal damage to target creature this turn, that source deals that damage to you instead"; - this.targetSource = new TargetSource(new FilterObject("source of your choice")); + this.targetSource = new TargetSource(new FilterSource("source of your choice")); } - + private JadeMonolithRedirectionEffect(final JadeMonolithRedirectionEffect effect) { super(effect); this.targetSource = effect.targetSource.copy(); @@ -96,11 +95,11 @@ class JadeMonolithRedirectionEffect extends ReplacementEffectImpl { public boolean checksEventType(GameEvent event, Game game) { return event.getType() == EventType.DAMAGE_PERMANENT; } - + @Override public boolean applies(GameEvent event, Ability source, Game game) { return event.getSourceId().equals(targetSource.getFirstTarget()) && event.getTargetId().equals(source.getFirstTarget()); } - + } diff --git a/Mage.Sets/src/mage/cards/m/MournersShield.java b/Mage.Sets/src/mage/cards/m/MournersShield.java index 1362b980ab7..3c190addd62 100644 --- a/Mage.Sets/src/mage/cards/m/MournersShield.java +++ b/Mage.Sets/src/mage/cards/m/MournersShield.java @@ -1,7 +1,5 @@ package mage.cards.m; -import java.util.UUID; - import mage.MageObject; import mage.MageObjectReference; import mage.ObjectColor; @@ -15,8 +13,11 @@ import mage.abilities.effects.PreventionEffectImpl; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.FilterObject; +import mage.constants.AbilityWord; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.SharesColorPredicate; import mage.game.ExileZone; import mage.game.Game; @@ -27,15 +28,16 @@ import mage.target.TargetSource; import mage.target.common.TargetCardInGraveyard; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author noahg */ public final class MournersShield extends CardImpl { public MournersShield(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); - + // Imprint - When Mourner's Shield enters the battlefield, you may exile target card from a graveyard. Ability ability = new EntersBattlefieldTriggeredAbility(new MournersShieldImprintEffect(), true); @@ -93,6 +95,10 @@ class MournersShieldImprintEffect extends OneShotEffect { } } +/** + * TODO: custom effect should not be needed with the properly set up {@link FilterSource} + * and {@link mage.abilities.effects.common.PreventDamageByChosenSourceEffect} + */ class MournersShieldEffect extends PreventionEffectImpl { private TargetSource target; @@ -131,17 +137,17 @@ class MournersShieldEffect extends PreventionEffectImpl { noneExiled = true; return; } - for (UUID imprinted : sourceObject.getImprinted()){ - if (imprinted != null && exileZone.contains(imprinted)){ + for (UUID imprinted : sourceObject.getImprinted()) { + if (imprinted != null && exileZone.contains(imprinted)) { Card card = game.getCard(imprinted); if (card != null) { colorsAmongImprinted = colorsAmongImprinted.union(card.getColor(game)); } } } - FilterObject filterObject = new FilterObject("a source of your choice that shares a color with the exiled card"); - filterObject.add(new SharesColorPredicate(colorsAmongImprinted)); - this.target = new TargetSource(filterObject); + FilterSource filterSource = new FilterSource("a source of your choice that shares a color with the exiled card"); + filterSource.add(new SharesColorPredicate(colorsAmongImprinted)); + this.target = new TargetSource(filterSource); this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game); if (target.getFirstTarget() != null) { mageObjectReference = new MageObjectReference(target.getFirstTarget(), game); @@ -152,7 +158,7 @@ class MournersShieldEffect extends PreventionEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (noneExiled || mageObjectReference == null){ + if (noneExiled || mageObjectReference == null) { return false; } if (super.applies(event, source, game)) { diff --git a/Mage.Sets/src/mage/cards/n/NewWayForward.java b/Mage.Sets/src/mage/cards/n/NewWayForward.java index 0d8bdfd7532..5da9902ad17 100644 --- a/Mage.Sets/src/mage/cards/n/NewWayForward.java +++ b/Mage.Sets/src/mage/cards/n/NewWayForward.java @@ -12,7 +12,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.game.Game; import mage.game.events.GameEvent; -import mage.target.TargetSource; +import mage.target.Target; import mage.target.targetpointer.FixedTarget; import java.util.UUID; @@ -47,12 +47,12 @@ public final class NewWayForward extends CardImpl { enum NewWayForwardPreventionApplier implements PreventNextDamageFromChosenSourceEffect.ApplierOnPrevention { instance; - public boolean apply(PreventionEffectData data, TargetSource targetSource, GameEvent event, Ability source, Game game) { + public boolean apply(PreventionEffectData data, Target target, GameEvent event, Ability source, Game game) { if (data == null || data.getPreventedDamage() <= 0) { return false; } int prevented = data.getPreventedDamage(); - UUID objectControllerId = game.getControllerId(targetSource.getFirstTarget()); + UUID objectControllerId = game.getControllerId(target.getFirstTarget()); ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( new DamageTargetEffect(prevented) .setTargetPointer(new FixedTarget(objectControllerId)), diff --git a/Mage.Sets/src/mage/cards/p/Penance.java b/Mage.Sets/src/mage/cards/p/Penance.java index dba57583455..194760b22ca 100644 --- a/Mage.Sets/src/mage/cards/p/Penance.java +++ b/Mage.Sets/src/mage/cards/p/Penance.java @@ -9,7 +9,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; @@ -20,7 +20,7 @@ import java.util.UUID; */ public final class Penance extends CardImpl { - private static final FilterObject filter = new FilterObject("black or red source"); + private static final FilterSource filter = new FilterSource("black or red source"); static { filter.add(Predicates.or(new ColorPredicate(ObjectColor.BLACK), new ColorPredicate(ObjectColor.RED))); diff --git a/Mage.Sets/src/mage/cards/p/PilgrimOfJustice.java b/Mage.Sets/src/mage/cards/p/PilgrimOfJustice.java index 50d3f014e82..d1b9a9c6a3c 100644 --- a/Mage.Sets/src/mage/cards/p/PilgrimOfJustice.java +++ b/Mage.Sets/src/mage/cards/p/PilgrimOfJustice.java @@ -13,7 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; import java.util.UUID; @@ -23,7 +23,7 @@ import java.util.UUID; */ public final class PilgrimOfJustice extends CardImpl { - private static final FilterObject filter = new FilterObject("red source"); + private static final FilterSource filter = new FilterSource("red source"); static { filter.add(new ColorPredicate(ObjectColor.RED)); diff --git a/Mage.Sets/src/mage/cards/p/PilgrimOfVirtue.java b/Mage.Sets/src/mage/cards/p/PilgrimOfVirtue.java index 57fa568fcf2..d5931e44263 100644 --- a/Mage.Sets/src/mage/cards/p/PilgrimOfVirtue.java +++ b/Mage.Sets/src/mage/cards/p/PilgrimOfVirtue.java @@ -13,7 +13,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; import java.util.UUID; @@ -23,7 +23,7 @@ import java.util.UUID; */ public final class PilgrimOfVirtue extends CardImpl { - private static final FilterObject filter = new FilterObject("black source"); + private static final FilterSource filter = new FilterSource("black source"); static { filter.add(new ColorPredicate(ObjectColor.BLACK)); diff --git a/Mage.Sets/src/mage/cards/p/PrismaticCircle.java b/Mage.Sets/src/mage/cards/p/PrismaticCircle.java index d2cbf98264f..1b3e4b9c3af 100644 --- a/Mage.Sets/src/mage/cards/p/PrismaticCircle.java +++ b/Mage.Sets/src/mage/cards/p/PrismaticCircle.java @@ -1,8 +1,6 @@ package mage.cards.p; -import mage.ObjectColor; -import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -14,9 +12,8 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.filter.FilterObject; -import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.Game; +import mage.filter.FilterSource; +import mage.filter.predicate.mageobject.ChosenColorPredicate; import java.util.UUID; @@ -25,6 +22,12 @@ import java.util.UUID; */ public final class PrismaticCircle extends CardImpl { + private static FilterSource filter = new FilterSource("a source of your choice of the chosen color"); + + static { + filter.add(ChosenColorPredicate.TRUE); + } + public PrismaticCircle(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); @@ -36,7 +39,7 @@ public final class PrismaticCircle extends CardImpl { // {1}: The next time a source of your choice of the chosen color would deal damage to you this turn, prevent that damage. this.addAbility(new SimpleActivatedAbility( - new PrismaticCircleEffect(), + new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, true, filter), new ManaCostsImpl<>("{1}") )); } @@ -49,30 +52,4 @@ public final class PrismaticCircle extends CardImpl { public PrismaticCircle copy() { return new PrismaticCircle(this); } -} - -// TODO: create a FilterSource that can handle ChosenColorPredicate.TRUE and simplify this. -class PrismaticCircleEffect extends PreventNextDamageFromChosenSourceEffect { - - PrismaticCircleEffect() { - super(Duration.EndOfTurn, true); - staticText = "The next time a source of your choice of the chosen color would deal damage to you this turn, prevent that damage."; - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - FilterObject filter = targetSource.getFilter(); - filter.add(new ColorPredicate((ObjectColor) game.getState().getValue(source.getSourceId() + "_color"))); - } - - private PrismaticCircleEffect(final PrismaticCircleEffect effect) { - super(effect); - } - - @Override - public PrismaticCircleEffect copy() { - return new PrismaticCircleEffect(this); - } - -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/r/RuneOfProtectionArtifacts.java b/Mage.Sets/src/mage/cards/r/RuneOfProtectionArtifacts.java index 0ce84d6773f..cebc562b4d6 100644 --- a/Mage.Sets/src/mage/cards/r/RuneOfProtectionArtifacts.java +++ b/Mage.Sets/src/mage/cards/r/RuneOfProtectionArtifacts.java @@ -10,7 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import java.util.UUID; @@ -19,7 +19,7 @@ import java.util.UUID; */ public final class RuneOfProtectionArtifacts extends CardImpl { - private static final FilterObject filter = new FilterObject("artifact source"); + private static final FilterSource filter = new FilterSource("artifact source"); static { filter.add(CardType.ARTIFACT.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/r/RuneOfProtectionBlack.java b/Mage.Sets/src/mage/cards/r/RuneOfProtectionBlack.java index 66c00fc55ed..4707a3dbaa6 100644 --- a/Mage.Sets/src/mage/cards/r/RuneOfProtectionBlack.java +++ b/Mage.Sets/src/mage/cards/r/RuneOfProtectionBlack.java @@ -11,7 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; import java.util.UUID; @@ -21,7 +21,7 @@ import java.util.UUID; */ public final class RuneOfProtectionBlack extends CardImpl { - private static final FilterObject filter = new FilterObject("black source"); + private static final FilterSource filter = new FilterSource("black source"); static { filter.add(new ColorPredicate(ObjectColor.BLACK)); diff --git a/Mage.Sets/src/mage/cards/r/RuneOfProtectionBlue.java b/Mage.Sets/src/mage/cards/r/RuneOfProtectionBlue.java index 7357af2a1eb..dcad39e7ac9 100644 --- a/Mage.Sets/src/mage/cards/r/RuneOfProtectionBlue.java +++ b/Mage.Sets/src/mage/cards/r/RuneOfProtectionBlue.java @@ -11,7 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; import java.util.UUID; @@ -21,7 +21,7 @@ import java.util.UUID; */ public final class RuneOfProtectionBlue extends CardImpl { - private static final FilterObject filter = new FilterObject("blue source"); + private static final FilterSource filter = new FilterSource("blue source"); static { filter.add(new ColorPredicate(ObjectColor.BLUE)); diff --git a/Mage.Sets/src/mage/cards/r/RuneOfProtectionGreen.java b/Mage.Sets/src/mage/cards/r/RuneOfProtectionGreen.java index cbc1c1bca5f..eacc032d2f9 100644 --- a/Mage.Sets/src/mage/cards/r/RuneOfProtectionGreen.java +++ b/Mage.Sets/src/mage/cards/r/RuneOfProtectionGreen.java @@ -11,7 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; import java.util.UUID; @@ -21,7 +21,7 @@ import java.util.UUID; */ public final class RuneOfProtectionGreen extends CardImpl { - private static final FilterObject filter = new FilterObject("green source"); + private static final FilterSource filter = new FilterSource("green source"); static { filter.add(new ColorPredicate(ObjectColor.GREEN)); diff --git a/Mage.Sets/src/mage/cards/r/RuneOfProtectionLands.java b/Mage.Sets/src/mage/cards/r/RuneOfProtectionLands.java index 6a380ddf11f..7c5b3d779b6 100644 --- a/Mage.Sets/src/mage/cards/r/RuneOfProtectionLands.java +++ b/Mage.Sets/src/mage/cards/r/RuneOfProtectionLands.java @@ -10,7 +10,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import java.util.UUID; @@ -19,7 +19,7 @@ import java.util.UUID; */ public final class RuneOfProtectionLands extends CardImpl { - private static final FilterObject filter = new FilterObject("land source"); + private static final FilterSource filter = new FilterSource("land source"); static { filter.add(CardType.LAND.getPredicate()); diff --git a/Mage.Sets/src/mage/cards/r/RuneOfProtectionRed.java b/Mage.Sets/src/mage/cards/r/RuneOfProtectionRed.java index 67298902764..78b019856ac 100644 --- a/Mage.Sets/src/mage/cards/r/RuneOfProtectionRed.java +++ b/Mage.Sets/src/mage/cards/r/RuneOfProtectionRed.java @@ -11,7 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; import java.util.UUID; @@ -21,7 +21,7 @@ import java.util.UUID; */ public final class RuneOfProtectionRed extends CardImpl { - private static final FilterObject filter = new FilterObject("red source"); + private static final FilterSource filter = new FilterSource("red source"); static { filter.add(new ColorPredicate(ObjectColor.RED)); diff --git a/Mage.Sets/src/mage/cards/r/RuneOfProtectionWhite.java b/Mage.Sets/src/mage/cards/r/RuneOfProtectionWhite.java index 85af0018656..b789a8e4751 100644 --- a/Mage.Sets/src/mage/cards/r/RuneOfProtectionWhite.java +++ b/Mage.Sets/src/mage/cards/r/RuneOfProtectionWhite.java @@ -11,7 +11,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.filter.predicate.mageobject.ColorPredicate; import java.util.UUID; @@ -21,7 +21,7 @@ import java.util.UUID; */ public final class RuneOfProtectionWhite extends CardImpl { - private static final FilterObject filter = new FilterObject("white source"); + private static final FilterSource filter = new FilterSource("white source"); static { filter.add(new ColorPredicate(ObjectColor.WHITE)); diff --git a/Mage.Sets/src/mage/cards/s/Shadowbane.java b/Mage.Sets/src/mage/cards/s/Shadowbane.java index df5992a53f5..c3c5be50446 100644 --- a/Mage.Sets/src/mage/cards/s/Shadowbane.java +++ b/Mage.Sets/src/mage/cards/s/Shadowbane.java @@ -13,7 +13,7 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.TargetSource; +import mage.target.Target; import java.util.UUID; @@ -77,11 +77,11 @@ enum ShadowbanePreventionApplier implements PreventNextDamageFromChosenSourceEff return "If damage from a black source is prevented this way, you gain that much life"; } - public boolean apply(PreventionEffectData data, TargetSource targetSource, GameEvent event, Ability source, Game game) { + public boolean apply(PreventionEffectData data, Target target, GameEvent event, Ability source, Game game) { if (data == null || data.getPreventedDamage() <= 0) { return false; } - MageObject sourceObject = game.getObject(targetSource.getFirstTarget()); + MageObject sourceObject = game.getObject(target.getFirstTarget()); if (!sourceObject.getColor(game).isBlack()) { return false; } diff --git a/Mage.Sets/src/mage/cards/s/StoryCircle.java b/Mage.Sets/src/mage/cards/s/StoryCircle.java index 9ea12887807..eba8e5d13d6 100644 --- a/Mage.Sets/src/mage/cards/s/StoryCircle.java +++ b/Mage.Sets/src/mage/cards/s/StoryCircle.java @@ -1,8 +1,6 @@ package mage.cards.s; -import mage.ObjectColor; -import mage.abilities.Ability; import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -13,9 +11,8 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; -import mage.filter.FilterObject; -import mage.filter.predicate.mageobject.ColorPredicate; -import mage.game.Game; +import mage.filter.FilterSource; +import mage.filter.predicate.mageobject.ChosenColorPredicate; import java.util.UUID; @@ -25,6 +22,12 @@ import java.util.UUID; public final class StoryCircle extends CardImpl { + private static FilterSource filter = new FilterSource("a source of your choice of the chosen color"); + + static { + filter.add(ChosenColorPredicate.TRUE); + } + public StoryCircle(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}{W}"); @@ -32,7 +35,7 @@ public final class StoryCircle extends CardImpl { this.addAbility(new AsEntersBattlefieldAbility(new ChooseColorEffect(Outcome.Neutral))); // {W}: The next time a source of your choice of the chosen color would deal damage to you this turn, prevent that damage. this.addAbility(new SimpleActivatedAbility( - new StoryCircleEffect(), + new PreventNextDamageFromChosenSourceEffect(Duration.EndOfTurn, true, filter), new ManaCostsImpl<>("{W}") )); } @@ -45,30 +48,4 @@ public final class StoryCircle extends CardImpl { public StoryCircle copy() { return new StoryCircle(this); } -} - -// TODO: create a FilterSource that can handle ChosenColorPredicate.TRUE and simplify this. -class StoryCircleEffect extends PreventNextDamageFromChosenSourceEffect { - - StoryCircleEffect() { - super(Duration.EndOfTurn, true); - staticText = "The next time a source of your choice of the chosen color would deal damage to you this turn, prevent that damage."; - } - - @Override - public void init(Ability source, Game game) { - super.init(source, game); - FilterObject filter = targetSource.getFilter(); - filter.add(new ColorPredicate((ObjectColor) game.getState().getValue(source.getSourceId() + "_color"))); - } - - private StoryCircleEffect(final StoryCircleEffect effect) { - super(effect); - } - - @Override - public StoryCircleEffect copy() { - return new StoryCircleEffect(this); - } - -} +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/drk/DarkSphereTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/drk/DarkSphereTest.java new file mode 100644 index 00000000000..4aa9b9f0b18 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/drk/DarkSphereTest.java @@ -0,0 +1,73 @@ +package org.mage.test.cards.single.drk; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class DarkSphereTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.d.DarkSphere Dark Sphere} {0} + * Artifact + * {T}, Sacrifice this artifact: The next time a source of your choice would deal damage to you this turn, prevent half that damage, rounded down. + */ + private static final String sphere = "Dark Sphere"; + + @Test + public void test_DamageOnCreature_NoPrevent() { + addCard(Zone.BATTLEFIELD, playerA, sphere, 1); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Caelorna, Coral Tyrant"); // 0/8 + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice"); + setChoice(playerA, "Goblin Piker"); // source to prevent from + + attack(2, playerB, "Goblin Piker", playerA); + block(2, playerA, "Caelorna, Coral Tyrant", "Goblin Piker"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertDamageReceived(playerA, "Caelorna, Coral Tyrant", 2); // no prevent + } + + @Test + public void test_DamageOnYou_Prevent() { + addCard(Zone.BATTLEFIELD, playerA, sphere, 1); + addCard(Zone.BATTLEFIELD, playerB, "Craw Wurm", 1); // 6/4 + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice"); + setChoice(playerA, "Craw Wurm"); // source to prevent from + + attack(2, playerB, "Craw Wurm", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 3); + } + + @Test + public void test_DoubleStrike_Prevent_ThenConsumedAndNoPrevent() { + addCard(Zone.BATTLEFIELD, playerA, sphere, 1); + addCard(Zone.BATTLEFIELD, playerB, "Blade Historian", 1); // 2/3 "Attacking creatures you control have double strike." + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice"); + setChoice(playerA, "Blade Historian"); // source to prevent from + + attack(2, playerB, "Blade Historian", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 3); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/lea/CircleOfProtectionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/lea/CircleOfProtectionTest.java index 4753c845ccf..3dc7052a307 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/lea/CircleOfProtectionTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/lea/CircleOfProtectionTest.java @@ -42,7 +42,7 @@ public class CircleOfProtectionTest extends CardTestPlayerBase { execute(); assertDamageReceived(playerA, "Caelorna, Coral Tyrant", 2); // no prevent - assertLife(playerB, 20); + assertLife(playerA, 20); assertTappedCount("Plains", true, 1); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mmq/StoryCircleTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mmq/StoryCircleTest.java index cd45674ff1b..947b3e477ac 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/mmq/StoryCircleTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mmq/StoryCircleTest.java @@ -97,7 +97,7 @@ public class StoryCircleTest extends CardTestPlayerBase { setChoice(playerA, "Green"); // color chosen activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{W}"); - setChoice(playerA, "Goblin Piker"); // TODO: this should not be a valid choice for TargetSource(chosencolor) + // no valid choice attack(2, playerB, "Goblin Piker", playerA); @@ -105,7 +105,7 @@ public class StoryCircleTest extends CardTestPlayerBase { setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); execute(); - assertLife(playerA, 20); // TODO: Should not prevent. + assertLife(playerA, 20 - 2); assertTappedCount("Plains", true, 4); } @@ -144,13 +144,13 @@ public class StoryCircleTest extends CardTestPlayerBase { castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", playerA); activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{W}", null, "Lightning Bolt"); - setChoice(playerA, "Lightning Bolt"); // TODO: this should not be a valid choice for TargetSource(chosencolor) + // no valid choice setStrictChooseMode(true); setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); execute(); - assertLife(playerA, 20); // TODO: Should not prevent. + assertLife(playerA, 20 - 3); assertGraveyardCount(playerB, "Lightning Bolt", 1); assertTappedCount("Plains", true, 4); } @@ -192,13 +192,13 @@ public class StoryCircleTest extends CardTestPlayerBase { castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Blisterstick Shaman"); addTarget(playerB, playerA); // target for Shaman trigger activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{W}", null, "stack ability (When"); - setChoice(playerA, "Blisterstick Shaman"); // TODO: this should not be a valid choice for TargetSource(chosencolor) + // no valid choice setStrictChooseMode(true); setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); execute(); - assertLife(playerA, 20); // TODO: Should not prevent. + assertLife(playerA, 20 - 1); assertPermanentCount(playerB, "Blisterstick Shaman", 1); assertTappedCount("Plains", true, 4); } @@ -238,14 +238,14 @@ public class StoryCircleTest extends CardTestPlayerBase { activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{R},", playerA); activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{W}", null, "stack ability ({R}"); - setChoice(playerA, "Anaba Shaman"); // TODO: this should not be a valid choice for TargetSource(chosencolor) + // no valid choice setStrictChooseMode(true); setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); execute(); assertTappedCount("Anaba Shaman", true, 1); - assertLife(playerA, 20); // TODO: Should not prevent. + assertLife(playerA, 20 - 1); assertTappedCount("Plains", true, 4); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/tmp/CircleOfProtectionShadowTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/tmp/CircleOfProtectionShadowTest.java new file mode 100644 index 00000000000..0d5aea0b782 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/tmp/CircleOfProtectionShadowTest.java @@ -0,0 +1,57 @@ +package org.mage.test.cards.single.tmp; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class CircleOfProtectionShadowTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.c.CircleOfProtectionShadow Circle of Protection: Shadow} {1}{W} + * Enchantment + * {1}: The next time a creature of your choice with shadow would deal damage to you this turn, prevent that damage. + */ + private static final String circle = "Circle of Protection: Shadow"; + + @Test + public void test_NotShadow_NoPrevent() { + addCard(Zone.BATTLEFIELD, playerA, circle, 1); + addCard(Zone.BATTLEFIELD, playerB, "Goblin Piker", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}"); + // no creature with shadow to choose + + attack(2, playerB, "Goblin Piker", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 2); + assertTappedCount("Plains", true, 1); + } + + @Test + public void test_Shadow_Prevent() { + addCard(Zone.BATTLEFIELD, playerA, circle, 1); + addCard(Zone.BATTLEFIELD, playerB, "Dauthi Marauder", 1); // 2/1 + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}"); + setChoice(playerA, "Dauthi Marauder"); + + attack(2, playerB, "Dauthi Marauder", playerA); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + assertTappedCount("Plains", true, 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/tmp/InvulnerabilityTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/tmp/InvulnerabilityTest.java new file mode 100644 index 00000000000..73cd21456bd --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/tmp/InvulnerabilityTest.java @@ -0,0 +1,45 @@ +package org.mage.test.cards.single.tmp; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class InvulnerabilityTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.i.Invulnerability Invulnerability} {1}{W} + * Instant + * Buyback {3} (You may pay an additional {3} as you cast this spell. If you do, put this card into your hand as it resolves.) + * The next time a source of your choice would deal damage to you this turn, prevent that damage. + */ + private static final String invulnerability = "Invulnerability"; + + @Test + public void test_EmblemDamage_Prevent() { + addCard(Zone.HAND, playerA, invulnerability, 1); + addCard(Zone.BATTLEFIELD, playerB, "Chandra, Awakened Inferno", 1); // +2: Each opponent gets an emblem with “At the beginning of your upkeep, this emblem deals 1 damage to you.” + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "+2"); + + checkEmblemCount("Chandra Emblem has been created", 2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Emblem Chandra", 1); + + // turn 3: prevent + castSpell(3, PhaseStep.UPKEEP, playerA, invulnerability); + setChoice(playerA, false); // don't pay for Buyback + setChoice(playerA, "Emblem Chandra"); // choice for source + checkLife("prevention turn 3", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, 20); + + // turn 5: no prevent to check effect ending + + setStrictChooseMode(true); + setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20 - 1); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageFromChosenSourceToYouEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageFromChosenSourceToYouEffect.java index 967cb6ccc63..5c8d58b8b42 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageFromChosenSourceToYouEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PreventAllDamageFromChosenSourceToYouEffect.java @@ -4,7 +4,7 @@ import mage.abilities.Ability; import mage.abilities.effects.PreventionEffectImpl; import mage.constants.Duration; import mage.constants.Outcome; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.game.Game; import mage.game.events.GameEvent; import mage.target.TargetSource; @@ -17,14 +17,14 @@ public class PreventAllDamageFromChosenSourceToYouEffect extends PreventionEffec protected final TargetSource targetSource; public PreventAllDamageFromChosenSourceToYouEffect(Duration duration) { - this(duration, new FilterObject("source")); + this(duration, new FilterSource()); } - public PreventAllDamageFromChosenSourceToYouEffect(Duration duration, FilterObject filter) { + public PreventAllDamageFromChosenSourceToYouEffect(Duration duration, FilterSource filter) { this(duration, filter, false); } - public PreventAllDamageFromChosenSourceToYouEffect(Duration duration, FilterObject filter, boolean onlyCombat) { + public PreventAllDamageFromChosenSourceToYouEffect(Duration duration, FilterSource filter, boolean onlyCombat) { super(duration, Integer.MAX_VALUE, onlyCombat); this.targetSource = new TargetSource(filter); this.staticText = setText(); diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventDamageByChosenSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventDamageByChosenSourceEffect.java index c07f955cc2b..e51a566979b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PreventDamageByChosenSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PreventDamageByChosenSourceEffect.java @@ -7,9 +7,12 @@ import mage.abilities.Ability; import mage.abilities.effects.PreventionEffectImpl; import mage.constants.Duration; import mage.constants.Outcome; -import mage.filter.FilterObject; +import mage.filter.FilterPermanent; +import mage.filter.FilterSource; import mage.game.Game; import mage.game.events.GameEvent; +import mage.target.Target; +import mage.target.TargetPermanent; import mage.target.TargetSource; /** @@ -18,25 +21,30 @@ import mage.target.TargetSource; public class PreventDamageByChosenSourceEffect extends PreventionEffectImpl { - private TargetSource target; + private Target target; private MageObjectReference mageObjectReference; public PreventDamageByChosenSourceEffect() { - this(new FilterObject("a source")); + this(new FilterSource("a source")); } - public PreventDamageByChosenSourceEffect(FilterObject filterObject) { - this(filterObject, false); + public PreventDamageByChosenSourceEffect(FilterSource filterSource) { + this(filterSource, false); } - public PreventDamageByChosenSourceEffect(FilterObject filterObject, boolean onlyCombat) { + public PreventDamageByChosenSourceEffect(FilterSource filterSource, boolean onlyCombat) { + this(new TargetSource(filterSource), filterSource.getMessage(), onlyCombat); + } + + public PreventDamageByChosenSourceEffect(FilterPermanent filterPermanent, boolean onlyCombat) { + this(new TargetPermanent(filterPermanent), filterPermanent.getMessage(), onlyCombat); + } + + private PreventDamageByChosenSourceEffect(Target target, String filterMessage, boolean onlyCombat) { super(Duration.EndOfTurn, Integer.MAX_VALUE, onlyCombat); - if (!filterObject.getMessage().endsWith("source")) { - filterObject.setMessage(filterObject.getMessage() + " source"); - } - this.target = new TargetSource(filterObject); + this.target = target; staticText = "Prevent all" + (onlyCombat ? " combat" : "") - + " damage " + filterObject.getMessage() + " of your choice would deal this turn"; + + " damage " + filterMessage + " of your choice would deal this turn"; } protected PreventDamageByChosenSourceEffect(final PreventDamageByChosenSourceEffect effect) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceEffect.java index 2d11e279deb..bec2dc502e9 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceEffect.java @@ -5,10 +5,13 @@ import mage.abilities.effects.PreventionEffectData; import mage.abilities.effects.PreventionEffectImpl; import mage.constants.Duration; import mage.constants.Outcome; -import mage.filter.FilterObject; +import mage.filter.FilterPermanent; +import mage.filter.FilterSource; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; +import mage.target.Target; +import mage.target.TargetPermanent; import mage.target.TargetSource; import mage.util.CardUtil; @@ -20,12 +23,12 @@ import java.util.UUID; */ public class PreventNextDamageFromChosenSourceEffect extends PreventionEffectImpl { - protected final TargetSource targetSource; + protected final Target target; private final boolean toYou; private final ApplierOnPrevention onPrevention; public interface ApplierOnPrevention extends Serializable { - boolean apply(PreventionEffectData data, TargetSource targetsource, GameEvent event, Ability source, Game game); + boolean apply(PreventionEffectData data, Target target, GameEvent event, Ability source, Game game); String getText(); } @@ -35,7 +38,7 @@ public class PreventNextDamageFromChosenSourceEffect extends PreventionEffectImp return "You gain life equal to the damage prevented this way"; } - public boolean apply(PreventionEffectData data, TargetSource targetSource, GameEvent event, Ability source, Game game) { + public boolean apply(PreventionEffectData data, Target target, GameEvent event, Ability source, Game game) { if (data == null || data.getPreventedDamage() <= 0) { return false; } @@ -50,24 +53,36 @@ public class PreventNextDamageFromChosenSourceEffect extends PreventionEffectImp }; public PreventNextDamageFromChosenSourceEffect(Duration duration, boolean toYou) { - this(duration, toYou, new FilterObject("source")); + this(duration, toYou, new FilterSource()); } - public PreventNextDamageFromChosenSourceEffect(Duration duration, boolean toYou, FilterObject filter) { - this(duration, toYou, filter, false, null); + public PreventNextDamageFromChosenSourceEffect(Duration duration, boolean toYou, FilterSource filterSource) { + this(duration, toYou, filterSource, false, null); } public PreventNextDamageFromChosenSourceEffect(Duration duration, boolean toYou, ApplierOnPrevention onPrevention) { - this(duration, toYou, new FilterObject("source"), onPrevention); + this(duration, toYou, new FilterSource(), onPrevention); } - public PreventNextDamageFromChosenSourceEffect(Duration duration, boolean toYou, FilterObject filter, ApplierOnPrevention onPrevention) { - this(duration, toYou, filter, false, onPrevention); + public PreventNextDamageFromChosenSourceEffect(Duration duration, boolean toYou, FilterSource filterSource, ApplierOnPrevention onPrevention) { + this(duration, toYou, filterSource, false, onPrevention); } - public PreventNextDamageFromChosenSourceEffect(Duration duration, boolean toYou, FilterObject filter, boolean onlyCombat, ApplierOnPrevention onPrevention) { + public PreventNextDamageFromChosenSourceEffect(Duration duration, boolean toYou, FilterSource filterSource, boolean onlyCombat, ApplierOnPrevention onPrevention) { + this(duration, toYou, new TargetSource(filterSource), onlyCombat, onPrevention); + } + + public PreventNextDamageFromChosenSourceEffect(Duration duration, boolean toYou, FilterPermanent filterPermanent) { + this(duration, toYou, filterPermanent, false, null); + } + + public PreventNextDamageFromChosenSourceEffect(Duration duration, boolean toYou, FilterPermanent filterPermanent, boolean onlyCombat, ApplierOnPrevention onPrevention) { + this(duration, toYou, new TargetPermanent(filterPermanent), onlyCombat, onPrevention); + } + + private PreventNextDamageFromChosenSourceEffect(Duration duration, boolean toYou, Target target, boolean onlyCombat, ApplierOnPrevention onPrevention) { super(duration, Integer.MAX_VALUE, onlyCombat); - this.targetSource = new TargetSource(filter); + this.target = target; this.toYou = toYou; this.onPrevention = onPrevention; this.staticText = setText(); @@ -75,7 +90,7 @@ public class PreventNextDamageFromChosenSourceEffect extends PreventionEffectImp protected PreventNextDamageFromChosenSourceEffect(final PreventNextDamageFromChosenSourceEffect effect) { super(effect); - this.targetSource = effect.targetSource.copy(); + this.target = effect.target.copy(); this.toYou = effect.toYou; this.onPrevention = effect.onPrevention; } @@ -89,8 +104,8 @@ public class PreventNextDamageFromChosenSourceEffect extends PreventionEffectImp public void init(Ability source, Game game) { super.init(source, game); UUID controllerId = source.getControllerId(); - if (this.targetSource.canChoose(controllerId, source, game)) { - this.targetSource.choose(Outcome.PreventDamage, controllerId, source.getSourceId(), source, game); + if (this.target.canChoose(controllerId, source, game)) { + this.target.choose(Outcome.PreventDamage, controllerId, source.getSourceId(), source, game); } } @@ -99,7 +114,7 @@ public class PreventNextDamageFromChosenSourceEffect extends PreventionEffectImp PreventionEffectData data = preventDamageAction(event, source, game); discard(); if (onPrevention != null) { - onPrevention.apply(data, targetSource, event, source, game); + onPrevention.apply(data, target, event, source, game); } return false; } @@ -108,12 +123,12 @@ public class PreventNextDamageFromChosenSourceEffect extends PreventionEffectImp public boolean applies(GameEvent event, Ability source, Game game) { return super.applies(event, source, game) && (!toYou || event.getTargetId().equals(source.getControllerId())) - && event.getSourceId().equals(targetSource.getFirstTarget()); + && target.getTargets().contains(event.getSourceId()); } private String setText() { StringBuilder sb = new StringBuilder("The next time ") - .append(CardUtil.addArticle(targetSource.getFilter().getMessage())); + .append(CardUtil.addArticle(target.getFilter().getMessage())); sb.append(" of your choice would deal damage"); if (toYou) { sb.append(" to you"); diff --git a/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToTargetEffect.java index 7b5c872dc7c..1b08d6bb798 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/PreventNextDamageFromChosenSourceToTargetEffect.java @@ -1,12 +1,11 @@ package mage.abilities.effects.common; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.PreventionEffectImpl; import mage.constants.Duration; import mage.constants.Outcome; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.game.Game; import mage.game.events.GameEvent; import mage.target.TargetSource; @@ -19,14 +18,14 @@ public class PreventNextDamageFromChosenSourceToTargetEffect extends PreventionE protected final TargetSource targetSource; public PreventNextDamageFromChosenSourceToTargetEffect(Duration duration) { - this(duration, new FilterObject<>("source")); + this(duration, new FilterSource()); } - public PreventNextDamageFromChosenSourceToTargetEffect(Duration duration, FilterObject filter) { + public PreventNextDamageFromChosenSourceToTargetEffect(Duration duration, FilterSource filter) { this(duration, filter, false); } - public PreventNextDamageFromChosenSourceToTargetEffect(Duration duration, FilterObject filter, boolean onlyCombat) { + public PreventNextDamageFromChosenSourceToTargetEffect(Duration duration, FilterSource filter, boolean onlyCombat) { super(duration, Integer.MAX_VALUE, onlyCombat); this.targetSource = new TargetSource(filter); } diff --git a/Mage/src/main/java/mage/filter/FilterSource.java b/Mage/src/main/java/mage/filter/FilterSource.java new file mode 100644 index 00000000000..335e30b4f9f --- /dev/null +++ b/Mage/src/main/java/mage/filter/FilterSource.java @@ -0,0 +1,77 @@ + +package mage.filter; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.cards.Card; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.filter.predicate.Predicate; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.command.CommandObject; +import mage.game.permanent.Permanent; +import mage.game.stack.StackObject; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * @author Susucr + */ +public class FilterSource extends FilterObject { + + protected final List> extraPredicates = new ArrayList<>(); + + public FilterSource() { + super("source"); + } + + public FilterSource(String name) { + super(name); + } + + private FilterSource(final FilterSource filter) { + super(filter); + this.extraPredicates.addAll(filter.extraPredicates); + } + + @Override + public FilterSource copy() { + return new FilterSource(this); + } + + public FilterSource add(ObjectSourcePlayerPredicate predicate) { + if (isLockedFilter()) { + throw new UnsupportedOperationException("You may not modify a locked filter"); + } + + // verify check -- make sure predicates work with all 3 Class that could be a Source + Predicates.makeSurePredicateCompatibleWithFilter(predicate, Permanent.class, Card.class, StackObject.class, CommandObject.class); + + extraPredicates.add(predicate); + return this; + } + + @Override + public boolean checkObjectClass(Object object) { + return object instanceof Permanent + || object instanceof Card + || object instanceof StackObject + || object instanceof CommandObject; + } + + public boolean match(MageObject object, UUID sourceControllerId, Ability source, Game game) { + if (!this.match(object, game)) { + return false; + } + ObjectSourcePlayer osp = new ObjectSourcePlayer<>(object, sourceControllerId, source); + return extraPredicates.stream().allMatch(p -> p.apply(osp, game)); + } + + @Override + public List getExtraPredicates() { + return new ArrayList<>(extraPredicates); + } +} diff --git a/Mage/src/main/java/mage/target/TargetSource.java b/Mage/src/main/java/mage/target/TargetSource.java index 84da2f19245..171e6b88236 100644 --- a/Mage/src/main/java/mage/target/TargetSource.java +++ b/Mage/src/main/java/mage/target/TargetSource.java @@ -2,40 +2,52 @@ package mage.target; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; - import mage.MageObject; import mage.abilities.Ability; import mage.cards.Card; import mage.constants.Zone; -import mage.filter.FilterObject; +import mage.filter.FilterSource; import mage.game.Game; +import mage.game.command.CommandObject; import mage.game.permanent.Permanent; import mage.game.stack.StackObject; import mage.players.Player; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** + * 609.7a. If an effect requires a player to choose a source of damage, they may choose a permanent; + * a spell on the stack (including a permanent spell); any object referred to by an object on the stack, + * by a replacement or prevention effect that's waiting to apply, or by a delayed triggered ability + * that's waiting to trigger (even if that object is no longer in the zone it used to be in); or a + * face-up object in the command zone. A source doesn't need to be capable of dealing damage to be + * a legal choice. The source is chosen when the effect is created. If the player chooses a permanent, + * the effect will apply to the next damage dealt by that permanent, regardless of whether it's combat + * damage or damage dealt as the result of a spell or ability. If the player chooses a permanent spell, + * the effect will apply to any damage dealt by that spell and any damage dealt by the permanent that + * spell becomes when it resolves. + * * @author BetaSteward_at_googlemail.com */ public class TargetSource extends TargetObject { - protected final FilterObject filter; + protected final FilterSource filter; public TargetSource() { - this(1, 1, new FilterObject("source of your choice")); + this(1, 1, new FilterSource("source of your choice")); } - public TargetSource(FilterObject filter) { + public TargetSource(FilterSource filter) { this(1, 1, filter); } - public TargetSource(int numTargets, FilterObject filter) { + public TargetSource(int numTargets, FilterSource filter) { this(numTargets, numTargets, filter); } - public TargetSource(int minNumTargets, int maxNumTargets, FilterObject filter) { + public TargetSource(int minNumTargets, int maxNumTargets, FilterSource filter) { super(minNumTargets, maxNumTargets, Zone.ALL, true); this.filter = filter; this.targetName = filter.getMessage(); @@ -47,7 +59,7 @@ public class TargetSource extends TargetObject { } @Override - public FilterObject getFilter() { + public FilterSource getFilter() { return filter; } @@ -78,15 +90,16 @@ public class TargetSource extends TargetObject { } @Override - public boolean canChoose(UUID sourceControllerId, Ability source, Game game) { - return canChoose(sourceControllerId, game); + public boolean canChoose(UUID sourceControllerId, Game game) { + return canChoose(sourceControllerId, (Ability) null, game); } @Override - public boolean canChoose(UUID sourceControllerId, Game game) { + public boolean canChoose(UUID sourceControllerId, Ability source, Game game) { int count = 0; for (StackObject stackObject : game.getStack()) { - if (game.getState().getPlayersInRange(sourceControllerId, game).contains(stackObject.getControllerId()) && filter.match(stackObject, game)) { + if (game.getState().getPlayersInRange(sourceControllerId, game).contains(stackObject.getControllerId()) + && filter.match(stackObject, sourceControllerId, source, game)) { count++; if (count >= this.minNumberOfTargets) { return true; @@ -94,7 +107,7 @@ public class TargetSource extends TargetObject { } } for (Permanent permanent : game.getBattlefield().getActivePermanents(sourceControllerId, game)) { - if (filter.match(permanent, game)) { + if (filter.match(permanent, sourceControllerId, source, game)) { count++; if (count >= this.minNumberOfTargets) { return true; @@ -103,7 +116,7 @@ public class TargetSource extends TargetObject { } for (Player player : game.getPlayers().values()) { for (Card card : player.getGraveyard().getCards(game)) { - if (filter.match(card, game)) { + if (filter.match(card, sourceControllerId, source, game)) { count++; if (count >= this.minNumberOfTargets) { return true; @@ -112,7 +125,15 @@ public class TargetSource extends TargetObject { } } for (Card card : game.getExile().getAllCards(game)) { - if (filter.match(card, game)) { + if (filter.match(card, sourceControllerId, source, game)) { + count++; + if (count >= this.minNumberOfTargets) { + return true; + } + } + } + for (CommandObject commandObject : game.getState().getCommand()) { + if (filter.match(commandObject, sourceControllerId, source, game)) { count++; if (count >= this.minNumberOfTargets) { return true; @@ -123,35 +144,41 @@ public class TargetSource extends TargetObject { } @Override - public Set possibleTargets(UUID sourceControllerId, Ability source, Game game) { - return possibleTargets(sourceControllerId, game); + public Set possibleTargets(UUID sourceControllerId, Game game) { + return possibleTargets(sourceControllerId, (Ability) null, game); } @Override - public Set possibleTargets(UUID sourceControllerId, Game game) { + public Set possibleTargets(UUID sourceControllerId, Ability source, Game game) { Set possibleTargets = new HashSet<>(); for (StackObject stackObject : game.getStack()) { - if (game.getState().getPlayersInRange(sourceControllerId, game).contains(stackObject.getControllerId()) && filter.match(stackObject, game)) { + if (game.getState().getPlayersInRange(sourceControllerId, game).contains(stackObject.getControllerId()) + && filter.match(stackObject, sourceControllerId, source, game)) { possibleTargets.add(stackObject.getId()); } } for (Permanent permanent : game.getBattlefield().getActivePermanents(sourceControllerId, game)) { - if (filter.match(permanent, game)) { + if (filter.match(permanent, sourceControllerId, source, game)) { possibleTargets.add(permanent.getId()); } } for (Player player : game.getPlayers().values()) { for (Card card : player.getGraveyard().getCards(game)) { - if (filter.match(card, game)) { + if (filter.match(card, sourceControllerId, source, game)) { possibleTargets.add(card.getId()); } } } for (Card card : game.getExile().getAllCards(game)) { - if (filter.match(card, game)) { + if (filter.match(card, sourceControllerId, source, game)) { possibleTargets.add(card.getId()); } } + for (CommandObject commandObject : game.getState().getCommand()) { + if (filter.match(commandObject, sourceControllerId, source, game)) { + possibleTargets.add(commandObject.getId()); + } + } return possibleTargets; }