refactor: remove notTarget targets from abilities (#13651)

Adds OneShotNonTargetEffect and PutCountersTargetCost
Fixes the target timing of spells/abilities, as non-targets should be chosen on resolution.
This commit is contained in:
ssk97 2025-05-31 20:36:15 -07:00 committed by GitHub
parent 8b2a81cb42
commit 3223d99b2a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 449 additions and 384 deletions

View file

@ -1,23 +1,26 @@
package mage.cards.a; package mage.cards.a;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.SavedDamageValue; import mage.abilities.dynamicvalue.common.SavedDamageValue;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.PartnerWithAbility; import mage.abilities.effects.OneShotNonTargetEffect;
import mage.cards.*;
import mage.constants.*;
import mage.abilities.keyword.DoctorsCompanionAbility; import mage.abilities.keyword.DoctorsCompanionAbility;
import mage.abilities.keyword.PartnerWithAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.counters.CounterType; import mage.counters.CounterType;
import mage.filter.common.FilterSuspendedCard; import mage.filter.common.FilterSuspendedCard;
import mage.game.Game; import mage.game.Game;
import mage.players.Player; import mage.players.Player;
import mage.target.common.TargetCardInExile; import mage.target.common.TargetCardInExile;
import java.util.UUID;
/** /**
* *
* @author Skiwkr * @author Skiwkr
@ -41,10 +44,9 @@ public final class AmyPond extends CardImpl {
this.addAbility(new PartnerWithAbility("Rory Williams")); this.addAbility(new PartnerWithAbility("Rory Williams"));
// Whenever Amy Pond deals combat damage to a player, choose a suspended card you own and remove that many time counters from it. // Whenever Amy Pond deals combat damage to a player, choose a suspended card you own and remove that many time counters from it.
Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new AmyPondEffect(SavedDamageValue.MANY), this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new OneShotNonTargetEffect(
false, true); new AmyPondEffect(SavedDamageValue.MANY), new TargetCardInExile(filter)),
ability.addTarget(new TargetCardInExile(filter).withNotTarget(true)); false, true));
this.addAbility(ability);
// Doctor's companion // Doctor's companion
this.addAbility(DoctorsCompanionAbility.getInstance()); this.addAbility(DoctorsCompanionAbility.getInstance());

View file

@ -6,6 +6,7 @@ import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.condition.common.CompletedDungeonCondition; import mage.abilities.condition.common.CompletedDungeonCondition;
import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.decorator.ConditionalOneShotEffect;
import mage.abilities.effects.OneShotNonTargetEffect;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.abilities.effects.keyword.VentureIntoTheDungeonEffect; import mage.abilities.effects.keyword.VentureIntoTheDungeonEffect;
import mage.abilities.hint.common.CurrentDungeonHint; import mage.abilities.hint.common.CurrentDungeonHint;
@ -50,11 +51,11 @@ public final class BarrowinOfClanUndurr extends CardImpl {
// Whenever Barrowin of Clan Undurr attacks, return up to one creature card with mana value 3 or less from your graveyard to the battlefield if you've completed a dungeon. // Whenever Barrowin of Clan Undurr attacks, return up to one creature card with mana value 3 or less from your graveyard to the battlefield if you've completed a dungeon.
Ability ability = new AttacksTriggeredAbility(new ConditionalOneShotEffect( Ability ability = new AttacksTriggeredAbility(new ConditionalOneShotEffect(
new ReturnFromGraveyardToBattlefieldTargetEffect(), new OneShotNonTargetEffect(new ReturnFromGraveyardToBattlefieldTargetEffect()
CompletedDungeonCondition.instance, "return up to one creature card " + .setText("return up to one creature card with mana value 3 or less from your graveyard to the battlefield"),
"with mana value 3 or less from your graveyard to the battlefield if you've completed a dungeon" new TargetCardInYourGraveyard(0, 1, filter, true)),
)); CompletedDungeonCondition.instance
ability.addTarget(new TargetCardInYourGraveyard(0, 1, filter, true)); ).withConditionTextAtEnd(true));
this.addAbility(ability.addHint(CompletedDungeonCondition.getHint()), new CompletedDungeonWatcher()); this.addAbility(ability.addHint(CompletedDungeonCondition.getHint()), new CompletedDungeonWatcher());
} }

View file

@ -1,9 +1,9 @@
package mage.cards.b; package mage.cards.b;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility; import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.OneShotNonTargetEffect;
import mage.abilities.effects.common.ChooseNewTargetsTargetEffect; import mage.abilities.effects.common.ChooseNewTargetsTargetEffect;
import mage.abilities.keyword.DisguiseAbility; import mage.abilities.keyword.DisguiseAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
@ -32,10 +32,9 @@ public final class Boltbender extends CardImpl {
this.addAbility(new DisguiseAbility(this, new ManaCostsImpl<>("{1}{R}"))); this.addAbility(new DisguiseAbility(this, new ManaCostsImpl<>("{1}{R}")));
// When Boltbender is turned face up, you may choose new targets for any number of other spells and/or abilities. // When Boltbender is turned face up, you may choose new targets for any number of other spells and/or abilities.
Ability ability = new TurnedFaceUpSourceTriggeredAbility(new ChooseNewTargetsTargetEffect() this.addAbility(new TurnedFaceUpSourceTriggeredAbility(new OneShotNonTargetEffect(
.setText("you may choose new targets for any number of other spells and/or abilities")); new ChooseNewTargetsTargetEffect().setText("you may choose new targets for any number of other spells and/or abilities"),
ability.addTarget(new TargetStackObject(0, Integer.MAX_VALUE, StaticFilters.FILTER_SPELL_OR_ABILITY)); new TargetStackObject(0, Integer.MAX_VALUE, StaticFilters.FILTER_SPELL_OR_ABILITY))));
this.addAbility(ability);
} }
private Boltbender(final Boltbender card) { private Boltbender(final Boltbender card) {

View file

@ -1,17 +1,13 @@
package mage.cards.b; package mage.cards.b;
import mage.abilities.Ability;
import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.common.DiesCreatureTriggeredAbility;
import mage.abilities.effects.common.SacrificeEffect; import mage.abilities.effects.common.SacrificeEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SetTargetPointer;
import mage.filter.StaticFilters; import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.targetadjustment.TargetAdjuster;
import mage.target.targetpointer.FixedTarget;
import java.util.UUID; import java.util.UUID;
@ -24,11 +20,9 @@ public final class BurningSands extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{R}{R}"); super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{R}{R}");
// Whenever a creature dies, that creature's controller sacrifices a land. // Whenever a creature dies, that creature's controller sacrifices a land.
Ability ability = new DiesCreatureTriggeredAbility(new SacrificeEffect( this.addAbility(new DiesCreatureTriggeredAbility(
StaticFilters.FILTER_LAND, 1, "that creature's controller" new SacrificeEffect(StaticFilters.FILTER_LAND, 1, "that creature's controller"),
), false, false, true); SetTargetPointer.PLAYER));
ability.setTargetAdjuster(BurningSandsAdjuster.instance);
this.addAbility(ability);
} }
private BurningSands(final BurningSands card) { private BurningSands(final BurningSands card) {
@ -40,16 +34,3 @@ public final class BurningSands extends CardImpl {
return new BurningSands(this); return new BurningSands(this);
} }
} }
enum BurningSandsAdjuster implements TargetAdjuster {
instance;
@Override
public void adjustTargets(Ability ability, Game game) {
UUID creatureId = ability.getEffects().get(0).getTargetPointer().getFirst(game, ability);
Permanent creature = game.getPermanentOrLKIBattlefield(creatureId);
if (creature != null) {
ability.getEffects().get(0).setTargetPointer(new FixedTarget(creature.getControllerId()));
}
}
}

View file

@ -1,7 +1,6 @@
package mage.cards.c; package mage.cards.c;
import java.util.UUID;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
@ -18,32 +17,23 @@ import mage.target.Target;
import mage.target.common.TargetCreaturePermanent; import mage.target.common.TargetCreaturePermanent;
import mage.util.functions.EmptyCopyApplier; import mage.util.functions.EmptyCopyApplier;
import java.util.UUID;
/** /**
* *
* @author jeffwadsworth * @author jeffwadsworth
*/ */
public final class Cytoshape extends CardImpl { public final class Cytoshape extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonlegendary creature");
static {
filter.add(Predicates.not(SuperType.LEGENDARY.getPredicate()));
}
public Cytoshape(UUID ownerId, CardSetInfo setInfo) { public Cytoshape(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}{U}"); super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}{U}");
// Choose a nonlegendary creature on the battlefield. Target creature becomes a copy of that creature until end of turn. // Choose a nonlegendary creature on the battlefield. Target creature becomes a copy of that creature until end of turn.
this.getSpellAbility().addEffect(new CytoshapeEffect()); this.getSpellAbility().addEffect(new CytoshapeEffect());
Target target = new TargetCreaturePermanent(1, 1, filter, true);
target.setTargetTag(1);
this.getSpellAbility().addTarget(target);
FilterCreaturePermanent filter2 = new FilterCreaturePermanent("target creature that will become a copy of that chosen creature");
target = new TargetCreaturePermanent(filter2);
target.setTargetTag(2);
this.getSpellAbility().addTarget(target);
FilterCreaturePermanent filter = new FilterCreaturePermanent("target creature that will become a copy");
this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter));
} }
private Cytoshape(final Cytoshape card) { private Cytoshape(final Cytoshape card) {
@ -58,6 +48,11 @@ public final class Cytoshape extends CardImpl {
class CytoshapeEffect extends OneShotEffect { class CytoshapeEffect extends OneShotEffect {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonlegendary creature");
static {
filter.add(Predicates.not(SuperType.LEGENDARY.getPredicate()));
}
CytoshapeEffect() { CytoshapeEffect() {
super(Outcome.Copy); super(Outcome.Copy);
this.staticText = "Choose a nonlegendary creature on the battlefield. Target creature becomes a copy of that creature until end of turn."; this.staticText = "Choose a nonlegendary creature on the battlefield. Target creature becomes a copy of that creature until end of turn.";
@ -74,9 +69,11 @@ class CytoshapeEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability ability) { public boolean apply(Game game, Ability ability) {
Permanent copyFrom = game.getPermanent(getTargetPointer().getFirst(game, ability)); Target target = new TargetCreaturePermanent(1, 1, filter, true);
target.choose(Outcome.Copy, ability.getControllerId(), ability, game);
Permanent copyFrom = game.getPermanent(target.getFirstTarget());
if (copyFrom != null) { if (copyFrom != null) {
Permanent copyTo = game.getPermanentOrLKIBattlefield(ability.getTargets().get(1).getFirstTarget()); Permanent copyTo = game.getPermanentOrLKIBattlefield(ability.getTargets().get(0).getFirstTarget());
if (copyTo != null) { if (copyTo != null) {
game.copyPermanent(Duration.EndOfTurn, copyFrom, copyTo.getId(), ability, new EmptyCopyApplier()); game.copyPermanent(Duration.EndOfTurn, copyFrom, copyTo.getId(), ability, new EmptyCopyApplier());
} }

View file

@ -1,25 +1,18 @@
package mage.cards.e; package mage.cards.e;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.common.DiesCreatureTriggeredAbility;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.SacrificeEffect; import mage.abilities.effects.common.SacrificeEffect;
import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect; import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect;
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Outcome; import mage.constants.SetTargetPointer;
import mage.constants.Zone;
import mage.filter.StaticFilters; import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.permanent.Permanent; import java.util.UUID;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
/** /**
* *
@ -34,7 +27,9 @@ public final class Earthlink extends CardImpl {
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SacrificeSourceUnlessPaysEffect(new ManaCostsImpl<>("{2}")))); this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SacrificeSourceUnlessPaysEffect(new ManaCostsImpl<>("{2}"))));
// Whenever a creature dies, that creature's controller sacrifices a land. // Whenever a creature dies, that creature's controller sacrifices a land.
this.addAbility(new DiesCreatureTriggeredAbility(new EarthlinkEffect(), false, false, true)); this.addAbility(new DiesCreatureTriggeredAbility(
new SacrificeEffect(StaticFilters.FILTER_LAND, 1, "that creature's controller"),
SetTargetPointer.PLAYER));
} }
private Earthlink(final Earthlink card) { private Earthlink(final Earthlink card) {
@ -46,34 +41,3 @@ public final class Earthlink extends CardImpl {
return new Earthlink(this); return new Earthlink(this);
} }
} }
class EarthlinkEffect extends OneShotEffect {
EarthlinkEffect() {
super(Outcome.DrawCard);
this.staticText = "that creature's controller sacrifices a land";
}
private EarthlinkEffect(final EarthlinkEffect effect) {
super(effect);
}
@Override
public EarthlinkEffect copy() {
return new EarthlinkEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = (Permanent) game.getLastKnownInformation(this.getTargetPointer().getFirst(game, source), Zone.BATTLEFIELD);
if (permanent != null) {
Player controller = game.getPlayer(permanent.getControllerId());
if (controller != null) {
Effect effect = new SacrificeEffect(StaticFilters.FILTER_LAND, 1, "that creature's controller");
effect.setTargetPointer(new FixedTarget(controller.getId(), game));
effect.apply(game, source);
}
}
return false;
}
}

View file

@ -3,24 +3,16 @@ package mage.cards.h;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.Cost; import mage.abilities.costs.common.PutCountersTargetCost;
import mage.abilities.costs.CostImpl;
import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.Zone;
import mage.counters.CounterType; import mage.counters.CounterType;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.Target;
import mage.target.common.TargetAnyTarget; import mage.target.common.TargetAnyTarget;
import mage.target.common.TargetControlledCreaturePermanent;
import java.util.UUID; import java.util.UUID;
@ -38,15 +30,11 @@ public final class HatchetBully extends CardImpl {
this.toughness = new MageInt(3); this.toughness = new MageInt(3);
// {2}{R}, {tap}, Put a -1/-1 counter on a creature you control: Hatchet Bully deals 2 damage to any target. // {2}{R}, {tap}, Put a -1/-1 counter on a creature you control: Hatchet Bully deals 2 damage to any target.
Ability ability = new SimpleActivatedAbility(new HatchetBullyEffect(), new ManaCostsImpl<>("{2}{R}")); Ability ability = new SimpleActivatedAbility(new DamageTargetEffect(2), new ManaCostsImpl<>("{2}{R}"));
ability.addCost(new TapSourceCost()); ability.addCost(new TapSourceCost());
ability.addCost(new HatchetBullyCost()); ability.addCost(new PutCountersTargetCost(CounterType.M1M1.createInstance()));
ability.addTarget(new TargetAnyTarget()); ability.addTarget(new TargetAnyTarget());
Target target = new TargetControlledCreaturePermanent();
target.withNotTarget(true);
ability.addTarget(target);
this.addAbility(ability); this.addAbility(ability);
} }
private HatchetBully(final HatchetBully card) { private HatchetBully(final HatchetBully card) {
@ -58,64 +46,3 @@ public final class HatchetBully extends CardImpl {
return new HatchetBully(this); return new HatchetBully(this);
} }
} }
class HatchetBullyCost extends CostImpl {
public HatchetBullyCost() {
this.text = "Put a -1/-1 counter on a creature you control";
}
private HatchetBullyCost(final HatchetBullyCost cost) {
super(cost);
}
@Override
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
return true;
}
@Override
public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) {
Permanent permanent = game.getPermanent(ability.getTargets().get(1).getFirstTarget());
if (permanent != null) {
permanent.addCounters(CounterType.M1M1.createInstance(), controllerId, ability, game);
this.paid = true;
}
return paid;
}
@Override
public HatchetBullyCost copy() {
return new HatchetBullyCost(this);
}
}
class HatchetBullyEffect extends OneShotEffect {
HatchetBullyEffect() {
super(Outcome.Damage);
staticText = "{this} deals 2 damage to any target";
}
private HatchetBullyEffect(final HatchetBullyEffect effect) {
super(effect);
}
@Override
public HatchetBullyEffect copy() {
return new HatchetBullyEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getFirstTarget());
if (permanent != null) {
permanent.damage(2, source.getSourceId(), source, game, false, true);
}
Player player = game.getPlayer(source.getFirstTarget());
if (player != null) {
player.damage(2, source.getSourceId(), source, game);
}
return true;
}
}

View file

@ -1,9 +1,6 @@
package mage.cards.k; package mage.cards.k;
import java.util.UUID; import mage.MageObjectReference;
import mage.constants.SubType;
import mage.game.events.DamageEvent;
import mage.target.common.TargetCreaturePermanent;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SimpleStaticAbility;
@ -12,18 +9,22 @@ import mage.abilities.costs.CostImpl;
import mage.abilities.effects.PreventionEffectImpl; import mage.abilities.effects.PreventionEffectImpl;
import mage.abilities.effects.RestrictionEffect; import mage.abilities.effects.RestrictionEffect;
import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.AttachEffect;
import mage.constants.Outcome;
import mage.target.TargetPermanent;
import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.EnchantAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.Zone; import mage.constants.Outcome;
import mage.constants.SubType;
import mage.game.Game; import mage.game.Game;
import mage.game.events.DamageEvent;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.target.TargetPermanent;
import mage.target.TargetSource; import mage.target.TargetSource;
import mage.target.common.TargetCreaturePermanent;
import java.util.UUID;
/** /**
* *
@ -40,8 +41,7 @@ public final class KithkinArmor extends CardImpl {
TargetPermanent auraTarget = new TargetCreaturePermanent(); TargetPermanent auraTarget = new TargetCreaturePermanent();
this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addTarget(auraTarget);
this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature));
Ability ability = new EnchantAbility(auraTarget); this.addAbility(new EnchantAbility(auraTarget));
this.addAbility(ability);
// Enchanted creature can't be blocked by creatures with power 3 or greater. // Enchanted creature can't be blocked by creatures with power 3 or greater.
this.addAbility(new SimpleStaticAbility(new KithkinArmorRestrictionEffect())); this.addAbility(new SimpleStaticAbility(new KithkinArmorRestrictionEffect()));
@ -50,7 +50,6 @@ public final class KithkinArmor extends CardImpl {
Ability protectionAbility = new SimpleActivatedAbility( Ability protectionAbility = new SimpleActivatedAbility(
new KithkinArmorPreventionEffect(), new KithkinArmorPreventionEffect(),
new KithkinArmorCost()); new KithkinArmorCost());
protectionAbility.addTarget(new TargetSource());
this.addAbility(protectionAbility); this.addAbility(protectionAbility);
} }
@ -137,6 +136,7 @@ class KithkinArmorRestrictionEffect extends RestrictionEffect {
class KithkinArmorPreventionEffect extends PreventionEffectImpl { class KithkinArmorPreventionEffect extends PreventionEffectImpl {
private MageObjectReference mageObjectReference;
KithkinArmorPreventionEffect() { KithkinArmorPreventionEffect() {
super(Duration.EndOfTurn, Integer.MAX_VALUE, false); super(Duration.EndOfTurn, Integer.MAX_VALUE, false);
staticText = "The next time a source of your choice would deal damage to enchanted creature this turn, prevent that damage"; staticText = "The next time a source of your choice would deal damage to enchanted creature this turn, prevent that damage";
@ -144,13 +144,20 @@ class KithkinArmorPreventionEffect extends PreventionEffectImpl {
private KithkinArmorPreventionEffect(final KithkinArmorPreventionEffect effect) { private KithkinArmorPreventionEffect(final KithkinArmorPreventionEffect effect) {
super(effect); super(effect);
mageObjectReference = effect.mageObjectReference;
} }
@Override @Override
public KithkinArmorPreventionEffect copy() { public KithkinArmorPreventionEffect copy() {
return new KithkinArmorPreventionEffect(this); return new KithkinArmorPreventionEffect(this);
} }
@Override
public void init(Ability source, Game game) {
super.init(source, game);
TargetSource target = new TargetSource();
target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game);
mageObjectReference = new MageObjectReference(target.getFirstTarget(), game);
}
@Override @Override
public boolean applies(GameEvent event, Ability source, Game game) { public boolean applies(GameEvent event, Ability source, Game game) {
if (super.applies(event, source, game) if (super.applies(event, source, game)

View file

@ -1,33 +1,32 @@
package mage.cards.m; package mage.cards.m;
import java.util.Objects; import mage.abilities.common.PutIntoGraveFromBattlefieldAllTriggeredAbility;
import java.util.UUID; import mage.abilities.effects.common.DamageTargetControllerEffect;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.DamageTargetEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Zone; import mage.filter.FilterPermanent;
import mage.game.Game; import mage.filter.common.FilterArtifactPermanent;
import mage.game.events.GameEvent; import mage.filter.predicate.mageobject.AnotherPredicate;
import mage.game.events.ZoneChangeEvent;
import mage.target.TargetPlayer; import java.util.UUID;
/** /**
* *
* @author North * @author North
*/ */
public final class MagneticMine extends CardImpl { public final class MagneticMine extends CardImpl {
private static final FilterPermanent filter = new FilterArtifactPermanent("another artifact");
static {
filter.add(AnotherPredicate.instance);
}
public MagneticMine(UUID ownerId, CardSetInfo setInfo) { public MagneticMine(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}"); super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}");
// Whenever another artifact is put into a graveyard from the battlefield, Magnetic Mine deals 2 damage to that artifacts controller. // Whenever another artifact is put into a graveyard from the battlefield, Magnetic Mine deals 2 damage to that artifacts controller.
MagneticMineTriggeredAbility ability = new MagneticMineTriggeredAbility(new DamageTargetEffect(2)); this.addAbility(new PutIntoGraveFromBattlefieldAllTriggeredAbility(
ability.addTarget(new TargetPlayer().withNotTarget(true)); new DamageTargetControllerEffect(2, "artifact"), false, filter, true));
this.addAbility(ability);
} }
private MagneticMine(final MagneticMine card) { private MagneticMine(final MagneticMine card) {
@ -39,41 +38,3 @@ public final class MagneticMine extends CardImpl {
return new MagneticMine(this); return new MagneticMine(this);
} }
} }
class MagneticMineTriggeredAbility extends TriggeredAbilityImpl {
public MagneticMineTriggeredAbility(Effect effect) {
super(Zone.BATTLEFIELD, effect, false);
}
private MagneticMineTriggeredAbility(final MagneticMineTriggeredAbility ability) {
super(ability);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
if (zEvent.isDiesEvent()
&& zEvent.getTarget().isArtifact(game)
&& !Objects.equals(zEvent.getTarget().getId(), this.getSourceId())) {
this.getTargets().get(0).add(zEvent.getTarget().getControllerId(), game);
return true;
}
return false;
}
@Override
public String getRule() {
return "Whenever another artifact is put into a graveyard from the battlefield, {this} deals 2 damage to that artifact's controller";
}
@Override
public MagneticMineTriggeredAbility copy() {
return new MagneticMineTriggeredAbility(this);
}
}

View file

@ -3,23 +3,20 @@ package mage.cards.s;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.Mode; import mage.abilities.Mode;
import mage.abilities.condition.common.ControlACommanderCondition; import mage.abilities.condition.common.ControlACommanderCondition;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotNonTargetEffect;
import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.abilities.triggers.BeginningOfCombatTriggeredAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType; import mage.constants.SubType;
import mage.counters.CounterType; import mage.counters.CounterType;
import mage.filter.FilterPermanent; import mage.filter.FilterPermanent;
import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterControlledPermanent;
import mage.game.Game;
import mage.game.permanent.token.SoldierToken; import mage.game.permanent.token.SoldierToken;
import mage.players.Player;
import mage.target.TargetPermanent; import mage.target.TargetPermanent;
import java.util.Optional;
import java.util.UUID; import java.util.UUID;
/** /**
@ -37,9 +34,15 @@ public final class SOLDIERMilitaryProgram extends CardImpl {
Ability ability = new BeginningOfCombatTriggeredAbility(new CreateTokenEffect(new SoldierToken())); Ability ability = new BeginningOfCombatTriggeredAbility(new CreateTokenEffect(new SoldierToken()));
ability.getModes().setChooseText("choose one. If you control a commander, you may choose both instead."); ability.getModes().setChooseText("choose one. If you control a commander, you may choose both instead.");
ability.getModes().setMoreCondition(2, ControlACommanderCondition.instance); ability.getModes().setMoreCondition(2, ControlACommanderCondition.instance);
ability.getModes().setChooseText(
"choose one. If you control a commander, you may choose both instead."
);
// * Put a +1/+1 counter on each of up to two Soldiers you control. // * Put a +1/+1 counter on each of up to two Soldiers you control.
ability.addMode(new Mode(new SOLDIERMilitaryProgramEffect())); ability.addMode(new Mode(new OneShotNonTargetEffect(
new AddCountersTargetEffect(CounterType.P1P1.createInstance())
.setText("Put a +1/+1 counter on each of up to two Soldiers you control"),
new TargetPermanent(0, 2, filter))));
this.addAbility(ability); this.addAbility(ability);
} }
@ -52,38 +55,3 @@ public final class SOLDIERMilitaryProgram extends CardImpl {
return new SOLDIERMilitaryProgram(this); return new SOLDIERMilitaryProgram(this);
} }
} }
class SOLDIERMilitaryProgramEffect extends OneShotEffect {
private static final FilterPermanent filter = new FilterControlledPermanent(SubType.SOLDIER, "Soldiers you control");
SOLDIERMilitaryProgramEffect() {
super(Outcome.Benefit);
staticText = "put a +1/+1 counter on each of up to two Soldiers you control";
}
private SOLDIERMilitaryProgramEffect(final SOLDIERMilitaryProgramEffect effect) {
super(effect);
}
@Override
public SOLDIERMilitaryProgramEffect copy() {
return new SOLDIERMilitaryProgramEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player == null) {
return false;
}
TargetPermanent target = new TargetPermanent(0, 2, filter, true);
player.choose(Outcome.BoostCreature, target, source, game);
for (UUID targetId : target.getTargets()) {
Optional.ofNullable(targetId)
.map(game::getPermanent)
.map(permanent -> permanent.addCounters(CounterType.P1P1.createInstance(), source, game));
}
return true;
}
}

View file

@ -1,17 +1,19 @@
package mage.cards.s; package mage.cards.s;
import mage.MageObject; import mage.MageObject;
import mage.MageObjectReference;
import mage.ObjectColor; import mage.ObjectColor;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.AlternativeCostSourceAbility;
import mage.abilities.costs.common.ExileFromHandCost; import mage.abilities.costs.common.ExileFromHandCost;
import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.ExileFromHandCostCardConvertedMana; import mage.abilities.dynamicvalue.common.ExileFromHandCostCardConvertedMana;
import mage.abilities.effects.common.RedirectDamageFromSourceToTargetEffect; import mage.abilities.effects.RedirectionEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.SubType; import mage.constants.SubType;
import mage.filter.common.FilterOwnedCard; import mage.filter.common.FilterOwnedCard;
import mage.filter.predicate.mageobject.ColorPredicate; import mage.filter.predicate.mageobject.ColorPredicate;
@ -50,7 +52,6 @@ public final class ShiningShoal extends CardImpl {
this.getSpellAbility().addEffect(new ShiningShoalRedirectDamageTargetEffect( this.getSpellAbility().addEffect(new ShiningShoalRedirectDamageTargetEffect(
Duration.EndOfTurn, ExileFromHandCostCardConvertedMana.instance Duration.EndOfTurn, ExileFromHandCostCardConvertedMana.instance
)); ));
this.getSpellAbility().addTarget(new TargetSource());
this.getSpellAbility().addTarget(new TargetAnyTarget()); this.getSpellAbility().addTarget(new TargetAnyTarget());
} }
@ -64,9 +65,10 @@ public final class ShiningShoal extends CardImpl {
} }
} }
class ShiningShoalRedirectDamageTargetEffect extends RedirectDamageFromSourceToTargetEffect { class ShiningShoalRedirectDamageTargetEffect extends RedirectionEffect {
private final DynamicValue dynamicAmount; private final DynamicValue dynamicAmount;
private MageObjectReference mageObjectReference;
public ShiningShoalRedirectDamageTargetEffect(Duration duration, DynamicValue dynamicAmount) { public ShiningShoalRedirectDamageTargetEffect(Duration duration, DynamicValue dynamicAmount) {
super(duration, 0, UsageType.ONE_USAGE_AT_THE_SAME_TIME); super(duration, 0, UsageType.ONE_USAGE_AT_THE_SAME_TIME);
@ -77,6 +79,7 @@ class ShiningShoalRedirectDamageTargetEffect extends RedirectDamageFromSourceToT
private ShiningShoalRedirectDamageTargetEffect(final ShiningShoalRedirectDamageTargetEffect effect) { private ShiningShoalRedirectDamageTargetEffect(final ShiningShoalRedirectDamageTargetEffect effect) {
super(effect); super(effect);
this.dynamicAmount = effect.dynamicAmount; this.dynamicAmount = effect.dynamicAmount;
this.mageObjectReference = effect.mageObjectReference;
} }
@Override @Override
@ -88,6 +91,9 @@ class ShiningShoalRedirectDamageTargetEffect extends RedirectDamageFromSourceToT
public void init(Ability source, Game game) { public void init(Ability source, Game game) {
super.init(source, game); super.init(source, game);
amountToRedirect = dynamicAmount.calculate(game, source, this); amountToRedirect = dynamicAmount.calculate(game, source, this);
TargetSource target = new TargetSource();
target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), source, game);
mageObjectReference = new MageObjectReference(target.getFirstTarget(), game);
} }
@Override @Override
@ -96,15 +102,13 @@ class ShiningShoalRedirectDamageTargetEffect extends RedirectDamageFromSourceToT
// get source of the damage event // get source of the damage event
MageObject sourceObject = game.getObject(event.getSourceId()); MageObject sourceObject = game.getObject(event.getSourceId());
// get the chosen damage source
MageObject chosenSourceObject = game.getObject(source.getFirstTarget());
// does the source of the damage exist? // does the source of the damage exist?
if (sourceObject == null) { if (sourceObject == null) {
game.informPlayers("Couldn't find source of damage"); game.informPlayers("Couldn't find source of damage");
return false; return false;
} }
// do the 2 objects match? // do the 2 objects match?
if (chosenSourceObject == null || !sourceObject.getId().equals(chosenSourceObject.getId())) { if (!mageObjectReference.refersTo(sourceObject, game)) {
return false; return false;
} }
@ -114,7 +118,7 @@ class ShiningShoalRedirectDamageTargetEffect extends RedirectDamageFromSourceToT
if (permanent != null && permanent.isCreature(game)) { if (permanent != null && permanent.isCreature(game)) {
if (permanent.isControlledBy(source.getControllerId())) { if (permanent.isControlledBy(source.getControllerId())) {
// it's your creature // it's your creature
redirectTarget = source.getTargets().get(1); redirectTarget = source.getTargets().get(0);
return true; return true;
} }
} }
@ -123,7 +127,7 @@ class ShiningShoalRedirectDamageTargetEffect extends RedirectDamageFromSourceToT
if (player != null) { if (player != null) {
if (player.getId().equals(source.getControllerId())) { if (player.getId().equals(source.getControllerId())) {
// it is you // it is you
redirectTarget = source.getTargets().get(1); redirectTarget = source.getTargets().get(0);
return true; return true;
} }
} }

View file

@ -3,7 +3,6 @@ package mage.cards.s;
import mage.Mana; import mage.Mana;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.effects.common.ChoosePlayerEffect;
import mage.abilities.effects.mana.ManaEffect; import mage.abilities.effects.mana.ManaEffect;
import mage.abilities.mana.SimpleManaAbility; import mage.abilities.mana.SimpleManaAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
@ -14,6 +13,7 @@ import mage.constants.Outcome;
import mage.constants.Zone; import mage.constants.Zone;
import mage.game.Game; import mage.game.Game;
import mage.players.Player; import mage.players.Player;
import mage.target.TargetPlayer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -30,8 +30,6 @@ public final class SpectralSearchlight extends CardImpl {
// {T}: Choose a player. That player adds one mana of any color they chooses. // {T}: Choose a player. That player adds one mana of any color they chooses.
ManaEffect effect = new SpectralSearchlightManaEffect(); ManaEffect effect = new SpectralSearchlightManaEffect();
Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, effect, new TapSourceCost());
// choosing player as first effect, before adding mana effect
ability.getEffects().add(0, new ChoosePlayerEffect(Outcome.PutManaInPool));
this.addAbility(ability); this.addAbility(ability);
} }
@ -49,7 +47,7 @@ class SpectralSearchlightManaEffect extends ManaEffect {
SpectralSearchlightManaEffect() { SpectralSearchlightManaEffect() {
super(); super();
this.staticText = "That player adds one mana of any color they choose"; this.staticText = "Choose a player. That player adds one mana of any color they choose";
} }
private SpectralSearchlightManaEffect(final SpectralSearchlightManaEffect effect) { private SpectralSearchlightManaEffect(final SpectralSearchlightManaEffect effect) {
@ -58,7 +56,13 @@ class SpectralSearchlightManaEffect extends ManaEffect {
@Override @Override
public Player getPlayer(Game game, Ability source) { public Player getPlayer(Game game, Ability source) {
return game.getPlayer((UUID) game.getState().getValue(source.getSourceId() + "_player")); if (!game.inCheckPlayableState()) {
TargetPlayer target = new TargetPlayer(1, 1, true);
if (target.choose(Outcome.PutManaInPool, source.getControllerId(), source, game)) {
return game.getPlayer(target.getFirstTarget());
}
}
return game.getPlayer(source.getControllerId()); // Count as controller's potential mana for card playability
} }
@Override @Override

View file

@ -1,6 +1,7 @@
package mage.cards.t; package mage.cards.t;
import mage.abilities.dynamicvalue.common.MultikickerCount; import mage.abilities.dynamicvalue.common.MultikickerCount;
import mage.abilities.effects.OneShotNonTargetEffect;
import mage.abilities.effects.common.DamageAllEffect; import mage.abilities.effects.common.DamageAllEffect;
import mage.abilities.effects.common.PhaseOutTargetEffect; import mage.abilities.effects.common.PhaseOutTargetEffect;
import mage.abilities.keyword.KickerAbility; import mage.abilities.keyword.KickerAbility;
@ -35,10 +36,10 @@ public final class TemporalFirestorm extends CardImpl {
this.addAbility(kickerAbility); this.addAbility(kickerAbility);
// Choose up to X creatures and/or planeswalkers you control, where X is the number of times this spell was kicked. Those permanents phase out. // Choose up to X creatures and/or planeswalkers you control, where X is the number of times this spell was kicked. Those permanents phase out.
this.getSpellAbility().addEffect(new PhaseOutTargetEffect().setText("choose up to X creatures and/or " + this.getSpellAbility().addEffect(new OneShotNonTargetEffect(new PhaseOutTargetEffect().setText("choose up to X creatures and/or " +
"planeswalkers you control, where X is the number of times this spell was kicked. Those permanents phase out")); "planeswalkers you control, where X is the number of times this spell was kicked. Those permanents phase out"),
this.getSpellAbility().addTarget(new TargetPermanent(0, 1, filter, true)); new TargetPermanent(0, 1, filter, true), new TargetsCountAdjuster(MultikickerCount.instance)
this.getSpellAbility().setTargetAdjuster(new TargetsCountAdjuster(MultikickerCount.instance)); ));
// Temporal Firestorm deals 5 damage to each creature and each planeswalker. // Temporal Firestorm deals 5 damage to each creature and each planeswalker.
this.getSpellAbility().addEffect(new DamageAllEffect( this.getSpellAbility().addEffect(new DamageAllEffect(

View file

@ -1,26 +1,29 @@
package mage.cards.v; package mage.cards.v;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.Mana; import mage.Mana;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.common.SacrificeTargetCost;
import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.effects.mana.AddManaToManaPoolTargetControllerEffect;
import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.DamageTargetEffect;
import mage.abilities.effects.mana.ManaEffect;
import mage.abilities.mana.SimpleManaAbility; import mage.abilities.mana.SimpleManaAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterControlledPermanent;
import mage.game.Game;
import mage.players.Player;
import mage.target.TargetPlayer; import mage.target.TargetPlayer;
import mage.target.common.TargetControlledPermanent;
import mage.target.common.TargetCreaturePermanent; import mage.target.common.TargetCreaturePermanent;
import java.util.UUID;
/** /**
* *
* @author jeffwadsworth * @author jeffwadsworth
@ -48,10 +51,9 @@ public final class Valleymaker extends CardImpl {
this.addAbility(ability); this.addAbility(ability);
// {tap}, Sacrifice a Forest: Choose a player. That player adds {G}{G}{G}. // {tap}, Sacrifice a Forest: Choose a player. That player adds {G}{G}{G}.
Ability ability2 = new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaToManaPoolTargetControllerEffect(Mana.GreenMana(3), "chosen player") Ability ability2 = new SimpleManaAbility(Zone.BATTLEFIELD, new ValleymakerManaEffect()
.setText("choose a player. That player adds {G}{G}{G}"), new TapSourceCost()); .setText("That player adds {G}{G}{G}"), new TapSourceCost());
ability2.addCost(new SacrificeTargetCost(filter2)); ability2.addCost(new SacrificeTargetCost(filter2));
ability2.addTarget(new TargetPlayer(1, 1, true));
this.addAbility(ability2); this.addAbility(ability2);
} }
@ -64,3 +66,37 @@ public final class Valleymaker extends CardImpl {
return new Valleymaker(this); return new Valleymaker(this);
} }
} }
//Based on Spectral Searchlight
class ValleymakerManaEffect extends ManaEffect {
ValleymakerManaEffect() {
super();
this.staticText = "Choose a player. That player adds {G}{G}{G}.";
}
private ValleymakerManaEffect(final ValleymakerManaEffect effect) {
super(effect);
}
@Override
public Player getPlayer(Game game, Ability source) {
if (!game.inCheckPlayableState()) {
TargetPlayer target = new TargetPlayer(1, 1, true);
if (target.choose(Outcome.PutManaInPool, source.getControllerId(), source, game)) {
return game.getPlayer(target.getFirstTarget());
}
}
return game.getPlayer(source.getControllerId()); // Count as controller's potential mana for card playability
}
@Override
public Mana produceMana(Game game, Ability source) {
return Mana.GreenMana(3);
}
@Override
public ValleymakerManaEffect copy() {
return new ValleymakerManaEffect(this);
}
}

View file

@ -4,9 +4,8 @@ import mage.Mana;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.effects.common.ChoosePlayerEffect;
import mage.abilities.effects.mana.ManaEffect;
import mage.abilities.effects.common.continuous.UntapSourceDuringEachOtherPlayersUntapStepEffect; import mage.abilities.effects.common.continuous.UntapSourceDuringEachOtherPlayersUntapStepEffect;
import mage.abilities.effects.mana.ManaEffect;
import mage.abilities.mana.SimpleManaAbility; import mage.abilities.mana.SimpleManaAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
@ -15,6 +14,7 @@ import mage.constants.Outcome;
import mage.constants.Zone; import mage.constants.Zone;
import mage.game.Game; import mage.game.Game;
import mage.players.Player; import mage.players.Player;
import mage.target.TargetPlayer;
import java.util.UUID; import java.util.UUID;
@ -33,8 +33,6 @@ public final class VictoryChimes extends CardImpl {
ManaEffect effect = new VictoryChimesManaEffect("chosen player"); ManaEffect effect = new VictoryChimesManaEffect("chosen player");
effect.setText("a player of your choice adds {C}"); effect.setText("a player of your choice adds {C}");
Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, effect, new TapSourceCost());
// choosing player as first effect, before adding mana effect
ability.getEffects().add(0, new ChoosePlayerEffect(Outcome.PutManaInPool).setText(""));
this.addAbility(ability); this.addAbility(ability);
} }
@ -61,7 +59,13 @@ class VictoryChimesManaEffect extends ManaEffect {
@Override @Override
public Player getPlayer(Game game, Ability source) { public Player getPlayer(Game game, Ability source) {
return game.getPlayer((UUID) game.getState().getValue(source.getSourceId() + "_player")); if (!game.inCheckPlayableState()) {
TargetPlayer target = new TargetPlayer(1, 1, true);
if (target.choose(Outcome.PutManaInPool, source.getControllerId(), source, game)) {
return game.getPlayer(target.getFirstTarget());
}
}
return game.getPlayer(source.getControllerId()); // Count as controller's potential mana for card playability
} }
@Override @Override

View file

@ -1,13 +1,11 @@
package mage.cards.w; package mage.cards.w;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.Cost;
import mage.abilities.costs.CostImpl;
import mage.abilities.costs.common.PayLifeCost; import mage.abilities.costs.common.PayLifeCost;
import mage.abilities.costs.common.PutCountersTargetCost;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.PreventDamageToTargetEffect; import mage.abilities.effects.common.PreventDamageToTargetEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
@ -15,17 +13,14 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.Zone;
import mage.counters.CounterType; import mage.counters.CounterType;
import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.Predicates; import mage.filter.predicate.Predicates;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.Target;
import mage.target.common.TargetControlledCreaturePermanent;
import mage.target.common.TargetCreaturePermanent; import mage.target.common.TargetCreaturePermanent;
import mage.target.common.TargetPlayerOrPlaneswalker; import mage.target.common.TargetPlayerOrPlaneswalker;
import java.util.UUID;
/** /**
* *
* @author fireshoes * @author fireshoes
@ -64,11 +59,8 @@ public final class WanderingMage extends CardImpl {
// {B}, Put a -1/-1 counter on a creature you control: Prevent the next 2 damage that would be dealt to target player this turn. // {B}, Put a -1/-1 counter on a creature you control: Prevent the next 2 damage that would be dealt to target player this turn.
ability = new SimpleActivatedAbility( ability = new SimpleActivatedAbility(
new PreventDamageToTargetEffect(Duration.EndOfTurn, 2), new ManaCostsImpl<>("{B}")); new PreventDamageToTargetEffect(Duration.EndOfTurn, 2), new ManaCostsImpl<>("{B}"));
ability.addCost(new WanderingMageCost()); ability.addCost(new PutCountersTargetCost(CounterType.M1M1.createInstance()));
ability.addTarget(new TargetPlayerOrPlaneswalker()); ability.addTarget(new TargetPlayerOrPlaneswalker());
Target target = new TargetControlledCreaturePermanent();
target.withNotTarget(true);
ability.addTarget(target);
this.addAbility(ability); this.addAbility(ability);
} }
@ -81,34 +73,3 @@ public final class WanderingMage extends CardImpl {
return new WanderingMage(this); return new WanderingMage(this);
} }
} }
class WanderingMageCost extends CostImpl {
public WanderingMageCost() {
this.text = "Put a -1/-1 counter on a creature you control";
}
private WanderingMageCost(final WanderingMageCost cost) {
super(cost);
}
@Override
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
return true;
}
@Override
public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) {
Permanent permanent = game.getPermanent(ability.getTargets().get(1).getFirstTarget());
if (permanent != null) {
permanent.addCounters(CounterType.M1M1.createInstance(), controllerId, ability, game);
this.paid = true;
}
return paid;
}
@Override
public WanderingMageCost copy() {
return new WanderingMageCost(this);
}
}

View file

@ -1,11 +1,11 @@
package mage.cards.w; package mage.cards.w;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.LeavesBattlefieldTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
import mage.abilities.effects.OneShotNonTargetEffect;
import mage.abilities.effects.common.ExileTargetForSourceEffect; import mage.abilities.effects.common.ExileTargetForSourceEffect;
import mage.abilities.effects.common.ReturnFromExileForSourceEffect; import mage.abilities.effects.common.ReturnFromExileForSourceEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
@ -14,11 +14,10 @@ import mage.constants.CardType;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.StaticFilters; import mage.filter.StaticFilters;
import mage.filter.common.FilterControlledLandPermanent;
import mage.filter.predicate.mageobject.AnotherPredicate;
import mage.target.Target;
import mage.target.TargetPermanent; import mage.target.TargetPermanent;
import java.util.UUID;
/** /**
* @author tcontis * @author tcontis
*/ */
@ -34,10 +33,9 @@ public final class WormfangNewt extends CardImpl {
this.toughness = new MageInt(2); this.toughness = new MageInt(2);
// When Wormfang Turtle enters the battlefield, exile a land you control. // When Wormfang Turtle enters the battlefield, exile a land you control.
Ability ability1 = new EntersBattlefieldTriggeredAbility(new ExileTargetForSourceEffect(), false); this.addAbility(new EntersBattlefieldTriggeredAbility(new OneShotNonTargetEffect(
Target target = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_A_LAND).withNotTarget(true); new ExileTargetForSourceEffect().setText("exile a land you control"),
ability1.addTarget(target); new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_A_LAND))));
this.addAbility(ability1);
// When Wormfang Turtle leaves the battlefield, return the exiled card to the battlefield under its owner's control. // When Wormfang Turtle leaves the battlefield, return the exiled card to the battlefield under its owner's control.
Ability ability2 = new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD), false); Ability ability2 = new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD), false);

View file

@ -1,11 +1,11 @@
package mage.cards.w; package mage.cards.w;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.LeavesBattlefieldTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
import mage.abilities.effects.OneShotNonTargetEffect;
import mage.abilities.effects.common.ExileTargetForSourceEffect; import mage.abilities.effects.common.ExileTargetForSourceEffect;
import mage.abilities.effects.common.ReturnFromExileForSourceEffect; import mage.abilities.effects.common.ReturnFromExileForSourceEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
@ -14,11 +14,10 @@ import mage.constants.CardType;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.StaticFilters; import mage.filter.StaticFilters;
import mage.filter.common.FilterControlledLandPermanent;
import mage.filter.predicate.mageobject.AnotherPredicate;
import mage.target.Target;
import mage.target.TargetPermanent; import mage.target.TargetPermanent;
import java.util.UUID;
/** /**
* @author tcontis * @author tcontis
*/ */
@ -34,10 +33,10 @@ public final class WormfangTurtle extends CardImpl {
this.toughness = new MageInt(4); this.toughness = new MageInt(4);
// When Wormfang Turtle enters the battlefield, exile a land you control. // When Wormfang Turtle enters the battlefield, exile a land you control.
Ability ability1 = new EntersBattlefieldTriggeredAbility(new ExileTargetForSourceEffect(), false); this.addAbility(new EntersBattlefieldTriggeredAbility(new OneShotNonTargetEffect(
Target target = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_A_LAND).withNotTarget(true); new ExileTargetForSourceEffect().setText("exile a land you control"),
ability1.addTarget(target); new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_A_LAND))));
this.addAbility(ability1);
// When Wormfang Turtle leaves the battlefield, return the exiled card to the battlefield under its owner's control. // When Wormfang Turtle leaves the battlefield, return the exiled card to the battlefield under its owner's control.
Ability ability2 = new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD), false); Ability ability2 = new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD), false);

