diff --git a/Mage.Sets/src/mage/cards/a/AngelOfSerenity.java b/Mage.Sets/src/mage/cards/a/AngelOfSerenity.java index 4dfb63bbc40..dbcca3ae73a 100644 --- a/Mage.Sets/src/mage/cards/a/AngelOfSerenity.java +++ b/Mage.Sets/src/mage/cards/a/AngelOfSerenity.java @@ -1,6 +1,5 @@ package mage.cards.a; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -13,18 +12,21 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreatureCard; +import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardIdPredicate; import mage.target.Target; import mage.target.common.TargetCardInGraveyardOrBattlefield; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class AngelOfSerenity extends CardImpl { - + private static final String rule = "you may exile up to three other target creatures from the battlefield and/or creature cards from graveyards."; public AngelOfSerenity(UUID ownerId, CardSetInfo setInfo) { @@ -38,10 +40,11 @@ public final class AngelOfSerenity extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Angel of Serenity enters the battlefield, you may exile up to three other target creatures from the battlefield and/or creature cards from graveyards. - FilterCreatureCard filter = new FilterCreatureCard("creatures from the battlefield and/or a graveyard"); - filter.add(Predicates.not(new CardIdPredicate(this.getId()))); + FilterCreaturePermanent filterBattle = new FilterCreaturePermanent("other target creatures"); + filterBattle.add(Predicates.not(new CardIdPredicate(this.getId()))); + FilterCreatureCard filterGrave = StaticFilters.FILTER_CARD_CREATURE; Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetForSourceEffect().setText(rule), true); - Target target = new TargetCardInGraveyardOrBattlefield(0, 3, filter); + Target target = new TargetCardInGraveyardOrBattlefield(0, 3, filterGrave, filterBattle); ability.addTarget(target); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/d/DarettiIngeniousIconoclast.java b/Mage.Sets/src/mage/cards/d/DarettiIngeniousIconoclast.java index 713cded9566..761505e9d42 100644 --- a/Mage.Sets/src/mage/cards/d/DarettiIngeniousIconoclast.java +++ b/Mage.Sets/src/mage/cards/d/DarettiIngeniousIconoclast.java @@ -1,4 +1,3 @@ - package mage.cards.d; import mage.abilities.Ability; @@ -14,10 +13,8 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.filter.FilterCard; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; -import mage.filter.common.FilterArtifactCard; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; import mage.game.permanent.token.DarettiConstructToken; @@ -34,8 +31,6 @@ public final class DarettiIngeniousIconoclast extends CardImpl { private static final FilterPermanent filter = new FilterPermanent("artifact or creature (to destroy)"); - private static final FilterCard filter2 - = new FilterArtifactCard("artifact card in a graveyard or artifact on the battlefield"); static { filter.add(Predicates.or( @@ -72,7 +67,8 @@ public final class DarettiIngeniousIconoclast extends CardImpl { .setText("Choose target artifact card in a graveyard or artifact on the battlefield. " + "Create three tokens that are copies of it"), -6 ); - ability.addTarget(new TargetCardInGraveyardOrBattlefield(filter2)); + ability.addTarget(new TargetCardInGraveyardOrBattlefield(1, 1, + StaticFilters.FILTER_CARD_ARTIFACT, StaticFilters.FILTER_PERMANENT_ARTIFACT)); this.addAbility(ability); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/EnterLeaveBattlefieldExileTargetTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/EnterLeaveBattlefieldExileTargetTest.java index f4192157f7e..821d98c3113 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/EnterLeaveBattlefieldExileTargetTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/EnterLeaveBattlefieldExileTargetTest.java @@ -1,4 +1,3 @@ - package org.mage.test.cards.triggers; import mage.constants.PhaseStep; @@ -7,7 +6,6 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author LevelX2 */ public class EnterLeaveBattlefieldExileTargetTest extends CardTestPlayerBase { @@ -25,9 +23,12 @@ public class EnterLeaveBattlefieldExileTargetTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Angel of Serenity"); addTarget(playerA, "Silvercoat Lion^Pillarfield Ox"); + setChoice(playerA, "Yes"); + setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); execute(); + assertAllCommandsUsed(); assertPermanentCount(playerA, "Angel of Serenity", 1); assertExileCount("Silvercoat Lion", 1); diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java index d5ec909522d..9026b1f8cbc 100644 --- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java +++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java @@ -765,7 +765,7 @@ public class VerifyCardDataTest { // debug only: show direct card info (takes it from class file, not from db repository) String cardName = "Essence Capture"; CardScanner.scan(); - CardSetInfo testSet = new CardSetInfo("test", "test", "123", Rarity.COMMON); + CardSetInfo testSet = new CardSetInfo(cardName, "test", "123", Rarity.COMMON); CardInfo cardInfo = CardRepository.instance.findCard(cardName); Card card = CardImpl.createCard(cardInfo.getClassName(), testSet); card.getRules().stream().forEach(System.out::println); diff --git a/Mage/src/main/java/mage/target/TargetCard.java b/Mage/src/main/java/mage/target/TargetCard.java index 741df22c11f..a1d00b7f824 100644 --- a/Mage/src/main/java/mage/target/TargetCard.java +++ b/Mage/src/main/java/mage/target/TargetCard.java @@ -1,11 +1,7 @@ - package mage.target; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; -import java.util.stream.Collectors; import mage.MageItem; +import mage.abilities.Ability; import mage.cards.Card; import mage.cards.Cards; import mage.constants.Zone; @@ -14,12 +10,17 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + /** - * * @author BetaSteward_at_googlemail.com */ public class TargetCard extends TargetObject { + // all targets will be filtered by one zone, don't use "multi-zone" filter (if you want then override all canTarget and possible methods) protected final FilterCard filter; protected TargetCard(Zone zone) { @@ -53,7 +54,7 @@ public class TargetCard extends TargetObject { /** * Checks if there are enough {@link Card} that can be chosen. * - * @param sourceId - the target event source + * @param sourceId - the target event source * @param sourceControllerId - controller of the target event source * @param game * @return - true if enough valid {@link Card} exist @@ -179,7 +180,7 @@ public class TargetCard extends TargetObject { } public Set possibleTargets(UUID sourceControllerId, Cards cards, Game game) { - return cards.getCards(filter,game).stream().map(MageItem::getId).collect(Collectors.toSet()); + return cards.getCards(filter, game).stream().map(MageItem::getId).collect(Collectors.toSet()); } @Override @@ -187,9 +188,28 @@ public class TargetCard extends TargetObject { return possibleTargets(null, sourceControllerId, game); } + // TODO: check all class targets, if it override canTarget then make sure it override ALL 3 METHODS with canTarget and possibleTargets (method with cards doesn't need) + + @Override + public boolean canTarget(UUID id, Game game) { + return super.canTarget(id, game); + } + + @Override + public boolean canTarget(UUID id, Ability source, Game game) { + return canTarget(id, game); + } + + @Override + public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) { + Card card = game.getCard(id); + return card != null + && zone != null && zone.match(game.getState().getZone(id)) + && getFilter() != null && getFilter().match(card, playerId, game); + } + public boolean canTarget(UUID id, Cards cards, Game game) { - Card card = cards.get(id, game); - return card != null && filter.match(card, game); + return cards.contains(id) && canTarget(id, game); } @Override diff --git a/Mage/src/main/java/mage/target/TargetImpl.java b/Mage/src/main/java/mage/target/TargetImpl.java index 8d7a0e061de..438bbb35ada 100644 --- a/Mage/src/main/java/mage/target/TargetImpl.java +++ b/Mage/src/main/java/mage/target/TargetImpl.java @@ -23,7 +23,7 @@ public abstract class TargetImpl implements Target { protected final Map zoneChangeCounters = new HashMap<>(); protected String targetName; - protected Zone zone; + protected Zone zone; // all targets will be filtered by that zone, don't use "multi-zone" filter protected int maxNumberOfTargets; protected int minNumberOfTargets; protected boolean required = true; diff --git a/Mage/src/main/java/mage/target/common/TargetCardInGraveyardOrBattlefield.java b/Mage/src/main/java/mage/target/common/TargetCardInGraveyardOrBattlefield.java index 79f7cbd1fcc..c19aebe893a 100644 --- a/Mage/src/main/java/mage/target/common/TargetCardInGraveyardOrBattlefield.java +++ b/Mage/src/main/java/mage/target/common/TargetCardInGraveyardOrBattlefield.java @@ -1,9 +1,7 @@ - package mage.target.common; import mage.MageObject; import mage.abilities.Ability; -import mage.cards.Card; import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.FilterPermanent; @@ -19,58 +17,76 @@ import java.util.UUID; */ public class TargetCardInGraveyardOrBattlefield extends TargetCard { - public TargetCardInGraveyardOrBattlefield() { - this(1, 1, new FilterCard("target card in a graveyard or permanent on the battlefield")); - } + protected final FilterPermanent filterBattlefield; - public TargetCardInGraveyardOrBattlefield(FilterCard filter) { - this(1, 1, filter); - } - - public TargetCardInGraveyardOrBattlefield(int numTargets, FilterCard filter) { - this(numTargets, numTargets, filter); - } - - public TargetCardInGraveyardOrBattlefield(int minNumTargets, int maxNumTargets, FilterCard filter) { - super(minNumTargets, maxNumTargets, Zone.GRAVEYARD, filter); // Zone for card - this.targetName = filter.getMessage(); + public TargetCardInGraveyardOrBattlefield(int minNumTargets, int maxNumTargets, FilterCard filterGraveyard, FilterPermanent filterBattlefield) { + super(minNumTargets, maxNumTargets, Zone.GRAVEYARD, filterGraveyard); // zone for card in graveyard, don't change + this.filterBattlefield = filterBattlefield; + this.targetName = filter.getMessage() + + " in a graveyard " + + (maxNumTargets > 1 ? " and/or " : " or ") + + this.filterBattlefield.getMessage() + + " on the battlefield"; } public TargetCardInGraveyardOrBattlefield(final TargetCardInGraveyardOrBattlefield target) { super(target); + this.filterBattlefield = target.filterBattlefield; } @Override public boolean canChoose(UUID sourceId, UUID sourceControllerId, Game game) { if (!super.canChoose(sourceId, sourceControllerId, game)) { MageObject targetSource = game.getObject(sourceId); - for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterPermanent(), sourceControllerId, game)) { - if ((notTarget || permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) && filter.match(permanent, sourceControllerId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filterBattlefield, sourceControllerId, game)) { + if ((notTarget || permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) + && filterBattlefield.match(permanent, sourceId, sourceControllerId, game)) { return true; } } return false; - } return true; } @Override public boolean canTarget(UUID id, Ability source, Game game) { - Permanent permanent = game.getPermanent(id); - if (permanent != null) { - return filter.match(permanent, game); + if (!super.canTarget(id, source, game)) { // in graveyard first + Permanent permanent = game.getPermanent(id); + if (permanent != null) { + return filterBattlefield.match(permanent, game); + } } - Card card = game.getCard(id); - return card != null && game.getState().getZone(card.getId()) == Zone.GRAVEYARD && filter.match(card, game); + return true; + } + + @Override + public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) { + if (!super.canTarget(playerId, id, source, game)) { // in graveyard first + Permanent permanent = game.getPermanent(id); + if (permanent != null) { + return filterBattlefield.match(permanent, source != null ? source.getSourceId() : null, playerId, game); + } + } + return true; + } + + @Override + public boolean canTarget(UUID id, Game game) { + if (!super.canTarget(id, game)) { // in graveyard first + Permanent permanent = game.getPermanent(id); + if (permanent != null) { + return filterBattlefield.match(permanent, game); + } + } + return true; } @Override public Set possibleTargets(UUID sourceControllerId, Game game) { - //return super.possibleTargets(sourceControllerId, game); //To change body of generated methods, choose Tools | Templates. - Set possibleTargets = super.possibleTargets(sourceControllerId, game); - for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterPermanent(), sourceControllerId, game)) { - if (filter.match(permanent, sourceControllerId, game)) { + Set possibleTargets = super.possibleTargets(sourceControllerId, game); // in graveyard first + for (Permanent permanent : game.getBattlefield().getActivePermanents(filterBattlefield, sourceControllerId, game)) { + if (filterBattlefield.match(permanent, null, sourceControllerId, game)) { possibleTargets.add(permanent.getId()); } } @@ -79,15 +95,14 @@ public class TargetCardInGraveyardOrBattlefield extends TargetCard { @Override public Set possibleTargets(UUID sourceId, UUID sourceControllerId, Game game) { - Set possibleTargets = super.possibleTargets(sourceId, sourceControllerId, game); + Set possibleTargets = super.possibleTargets(sourceId, sourceControllerId, game); // in graveyard first MageObject targetSource = game.getObject(sourceId); - for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterPermanent(), sourceControllerId, game)) { - if ((notTarget || permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) && filter.match(permanent, sourceId, sourceControllerId, game)) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(filterBattlefield, sourceControllerId, sourceId, game)) { + if ((notTarget || permanent.canBeTargetedBy(targetSource, sourceControllerId, game)) && filterBattlefield.match(permanent, sourceId, sourceControllerId, game)) { possibleTargets.add(permanent.getId()); } } return possibleTargets; - } @Override