diff --git a/Mage.Sets/src/mage/cards/b/BrutalExpulsion.java b/Mage.Sets/src/mage/cards/b/BrutalExpulsion.java index 8eec6a17a4ac..64066e1d184c 100644 --- a/Mage.Sets/src/mage/cards/b/BrutalExpulsion.java +++ b/Mage.Sets/src/mage/cards/b/BrutalExpulsion.java @@ -10,7 +10,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterSpellOrPermanent; import mage.target.common.TargetCreatureOrPlaneswalker; import mage.target.common.TargetSpellOrPermanent; @@ -26,7 +25,7 @@ public final class BrutalExpulsion extends CardImpl { private static final FilterSpellOrPermanent filter = new FilterSpellOrPermanent("spell or creature"); static { - filter.setPermanentFilter(new FilterCreaturePermanent()); + filter.getPermanentFilter().add(CardType.CREATURE.getPredicate()); } public BrutalExpulsion(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/c/CommitMemory.java b/Mage.Sets/src/mage/cards/c/CommitMemory.java index 0c5f6b523ca8..49e3cdc7e8a9 100644 --- a/Mage.Sets/src/mage/cards/c/CommitMemory.java +++ b/Mage.Sets/src/mage/cards/c/CommitMemory.java @@ -1,7 +1,6 @@ package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; @@ -13,16 +12,17 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SpellAbilityType; -import mage.filter.common.FilterNonlandPermanent; import mage.filter.common.FilterSpellOrPermanent; +import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.players.Player; import mage.target.common.TargetSpellOrPermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class CommitMemory extends SplitCard { @@ -30,7 +30,7 @@ public final class CommitMemory extends SplitCard { private static final FilterSpellOrPermanent filter = new FilterSpellOrPermanent("spell or nonland permanent"); static { - filter.setPermanentFilter(new FilterNonlandPermanent()); + filter.getPermanentFilter().add(Predicates.not(CardType.LAND.getPredicate())); } public CommitMemory(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/u/Unsubstantiate.java b/Mage.Sets/src/mage/cards/u/Unsubstantiate.java index 764262a00e69..0e3fd807e54b 100644 --- a/Mage.Sets/src/mage/cards/u/Unsubstantiate.java +++ b/Mage.Sets/src/mage/cards/u/Unsubstantiate.java @@ -1,17 +1,16 @@ package mage.cards.u; -import java.util.UUID; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterSpellOrPermanent; import mage.target.common.TargetSpellOrPermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class Unsubstantiate extends CardImpl { @@ -19,11 +18,11 @@ public final class Unsubstantiate extends CardImpl { private static final FilterSpellOrPermanent filter = new FilterSpellOrPermanent("spell or creature"); static { - filter.setPermanentFilter(new FilterCreaturePermanent()); + filter.getPermanentFilter().add(CardType.CREATURE.getPredicate()); } public Unsubstantiate(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}"); // Return target spell or creature to its owner's hand. this.getSpellAbility().addTarget(new TargetSpellOrPermanent(1, 1, filter, false)); diff --git a/Mage/src/main/java/mage/filter/Filter.java b/Mage/src/main/java/mage/filter/Filter.java index 90486321ab9a..38f17f8d9ae8 100644 --- a/Mage/src/main/java/mage/filter/Filter.java +++ b/Mage/src/main/java/mage/filter/Filter.java @@ -1,5 +1,7 @@ package mage.filter; +import mage.abilities.Ability; +import mage.filter.predicate.ObjectSourcePlayerPredicate; import mage.filter.predicate.Predicate; import mage.game.Game; import mage.util.Copyable; @@ -7,6 +9,7 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.List; +import java.util.UUID; /** * @param @@ -21,8 +24,25 @@ enum ComparisonScope { boolean match(E o, Game game); + boolean match(E object, UUID sourceControllerId, Ability source, Game game); + Filter add(Predicate predicate); + /** + * Make sure on setting a new Filter that you overwrite this method + * and call Predicates.makeSurePredicateCompatibleWithFilter + * to check that the filter is able to process objects + * of the right kind. Helps with checks the Compiler can't do + * due to ObjectSourcePlayer casting in the this.match(4 arguments). + *

+ * (method should then call this.addExtra(predicate) after verify checks) + */ + Filter add(ObjectSourcePlayerPredicate predicate); + + // TODO: if someone can find a way to not have to add this + // Compiler was confused between overloads + void addExtra(ObjectSourcePlayerPredicate predicate); + boolean checkObjectClass(Object object); String getMessage(); @@ -31,13 +51,13 @@ enum ComparisonScope { Filter copy(); - public boolean isLockedFilter(); + boolean isLockedFilter(); - public void setLockedFilter(boolean lockedFilter); + void setLockedFilter(boolean lockedFilter); List> getPredicates(); - default List getExtraPredicates() { + default List> getExtraPredicates() { return new ArrayList<>(); } } diff --git a/Mage/src/main/java/mage/filter/FilterAbility.java b/Mage/src/main/java/mage/filter/FilterAbility.java index 887d3b3eb656..6859e7ed92fd 100644 --- a/Mage/src/main/java/mage/filter/FilterAbility.java +++ b/Mage/src/main/java/mage/filter/FilterAbility.java @@ -4,11 +4,13 @@ import mage.abilities.Ability; import mage.constants.AbilityType; import mage.constants.Zone; +import mage.filter.predicate.ObjectSourcePlayerPredicate; import mage.filter.predicate.Predicate; +import mage.filter.predicate.Predicates; import mage.game.Game; +import mage.game.stack.StackObject; /** - * * @author North */ public class FilterAbility extends FilterImpl { @@ -38,6 +40,14 @@ public static Predicate type(AbilityType type) { return new AbilityTypePredicate(type); } + @Override + public FilterAbility add(ObjectSourcePlayerPredicate predicate) { + // Verify Checks + Predicates.makeSurePredicateCompatibleWithFilter(predicate, Ability.class, StackObject.class); + this.addExtra(predicate); + return this; + } + @Override public boolean checkObjectClass(Object object) { return object instanceof Ability; diff --git a/Mage/src/main/java/mage/filter/FilterCard.java b/Mage/src/main/java/mage/filter/FilterCard.java index c4eb8cf57ef0..edffb2e0f472 100644 --- a/Mage/src/main/java/mage/filter/FilterCard.java +++ b/Mage/src/main/java/mage/filter/FilterCard.java @@ -1,17 +1,13 @@ package mage.filter; -import mage.abilities.Ability; import mage.cards.Card; import mage.constants.TargetController; -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 java.util.ArrayList; import java.util.List; -import java.util.UUID; /** * Works with cards only. For objects like commanders you must override your canTarget method. @@ -22,7 +18,6 @@ public class FilterCard extends FilterObject { private static final long serialVersionUID = 1L; - protected final List> extraPredicates = new ArrayList<>(); public FilterCard() { super("card"); @@ -34,48 +29,6 @@ public FilterCard(String name) { protected FilterCard(final FilterCard filter) { super(filter); - this.extraPredicates.addAll(filter.extraPredicates); - } - - //20130711 708.6c - /* If anything performs a comparison involving multiple characteristics or - * values of one or more split cards in any zone other than the stack or - * involving multiple characteristics or values of one or more fused split - * spells, each characteristic or value is compared separately. If each of - * the individual comparisons would return a “yes” answer, the whole - * comparison returns a “yes” answer. The individual comparisons may involve - * different halves of the same split card. - */ - @Override - public boolean match(Card card, Game game) { - if (card == null) { - return false; - } - return super.match(card, game); - } - - public boolean match(Card card, UUID playerId, Ability source, Game game) { - if (!this.match(card, game)) { - return false; - } - ObjectSourcePlayer osp = new ObjectSourcePlayer<>(card, playerId, source); - return extraPredicates.stream().allMatch(p -> p.apply(osp, game)); - } - - public final void add(ObjectSourcePlayerPredicate predicate) { - if (isLockedFilter()) { - throw new UnsupportedOperationException("You may not modify a locked filter"); - } - - // verify check - checkPredicateIsSuitableForCardFilter(predicate); - Predicates.makeSurePredicateCompatibleWithFilter(predicate, Card.class); - - extraPredicates.add(predicate); - } - - public boolean hasPredicates() { - return !predicates.isEmpty() || !extraPredicates.isEmpty(); } @Override @@ -83,13 +36,8 @@ public FilterCard copy() { return new FilterCard(this); } - @Override - public List getExtraPredicates() { - return new ArrayList<>(extraPredicates); - } - public static void checkPredicateIsSuitableForCardFilter(Predicate predicate) { - // card filter can't contain controller predicate (only permanents on battlefield have controller) + // card filter can't contain controller predicate (only permanents on battlefield and StackObjects have controller) List list = new ArrayList<>(); Predicates.collectAllComponents(predicate, list); if (list.stream().anyMatch(TargetController.ControllerPredicate.class::isInstance)) { @@ -97,8 +45,26 @@ public static void checkPredicateIsSuitableForCardFilter(Predicate predicate) { } } + public FilterCard withMessage(String message) { this.setMessage(message); return this; } + + @Override + public FilterCard add(ObjectSourcePlayerPredicate predicate) { + // verify checks + checkPredicateIsSuitableForCardFilter(predicate); + Predicates.makeSurePredicateCompatibleWithFilter(predicate, Card.class); + this.addExtra(predicate); + return this; + } + + @Override + public boolean checkObjectClass(Object object) { + // TODO: investigate if we can/should exclude Permanent here. + // as it does extend Card (if so do cleanup the + // MultiFilterImpl that match Permanent and Card/Spell) + return object instanceof Card; + } } diff --git a/Mage/src/main/java/mage/filter/FilterImpl.java b/Mage/src/main/java/mage/filter/FilterImpl.java index 428610eabbb7..28ac687668d3 100644 --- a/Mage/src/main/java/mage/filter/FilterImpl.java +++ b/Mage/src/main/java/mage/filter/FilterImpl.java @@ -1,11 +1,15 @@ package mage.filter; +import mage.abilities.Ability; +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 java.util.ArrayList; import java.util.List; +import java.util.UUID; /** * @param @@ -14,7 +18,8 @@ */ public abstract class FilterImpl implements Filter { - protected List> predicates = new ArrayList<>(); + private List> predicates = new ArrayList<>(); + private List> extraPredicates = new ArrayList<>(); protected String message; protected boolean lockedFilter; // Helps to prevent "accidentally" modifying the StaticFilters objects @@ -29,6 +34,7 @@ public FilterImpl(String name) { protected FilterImpl(final FilterImpl filter) { this.message = filter.message; this.predicates = new ArrayList<>(filter.predicates); + this.extraPredicates = new ArrayList<>(filter.extraPredicates); this.lockedFilter = false;// After copying a filter it's allowed to modify } @@ -41,14 +47,27 @@ public boolean match(E e, Game game) { } @Override - public final Filter add(Predicate predicate) { - if (isLockedFilter()) { - throw new UnsupportedOperationException("You may not modify a locked filter"); + public boolean match(E 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 Filter add(Predicate predicate) { + checkUnlockedFilter(); predicates.add(predicate); return this; } + @Override + public final void addExtra(ObjectSourcePlayerPredicate predicate) { + checkUnlockedFilter(); + extraPredicates.add(predicate); + } + @Override public String getMessage() { return message; @@ -56,10 +75,14 @@ public String getMessage() { @Override public final void setMessage(String message) { + checkUnlockedFilter(); + this.message = message; + } + + protected void checkUnlockedFilter() { if (isLockedFilter()) { throw new UnsupportedOperationException("You may not modify a locked filter"); } - this.message = message; } @Override @@ -80,4 +103,12 @@ public void setLockedFilter(boolean lockedFilter) { public List> getPredicates() { return predicates; } + + public List> getExtraPredicates() { + return new ArrayList<>(extraPredicates); + } + + public boolean hasPredicates() { + return !predicates.isEmpty() || !extraPredicates.isEmpty(); + } } diff --git a/Mage/src/main/java/mage/filter/FilterInPlay.java b/Mage/src/main/java/mage/filter/FilterInPlay.java deleted file mode 100644 index ba0b9979a7e9..000000000000 --- a/Mage/src/main/java/mage/filter/FilterInPlay.java +++ /dev/null @@ -1,18 +0,0 @@ -package mage.filter; - -import mage.abilities.Ability; -import mage.game.Game; - -import java.util.UUID; - -/** - * @param - * @author BetaSteward_at_googlemail.com - */ -public interface FilterInPlay extends Filter { - - boolean match(E o, UUID playerId, Ability source, Game game); - - @Override - FilterInPlay copy(); -} diff --git a/Mage/src/main/java/mage/filter/FilterMana.java b/Mage/src/main/java/mage/filter/FilterMana.java index a8584e7fcd8c..81410fe112f0 100644 --- a/Mage/src/main/java/mage/filter/FilterMana.java +++ b/Mage/src/main/java/mage/filter/FilterMana.java @@ -7,6 +7,8 @@ import java.util.List; /** + * TODO: Not really a Filter. Should be renamed? + * * @author nantuko */ public class FilterMana implements Serializable { diff --git a/Mage/src/main/java/mage/filter/FilterObject.java b/Mage/src/main/java/mage/filter/FilterObject.java index 6795d65b76d2..f1fdd611b9c2 100644 --- a/Mage/src/main/java/mage/filter/FilterObject.java +++ b/Mage/src/main/java/mage/filter/FilterObject.java @@ -2,29 +2,40 @@ package mage.filter; import mage.MageObject; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.filter.predicate.Predicates; /** + * // TODO: migrate all FilterObject to more specific ones, then remove this class? * - * @author North * @param + * @author North */ public class FilterObject extends FilterImpl { - @Override - public FilterObject copy() { - return new FilterObject<>(this); + public FilterObject(String name) { + super(name); + } + + protected FilterObject(final FilterObject filter) { + super(filter); } @Override - public boolean checkObjectClass(Object object) { - return object instanceof MageObject; + public FilterObject copy() { + return new FilterObject(this); } - public FilterObject(String name) { - super(name); + @Override + public FilterObject add(ObjectSourcePlayerPredicate predicate) { + // verify checks + Predicates.makeSurePredicateCompatibleWithFilter(predicate, MageObject.class); + this.addExtra(predicate); + return this; } - protected FilterObject(final FilterObject filter) { - super(filter); + @Override + public boolean checkObjectClass(Object object) { + return object instanceof MageObject; } } diff --git a/Mage/src/main/java/mage/filter/FilterPermanent.java b/Mage/src/main/java/mage/filter/FilterPermanent.java index 9eaee653392f..3d8f8b40c5a5 100644 --- a/Mage/src/main/java/mage/filter/FilterPermanent.java +++ b/Mage/src/main/java/mage/filter/FilterPermanent.java @@ -2,24 +2,18 @@ import mage.abilities.Ability; import mage.constants.SubType; -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.permanent.Permanent; -import java.util.ArrayList; -import java.util.List; import java.util.Set; import java.util.UUID; /** * @author North */ -public class FilterPermanent extends FilterObject implements FilterInPlay { - - protected final List> extraPredicates = new ArrayList<>(); +public class FilterPermanent extends FilterObject { public FilterPermanent() { super("permanent"); @@ -43,41 +37,39 @@ public FilterPermanent(Set subtypesList, String name) { protected FilterPermanent(final FilterPermanent filter) { super(filter); - this.extraPredicates.addAll(filter.extraPredicates); } @Override - public boolean checkObjectClass(Object object) { - return object instanceof Permanent; + public FilterPermanent copy() { + return new FilterPermanent(this); } @Override - public boolean match(Permanent permanent, UUID playerId, Ability source, Game game) { - if (!this.match(permanent, game) || !permanent.isPhasedIn()) { - return false; - } - ObjectSourcePlayer osp = new ObjectSourcePlayer<>(permanent, playerId, source); - return extraPredicates.stream().allMatch(p -> p.apply(osp, game)); - } - - public final void add(ObjectSourcePlayerPredicate predicate) { - if (isLockedFilter()) { - throw new UnsupportedOperationException("You may not modify a locked filter"); - } - - // verify check + public FilterPermanent add(ObjectSourcePlayerPredicate predicate) { + // verify checks Predicates.makeSurePredicateCompatibleWithFilter(predicate, Permanent.class); + this.addExtra(predicate); + return this; + } - extraPredicates.add(predicate); + @Override + public boolean match(Permanent permanent, Game game) { + // TODO: if we can trust the target/checks using FilterPermanent to filter out Phased out permanent, + // this overload would be no longer necessary. + return super.match(permanent, game) + && permanent.isPhasedIn(); } @Override - public FilterPermanent copy() { - return new FilterPermanent(this); + public boolean match(Permanent permanent, UUID sourceControllerId, Ability source, Game game) { + // TODO: if we can trust the target/checks using FilterPermanent to filter out Phased out permanent, + // this overload would be no longer necessary. + return super.match(permanent, sourceControllerId, source, game) + && permanent.isPhasedIn(); } @Override - public List getExtraPredicates() { - return new ArrayList<>(extraPredicates); + public boolean checkObjectClass(Object object) { + return object instanceof Permanent; } } diff --git a/Mage/src/main/java/mage/filter/FilterPlayer.java b/Mage/src/main/java/mage/filter/FilterPlayer.java index 766cba8253b6..86949e3774a7 100644 --- a/Mage/src/main/java/mage/filter/FilterPlayer.java +++ b/Mage/src/main/java/mage/filter/FilterPlayer.java @@ -1,25 +1,15 @@ package mage.filter; -import mage.abilities.Ability; -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.players.Player; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - /** * @author BetaSteward_at_googlemail.com * @author North */ public class FilterPlayer extends FilterImpl { - protected final List> extraPredicates = new ArrayList<>(); - public FilterPlayer() { this("player"); } @@ -30,18 +20,18 @@ public FilterPlayer(String name) { protected FilterPlayer(final FilterPlayer filter) { super(filter); - this.extraPredicates.addAll(filter.extraPredicates); } - public FilterPlayer add(ObjectSourcePlayerPredicate predicate) { - if (isLockedFilter()) { - throw new UnsupportedOperationException("You may not modify a locked filter"); - } + @Override + public FilterPlayer copy() { + return new FilterPlayer(this); + } - // verify check + @Override + public FilterPlayer add(ObjectSourcePlayerPredicate predicate) { + // verify checks Predicates.makeSurePredicateCompatibleWithFilter(predicate, Player.class); - - extraPredicates.add(predicate); + this.addExtra(predicate); return this; } @@ -49,22 +39,4 @@ public FilterPlayer add(ObjectSourcePlayerPredicate predicate) { public boolean checkObjectClass(Object object) { return object instanceof Player; } - - public boolean match(Player checkPlayer, UUID sourceControllerId, Ability source, Game game) { - if (!this.match(checkPlayer, game)) { - return false; - } - ObjectSourcePlayer osp = new ObjectSourcePlayer<>(checkPlayer, sourceControllerId, source); - return extraPredicates.stream().allMatch(p -> p.apply(osp, game)); - } - - @Override - public FilterPlayer copy() { - return new FilterPlayer(this); - } - - @Override - public List getExtraPredicates() { - return new ArrayList<>(extraPredicates); - } } diff --git a/Mage/src/main/java/mage/filter/FilterSource.java b/Mage/src/main/java/mage/filter/FilterSource.java index 335e30b4f9f8..dd62cbc07336 100644 --- a/Mage/src/main/java/mage/filter/FilterSource.java +++ b/Mage/src/main/java/mage/filter/FilterSource.java @@ -2,28 +2,18 @@ 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"); } @@ -34,7 +24,6 @@ public FilterSource(String name) { private FilterSource(final FilterSource filter) { super(filter); - this.extraPredicates.addAll(filter.extraPredicates); } @Override @@ -42,15 +31,12 @@ public FilterSource copy() { return new FilterSource(this); } + @Override 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 + // verify checks + // A source can be a lot of different things, so a variety of predicates can be fed here Predicates.makeSurePredicateCompatibleWithFilter(predicate, Permanent.class, Card.class, StackObject.class, CommandObject.class); - - extraPredicates.add(predicate); + this.addExtra(predicate); return this; } @@ -61,17 +47,4 @@ public boolean checkObjectClass(Object object) { || 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/filter/FilterSpell.java b/Mage/src/main/java/mage/filter/FilterSpell.java index 619fd45b0e94..1abaabdb0d1b 100644 --- a/Mage/src/main/java/mage/filter/FilterSpell.java +++ b/Mage/src/main/java/mage/filter/FilterSpell.java @@ -1,12 +1,8 @@ package mage.filter; -import java.util.UUID; - -import mage.abilities.Ability; -import mage.game.Game; +import mage.cards.Card; import mage.game.stack.Spell; -import mage.game.stack.StackObject; /** * @author North, Quercitron @@ -26,11 +22,9 @@ protected FilterSpell(final FilterSpell filter) { } @Override - public boolean match(StackObject stackObject, UUID playerId, Ability source, Game game) { - if (!(stackObject instanceof Spell)) { - return false; - } - return super.match(stackObject, playerId, source, game); + public boolean checkObjectClass(Object object) { + return object instanceof Spell + || object instanceof Card; // TODO: investigate. Is sometimes used for checking a spell's characteristic before cast } @Override diff --git a/Mage/src/main/java/mage/filter/FilterStackObject.java b/Mage/src/main/java/mage/filter/FilterStackObject.java index aa3b506c1aeb..7b4aef373793 100644 --- a/Mage/src/main/java/mage/filter/FilterStackObject.java +++ b/Mage/src/main/java/mage/filter/FilterStackObject.java @@ -1,25 +1,16 @@ package mage.filter; -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.stack.Spell; import mage.game.stack.StackObject; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - /** * @author North */ public class FilterStackObject extends FilterObject { - protected final List> extraPredicates = new ArrayList<>(); public FilterStackObject() { this("spell or ability"); @@ -31,36 +22,25 @@ public FilterStackObject(String name) { protected FilterStackObject(final FilterStackObject filter) { super(filter); - this.extraPredicates.addAll(filter.extraPredicates); } - public boolean match(StackObject stackObject, UUID playerId, Ability source, Game game) { - if (!this.match(stackObject, game)) { - return false; - } - ObjectSourcePlayer osp = new ObjectSourcePlayer<>(stackObject, playerId, source); - return extraPredicates.stream().allMatch(p -> p.apply(osp, game)); + @Override + public FilterStackObject copy() { + return new FilterStackObject(this); } - public final void add(ObjectSourcePlayerPredicate predicate) { - if (isLockedFilter()) { - throw new UnsupportedOperationException("You may not modify a locked filter"); - } - - // verify check + @Override + public final FilterStackObject add(ObjectSourcePlayerPredicate predicate) { + // verify checks // Spell implements Card interface, so it can use some default predicates like owner Predicates.makeSurePredicateCompatibleWithFilter(predicate, StackObject.class, Spell.class, Card.class); - - extraPredicates.add(predicate); - } - - @Override - public FilterStackObject copy() { - return new FilterStackObject(this); + this.addExtra(predicate); + return this; } @Override - public List getExtraPredicates() { - return new ArrayList<>(extraPredicates); + public boolean checkObjectClass(Object object) { + return object instanceof StackObject + || object instanceof Card; // TODO: investigate. Is sometimes used for checking a spell's characteristic before cast } } diff --git a/Mage/src/main/java/mage/filter/MultiFilterImpl.java b/Mage/src/main/java/mage/filter/MultiFilterImpl.java new file mode 100644 index 000000000000..27b1b1583168 --- /dev/null +++ b/Mage/src/main/java/mage/filter/MultiFilterImpl.java @@ -0,0 +1,133 @@ +package mage.filter; + +import mage.abilities.Ability; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.filter.predicate.Predicate; +import mage.game.Game; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * Make a Filter out of multiple inner filters. + * If making a multi filter out of filterA and filterB, + * any object match the multi filter if it either match + * filterA or match filterB. + * + * @author Susucr + */ +public abstract class MultiFilterImpl implements Filter { + + protected List> innerFilters = new ArrayList<>(); + private String message; + + @Override + public abstract MultiFilterImpl copy(); + + protected MultiFilterImpl(String name, Filter... filters) { + this.message = name; + if (filters.length < 2) { + throw new IllegalArgumentException("Wrong code usage: MultiFilterImpl should have at least 2 inner filters"); + } + this.innerFilters.addAll( + Arrays.stream(filters) + .map(f -> f.copy()) + .collect(Collectors.toList())); + } + + protected MultiFilterImpl(final MultiFilterImpl filter) { + this.message = filter.message; + for (Filter innerFilter : filter.innerFilters) { + this.innerFilters.add(innerFilter.copy()); + } + } + + @Override + public boolean match(E object, Game game) { + return innerFilters + .stream() + .anyMatch((Filter filter) -> filter.checkObjectClass(object) && filter.match(object, game)); + } + + @Override + public boolean match(E object, UUID sourceControllerId, Ability source, Game game) { + return innerFilters + .stream() + .anyMatch((Filter filter) -> filter.checkObjectClass(object) && filter.match(object, sourceControllerId, source, game)); + } + + @Override + public MultiFilterImpl add(Predicate predicate) { + innerFilters.forEach((Filter filter) -> filter.add(predicate)); + return this; + } + + @Override + public MultiFilterImpl add(ObjectSourcePlayerPredicate predicate) { + innerFilters.forEach((Filter filter) -> filter.add(predicate)); + return this; + } + + @Override + public void addExtra(ObjectSourcePlayerPredicate predicate) { + innerFilters.forEach((Filter filter) -> filter.addExtra(predicate)); + } + + @Override + public boolean checkObjectClass(Object object) { + return innerFilters + .stream() + .anyMatch((Filter filter) -> filter.checkObjectClass(object)); + } + + @Override + public String getMessage() { + return message; + } + + @Override + public final void setMessage(String message) { + if (isLockedFilter()) { + throw new UnsupportedOperationException("You may not modify a locked filter"); + } + this.message = message; + } + + @Override + public String toString() { + return message; + } + + @Override + public boolean isLockedFilter() { + return innerFilters.stream().anyMatch((Filter filter) -> filter.isLockedFilter()); + } + + @Override + public void setLockedFilter(boolean lockedFilter) { + innerFilters.forEach((Filter filter) -> filter.setLockedFilter(lockedFilter)); + } + + public List> getPredicates() { + List> predicates = new ArrayList<>(); + for (Filter filter : innerFilters) { + for (Predicate predicate : filter.getPredicates()) { + predicates.add(predicate); + } + } + return predicates; + } + + public List> getExtraPredicates() { + List> predicates = new ArrayList<>(); + for (Filter filter : innerFilters) { + for (ObjectSourcePlayerPredicate predicate : filter.getExtraPredicates()) { + predicates.add(predicate); + } + } + return predicates; + } +} diff --git a/Mage/src/main/java/mage/filter/common/FilterAnyTarget.java b/Mage/src/main/java/mage/filter/common/FilterAnyTarget.java index 0958607c3706..ad4bf776e2b9 100644 --- a/Mage/src/main/java/mage/filter/common/FilterAnyTarget.java +++ b/Mage/src/main/java/mage/filter/common/FilterAnyTarget.java @@ -14,7 +14,7 @@ public FilterAnyTarget() { public FilterAnyTarget(String name) { super(name); - this.permanentFilter.add(Predicates.or( + this.getPermanentFilter().add(Predicates.or( CardType.CREATURE.getPredicate(), CardType.PLANESWALKER.getPredicate(), CardType.BATTLE.getPredicate() diff --git a/Mage/src/main/java/mage/filter/common/FilterCreaturePlayerOrPlaneswalker.java b/Mage/src/main/java/mage/filter/common/FilterCreaturePlayerOrPlaneswalker.java index 996205563c6b..ee8afd2a19ae 100644 --- a/Mage/src/main/java/mage/filter/common/FilterCreaturePlayerOrPlaneswalker.java +++ b/Mage/src/main/java/mage/filter/common/FilterCreaturePlayerOrPlaneswalker.java @@ -16,7 +16,7 @@ public FilterCreaturePlayerOrPlaneswalker() { public FilterCreaturePlayerOrPlaneswalker(String name) { super(name); - this.permanentFilter.add(Predicates.or( + this.getPermanentFilter().add(Predicates.or( CardType.CREATURE.getPredicate(), CardType.PLANESWALKER.getPredicate() )); diff --git a/Mage/src/main/java/mage/filter/common/FilterDefender.java b/Mage/src/main/java/mage/filter/common/FilterDefender.java index 61b335dd0a16..5920d9b5a10a 100644 --- a/Mage/src/main/java/mage/filter/common/FilterDefender.java +++ b/Mage/src/main/java/mage/filter/common/FilterDefender.java @@ -3,7 +3,6 @@ import mage.constants.CardType; import mage.filter.predicate.Predicates; import mage.filter.predicate.other.PlayerIdPredicate; -import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.filter.predicate.permanent.PermanentIdPredicate; import java.util.Set; @@ -17,17 +16,17 @@ public class FilterDefender extends FilterPermanentOrPlayer { public FilterDefender(Set defenders) { super("player, planeswalker, or battle to attack"); - this.permanentFilter.add(Predicates.or( + this.getPermanentFilter().add(Predicates.or( CardType.PLANESWALKER.getPredicate(), CardType.BATTLE.getPredicate() )); - this.permanentFilter.add(Predicates.or( + this.getPermanentFilter().add(Predicates.or( defenders .stream() .map(PermanentIdPredicate::new) .collect(Collectors.toList()) )); - this.playerFilter.add(Predicates.or( + this.getPlayerFilter().add(Predicates.or( defenders .stream() .map(PlayerIdPredicate::new) diff --git a/Mage/src/main/java/mage/filter/common/FilterPermanentOrPlayer.java b/Mage/src/main/java/mage/filter/common/FilterPermanentOrPlayer.java index 385d18f915de..136477a1c038 100644 --- a/Mage/src/main/java/mage/filter/common/FilterPermanentOrPlayer.java +++ b/Mage/src/main/java/mage/filter/common/FilterPermanentOrPlayer.java @@ -1,26 +1,14 @@ package mage.filter.common; import mage.MageItem; -import mage.abilities.Ability; -import mage.filter.FilterImpl; -import mage.filter.FilterInPlay; import mage.filter.FilterPermanent; import mage.filter.FilterPlayer; -import mage.filter.predicate.ObjectSourcePlayerPredicate; -import mage.filter.predicate.Predicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; - -import java.util.UUID; +import mage.filter.MultiFilterImpl; /** * @author nantuko */ -public class FilterPermanentOrPlayer extends FilterImpl implements FilterInPlay { - - protected final FilterPermanent permanentFilter; - protected final FilterPlayer playerFilter; +public class FilterPermanentOrPlayer extends MultiFilterImpl { public FilterPermanentOrPlayer() { this("player or permanent"); @@ -31,57 +19,24 @@ public FilterPermanentOrPlayer(String name) { } public FilterPermanentOrPlayer(String name, FilterPermanent permanentFilter, FilterPlayer playerFilter) { - super(name); - this.permanentFilter = permanentFilter; - this.playerFilter = playerFilter; + super(name, permanentFilter, playerFilter); } protected FilterPermanentOrPlayer(final FilterPermanentOrPlayer filter) { super(filter); - this.permanentFilter = filter.permanentFilter.copy(); - this.playerFilter = filter.playerFilter.copy(); - } - - @Override - public boolean checkObjectClass(Object object) { - return true; - } - - @Override - public boolean match(MageItem o, Game game) { - if (super.match(o, game)) { - if (o instanceof Player) { - return playerFilter.match((Player) o, game); - } else if (o instanceof Permanent) { - return permanentFilter.match((Permanent) o, game); - } - } - return false; } @Override - public boolean match(MageItem o, UUID playerId, Ability source, Game game) { - if (super.match(o, game)) { // process predicates - if (o instanceof Player) { - return playerFilter.match((Player) o, playerId, source, game); - } else if (o instanceof Permanent) { - return permanentFilter.match((Permanent) o, playerId, source, game); - } - } - return false; + public FilterPermanentOrPlayer copy() { + return new FilterPermanentOrPlayer(this); } public FilterPermanent getPermanentFilter() { - return this.permanentFilter; + return (FilterPermanent) this.innerFilters.get(0); } public FilterPlayer getPlayerFilter() { - return this.playerFilter; - } - - @Override - public FilterPermanentOrPlayer copy() { - return new FilterPermanentOrPlayer(this); + return (FilterPlayer) this.innerFilters.get(1); } } diff --git a/Mage/src/main/java/mage/filter/common/FilterPermanentOrSuspendedCard.java b/Mage/src/main/java/mage/filter/common/FilterPermanentOrSuspendedCard.java index 1dec36f68751..f6dc93650b34 100644 --- a/Mage/src/main/java/mage/filter/common/FilterPermanentOrSuspendedCard.java +++ b/Mage/src/main/java/mage/filter/common/FilterPermanentOrSuspendedCard.java @@ -1,89 +1,74 @@ package mage.filter.common; -import java.util.UUID; - import mage.MageObject; import mage.abilities.Ability; import mage.abilities.keyword.SuspendAbility; import mage.cards.Card; import mage.counters.CounterType; import mage.filter.FilterCard; -import mage.filter.FilterImpl; -import mage.filter.FilterInPlay; import mage.filter.FilterPermanent; +import mage.filter.MultiFilterImpl; import mage.filter.predicate.mageobject.AbilityPredicate; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.UUID; + /** * @author emerald000 */ -public class FilterPermanentOrSuspendedCard extends FilterImpl implements FilterInPlay { - - protected FilterCard cardFilter; - protected FilterPermanent permanentFilter; +public class FilterPermanentOrSuspendedCard extends MultiFilterImpl { public FilterPermanentOrSuspendedCard() { this("permanent or suspended card"); } public FilterPermanentOrSuspendedCard(String name) { - super(name); - permanentFilter = new FilterPermanent(); - cardFilter = new FilterCard(); - cardFilter.add(new AbilityPredicate(SuspendAbility.class)); - cardFilter.add(CounterType.TIME.getPredicate()); + super(name, new FilterPermanent(), new FilterCard()); + this.getCardFilter().add(new AbilityPredicate(SuspendAbility.class)); + this.getCardFilter().add(CounterType.TIME.getPredicate()); } protected FilterPermanentOrSuspendedCard(final FilterPermanentOrSuspendedCard filter) { super(filter); - this.permanentFilter = filter.permanentFilter.copy(); - this.cardFilter = filter.cardFilter.copy(); } @Override - public boolean checkObjectClass(Object object) { - return object instanceof MageObject; + public boolean match(MageObject object, Game game) { + // We can not rely on super.match, since Permanent extend Card + // so a Permanent could get filtered by the FilterCard + if (object instanceof Permanent) { + return getPermanentFilter().match((Permanent) object, game); + } else if (object instanceof Card) { + return getCardFilter().match((Card) object, game); + } else { + return false; + } } @Override - public boolean match(MageObject o, Game game) { - if (o instanceof Permanent) { - return permanentFilter.match((Permanent) o, game); - } else if (o instanceof Card) { - return cardFilter.match((Card) o, game); + public boolean match(MageObject object, UUID sourceControllerId, Ability source, Game game) { + // We can not rely on super.match, since Permanent extend Card + // so a Permanent could get filtered by the FilterCard + if (object instanceof Permanent) { + return getPermanentFilter().match((Permanent) object, sourceControllerId, source, game); + } else if (object instanceof Card) { + return getCardFilter().match((Card) object, sourceControllerId, source, game); + } else { + return false; } - return false; } @Override - public boolean match(MageObject o, UUID playerId, Ability source, Game game) { - if (o instanceof Permanent) { - return permanentFilter.match((Permanent) o, playerId, source, game); - } else if (o instanceof Card) { - return cardFilter.match((Card) o, playerId, source, game); - } - return false; + public FilterPermanentOrSuspendedCard copy() { + return new FilterPermanentOrSuspendedCard(this); } public FilterPermanent getPermanentFilter() { - return this.permanentFilter; + return (FilterPermanent) this.innerFilters.get(0); } public FilterCard getCardFilter() { - return this.cardFilter; - } - - public void setPermanentFilter(FilterPermanent permanentFilter) { - this.permanentFilter = permanentFilter; - } - - public void setCardFilter(FilterCard cardFilter) { - this.cardFilter = cardFilter; - } - - @Override - public FilterPermanentOrSuspendedCard copy() { - return new FilterPermanentOrSuspendedCard(this); + return (FilterCard) this.innerFilters.get(1); } } diff --git a/Mage/src/main/java/mage/filter/common/FilterSpellOrPermanent.java b/Mage/src/main/java/mage/filter/common/FilterSpellOrPermanent.java index fa5af86f978c..39be00d4084e 100644 --- a/Mage/src/main/java/mage/filter/common/FilterSpellOrPermanent.java +++ b/Mage/src/main/java/mage/filter/common/FilterSpellOrPermanent.java @@ -2,10 +2,9 @@ import mage.MageObject; import mage.abilities.Ability; -import mage.filter.FilterImpl; -import mage.filter.FilterInPlay; import mage.filter.FilterPermanent; import mage.filter.FilterSpell; +import mage.filter.MultiFilterImpl; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.Spell; @@ -15,78 +14,57 @@ /** * @author LevelX */ -public class FilterSpellOrPermanent extends FilterImpl implements FilterInPlay { - - protected FilterPermanent permanentFilter; - protected FilterSpell spellFilter; +public class FilterSpellOrPermanent extends MultiFilterImpl { public FilterSpellOrPermanent() { this("spell or permanent"); } public FilterSpellOrPermanent(String name) { - super(name); - permanentFilter = new FilterPermanent(); - spellFilter = new FilterSpell(); + super(name, new FilterPermanent(), new FilterSpell()); } protected FilterSpellOrPermanent(final FilterSpellOrPermanent filter) { super(filter); - this.permanentFilter = filter.permanentFilter.copy(); - this.spellFilter = filter.spellFilter.copy(); } - @Override - public boolean checkObjectClass(Object object) { - return object instanceof MageObject; - } @Override - public boolean match(MageObject o, Game game) { - if (o instanceof Spell) { - return spellFilter.match((Spell) o, game); - } else if (o instanceof Permanent) { - return permanentFilter.match((Permanent) o, game); + public boolean match(MageObject object, Game game) { + // We can not rely on super.match, since Permanent extend Card + // and currently FilterSpell accepts to filter Cards + if (object instanceof Permanent) { + return getPermanentFilter().match((Permanent) object, game); + } else if (object instanceof Spell) { + return getSpellFilter().match((Spell) object, game); + } else { + return false; } - return false; } @Override - public boolean match(MageObject o, UUID playerId, Ability source, Game game) { - if (o instanceof Spell) { - return spellFilter.match((Spell) o, playerId, source, game); - } else if (o instanceof Permanent) { - return permanentFilter.match((Permanent) o, playerId, source, game); + public boolean match(MageObject object, UUID sourceControllerId, Ability source, Game game) { + // We can not rely on super.match, since Permanent extend Card + // and currently FilterSpell accepts to filter Cards + if (object instanceof Permanent) { + return getPermanentFilter().match((Permanent) object, sourceControllerId, source, game); + } else if (object instanceof Spell) { + return getSpellFilter().match((Spell) object, sourceControllerId, source, game); + } else { + return false; } - return false; } @Override - public void setLockedFilter(boolean lockedFilter) { - super.setLockedFilter(lockedFilter); - spellFilter.setLockedFilter(lockedFilter); - permanentFilter.setLockedFilter(lockedFilter); + public FilterSpellOrPermanent copy() { + return new FilterSpellOrPermanent(this); } public FilterPermanent getPermanentFilter() { - return this.permanentFilter; + return (FilterPermanent) this.innerFilters.get(0); } public FilterSpell getSpellFilter() { - return this.spellFilter; - } - - public void setPermanentFilter(FilterPermanent permanentFilter) { - this.permanentFilter = permanentFilter; + return (FilterSpell) this.innerFilters.get(1); } - - public void setSpellFilter(FilterSpell spellFilter) { - this.spellFilter = spellFilter; - } - - @Override - public FilterSpellOrPermanent copy() { - return new FilterSpellOrPermanent(this); - } - } diff --git a/Mage/src/main/java/mage/players/ManaPool.java b/Mage/src/main/java/mage/players/ManaPool.java index 82bca7edcef1..e4f8c0015755 100644 --- a/Mage/src/main/java/mage/players/ManaPool.java +++ b/Mage/src/main/java/mage/players/ManaPool.java @@ -101,7 +101,7 @@ public int getBlack() { /** * @param manaType the mana type that should be paid * @param ability - * @param filter + * @param filter filters the source of mana, only matching source are accepted. * @param game * @param costToPay complete costs to pay (needed to check conditional * mana) @@ -140,7 +140,8 @@ && isAutoPaymentRestricted() } for (ManaPoolItem mana : manaItems) { - if (filter != null && !filter.match(mana.getSourceObject(), game)) { + MageObject sourceObject = mana.getSourceObject(); + if (filter != null && (!filter.checkObjectClass(sourceObject) || !filter.match(sourceObject, game))) { // If here, then mana source does not match the filter // However, alternate mana payment abilities such as convoke won't match the filter but are valid // So we need to do some ugly checks to allow them diff --git a/Mage/src/main/java/mage/target/common/TargetOpponent.java b/Mage/src/main/java/mage/target/common/TargetOpponent.java index 3fbe826de18b..7c52c08a6c90 100644 --- a/Mage/src/main/java/mage/target/common/TargetOpponent.java +++ b/Mage/src/main/java/mage/target/common/TargetOpponent.java @@ -1,7 +1,6 @@ package mage.target.common; import mage.filter.FilterOpponent; -import mage.filter.FilterPlayer; import mage.target.TargetPlayer; /**