View file

@ -1,8 +1,8 @@
package mage.cards.y; package mage.cards.y;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.OneShotNonTargetEffect;
import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
@ -26,9 +26,9 @@ public final class YaroksWavecrasher extends CardImpl {
this.toughness = new MageInt(4); this.toughness = new MageInt(4);
// When Yaroks Wavecrasher enters the battlefield, return another creature you control to its owners hand. // When Yaroks Wavecrasher enters the battlefield, return another creature you control to its owners hand.
Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect(), false); this.addAbility(new EntersBattlefieldTriggeredAbility(new OneShotNonTargetEffect(
ability.addTarget(new TargetControlledPermanent(StaticFilters.FILTER_ANOTHER_CREATURE_YOU_CONTROL).withNotTarget(true)); new ReturnToHandTargetEffect().setText("return another creature you control to its owner's hand"),
this.addAbility(ability); new TargetControlledPermanent(StaticFilters.FILTER_ANOTHER_CREATURE_YOU_CONTROL))));
} }
private YaroksWavecrasher(final YaroksWavecrasher card) { private YaroksWavecrasher(final YaroksWavecrasher card) {

View file

@ -1,8 +1,8 @@
package mage.cards.y; package mage.cards.y;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.OneShotNonTargetEffect;
import mage.abilities.effects.common.ExileReturnBattlefieldNextEndStepTargetEffect; import mage.abilities.effects.common.ExileReturnBattlefieldNextEndStepTargetEffect;
import mage.abilities.keyword.CompanionAbility; import mage.abilities.keyword.CompanionAbility;
import mage.abilities.keyword.CompanionCondition; import mage.abilities.keyword.CompanionCondition;
@ -54,10 +54,9 @@ public final class YorionSkyNomad extends CardImpl {
// When Yorion enters the battlefield, exile any number of other nonland permanents you own // When Yorion enters the battlefield, exile any number of other nonland permanents you own
// and control. Return those cards to the battlefield at the beginning of the next end step. // and control. Return those cards to the battlefield at the beginning of the next end step.
Ability ability = new EntersBattlefieldTriggeredAbility( this.addAbility(new EntersBattlefieldTriggeredAbility(new OneShotNonTargetEffect(
new ExileReturnBattlefieldNextEndStepTargetEffect().setText(ruleText)); new ExileReturnBattlefieldNextEndStepTargetEffect().setText(ruleText),
ability.addTarget(new TargetPermanent(0, Integer.MAX_VALUE, filter, true)); new TargetPermanent(0, Integer.MAX_VALUE, filter, true))));
this.addAbility(ability);
} }
private YorionSkyNomad(final YorionSkyNomad card) { private YorionSkyNomad(final YorionSkyNomad card) {

View file

@ -0,0 +1,77 @@
package org.mage.test.cards.abilities.oneshot;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.player.TestPlayer;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author notgreat
*/
public class OneShotNonTargetTest extends CardTestPlayerBase {
@Test
public void YorionChooseAfterTriggerTest() {
addCard(Zone.HAND, playerA, "Yorion, Sky Nomad");
addCard(Zone.BATTLEFIELD, playerA, "Plains", 7);
addCard(Zone.HAND, playerA, "Resolute Reinforcements");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Yorion, Sky Nomad");
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true);
checkPermanentCount("Yorion on battlefield", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Yorion, Sky Nomad", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Resolute Reinforcements");
setChoice(playerA, "Resolute Reinforcements");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertTokenCount(playerA, "Soldier Token", 2);
assertPermanentCount(playerA, "Yorion, Sky Nomad", 1);
assertPermanentCount(playerA, "Resolute Reinforcements", 1);
assertTappedCount("Plains", true, 7);
}
@Test
public void NonTargetAdjusterTest() {
addCard(Zone.HAND, playerA, "Temporal Firestorm");
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
addCard(Zone.BATTLEFIELD, playerA, "Squire");
addCard(Zone.BATTLEFIELD, playerA, "Python");
addCard(Zone.BATTLEFIELD, playerA, "Watchwolf");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Temporal Firestorm");
setChoice(playerA, true);
setChoice(playerA, true);
setChoice(playerA, "Squire^Python");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerA, "Squire", 0);
assertGraveyardCount(playerA, "Python", 0);
assertGraveyardCount(playerA, "Watchwolf", 1);
}
@Test
public void ModeSelectionTest() {
addCard(Zone.HAND, playerA, "SOLDIER Military Program");
addCard(Zone.BATTLEFIELD, playerA, "Plains", 3);
addCard(Zone.BATTLEFIELD, playerA, "Squire", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "SOLDIER Military Program");
setModeChoice(playerA, "2");
setChoice(playerA, "Squire");
setChoice(playerA, TestPlayer.CHOICE_SKIP);
setModeChoice(playerA, "1");
setModeChoice(playerA, "2");
setChoice(playerA, "Squire^Soldier Token");
setStrictChooseMode(true);
setStopAt(5, PhaseStep.END_TURN);
execute();
assertPowerToughness(playerA, "Squire", 3, 4);
assertPowerToughness(playerA, "Soldier Token", 2, 2);
}
}

View file

@ -1749,6 +1749,10 @@ public abstract class AbilityImpl implements Ability {
@Override @Override
public AbilityImpl setTargetAdjuster(TargetAdjuster targetAdjuster) { public AbilityImpl setTargetAdjuster(TargetAdjuster targetAdjuster) {
if (targetAdjuster == null) {
this.targetAdjuster = null;
return this;
}
if (targetAdjuster instanceof GenericTargetAdjuster && this.getTargets().isEmpty()) { if (targetAdjuster instanceof GenericTargetAdjuster && this.getTargets().isEmpty()) {
throw new IllegalStateException("Target adjuster being added but no targets are set!"); throw new IllegalStateException("Target adjuster being added but no targets are set!");
} }

View file

@ -3,6 +3,7 @@ package mage.abilities.common;
import mage.MageObject; import mage.MageObject;
import mage.abilities.TriggeredAbilityImpl; import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect; import mage.abilities.effects.Effect;
import mage.constants.SetTargetPointer;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.FilterPermanent; import mage.filter.FilterPermanent;
import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterCreaturePermanent;
@ -18,7 +19,7 @@ import mage.target.targetpointer.FixedTarget;
public class DiesCreatureTriggeredAbility extends TriggeredAbilityImpl { public class DiesCreatureTriggeredAbility extends TriggeredAbilityImpl {
protected FilterPermanent filter; protected FilterPermanent filter;
private boolean setTargetPointer; private SetTargetPointer setTargetPointer;
public DiesCreatureTriggeredAbility(Effect effect, boolean optional) { public DiesCreatureTriggeredAbility(Effect effect, boolean optional) {
this(effect, optional, false); this(effect, optional, false);
@ -33,7 +34,7 @@ public class DiesCreatureTriggeredAbility extends TriggeredAbilityImpl {
if (another) { if (another) {
filter.add(AnotherPredicate.instance); filter.add(AnotherPredicate.instance);
} }
this.setTargetPointer = setTargetPointer; this.setTargetPointer = (setTargetPointer ? SetTargetPointer.PERMANENT : SetTargetPointer.NONE);
} }
public DiesCreatureTriggeredAbility(Effect effect, boolean optional, FilterPermanent filter) { public DiesCreatureTriggeredAbility(Effect effect, boolean optional, FilterPermanent filter) {
@ -44,7 +45,14 @@ public class DiesCreatureTriggeredAbility extends TriggeredAbilityImpl {
this(Zone.BATTLEFIELD, effect, optional, filter, setTargetPointer); this(Zone.BATTLEFIELD, effect, optional, filter, setTargetPointer);
} }
public DiesCreatureTriggeredAbility(Effect effect, SetTargetPointer setTargetPointer) {
this(Zone.BATTLEFIELD, effect, false, new FilterCreaturePermanent("a creature"), setTargetPointer);
}
public DiesCreatureTriggeredAbility(Zone zone, Effect effect, boolean optional, FilterPermanent filter, boolean setTargetPointer) { public DiesCreatureTriggeredAbility(Zone zone, Effect effect, boolean optional, FilterPermanent filter, boolean setTargetPointer) {
this(zone, effect, optional, filter, (setTargetPointer ? SetTargetPointer.PERMANENT : SetTargetPointer.NONE));
}
public DiesCreatureTriggeredAbility(Zone zone, Effect effect, boolean optional, FilterPermanent filter, SetTargetPointer setTargetPointer) {
super(zone, effect, optional); super(zone, effect, optional);
this.filter = filter; this.filter = filter;
this.setTargetPointer = setTargetPointer; this.setTargetPointer = setTargetPointer;
@ -75,8 +83,17 @@ public class DiesCreatureTriggeredAbility extends TriggeredAbilityImpl {
return false; return false;
} }
getEffects().setValue("creatureDied", zEvent.getTarget()); getEffects().setValue("creatureDied", zEvent.getTarget());
if (setTargetPointer) { switch (setTargetPointer) {
this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game)); case PLAYER:
this.getAllEffects().setTargetPointer(new FixedTarget(event.getPlayerId(), game));
break;
case PERMANENT:
this.getAllEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game));
break;
case NONE:
break;
default:
throw new IllegalArgumentException("Unsupported SetTargetPointer in DiesCreatureTriggeredAbility");
} }
return true; return true;
} }

View file

@ -0,0 +1,63 @@
package mage.abilities.costs.common;
import mage.abilities.Ability;
import mage.abilities.costs.Cost;
import mage.abilities.costs.CostImpl;
import mage.constants.Outcome;
import mage.counters.Counter;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetControlledCreaturePermanent;
import mage.target.common.TargetControlledPermanent;
import java.util.UUID;
/**
* @author notgreat
*/
public class PutCountersTargetCost extends CostImpl {
private final Counter counter;
public PutCountersTargetCost(Counter counter){
this(counter, new TargetControlledCreaturePermanent());
}
public PutCountersTargetCost(Counter counter, TargetControlledPermanent target) {
this.counter = counter.copy();
target.withNotTarget(true);
this.addTarget(target);
this.text = "put " + counter.getDescription() + " on " + target.getDescription();
}
public PutCountersTargetCost(PutCountersTargetCost cost) {
super(cost);
this.counter = cost.counter.copy();
}
public PutCountersTargetCost copy() {
return new PutCountersTargetCost(this);
}
@Override
public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) {
Player player = game.getPlayer(ability.getControllerId());
if (player == null || !this.getTargets().choose(Outcome.Exile, controllerId, source.getSourceId(), source, game)) {
return paid;
}
for (UUID targetId : this.getTargets().get(0).getTargets()) {
Permanent permanent = game.getPermanent(targetId);
if (permanent == null) {
return false;
}
paid |= permanent.addCounters(counter, controllerId, ability, game);
}
return paid;
}
@Override
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
return this.getTargets().canChoose(controllerId, source, game);
}
}

View file

@ -0,0 +1,80 @@
package mage.abilities.effects;
import mage.abilities.Ability;
import mage.game.Game;
import mage.target.Target;
import mage.target.targetadjustment.TargetAdjuster;
import mage.target.targetpointer.TargetPointer;
/**
* @author notgreat
*/
public class OneShotNonTargetEffect extends OneShotEffect {
OneShotEffect effect;
Target notTarget;
TargetAdjuster adjuster;
public OneShotNonTargetEffect(OneShotEffect effect, Target notTarget) {
this(effect, notTarget, null);
}
public OneShotNonTargetEffect(OneShotEffect effect, Target notTarget, TargetAdjuster adjuster) {
super(effect.outcome);
this.effect = effect;
this.notTarget = notTarget;
this.notTarget.withNotTarget(true);
this.adjuster = adjuster;
if (effect.staticText == null || effect.staticText.equals("")){
throw new IllegalArgumentException("Effect must use static text");
}
this.setText(effect.staticText);
}
private OneShotNonTargetEffect(OneShotNonTargetEffect eff) {
super(eff);
this.effect = eff.effect.copy();
this.notTarget = eff.notTarget.copy();
this.adjuster = eff.adjuster;
}
public OneShotNonTargetEffect copy() {
return new OneShotNonTargetEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
boolean result = false;
Target target = notTarget.copy();
if (source.getTargetAdjuster() != null || !source.getTargets().isEmpty()){
throw new IllegalStateException("source ability already has target but is using OneShotNonTargetEffect");
}
source.addTarget(target);
if (adjuster != null) {
adjuster.clearDefaultTargets();
source.setTargetAdjuster(adjuster);
source.adjustTargets(game);
source.setTargetAdjuster(null);
}
if (source.getTargets().choose(outcome, source.getControllerId(), source.getId(), source, game)) {
result = effect.apply(game, source);
}
source.getTargets().clear();
return result;
}
@Override
public OneShotEffect setTargetPointer(TargetPointer targetPointer) {
if (targetPointer == null) {
return null;
}
effect.setTargetPointer(targetPointer);
return super.setTargetPointer(targetPointer);
}
@Override
public void setValue(String key, Object value) {
effect.setValue(key, value);
super.setValue(key, value);
}
}

View file

@ -71,6 +71,10 @@ public class ConditionalTargetAdjuster implements TargetAdjuster {
blueprintTarget = ability.getTargets().get(0).copy(); blueprintTarget = ability.getTargets().get(0).copy();
} }
} }
@Override
public void clearDefaultTargets() {
blueprintTarget = null;
}
@Override @Override
public void adjustTargets(Ability ability, Game game) { public void adjustTargets(Ability ability, Game game) {

View file

@ -14,4 +14,8 @@ public abstract class GenericTargetAdjuster implements TargetAdjuster {
throw new IllegalStateException("Wrong code usage: target adjuster already has blueprint target - " + blueprintTarget); throw new IllegalStateException("Wrong code usage: target adjuster already has blueprint target - " + blueprintTarget);
} }
} }
@Override
public void clearDefaultTargets() {
blueprintTarget = null;
}
} }

View file

@ -20,4 +20,7 @@ public interface TargetAdjuster extends Serializable {
*/ */
default void addDefaultTargets(Ability ability) { default void addDefaultTargets(Ability ability) {
} }
default void clearDefaultTargets() {
}
} }