diff --git a/Mage.Sets/src/mage/cards/a/AmyPond.java b/Mage.Sets/src/mage/cards/a/AmyPond.java index c9313e55bdd..c64b3fa0cc1 100644 --- a/Mage.Sets/src/mage/cards/a/AmyPond.java +++ b/Mage.Sets/src/mage/cards/a/AmyPond.java @@ -1,23 +1,26 @@ package mage.cards.a; -import java.util.UUID; - import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.SavedDamageValue; import mage.abilities.effects.OneShotEffect; -import mage.abilities.keyword.PartnerWithAbility; -import mage.cards.*; -import mage.constants.*; +import mage.abilities.effects.OneShotNonTargetEffect; 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.filter.common.FilterSuspendedCard; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInExile; +import java.util.UUID; + /** * * @author Skiwkr @@ -41,10 +44,9 @@ public final class AmyPond extends CardImpl { 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. - Ability ability = new DealsCombatDamageToAPlayerTriggeredAbility(new AmyPondEffect(SavedDamageValue.MANY), - false, true); - ability.addTarget(new TargetCardInExile(filter).withNotTarget(true)); - this.addAbility(ability); + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new OneShotNonTargetEffect( + new AmyPondEffect(SavedDamageValue.MANY), new TargetCardInExile(filter)), + false, true)); // Doctor's companion this.addAbility(DoctorsCompanionAbility.getInstance()); @@ -68,7 +70,7 @@ class AmyPondEffect extends OneShotEffect { AmyPondEffect(DynamicValue numberCounters) { super(Outcome.Benefit); this.numberCounters = numberCounters; - this.staticText= "choose a suspended card you own and remove that many time counters from it"; + this.staticText = "choose a suspended card you own and remove that many time counters from it"; } private AmyPondEffect(final AmyPondEffect effect) { diff --git a/Mage.Sets/src/mage/cards/b/BarrowinOfClanUndurr.java b/Mage.Sets/src/mage/cards/b/BarrowinOfClanUndurr.java index 4179fe20e0f..e88193a920d 100644 --- a/Mage.Sets/src/mage/cards/b/BarrowinOfClanUndurr.java +++ b/Mage.Sets/src/mage/cards/b/BarrowinOfClanUndurr.java @@ -6,6 +6,7 @@ import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.CompletedDungeonCondition; import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.OneShotNonTargetEffect; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.abilities.effects.keyword.VentureIntoTheDungeonEffect; 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. Ability ability = new AttacksTriggeredAbility(new ConditionalOneShotEffect( - new ReturnFromGraveyardToBattlefieldTargetEffect(), - CompletedDungeonCondition.instance, "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.addTarget(new TargetCardInYourGraveyard(0, 1, filter, true)); + new OneShotNonTargetEffect(new ReturnFromGraveyardToBattlefieldTargetEffect() + .setText("return up to one creature card with mana value 3 or less from your graveyard to the battlefield"), + new TargetCardInYourGraveyard(0, 1, filter, true)), + CompletedDungeonCondition.instance + ).withConditionTextAtEnd(true)); this.addAbility(ability.addHint(CompletedDungeonCondition.getHint()), new CompletedDungeonWatcher()); } diff --git a/Mage.Sets/src/mage/cards/b/Boltbender.java b/Mage.Sets/src/mage/cards/b/Boltbender.java index a88d773039f..68adb07b509 100644 --- a/Mage.Sets/src/mage/cards/b/Boltbender.java +++ b/Mage.Sets/src/mage/cards/b/Boltbender.java @@ -1,9 +1,9 @@ package mage.cards.b; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotNonTargetEffect; import mage.abilities.effects.common.ChooseNewTargetsTargetEffect; import mage.abilities.keyword.DisguiseAbility; import mage.cards.CardImpl; @@ -32,10 +32,9 @@ public final class Boltbender extends CardImpl { 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. - Ability ability = new TurnedFaceUpSourceTriggeredAbility(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)); - this.addAbility(ability); + this.addAbility(new TurnedFaceUpSourceTriggeredAbility(new OneShotNonTargetEffect( + new ChooseNewTargetsTargetEffect().setText("you may choose new targets for any number of other spells and/or abilities"), + new TargetStackObject(0, Integer.MAX_VALUE, StaticFilters.FILTER_SPELL_OR_ABILITY)))); } private Boltbender(final Boltbender card) { diff --git a/Mage.Sets/src/mage/cards/b/BurningSands.java b/Mage.Sets/src/mage/cards/b/BurningSands.java index a67dcebbebd..aa4aff061d5 100644 --- a/Mage.Sets/src/mage/cards/b/BurningSands.java +++ b/Mage.Sets/src/mage/cards/b/BurningSands.java @@ -1,17 +1,13 @@ package mage.cards.b; -import mage.abilities.Ability; import mage.abilities.common.DiesCreatureTriggeredAbility; import mage.abilities.effects.common.SacrificeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; 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; @@ -24,11 +20,9 @@ public final class BurningSands extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{R}{R}"); // Whenever a creature dies, that creature's controller sacrifices a land. - Ability ability = new DiesCreatureTriggeredAbility(new SacrificeEffect( - StaticFilters.FILTER_LAND, 1, "that creature's controller" - ), false, false, true); - ability.setTargetAdjuster(BurningSandsAdjuster.instance); - this.addAbility(ability); + this.addAbility(new DiesCreatureTriggeredAbility( + new SacrificeEffect(StaticFilters.FILTER_LAND, 1, "that creature's controller"), + SetTargetPointer.PLAYER)); } private BurningSands(final BurningSands card) { @@ -40,16 +34,3 @@ public final class BurningSands extends CardImpl { 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())); - } - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/c/Cytoshape.java b/Mage.Sets/src/mage/cards/c/Cytoshape.java index a5795cb596b..bbbed0cfc4d 100644 --- a/Mage.Sets/src/mage/cards/c/Cytoshape.java +++ b/Mage.Sets/src/mage/cards/c/Cytoshape.java @@ -1,7 +1,6 @@ package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; @@ -18,32 +17,23 @@ import mage.target.Target; import mage.target.common.TargetCreaturePermanent; import mage.util.functions.EmptyCopyApplier; +import java.util.UUID; + /** * * @author jeffwadsworth */ 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) { 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. 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) { @@ -58,6 +48,11 @@ public final class Cytoshape extends CardImpl { class CytoshapeEffect extends OneShotEffect { + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonlegendary creature"); + + static { + filter.add(Predicates.not(SuperType.LEGENDARY.getPredicate())); + } CytoshapeEffect() { super(Outcome.Copy); 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 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) { - Permanent copyTo = game.getPermanentOrLKIBattlefield(ability.getTargets().get(1).getFirstTarget()); + Permanent copyTo = game.getPermanentOrLKIBattlefield(ability.getTargets().get(0).getFirstTarget()); if (copyTo != null) { game.copyPermanent(Duration.EndOfTurn, copyFrom, copyTo.getId(), ability, new EmptyCopyApplier()); } diff --git a/Mage.Sets/src/mage/cards/e/Earthlink.java b/Mage.Sets/src/mage/cards/e/Earthlink.java index 3d33712a78b..5f770afb5a4 100644 --- a/Mage.Sets/src/mage/cards/e/Earthlink.java +++ b/Mage.Sets/src/mage/cards/e/Earthlink.java @@ -1,25 +1,18 @@ 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.costs.mana.ManaCostsImpl; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.SacrificeEffect; import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.SetTargetPointer; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; /** * @@ -34,7 +27,9 @@ public final class Earthlink extends CardImpl { this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SacrificeSourceUnlessPaysEffect(new ManaCostsImpl<>("{2}")))); // 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) { @@ -46,34 +41,3 @@ public final class Earthlink extends CardImpl { 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; - } -} diff --git a/Mage.Sets/src/mage/cards/h/HatchetBully.java b/Mage.Sets/src/mage/cards/h/HatchetBully.java index 4c753e09863..84d5ad768e3 100644 --- a/Mage.Sets/src/mage/cards/h/HatchetBully.java +++ b/Mage.Sets/src/mage/cards/h/HatchetBully.java @@ -3,24 +3,16 @@ package mage.cards.h; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; +import mage.abilities.costs.common.PutCountersTargetCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; 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.TargetControlledCreaturePermanent; import java.util.UUID; @@ -38,15 +30,11 @@ public final class HatchetBully extends CardImpl { 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. - 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 HatchetBullyCost()); + ability.addCost(new PutCountersTargetCost(CounterType.M1M1.createInstance())); ability.addTarget(new TargetAnyTarget()); - Target target = new TargetControlledCreaturePermanent(); - target.withNotTarget(true); - ability.addTarget(target); this.addAbility(ability); - } private HatchetBully(final HatchetBully card) { @@ -58,64 +46,3 @@ public final class HatchetBully extends CardImpl { 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; - } -} diff --git a/Mage.Sets/src/mage/cards/k/KithkinArmor.java b/Mage.Sets/src/mage/cards/k/KithkinArmor.java index 0db15948f2e..e367ce69c25 100644 --- a/Mage.Sets/src/mage/cards/k/KithkinArmor.java +++ b/Mage.Sets/src/mage/cards/k/KithkinArmor.java @@ -1,9 +1,6 @@ package mage.cards.k; -import java.util.UUID; -import mage.constants.SubType; -import mage.game.events.DamageEvent; -import mage.target.common.TargetCreaturePermanent; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; @@ -12,18 +9,22 @@ import mage.abilities.costs.CostImpl; import mage.abilities.effects.PreventionEffectImpl; import mage.abilities.effects.RestrictionEffect; import mage.abilities.effects.common.AttachEffect; -import mage.constants.Outcome; -import mage.target.TargetPermanent; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Zone; +import mage.constants.Outcome; +import mage.constants.SubType; import mage.game.Game; +import mage.game.events.DamageEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; 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(); this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); - Ability ability = new EnchantAbility(auraTarget); - this.addAbility(ability); + this.addAbility(new EnchantAbility(auraTarget)); // Enchanted creature can't be blocked by creatures with power 3 or greater. this.addAbility(new SimpleStaticAbility(new KithkinArmorRestrictionEffect())); @@ -50,7 +50,6 @@ public final class KithkinArmor extends CardImpl { Ability protectionAbility = new SimpleActivatedAbility( new KithkinArmorPreventionEffect(), new KithkinArmorCost()); - protectionAbility.addTarget(new TargetSource()); this.addAbility(protectionAbility); } @@ -137,6 +136,7 @@ class KithkinArmorRestrictionEffect extends RestrictionEffect { class KithkinArmorPreventionEffect extends PreventionEffectImpl { + private MageObjectReference mageObjectReference; KithkinArmorPreventionEffect() { 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"; @@ -144,13 +144,20 @@ class KithkinArmorPreventionEffect extends PreventionEffectImpl { private KithkinArmorPreventionEffect(final KithkinArmorPreventionEffect effect) { super(effect); + mageObjectReference = effect.mageObjectReference; } @Override public KithkinArmorPreventionEffect copy() { 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 public boolean applies(GameEvent event, Ability source, Game game) { if (super.applies(event, source, game) diff --git a/Mage.Sets/src/mage/cards/m/MagneticMine.java b/Mage.Sets/src/mage/cards/m/MagneticMine.java index 5446f865141..8922a1c138c 100644 --- a/Mage.Sets/src/mage/cards/m/MagneticMine.java +++ b/Mage.Sets/src/mage/cards/m/MagneticMine.java @@ -1,33 +1,32 @@ package mage.cards.m; -import java.util.Objects; -import java.util.UUID; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.common.PutIntoGraveFromBattlefieldAllTriggeredAbility; +import mage.abilities.effects.common.DamageTargetControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; -import mage.target.TargetPlayer; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterArtifactPermanent; +import mage.filter.predicate.mageobject.AnotherPredicate; + +import java.util.UUID; /** * * @author North */ 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) { 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 artifact’s controller. - MagneticMineTriggeredAbility ability = new MagneticMineTriggeredAbility(new DamageTargetEffect(2)); - ability.addTarget(new TargetPlayer().withNotTarget(true)); - this.addAbility(ability); + this.addAbility(new PutIntoGraveFromBattlefieldAllTriggeredAbility( + new DamageTargetControllerEffect(2, "artifact"), false, filter, true)); } private MagneticMine(final MagneticMine card) { @@ -39,41 +38,3 @@ public final class MagneticMine extends CardImpl { 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); - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/SOLDIERMilitaryProgram.java b/Mage.Sets/src/mage/cards/s/SOLDIERMilitaryProgram.java index acc52444607..3e3152415ca 100644 --- a/Mage.Sets/src/mage/cards/s/SOLDIERMilitaryProgram.java +++ b/Mage.Sets/src/mage/cards/s/SOLDIERMilitaryProgram.java @@ -3,23 +3,20 @@ package mage.cards.s; import mage.abilities.Ability; import mage.abilities.Mode; 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.counter.AddCountersTargetEffect; import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; -import mage.game.Game; import mage.game.permanent.token.SoldierToken; -import mage.players.Player; import mage.target.TargetPermanent; -import java.util.Optional; import java.util.UUID; /** @@ -37,9 +34,15 @@ public final class SOLDIERMilitaryProgram extends CardImpl { 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().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. - 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); } @@ -52,38 +55,3 @@ public final class SOLDIERMilitaryProgram extends CardImpl { 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; - } -} diff --git a/Mage.Sets/src/mage/cards/s/ShiningShoal.java b/Mage.Sets/src/mage/cards/s/ShiningShoal.java index 10f1f3aca49..95cbe6916bc 100644 --- a/Mage.Sets/src/mage/cards/s/ShiningShoal.java +++ b/Mage.Sets/src/mage/cards/s/ShiningShoal.java @@ -1,17 +1,19 @@ package mage.cards.s; import mage.MageObject; +import mage.MageObjectReference; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.costs.common.ExileFromHandCost; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.dynamicvalue.common.ExileFromHandCostCardConvertedMana; -import mage.abilities.effects.common.RedirectDamageFromSourceToTargetEffect; +import mage.abilities.effects.RedirectionEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.Outcome; import mage.constants.SubType; import mage.filter.common.FilterOwnedCard; import mage.filter.predicate.mageobject.ColorPredicate; @@ -50,7 +52,6 @@ public final class ShiningShoal extends CardImpl { this.getSpellAbility().addEffect(new ShiningShoalRedirectDamageTargetEffect( Duration.EndOfTurn, ExileFromHandCostCardConvertedMana.instance )); - this.getSpellAbility().addTarget(new TargetSource()); 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 MageObjectReference mageObjectReference; public ShiningShoalRedirectDamageTargetEffect(Duration duration, DynamicValue dynamicAmount) { super(duration, 0, UsageType.ONE_USAGE_AT_THE_SAME_TIME); @@ -77,6 +79,7 @@ class ShiningShoalRedirectDamageTargetEffect extends RedirectDamageFromSourceToT private ShiningShoalRedirectDamageTargetEffect(final ShiningShoalRedirectDamageTargetEffect effect) { super(effect); this.dynamicAmount = effect.dynamicAmount; + this.mageObjectReference = effect.mageObjectReference; } @Override @@ -88,6 +91,9 @@ class ShiningShoalRedirectDamageTargetEffect extends RedirectDamageFromSourceToT public void init(Ability source, Game game) { super.init(source, game); 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 @@ -96,15 +102,13 @@ class ShiningShoalRedirectDamageTargetEffect extends RedirectDamageFromSourceToT // get source of the damage event MageObject sourceObject = game.getObject(event.getSourceId()); - // get the chosen damage source - MageObject chosenSourceObject = game.getObject(source.getFirstTarget()); // does the source of the damage exist? if (sourceObject == null) { game.informPlayers("Couldn't find source of damage"); return false; } // do the 2 objects match? - if (chosenSourceObject == null || !sourceObject.getId().equals(chosenSourceObject.getId())) { + if (!mageObjectReference.refersTo(sourceObject, game)) { return false; } @@ -114,7 +118,7 @@ class ShiningShoalRedirectDamageTargetEffect extends RedirectDamageFromSourceToT if (permanent != null && permanent.isCreature(game)) { if (permanent.isControlledBy(source.getControllerId())) { // it's your creature - redirectTarget = source.getTargets().get(1); + redirectTarget = source.getTargets().get(0); return true; } } @@ -123,7 +127,7 @@ class ShiningShoalRedirectDamageTargetEffect extends RedirectDamageFromSourceToT if (player != null) { if (player.getId().equals(source.getControllerId())) { // it is you - redirectTarget = source.getTargets().get(1); + redirectTarget = source.getTargets().get(0); return true; } } diff --git a/Mage.Sets/src/mage/cards/s/SpectralSearchlight.java b/Mage.Sets/src/mage/cards/s/SpectralSearchlight.java index 3891bacf41f..342afa6ab7e 100644 --- a/Mage.Sets/src/mage/cards/s/SpectralSearchlight.java +++ b/Mage.Sets/src/mage/cards/s/SpectralSearchlight.java @@ -3,7 +3,6 @@ package mage.cards.s; import mage.Mana; import mage.abilities.Ability; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.common.ChoosePlayerEffect; import mage.abilities.effects.mana.ManaEffect; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; @@ -14,6 +13,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; import mage.players.Player; +import mage.target.TargetPlayer; import java.util.ArrayList; 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. ManaEffect effect = new SpectralSearchlightManaEffect(); 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); } @@ -49,7 +47,7 @@ class SpectralSearchlightManaEffect extends ManaEffect { SpectralSearchlightManaEffect() { 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) { @@ -58,7 +56,13 @@ class SpectralSearchlightManaEffect extends ManaEffect { @Override 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 diff --git a/Mage.Sets/src/mage/cards/t/TemporalFirestorm.java b/Mage.Sets/src/mage/cards/t/TemporalFirestorm.java index ac5d7f964e6..62e1cec5d6b 100644 --- a/Mage.Sets/src/mage/cards/t/TemporalFirestorm.java +++ b/Mage.Sets/src/mage/cards/t/TemporalFirestorm.java @@ -1,6 +1,7 @@ package mage.cards.t; import mage.abilities.dynamicvalue.common.MultikickerCount; +import mage.abilities.effects.OneShotNonTargetEffect; import mage.abilities.effects.common.DamageAllEffect; import mage.abilities.effects.common.PhaseOutTargetEffect; import mage.abilities.keyword.KickerAbility; @@ -35,10 +36,10 @@ public final class TemporalFirestorm extends CardImpl { 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. - this.getSpellAbility().addEffect(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")); - this.getSpellAbility().addTarget(new TargetPermanent(0, 1, filter, true)); - this.getSpellAbility().setTargetAdjuster(new TargetsCountAdjuster(MultikickerCount.instance)); + 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"), + new TargetPermanent(0, 1, filter, true), new TargetsCountAdjuster(MultikickerCount.instance) + )); // Temporal Firestorm deals 5 damage to each creature and each planeswalker. this.getSpellAbility().addEffect(new DamageAllEffect( diff --git a/Mage.Sets/src/mage/cards/v/Valleymaker.java b/Mage.Sets/src/mage/cards/v/Valleymaker.java index a10e061391d..d1338338f0f 100644 --- a/Mage.Sets/src/mage/cards/v/Valleymaker.java +++ b/Mage.Sets/src/mage/cards/v/Valleymaker.java @@ -1,26 +1,29 @@ package mage.cards.v; -import java.util.UUID; import mage.MageInt; import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.effects.mana.AddManaToManaPoolTargetControllerEffect; import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.mana.ManaEffect; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.players.Player; import mage.target.TargetPlayer; -import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** * * @author jeffwadsworth @@ -48,10 +51,9 @@ public final class Valleymaker extends CardImpl { this.addAbility(ability); // {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") - .setText("choose a player. That player adds {G}{G}{G}"), new TapSourceCost()); + Ability ability2 = new SimpleManaAbility(Zone.BATTLEFIELD, new ValleymakerManaEffect() + .setText("That player adds {G}{G}{G}"), new TapSourceCost()); ability2.addCost(new SacrificeTargetCost(filter2)); - ability2.addTarget(new TargetPlayer(1, 1, true)); this.addAbility(ability2); } @@ -64,3 +66,37 @@ public final class Valleymaker extends CardImpl { 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); + } +} diff --git a/Mage.Sets/src/mage/cards/v/VictoryChimes.java b/Mage.Sets/src/mage/cards/v/VictoryChimes.java index 4cf0e7d3df8..c02c16c7726 100644 --- a/Mage.Sets/src/mage/cards/v/VictoryChimes.java +++ b/Mage.Sets/src/mage/cards/v/VictoryChimes.java @@ -4,9 +4,8 @@ import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; 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.mana.ManaEffect; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -15,6 +14,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; import mage.players.Player; +import mage.target.TargetPlayer; import java.util.UUID; @@ -33,8 +33,6 @@ public final class VictoryChimes extends CardImpl { ManaEffect effect = new VictoryChimesManaEffect("chosen player"); effect.setText("a player of your choice adds {C}"); 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); } @@ -61,7 +59,13 @@ class VictoryChimesManaEffect extends ManaEffect { @Override 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 diff --git a/Mage.Sets/src/mage/cards/w/WanderingMage.java b/Mage.Sets/src/mage/cards/w/WanderingMage.java index ff860723176..af772345bdd 100644 --- a/Mage.Sets/src/mage/cards/w/WanderingMage.java +++ b/Mage.Sets/src/mage/cards/w/WanderingMage.java @@ -1,13 +1,11 @@ package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; 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.PutCountersTargetCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.PreventDamageToTargetEffect; import mage.cards.CardImpl; @@ -15,17 +13,14 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.common.FilterCreaturePermanent; 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.TargetPlayerOrPlaneswalker; +import java.util.UUID; + /** * * @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. ability = new SimpleActivatedAbility( new PreventDamageToTargetEffect(Duration.EndOfTurn, 2), new ManaCostsImpl<>("{B}")); - ability.addCost(new WanderingMageCost()); + ability.addCost(new PutCountersTargetCost(CounterType.M1M1.createInstance())); ability.addTarget(new TargetPlayerOrPlaneswalker()); - Target target = new TargetControlledCreaturePermanent(); - target.withNotTarget(true); - ability.addTarget(target); this.addAbility(ability); } @@ -81,34 +73,3 @@ public final class WanderingMage extends CardImpl { 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); - } -} diff --git a/Mage.Sets/src/mage/cards/w/WormfangNewt.java b/Mage.Sets/src/mage/cards/w/WormfangNewt.java index 47473a70185..20cc79dfb13 100644 --- a/Mage.Sets/src/mage/cards/w/WormfangNewt.java +++ b/Mage.Sets/src/mage/cards/w/WormfangNewt.java @@ -1,11 +1,11 @@ package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotNonTargetEffect; import mage.abilities.effects.common.ExileTargetForSourceEffect; import mage.abilities.effects.common.ReturnFromExileForSourceEffect; import mage.cards.CardImpl; @@ -14,18 +14,17 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; import mage.filter.StaticFilters; -import mage.filter.common.FilterControlledLandPermanent; -import mage.filter.predicate.mageobject.AnotherPredicate; -import mage.target.Target; import mage.target.TargetPermanent; +import java.util.UUID; + /** * @author tcontis */ public final class WormfangNewt extends CardImpl { public WormfangNewt(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.NIGHTMARE); this.subtype.add(SubType.SALAMANDER); this.subtype.add(SubType.BEAST); @@ -34,10 +33,9 @@ public final class WormfangNewt extends CardImpl { this.toughness = new MageInt(2); // When Wormfang Turtle enters the battlefield, exile a land you control. - Ability ability1 = new EntersBattlefieldTriggeredAbility(new ExileTargetForSourceEffect(), false); - Target target = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_A_LAND).withNotTarget(true); - ability1.addTarget(target); - this.addAbility(ability1); + this.addAbility(new EntersBattlefieldTriggeredAbility(new OneShotNonTargetEffect( + new ExileTargetForSourceEffect().setText("exile a land you control"), + new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_A_LAND)))); // 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); diff --git a/Mage.Sets/src/mage/cards/w/WormfangTurtle.java b/Mage.Sets/src/mage/cards/w/WormfangTurtle.java index a3ca56d6f58..5705d2e57ac 100644 --- a/Mage.Sets/src/mage/cards/w/WormfangTurtle.java +++ b/Mage.Sets/src/mage/cards/w/WormfangTurtle.java @@ -1,11 +1,11 @@ package mage.cards.w; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotNonTargetEffect; import mage.abilities.effects.common.ExileTargetForSourceEffect; import mage.abilities.effects.common.ReturnFromExileForSourceEffect; import mage.cards.CardImpl; @@ -14,11 +14,10 @@ import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; import mage.filter.StaticFilters; -import mage.filter.common.FilterControlledLandPermanent; -import mage.filter.predicate.mageobject.AnotherPredicate; -import mage.target.Target; import mage.target.TargetPermanent; +import java.util.UUID; + /** * @author tcontis */ @@ -34,10 +33,10 @@ public final class WormfangTurtle extends CardImpl { this.toughness = new MageInt(4); // When Wormfang Turtle enters the battlefield, exile a land you control. - Ability ability1 = new EntersBattlefieldTriggeredAbility(new ExileTargetForSourceEffect(), false); - Target target = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_A_LAND).withNotTarget(true); - ability1.addTarget(target); - this.addAbility(ability1); + this.addAbility(new EntersBattlefieldTriggeredAbility(new OneShotNonTargetEffect( + new ExileTargetForSourceEffect().setText("exile a land you control"), + new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_A_LAND)))); + // 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); diff --git a/Mage.Sets/src/mage/cards/y/YaroksWavecrasher.java b/Mage.Sets/src/mage/cards/y/YaroksWavecrasher.java index b61f3a44859..27209c5ff6d 100644 --- a/Mage.Sets/src/mage/cards/y/YaroksWavecrasher.java +++ b/Mage.Sets/src/mage/cards/y/YaroksWavecrasher.java @@ -1,8 +1,8 @@ package mage.cards.y; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotNonTargetEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -26,9 +26,9 @@ public final class YaroksWavecrasher extends CardImpl { this.toughness = new MageInt(4); // When Yarok’s Wavecrasher enters the battlefield, return another creature you control to its owner’s hand. - Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnToHandTargetEffect(), false); - ability.addTarget(new TargetControlledPermanent(StaticFilters.FILTER_ANOTHER_CREATURE_YOU_CONTROL).withNotTarget(true)); - this.addAbility(ability); + this.addAbility(new EntersBattlefieldTriggeredAbility(new OneShotNonTargetEffect( + new ReturnToHandTargetEffect().setText("return another creature you control to its owner's hand"), + new TargetControlledPermanent(StaticFilters.FILTER_ANOTHER_CREATURE_YOU_CONTROL)))); } private YaroksWavecrasher(final YaroksWavecrasher card) { diff --git a/Mage.Sets/src/mage/cards/y/YorionSkyNomad.java b/Mage.Sets/src/mage/cards/y/YorionSkyNomad.java index f85ab9ecd3e..c65013cc80d 100644 --- a/Mage.Sets/src/mage/cards/y/YorionSkyNomad.java +++ b/Mage.Sets/src/mage/cards/y/YorionSkyNomad.java @@ -1,8 +1,8 @@ package mage.cards.y; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.OneShotNonTargetEffect; import mage.abilities.effects.common.ExileReturnBattlefieldNextEndStepTargetEffect; import mage.abilities.keyword.CompanionAbility; 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 // and control. Return those cards to the battlefield at the beginning of the next end step. - Ability ability = new EntersBattlefieldTriggeredAbility( - new ExileReturnBattlefieldNextEndStepTargetEffect().setText(ruleText)); - ability.addTarget(new TargetPermanent(0, Integer.MAX_VALUE, filter, true)); - this.addAbility(ability); + this.addAbility(new EntersBattlefieldTriggeredAbility(new OneShotNonTargetEffect( + new ExileReturnBattlefieldNextEndStepTargetEffect().setText(ruleText), + new TargetPermanent(0, Integer.MAX_VALUE, filter, true)))); } private YorionSkyNomad(final YorionSkyNomad card) { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/OneShotNonTargetTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/OneShotNonTargetTest.java new file mode 100644 index 00000000000..686b7ae7597 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/OneShotNonTargetTest.java @@ -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); + } +} diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index cb73a0ec282..e8848440b9d 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -1749,6 +1749,10 @@ public abstract class AbilityImpl implements Ability { @Override public AbilityImpl setTargetAdjuster(TargetAdjuster targetAdjuster) { + if (targetAdjuster == null) { + this.targetAdjuster = null; + return this; + } if (targetAdjuster instanceof GenericTargetAdjuster && this.getTargets().isEmpty()) { throw new IllegalStateException("Target adjuster being added but no targets are set!"); } diff --git a/Mage/src/main/java/mage/abilities/common/DiesCreatureTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DiesCreatureTriggeredAbility.java index 9b885b1bf6e..7df6846dc67 100644 --- a/Mage/src/main/java/mage/abilities/common/DiesCreatureTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DiesCreatureTriggeredAbility.java @@ -3,6 +3,7 @@ package mage.abilities.common; import mage.MageObject; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; +import mage.constants.SetTargetPointer; import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; @@ -18,7 +19,7 @@ import mage.target.targetpointer.FixedTarget; public class DiesCreatureTriggeredAbility extends TriggeredAbilityImpl { protected FilterPermanent filter; - private boolean setTargetPointer; + private SetTargetPointer setTargetPointer; public DiesCreatureTriggeredAbility(Effect effect, boolean optional) { this(effect, optional, false); @@ -33,7 +34,7 @@ public class DiesCreatureTriggeredAbility extends TriggeredAbilityImpl { if (another) { filter.add(AnotherPredicate.instance); } - this.setTargetPointer = setTargetPointer; + this.setTargetPointer = (setTargetPointer ? SetTargetPointer.PERMANENT : SetTargetPointer.NONE); } public DiesCreatureTriggeredAbility(Effect effect, boolean optional, FilterPermanent filter) { @@ -44,7 +45,14 @@ public class DiesCreatureTriggeredAbility extends TriggeredAbilityImpl { 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) { + 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); this.filter = filter; this.setTargetPointer = setTargetPointer; @@ -75,8 +83,17 @@ public class DiesCreatureTriggeredAbility extends TriggeredAbilityImpl { return false; } getEffects().setValue("creatureDied", zEvent.getTarget()); - if (setTargetPointer) { - this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId(), game)); + switch (setTargetPointer) { + 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; } diff --git a/Mage/src/main/java/mage/abilities/costs/common/PutCountersTargetCost.java b/Mage/src/main/java/mage/abilities/costs/common/PutCountersTargetCost.java new file mode 100644 index 00000000000..1baac83c362 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/costs/common/PutCountersTargetCost.java @@ -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); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/OneShotNonTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/OneShotNonTargetEffect.java new file mode 100644 index 00000000000..acb09c56ac1 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/OneShotNonTargetEffect.java @@ -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); + } +} diff --git a/Mage/src/main/java/mage/target/targetadjustment/ConditionalTargetAdjuster.java b/Mage/src/main/java/mage/target/targetadjustment/ConditionalTargetAdjuster.java index 0a3f94ee8e8..9bc39becaaf 100644 --- a/Mage/src/main/java/mage/target/targetadjustment/ConditionalTargetAdjuster.java +++ b/Mage/src/main/java/mage/target/targetadjustment/ConditionalTargetAdjuster.java @@ -71,6 +71,10 @@ public class ConditionalTargetAdjuster implements TargetAdjuster { blueprintTarget = ability.getTargets().get(0).copy(); } } + @Override + public void clearDefaultTargets() { + blueprintTarget = null; + } @Override public void adjustTargets(Ability ability, Game game) { diff --git a/Mage/src/main/java/mage/target/targetadjustment/GenericTargetAdjuster.java b/Mage/src/main/java/mage/target/targetadjustment/GenericTargetAdjuster.java index 83184a706ec..89162d21524 100644 --- a/Mage/src/main/java/mage/target/targetadjustment/GenericTargetAdjuster.java +++ b/Mage/src/main/java/mage/target/targetadjustment/GenericTargetAdjuster.java @@ -14,4 +14,8 @@ public abstract class GenericTargetAdjuster implements TargetAdjuster { throw new IllegalStateException("Wrong code usage: target adjuster already has blueprint target - " + blueprintTarget); } } + @Override + public void clearDefaultTargets() { + blueprintTarget = null; + } } diff --git a/Mage/src/main/java/mage/target/targetadjustment/TargetAdjuster.java b/Mage/src/main/java/mage/target/targetadjustment/TargetAdjuster.java index 5b4020e1e70..bd93e6ea8d5 100644 --- a/Mage/src/main/java/mage/target/targetadjustment/TargetAdjuster.java +++ b/Mage/src/main/java/mage/target/targetadjustment/TargetAdjuster.java @@ -20,4 +20,7 @@ public interface TargetAdjuster extends Serializable { */ default void addDefaultTargets(Ability ability) { } + + default void clearDefaultTargets() { + } }