forked from External/mage
Alternative solution to problem of unplayable cards from target adjustment (#12842)
* Alternative solution to problem of unplayable cards from target adjustment * Review fixes
This commit is contained in:
parent
d293200198
commit
f2ff4828b3
11 changed files with 117 additions and 34 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
+ "<i> (Those targets can include players and planeswalkers.)</i>"
|
||||
));
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -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. " +
|
||||
"<i>(Treat phased-out creatures and anything attached to them as though they don't exist until their controller's next turn.)</i>"
|
||||
));
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
* <p>
|
||||
* 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue