diff --git a/Mage.Sets/src/mage/cards/b/BloodchiefsThirst.java b/Mage.Sets/src/mage/cards/b/BloodchiefsThirst.java index dc2dcd8141a..8dde99447af 100644 --- a/Mage.Sets/src/mage/cards/b/BloodchiefsThirst.java +++ b/Mage.Sets/src/mage/cards/b/BloodchiefsThirst.java @@ -40,9 +40,9 @@ public final class BloodchiefsThirst extends CardImpl { "Destroy target creature or planeswalker with mana value 2 or less. " + "If this spell was kicked, instead destroy target creature or planeswalker." )); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker()); this.getSpellAbility().setTargetAdjuster(new ConditionalTargetAdjuster(KickedCondition.ONCE, - new TargetCreatureOrPlaneswalker())); + new TargetPermanent(filter), new TargetCreatureOrPlaneswalker())); } private BloodchiefsThirst(final BloodchiefsThirst card) { diff --git a/Mage.Sets/src/mage/cards/e/ExpelTheUnworthy.java b/Mage.Sets/src/mage/cards/e/ExpelTheUnworthy.java index e64da055440..c087d318889 100644 --- a/Mage.Sets/src/mage/cards/e/ExpelTheUnworthy.java +++ b/Mage.Sets/src/mage/cards/e/ExpelTheUnworthy.java @@ -43,9 +43,9 @@ public final class ExpelTheUnworthy extends CardImpl { this.getSpellAbility().addEffect(new InfoEffect("Choose target creature with mana value 3 or less. If this spell was kicked, instead choose target creature.")); this.getSpellAbility().addEffect(new ExileTargetEffect().setText("Exile the chosen creature")); this.getSpellAbility().addEffect(new ExpelTheUnworthyEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().setTargetAdjuster(new ConditionalTargetAdjuster(KickedCondition.ONCE, - new TargetCreaturePermanent())); + new TargetCreaturePermanent(filter), new TargetCreaturePermanent())); } private ExpelTheUnworthy(final ExpelTheUnworthy card) { diff --git a/Mage.Sets/src/mage/cards/f/FightWithFire.java b/Mage.Sets/src/mage/cards/f/FightWithFire.java index f501c8714cd..91c66490787 100644 --- a/Mage.Sets/src/mage/cards/f/FightWithFire.java +++ b/Mage.Sets/src/mage/cards/f/FightWithFire.java @@ -8,6 +8,7 @@ import mage.abilities.keyword.KickerAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.target.common.TargetAnyTarget; import mage.target.common.TargetAnyTargetAmount; import mage.target.common.TargetCreaturePermanent; import mage.target.targetadjustment.ConditionalTargetAdjuster; @@ -35,9 +36,9 @@ public final class FightWithFire extends CardImpl { + "it deals 10 damage divided as you choose among any number of targets instead." + " (Those targets can include players and planeswalkers.)" )); - this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addTarget(new TargetAnyTarget()); this.getSpellAbility().setTargetAdjuster(new ConditionalTargetAdjuster(KickedCondition.ONCE, - new TargetAnyTargetAmount(10))); + new TargetCreaturePermanent(), new TargetAnyTargetAmount(10))); } private FightWithFire(final FightWithFire card) { diff --git a/Mage.Sets/src/mage/cards/g/GaladrielsDismissal.java b/Mage.Sets/src/mage/cards/g/GaladrielsDismissal.java index ae773855997..1e256f06796 100644 --- a/Mage.Sets/src/mage/cards/g/GaladrielsDismissal.java +++ b/Mage.Sets/src/mage/cards/g/GaladrielsDismissal.java @@ -16,6 +16,7 @@ import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPlayer; +import mage.target.common.TargetCreatureOrPlayer; import mage.target.common.TargetCreaturePermanent; import mage.target.targetadjustment.ConditionalTargetAdjuster; @@ -42,9 +43,9 @@ public final class GaladrielsDismissal extends CardImpl { KickedCondition.ONCE, "Target creature phases out. If this spell was kicked, each creature target player controls phases out instead. " + "(Treat phased-out creatures and anything attached to them as though they don't exist until their controller's next turn.)" )); - this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); this.getSpellAbility().setTargetAdjuster(new ConditionalTargetAdjuster(KickedCondition.ONCE, - new TargetPlayer())); + new TargetCreaturePermanent(), new TargetPlayer())); } private GaladrielsDismissal(final GaladrielsDismissal card) { diff --git a/Mage.Sets/src/mage/cards/i/IntoTheFloodMaw.java b/Mage.Sets/src/mage/cards/i/IntoTheFloodMaw.java index 6c2cb9e2a6b..c0a5d0adb09 100644 --- a/Mage.Sets/src/mage/cards/i/IntoTheFloodMaw.java +++ b/Mage.Sets/src/mage/cards/i/IntoTheFloodMaw.java @@ -7,7 +7,10 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.GiftType; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; import mage.filter.StaticFilters; +import mage.filter.predicate.Predicates; import mage.target.TargetPermanent; import mage.target.common.TargetOpponentsCreaturePermanent; import mage.target.targetadjustment.ConditionalTargetAdjuster; @@ -18,6 +21,15 @@ import java.util.UUID; * @author TheElk801 */ public final class IntoTheFloodMaw extends CardImpl { + private static final FilterPermanent playableFilter = new FilterPermanent("creature or nonland permanent"); + + static { + playableFilter.add(TargetController.OPPONENT.getControllerPredicate()); + playableFilter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + Predicates.not(CardType.LAND.getPredicate()) + )); + } public IntoTheFloodMaw(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}"); @@ -29,9 +41,9 @@ public final class IntoTheFloodMaw extends CardImpl { this.getSpellAbility().addEffect(new ReturnToHandTargetEffect() .setText("return target creature an opponent controls to its owner's hand. If the gift was promise, " + "instead return target nonland permanent an opponent controls to its owner's hand")); - this.getSpellAbility().addTarget(new TargetOpponentsCreaturePermanent()); + this.getSpellAbility().addTarget(new TargetPermanent(playableFilter)); this.getSpellAbility().setTargetAdjuster(new ConditionalTargetAdjuster(GiftWasPromisedCondition.TRUE, - new TargetPermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_NON_LAND))); + new TargetOpponentsCreaturePermanent(), new TargetPermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_NON_LAND))); } private IntoTheFloodMaw(final IntoTheFloodMaw card) { diff --git a/Mage.Sets/src/mage/cards/l/LongRiversPull.java b/Mage.Sets/src/mage/cards/l/LongRiversPull.java index be2419f179a..62a51398baf 100644 --- a/Mage.Sets/src/mage/cards/l/LongRiversPull.java +++ b/Mage.Sets/src/mage/cards/l/LongRiversPull.java @@ -27,9 +27,9 @@ public final class LongRiversPull extends CardImpl { // Counter target creature spell. If the gift was promised, instead counter target spell. this.getSpellAbility().addEffect(new CounterTargetEffect() .setText("counter target creature spell. If the gift was promised, instead counter target spell")); - this.getSpellAbility().addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_CREATURE)); + this.getSpellAbility().addTarget(new TargetSpell()); this.getSpellAbility().setTargetAdjuster(new ConditionalTargetAdjuster(GiftWasPromisedCondition.TRUE, - new TargetSpell())); + new TargetSpell(StaticFilters.FILTER_SPELL_CREATURE), new TargetSpell())); } private LongRiversPull(final LongRiversPull card) { diff --git a/Mage.Sets/src/mage/cards/r/RushingRiver.java b/Mage.Sets/src/mage/cards/r/RushingRiver.java index 6f53eb8e729..b3f2d86feb4 100644 --- a/Mage.Sets/src/mage/cards/r/RushingRiver.java +++ b/Mage.Sets/src/mage/cards/r/RushingRiver.java @@ -10,7 +10,6 @@ import mage.constants.CardType; import mage.filter.StaticFilters; import mage.target.common.TargetNonlandPermanent; import mage.target.targetadjustment.ConditionalTargetAdjuster; -import mage.target.targetpointer.EachTargetPointer; import java.util.UUID; @@ -27,7 +26,6 @@ public final class RushingRiver extends CardImpl { // Return target nonland permanent to its owner's hand. If Rushing River was kicked, return another target nonland permanent to its owner's hand. this.getSpellAbility().addEffect(new ReturnToHandTargetEffect() - .setTargetPointer(new EachTargetPointer()) .setText("Return target nonland permanent to its owner's hand. " + "If this spell was kicked, return another target nonland permanent to its owner's hand")); this.getSpellAbility().addTarget(new TargetNonlandPermanent()); diff --git a/Mage.Sets/src/mage/cards/t/TearAsunder.java b/Mage.Sets/src/mage/cards/t/TearAsunder.java index a9ae45d0e5c..10245aa97c3 100644 --- a/Mage.Sets/src/mage/cards/t/TearAsunder.java +++ b/Mage.Sets/src/mage/cards/t/TearAsunder.java @@ -6,7 +6,9 @@ import mage.abilities.keyword.KickerAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.filter.FilterPermanent; import mage.filter.StaticFilters; +import mage.filter.predicate.Predicates; import mage.target.TargetPermanent; import mage.target.common.TargetNonlandPermanent; import mage.target.targetadjustment.ConditionalTargetAdjuster; @@ -17,6 +19,15 @@ import java.util.UUID; * @author TheElk801 */ public final class TearAsunder extends CardImpl { + private static final FilterPermanent playableFilter = new FilterPermanent("artifact, enchantment, or nonland permanent"); + + static { + playableFilter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.ENCHANTMENT.getPredicate(), + Predicates.not(CardType.LAND.getPredicate()) + )); + } public TearAsunder(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}"); @@ -26,9 +37,9 @@ public final class TearAsunder extends CardImpl { // Exile target artifact or enchantment. If this spell was kicked, exile target nonland permanent instead. this.getSpellAbility().addEffect(new ExileTargetEffect().setText("Exile target artifact or enchantment. If this spell was kicked, exile target nonland permanent instead.")); - this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); + this.getSpellAbility().addTarget(new TargetPermanent(playableFilter)); this.getSpellAbility().setTargetAdjuster(new ConditionalTargetAdjuster(KickedCondition.ONCE, - new TargetNonlandPermanent())); + new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT), new TargetNonlandPermanent())); } private TearAsunder(final TearAsunder card) { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GiftTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GiftTest.java index 776a785dc6f..5cbc7518da2 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GiftTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/GiftTest.java @@ -215,4 +215,26 @@ public class GiftTest extends CardTestPlayerBase { assertHandCount(playerA, 1); assertHandCount(playerB, 1); } + + //Test Conditional Target Adjuster allowing more generic casts + @Test + public void testLongRiversPull() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 3); + addCard(Zone.HAND, playerA, "Ponder"); + addCard(Zone.HAND, playerA, "Long River's Pull"); // UU, counter noncreature only if gift + + setChoice(playerA, true); + setChoice(playerA, playerB.getName()); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ponder"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Long River's Pull"); + addTarget(playerA, "Ponder"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertHandCount(playerA, 0); + assertGraveyardCount(playerA, 2); + assertHandCount(playerB, 1); + } } diff --git a/Mage/src/main/java/mage/target/targetadjustment/ConditionalTargetAdjuster.java b/Mage/src/main/java/mage/target/targetadjustment/ConditionalTargetAdjuster.java index 7ba63269602..0a3f94ee8e8 100644 --- a/Mage/src/main/java/mage/target/targetadjustment/ConditionalTargetAdjuster.java +++ b/Mage/src/main/java/mage/target/targetadjustment/ConditionalTargetAdjuster.java @@ -4,48 +4,85 @@ import mage.abilities.Ability; import mage.abilities.condition.Condition; import mage.game.Game; import mage.target.Target; -import mage.target.Targets; /** * @author notgreat */ public class ConditionalTargetAdjuster implements TargetAdjuster { private final Condition condition; - private final boolean keepExistingTargets; - private final Targets replacementTargets; + private final boolean keepBlueprintTarget; + private final Target replacementTarget; + private Target blueprintTarget; /** - * If the condition is true, replace the target + * If the condition is true, replace the ability's target. + *
+ * Note that if the conditional target can be found when the original can not, + * you must use the two-target constructor and give the ability a separate general target + * that can encompass both targets * * @param condition The condition to be checked * @param replacementTarget The target to use if the condition is true. */ public ConditionalTargetAdjuster(Condition condition, Target replacementTarget) { - this(condition, false, replacementTarget); + this(condition, null, false, replacementTarget); } /** - * If the condition is true, change the target list with multiple targets at once + * If the condition is true, add another target to the ability * - * @param condition The condition to be checked - * @param keepExistingTargets if true, don't clear existing targets when adding the new ones - * @param replacementTargets Targets to be added if the condition is true + * @param condition The condition to be checked + * @param keepBlueprintTarget if true, don't remove the original target when adding the new one + * @param replacementTarget The target to use if the condition is true. */ - public ConditionalTargetAdjuster(Condition condition, boolean keepExistingTargets, Target... replacementTargets) { + public ConditionalTargetAdjuster(Condition condition, boolean keepBlueprintTarget, Target replacementTarget) { + this(condition, null, keepBlueprintTarget, replacementTarget); + } + + /** + * If the condition is false, use the blueprint. If the condition is true, use the replacement target. + * + * @param condition The condition to be checked + * @param blueprintTarget The blueprint/original target to use (set to null to use the ability's first target) + * @param replacementTarget The target to use if the condition is true. + */ + public ConditionalTargetAdjuster(Condition condition, Target blueprintTarget, Target replacementTarget) { + this(condition, blueprintTarget, false, replacementTarget); + } + + /** + * If the condition is false, use the blueprint. If the condition is true, add or use the replacement target. + * + * @param condition The condition to be checked + * @param blueprintTarget The blueprint/original target to use (set to null to use the ability's first target) + * @param keepBlueprintTarget if true, don't remove the original target when adding the new one + * @param replacementTarget Target to be added if the condition is true + */ + public ConditionalTargetAdjuster(Condition condition, Target blueprintTarget, boolean keepBlueprintTarget, Target replacementTarget) { this.condition = condition; - this.keepExistingTargets = keepExistingTargets; - this.replacementTargets = new Targets(replacementTargets); + this.keepBlueprintTarget = keepBlueprintTarget; + this.blueprintTarget = blueprintTarget; + this.replacementTarget = replacementTarget; + } + + @Override + public void addDefaultTargets(Ability ability) { + if (blueprintTarget == null && !ability.getTargets().isEmpty()) { + blueprintTarget = ability.getTargets().get(0).copy(); + } } @Override public void adjustTargets(Ability ability, Game game) { - if (condition.apply(game, ability)) { - if (!keepExistingTargets) { - ability.getTargets().clear(); - } - for (Target target : replacementTargets) { - ability.addTarget(target.copy()); + ability.getTargets().clear(); + boolean result = condition.apply(game, ability); + if (keepBlueprintTarget || !result) { + if (blueprintTarget != null) { + ability.addTarget(blueprintTarget.copy()); } } + if (result) { + ability.addTarget(replacementTarget.copy()); + } } } diff --git a/Mage/src/main/java/mage/target/targetadjustment/TargetAdjuster.java b/Mage/src/main/java/mage/target/targetadjustment/TargetAdjuster.java index cebcb8f6042..5b4020e1e70 100644 --- a/Mage/src/main/java/mage/target/targetadjustment/TargetAdjuster.java +++ b/Mage/src/main/java/mage/target/targetadjustment/TargetAdjuster.java @@ -12,6 +12,7 @@ import java.io.Serializable; public interface TargetAdjuster extends Serializable { // Warning: This is not Copyable, do not use changeable data inside (only use static objects like Filter) + // Note: in playability check for cards, targets are not adjusted. void adjustTargets(Ability ability, Game game); /**