diff --git a/Mage.Sets/src/mage/cards/e/EmetSelchOfTheThirdSeat.java b/Mage.Sets/src/mage/cards/e/EmetSelchOfTheThirdSeat.java new file mode 100644 index 00000000000..8b478cbe95e --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EmetSelchOfTheThirdSeat.java @@ -0,0 +1,137 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.BatchTriggeredAbility; +import mage.abilities.TriggeredAbility; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; +import mage.abilities.effects.common.replacement.ThatSpellGraveyardExileReplacementEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.predicate.card.CastFromZonePredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.LifeLostBatchEvent; +import mage.game.events.LifeLostEvent; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +import java.util.List; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class EmetSelchOfTheThirdSeat extends CardImpl { + + private static final FilterCard filter = new FilterCard("spells you cast from your graveyard"); + + static { + filter.add(new CastFromZonePredicate(Zone.GRAVEYARD)); + } + + public EmetSelchOfTheThirdSeat(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.ELDER); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Spells you cast from your graveyard cost {2} less to cast. + this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 2))); + + // Whenever one or more opponents lose life, you may cast target instant or sorcery card from your graveyard. If that spell would be put into your graveyard, exile it instead. Do this only once each turn. + this.addAbility(new EmetSelchOfTheThirdSeatAbility()); + } + + private EmetSelchOfTheThirdSeat(final EmetSelchOfTheThirdSeat card) { + super(card); + } + + @Override + public EmetSelchOfTheThirdSeat copy() { + return new EmetSelchOfTheThirdSeat(this); + } +} + +class EmetSelchOfTheThirdSeatAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility { + + EmetSelchOfTheThirdSeatAbility() { + super(Zone.BATTLEFIELD, new EmetSelchOfTheThirdSeatEffect()); + this.setTriggerPhrase("Whenever one or more opponents lose life, "); + this.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY_FROM_YOUR_GRAVEYARD)); + this.setDoOnlyOnceEachTurn(true); + } + + private EmetSelchOfTheThirdSeatAbility(final EmetSelchOfTheThirdSeatAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.LOST_LIFE_BATCH; + } + + @Override + public boolean checkEvent(LifeLostEvent event, Game game) { + return game.getOpponents(getControllerId()).contains(event.getTargetId()); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + List filteredEvents = getFilteredEvents((LifeLostBatchEvent) event, game); + return !filteredEvents.isEmpty() + && CardUtil + .getEventTargets(event) + .stream() + .anyMatch(uuid -> LifeLostBatchEvent.getLifeLostByPlayer(filteredEvents, uuid) > 0); + } + + @Override + public EmetSelchOfTheThirdSeatAbility copy() { + return new EmetSelchOfTheThirdSeatAbility(this); + } +} + +class EmetSelchOfTheThirdSeatEffect extends OneShotEffect { + + EmetSelchOfTheThirdSeatEffect() { + super(Outcome.Benefit); + staticText = "cast target instant or sorcery card from your graveyard. " + + "If that spell would be put into your graveyard, exile it instead"; + } + + private EmetSelchOfTheThirdSeatEffect(final EmetSelchOfTheThirdSeatEffect effect) { + super(effect); + } + + @Override + public EmetSelchOfTheThirdSeatEffect copy() { + return new EmetSelchOfTheThirdSeatEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(getTargetPointer().getFirst(game, source)); + if (player == null || card == null || !CardUtil.castSingle(player, source, game, card)) { + // if the spell isn't cast then the ability can be used again in the same turn + TriggeredAbility.clearDidThisTurn(source, game); + return false; + } + game.addEffect(new ThatSpellGraveyardExileReplacementEffect(true) + .setTargetPointer(new FixedTarget(card, game)), source); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GrahaTiaScionReborn.java b/Mage.Sets/src/mage/cards/g/GrahaTiaScionReborn.java new file mode 100644 index 00000000000..4cd50d9f558 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GrahaTiaScionReborn.java @@ -0,0 +1,123 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.permanent.token.HeroToken; +import mage.game.permanent.token.Token; +import mage.game.stack.Spell; +import mage.util.CardUtil; + +import java.util.Optional; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class GrahaTiaScionReborn extends CardImpl { + + public GrahaTiaScionReborn(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{U}{B}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.CAT); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Throw Wide the Gates -- Whenever you cast a noncreature spell, you may pay X life, where X is that spell's mana value. If you do, create a 1/1 colorless Hero creature token and put X +1/+1 counters on it. Do this only once each turn. + this.addAbility(new SpellCastControllerTriggeredAbility(new DoIfCostPaid( + new GrahaTiaScionRebornEffect(), + new PayLifeCost( + GrahaTiaScionRebornValue.instance, + "pay X life, where X is that spell's mana value" + ) + ), StaticFilters.FILTER_SPELL_A_NON_CREATURE, false).setDoOnlyOnceEachTurn(true).withFlavorWord("Throw Wide the Gates")); + } + + private GrahaTiaScionReborn(final GrahaTiaScionReborn card) { + super(card); + } + + @Override + public GrahaTiaScionReborn copy() { + return new GrahaTiaScionReborn(this); + } +} + +enum GrahaTiaScionRebornValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return CardUtil.getEffectValueFromAbility(sourceAbility, "spellCast", Spell.class) + .map(Spell::getManaValue) + .orElse(0); + } + + @Override + public GrahaTiaScionRebornValue copy() { + return this; + } + + @Override + public String getMessage() { + return ""; + } + + @Override + public String toString() { + return "X"; + } +} + +class GrahaTiaScionRebornEffect extends OneShotEffect { + + GrahaTiaScionRebornEffect() { + super(Outcome.Benefit); + staticText = "create a 1/1 colorless Hero creature token and put X +1/+1 counters on it"; + } + + private GrahaTiaScionRebornEffect(final GrahaTiaScionRebornEffect effect) { + super(effect); + } + + @Override + public GrahaTiaScionRebornEffect copy() { + return new GrahaTiaScionRebornEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Token token = new HeroToken(); + token.putOntoBattlefield(1, game, source); + int count = GrahaTiaScionRebornValue.instance.calculate(game, source, this); + if (count < 1) { + return true; + } + for (UUID tokenId : token.getLastAddedTokenIds()) { + Optional.ofNullable(tokenId) + .map(game::getPermanent) + .ifPresent(permanent -> permanent.addCounters(CounterType.P1P1.createInstance(count), source, game)); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/i/IrreverentGremlin.java b/Mage.Sets/src/mage/cards/i/IrreverentGremlin.java index 8c96753fd03..00b7a9d84db 100644 --- a/Mage.Sets/src/mage/cards/i/IrreverentGremlin.java +++ b/Mage.Sets/src/mage/cards/i/IrreverentGremlin.java @@ -1,7 +1,5 @@ package mage.cards.i; -import java.util.UUID; - import mage.MageInt; import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; import mage.abilities.costs.common.DiscardCardCost; @@ -18,6 +16,8 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.mageobject.AnotherPredicate; import mage.filter.predicate.mageobject.PowerPredicate; +import java.util.UUID; + /** * @author paasar */ @@ -41,15 +41,9 @@ public final class IrreverentGremlin extends CardImpl { this.addAbility(new MenaceAbility()); // Whenever another creature you control with power 2 or less enters, you may discard a card. If you do, draw a card. Do this only once each turn. - this.addAbility( - new EntersBattlefieldAllTriggeredAbility( - new DoIfCostPaid( - new DrawCardSourceControllerEffect(1), - new DiscardCardCost(), - null, - false), // since triggered ability is optional (do only once), DoIfCostPaid must not be - filter) - .setDoOnlyOnceEachTurn(true)); + this.addAbility(new EntersBattlefieldAllTriggeredAbility( + new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new DiscardCardCost()), filter + ).setDoOnlyOnceEachTurn(true)); } private IrreverentGremlin(final IrreverentGremlin card) { diff --git a/Mage.Sets/src/mage/cards/s/ShaunFatherOfSynths.java b/Mage.Sets/src/mage/cards/s/ShaunFatherOfSynths.java index d4047a1f080..e0f5767d246 100644 --- a/Mage.Sets/src/mage/cards/s/ShaunFatherOfSynths.java +++ b/Mage.Sets/src/mage/cards/s/ShaunFatherOfSynths.java @@ -1,9 +1,7 @@ package mage.cards.s; -import java.util.UUID; - import mage.MageInt; -import mage.abilities.TriggeredAbility; +import mage.abilities.Ability; import mage.abilities.common.AttacksWithCreaturesTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; import mage.abilities.effects.Effect; @@ -14,6 +12,8 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; +import mage.constants.Zone; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.AnotherPredicate; @@ -21,8 +21,9 @@ import mage.filter.predicate.permanent.AttackingPredicate; import mage.filter.predicate.permanent.TokenPredicate; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author alexander-novo */ public class ShaunFatherOfSynths extends CardImpl { @@ -41,7 +42,7 @@ public class ShaunFatherOfSynths extends CardImpl { } public ShaunFatherOfSynths(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[] { CardType.CREATURE }, "{3}{U}{R}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{R}"); this.supertype.add(SuperType.LEGENDARY); this.subtype.add(SubType.HUMAN); @@ -58,11 +59,14 @@ public class ShaunFatherOfSynths extends CardImpl { token.addCardType(CardType.CREATURE); token.addCardType(CardType.ARTIFACT); token.addSubType(SubType.SYNTH); - }).setText( - "create a tapped and attacking token that's a copy of target attacking legendary creature you control other than Shaun, except it's not legendary and it's a Synth artifact creature in addition to its other types"); - TriggeredAbility ability = new AttacksWithCreaturesTriggeredAbility(effect, 1); + }).setText("create a tapped and attacking token that's a copy of target " + + "attacking legendary creature you control other than {this}, " + + "except it's not legendary and it's a Synth artifact creature in addition to its other types"); + Ability ability = new AttacksWithCreaturesTriggeredAbility( + Zone.BATTLEFIELD, effect, 1, + StaticFilters.FILTER_PERMANENT_CREATURES, false, true + ); ability.addTarget(new TargetPermanent(attackFilter)); - ability.setOptional(); this.addAbility(ability); // When Shaun leaves the battlefield, exile all Synth tokens you control. diff --git a/Mage.Sets/src/mage/sets/FinalFantasyCommander.java b/Mage.Sets/src/mage/sets/FinalFantasyCommander.java index 5ae88676e97..5ea675ee143 100644 --- a/Mage.Sets/src/mage/sets/FinalFantasyCommander.java +++ b/Mage.Sets/src/mage/sets/FinalFantasyCommander.java @@ -133,6 +133,8 @@ public final class FinalFantasyCommander extends ExpansionSet { cards.add(new SetCardInfo("Edgar, Master Machinist", 80, Rarity.RARE, mage.cards.e.EdgarMasterMachinist.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Elena, Turk Recruit", 133, Rarity.RARE, mage.cards.e.ElenaTurkRecruit.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Elena, Turk Recruit", 18, Rarity.RARE, mage.cards.e.ElenaTurkRecruit.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Emet-Selch of the Third Seat", 170, Rarity.RARE, mage.cards.e.EmetSelchOfTheThirdSeat.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Emet-Selch of the Third Seat", 81, Rarity.RARE, mage.cards.e.EmetSelchOfTheThirdSeat.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Endless Detour", 324, Rarity.RARE, mage.cards.e.EndlessDetour.class)); cards.add(new SetCardInfo("Espers to Magicite", 114, Rarity.RARE, mage.cards.e.EspersToMagicite.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Espers to Magicite", 43, Rarity.RARE, mage.cards.e.EspersToMagicite.class, NON_FULL_USE_VARIOUS)); @@ -160,6 +162,11 @@ public final class FinalFantasyCommander extends ExpansionSet { cards.add(new SetCardInfo("Fortified Village", 396, Rarity.RARE, mage.cards.f.FortifiedVillage.class)); cards.add(new SetCardInfo("Furious Rise", 294, Rarity.UNCOMMON, mage.cards.f.FuriousRise.class)); cards.add(new SetCardInfo("Furycalm Snarl", 397, Rarity.RARE, mage.cards.f.FurycalmSnarl.class)); + cards.add(new SetCardInfo("G'raha Tia, Scion Reborn", 172, Rarity.MYTHIC, mage.cards.g.GrahaTiaScionReborn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("G'raha Tia, Scion Reborn", 203, Rarity.MYTHIC, mage.cards.g.GrahaTiaScionReborn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("G'raha Tia, Scion Reborn", 211, Rarity.MYTHIC, mage.cards.g.GrahaTiaScionReborn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("G'raha Tia, Scion Reborn", 222, Rarity.MYTHIC, mage.cards.g.GrahaTiaScionReborn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("G'raha Tia, Scion Reborn", 3, Rarity.MYTHIC, mage.cards.g.GrahaTiaScionReborn.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Game Trail", 398, Rarity.RARE, mage.cards.g.GameTrail.class)); cards.add(new SetCardInfo("Gatta and Luzzu", 134, Rarity.RARE, mage.cards.g.GattaAndLuzzu.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Gatta and Luzzu", 19, Rarity.RARE, mage.cards.g.GattaAndLuzzu.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/rules/DoThisOnlyOnceEachTurnTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/rules/DoThisOnlyOnceEachTurnTest.java index 462169c4e42..25825af7b7e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/rules/DoThisOnlyOnceEachTurnTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/rules/DoThisOnlyOnceEachTurnTest.java @@ -124,4 +124,31 @@ public class DoThisOnlyOnceEachTurnTest extends CardTestPlayerBase { setStopAt(1, PhaseStep.BEGIN_COMBAT); execute(); } + + private static final String emetSelch = "Emet-Selch of the Third Seat"; + private static final String hazard = "Tectonic Hazard"; + private static final String slinger = "Goblin Fireslinger"; + + @Test + public void testEmetSelch() { + addCard(Zone.HAND, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, emetSelch); + addCard(Zone.BATTLEFIELD, playerA, slinger, 2); + addCard(Zone.GRAVEYARD, playerA, hazard); + + // player is unable to cast spell despite choosing to, choice is reset + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}:", playerB); + addTarget(playerA, hazard); + setChoice(playerA, true); + + // this time player can cast and does, so ability doesn't trigger again + playLand(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Mountain"); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}:", playerB); + addTarget(playerA, hazard); + setChoice(playerA, true); + + setStrictChooseMode(true); + execute(); + assertLife(playerB, 20 - 1 - 1 - 1); + } } diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbility.java b/Mage/src/main/java/mage/abilities/TriggeredAbility.java index db5e60317f1..37785373766 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbility.java @@ -3,7 +3,9 @@ package mage.abilities; import mage.abilities.condition.Condition; import mage.game.Game; import mage.game.events.GameEvent; +import mage.util.CardUtil; +import java.util.Optional; import java.util.UUID; /** @@ -97,8 +99,6 @@ public interface TriggeredAbility extends Ability { boolean isOptional(); - TriggeredAbility setOptional(); - /** * Allow trigger to fire after source leave the battlefield (example: will use LKI on itself sacrifice) */ @@ -131,4 +131,34 @@ public interface TriggeredAbility extends Ability { TriggeredAbility setTriggerPhrase(String triggerPhrase); String getTriggerPhrase(); + + static String makeDidThisTurnString(Ability ability, Game game) { + return CardUtil.getCardZoneString("lastTurnUsed" + ability.getOriginalId(), ability.getSourceId(), game); + } + + static void setDidThisTurn(Ability ability, Game game) { + game.getState().setValue(makeDidThisTurnString(ability, game), game.getTurnNum()); + } + + /** + * For abilities which say "Do this only once each turn". + * Most of the time this is handled automatically by calling setDoOnlyOnceEachTurn(true), + * but sometimes the ability will need a way to clear whether it's been used this turn within an effect. + * + * @param ability + * @param game + */ + static void clearDidThisTurn(Ability ability, Game game) { + game.getState().removeValue(makeDidThisTurnString(ability, game)); + } + + static boolean checkDidThisTurn(Ability ability, Game game) { + return Optional + .ofNullable(makeDidThisTurnString(ability, game)) + .map(game.getState()::getValue) + .filter(Integer.class::isInstance) + .map(Integer.class::cast) + .filter(x -> x == game.getTurnNum()) + .isPresent(); + } } diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java b/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java index 3a7b24f68b2..7aa43910eca 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java @@ -156,13 +156,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge @Override public boolean checkUsedAlready(Game game) { - if (!doOnlyOnceEachTurn) { - return false; - } - Integer lastTurnUsed = (Integer) game.getState().getValue( - CardUtil.getCardZoneString("lastTurnUsed" + getOriginalId(), sourceId, game) - ); - return lastTurnUsed != null && lastTurnUsed == game.getTurnNum(); + return doOnlyOnceEachTurn && TriggeredAbility.checkDidThisTurn(this, game); } @Override @@ -209,7 +203,9 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge @Override public TriggeredAbility setDoOnlyOnceEachTurn(boolean doOnlyOnce) { this.doOnlyOnceEachTurn = doOnlyOnce; - setOptional(); + if (CardUtil.castStream(this.getAllEffects(), DoIfCostPaid.class).noneMatch(DoIfCostPaid::isOptional)) { + this.optional = true; + } return this; } @@ -268,11 +264,9 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge )) { return false; } - } - if (doOnlyOnceEachTurn) { - game.getState().setValue(CardUtil.getCardZoneString( - "lastTurnUsed" + getOriginalId(), sourceId, game - ), game.getTurnNum()); + if (doOnlyOnceEachTurn) { + TriggeredAbility.setDidThisTurn(this, game); + } } //20091005 - 603.4 if (!super.resolve(game)) { @@ -387,6 +381,7 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge private static boolean startsWithVerb(String ruleLow) { return ruleLow.startsWith("attach") + || ruleLow.startsWith("cast") || ruleLow.startsWith("change") || ruleLow.startsWith("counter") || ruleLow.startsWith("create") @@ -501,20 +496,6 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge return optional; } - @Override - public TriggeredAbility setOptional() { - this.optional = true; - - if (getEffects().stream().anyMatch( - effect -> effect instanceof DoIfCostPaid && ((DoIfCostPaid) effect).isOptional())) { - throw new IllegalArgumentException( - "DoIfCostPaid effect must have only one optional settings, but it have two (trigger + DoIfCostPaid): " - + this.getClass().getSimpleName()); - } - - return this; - } - @Override public TriggeredAbilityImpl setAbilityWord(AbilityWord abilityWord) { super.setAbilityWord(abilityWord); diff --git a/Mage/src/main/java/mage/abilities/common/AttacksWithCreaturesTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/AttacksWithCreaturesTriggeredAbility.java index 540dc1852ce..a818d19153c 100644 --- a/Mage/src/main/java/mage/abilities/common/AttacksWithCreaturesTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/AttacksWithCreaturesTriggeredAbility.java @@ -41,7 +41,11 @@ public class AttacksWithCreaturesTriggeredAbility extends TriggeredAbilityImpl { } public AttacksWithCreaturesTriggeredAbility(Zone zone, Effect effect, int minAttackers, FilterPermanent filter, boolean setTargetPointer) { - super(zone, effect); + this(zone, effect, minAttackers, filter, setTargetPointer, false); + } + + public AttacksWithCreaturesTriggeredAbility(Zone zone, Effect effect, int minAttackers, FilterPermanent filter, boolean setTargetPointer, boolean optional) { + super(zone, effect, optional); this.filter = filter; this.minAttackers = minAttackers; this.setTargetPointer = setTargetPointer; diff --git a/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java b/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java index 321c0ef7787..1118d48eb50 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java +++ b/Mage/src/main/java/mage/abilities/effects/common/DoIfCostPaid.java @@ -3,6 +3,7 @@ package mage.abilities.effects.common; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.Mode; +import mage.abilities.TriggeredAbility; import mage.abilities.costs.Cost; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; @@ -110,6 +111,7 @@ public class DoIfCostPaid extends OneShotEffect { didPay = true; game.informPlayers(player.getLogName() + " paid for " + mageObject.getLogName() + " - " + message); applyEffects(game, source, executingEffects); + TriggeredAbility.setDidThisTurn(source, game); player.resetStoredBookmark(game); // otherwise you can e.g. undo card drawn with Mentor of the Meek } else { // Paying cost was cancels so try to undo payment so far diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index e1088041d30..eb12ec70b86 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -1710,6 +1710,8 @@ public final class CardUtil { player.setCastSourceIdWithAlternateMana(card.getMainCard().getId(), manaCost, additionalCostsNormalCard, MageIdentifier.Default); } + game.getState().setValue("PlayFromNotOwnHandZone" + card.getMainCard().getId(), Boolean.TRUE); + // cast it boolean result = player.cast(player.chooseAbilityForCast(card.getMainCard(), game, noMana), game, noMana, new ApprovingObject(source, game));