diff --git a/Mage.Sets/src/mage/cards/r/RevengeStarWars.java b/Mage.Sets/src/mage/cards/r/RevengeStarWars.java index b3a4863c196..47d89de0233 100644 --- a/Mage.Sets/src/mage/cards/r/RevengeStarWars.java +++ b/Mage.Sets/src/mage/cards/r/RevengeStarWars.java @@ -1,8 +1,8 @@ package mage.cards.r; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.condition.Condition; +import mage.abilities.condition.common.YouLostLifeCondition; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; @@ -18,21 +18,22 @@ import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetOpponentsCreaturePermanent; import mage.target.targetpointer.FixedTarget; -import mage.watchers.common.PlayerLostLifeWatcher; + +import java.util.UUID; /** - * * @author Styxo */ public final class RevengeStarWars extends CardImpl { + private static final Condition condition = new YouLostLifeCondition(); + public RevengeStarWars(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}"); // Target creature you control gets +4/+0 until end of turn before it fights if you lost life this turn. this.getSpellAbility().addEffect(new ConditionalOneShotEffect( - new RevengeEffect(), - LostLifeCondition.instance, + new RevengeEffect(), condition, "Target creature you control gets +4/+0 until end of turn before it fights if you lost life this turn")); // Target creature you control fights target creature an opponent controls. @@ -52,22 +53,6 @@ public final class RevengeStarWars extends CardImpl { } } -enum LostLifeCondition implements Condition { - - instance; - - @Override - public boolean apply(Game game, Ability source) { - PlayerLostLifeWatcher watcher = game.getState().getWatcher(PlayerLostLifeWatcher.class); - UUID player = source.getControllerId(); - if (watcher != null && player != null) { - return watcher.getLifeLost(player) > 0; - } - return false; - } - -} - class RevengeEffect extends OneShotEffect { RevengeEffect() { diff --git a/Mage.Sets/src/mage/cards/s/StarseerMentor.java b/Mage.Sets/src/mage/cards/s/StarseerMentor.java new file mode 100644 index 00000000000..afa92a9b64e --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/StarseerMentor.java @@ -0,0 +1,96 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.OrCondition; +import mage.abilities.condition.common.YouGainedLifeCondition; +import mage.abilities.condition.common.YouLostLifeCondition; +import mage.abilities.costs.OrCost; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.DoUnlessTargetPlayerOrTargetsControllerPaysEffect; +import mage.abilities.effects.common.LoseLifeTargetEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; +import mage.target.common.TargetOpponent; +import mage.watchers.common.PlayerGainedLifeWatcher; +import mage.watchers.common.PlayerLostLifeWatcher; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class StarseerMentor extends CardImpl { + + private static final Condition condition = + new OrCondition( + "if you gained or lost life this turn", + new YouGainedLifeCondition(), + new YouLostLifeCondition() + ); + private static final Hint hint = new ConditionHint(condition); + + private static final FilterControlledPermanent filter = new FilterControlledPermanent("nonland permanent"); + + static { + filter.add(Predicates.not(CardType.LAND.getPredicate())); + } + + public StarseerMentor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{B}"); + + this.subtype.add(SubType.BAT); + this.subtype.add(SubType.WARLOCK); + this.power = new MageInt(3); + this.toughness = new MageInt(5); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // At the beginning of your end step, if you gained or lost life this turn, target opponent loses 3 life unless they sacrifice a nonland permanent or discard a card. + Ability ability = new ConditionalInterveningIfTriggeredAbility( + new BeginningOfEndStepTriggeredAbility( + new DoUnlessTargetPlayerOrTargetsControllerPaysEffect( + new LoseLifeTargetEffect(3), + new OrCost( + "sacrifice a nonland permanent or discard a card", + new SacrificeTargetCost(filter), + new DiscardCardCost() + ), + "Sacrifice a nonland permanent or discard a card to prevent losing 3 life?" + ), TargetController.YOU, false + ), condition, "At the beginning of your end step, if you gained or lost life this turn, " + + "target opponent loses 3 life unless they sacrifice a nonland permanent or discard a card." + ); + ability.addTarget(new TargetOpponent()); + ability.addWatcher(new PlayerGainedLifeWatcher()); + ability.addWatcher(new PlayerLostLifeWatcher()); + ability.addHint(hint); + this.addAbility(ability); + } + + private StarseerMentor(final StarseerMentor card) { + super(card); + } + + @Override + public StarseerMentor copy() { + return new StarseerMentor(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/t/TergridGodOfFright.java b/Mage.Sets/src/mage/cards/t/TergridGodOfFright.java index 6be4b3fb057..7da57a76f48 100644 --- a/Mage.Sets/src/mage/cards/t/TergridGodOfFright.java +++ b/Mage.Sets/src/mage/cards/t/TergridGodOfFright.java @@ -3,30 +3,30 @@ package mage.cards.t; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.OrCost; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DoUnlessTargetPlayerOrTargetsControllerPaysEffect; +import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.effects.common.UntapSourceEffect; import mage.abilities.keyword.MenaceAbility; import mage.cards.Card; import mage.cards.CardSetInfo; import mage.cards.ModalDoubleFacedCard; -import mage.choices.Choice; -import mage.choices.ChoiceImpl; import mage.constants.*; -import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentToken; import mage.players.Player; -import mage.target.TargetPermanent; import mage.target.TargetPlayer; -import mage.target.common.TargetSacrifice; import mage.target.targetpointer.FixedTarget; -import java.util.HashSet; -import java.util.Set; import java.util.UUID; /** @@ -34,6 +34,12 @@ import java.util.UUID; */ public final class TergridGodOfFright extends ModalDoubleFacedCard { + private static final FilterControlledPermanent filter = new FilterControlledPermanent("nonland permanent"); + + static { + filter.add(Predicates.not(CardType.LAND.getPredicate())); + } + public TergridGodOfFright(UUID ownerId, CardSetInfo setInfo) { super( ownerId, setInfo, @@ -58,7 +64,15 @@ public final class TergridGodOfFright extends ModalDoubleFacedCard { // Legendary Artifact // {T}: Target player loses 3 life unless they sacrifice a nonland permanent or discard a card. Ability tergridsLaternActivatedAbility = new SimpleActivatedAbility( - new TergridsLaternEffect(), new TapSourceCost() + new DoUnlessTargetPlayerOrTargetsControllerPaysEffect( + new LoseLifeTargetEffect(3), + new OrCost( + "sacrifice a nonland permanent or discard a card", + new SacrificeTargetCost(filter), + new DiscardCardCost() + ), + "Sacrifice a nonland permanent or discard a card to prevent losing 3 life?" + ), new TapSourceCost() ); tergridsLaternActivatedAbility.addTarget(new TargetPlayer()); this.getRightHalfCard().addAbility(tergridsLaternActivatedAbility); @@ -165,70 +179,4 @@ class TergridGodOfFrightEffect extends OneShotEffect { } return false; } -} - -class TergridsLaternEffect extends OneShotEffect { - - private static final String SACRIFICE_CHOICE = "Sacrifice a nonland permanent"; - private static final String DISCARD_CHOICE = "Discard a card"; - private static final String LIFE_LOSS_CHOICE = "Lose 3 life"; - - public TergridsLaternEffect() { - super(Outcome.Detriment); - staticText = "Target player loses 3 life unless they sacrifice a nonland permanent or discard a card"; - } - - private TergridsLaternEffect(final TergridsLaternEffect effect) { - super(effect); - } - - @Override - public TergridsLaternEffect copy() { - return new TergridsLaternEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player targetedPlayer = game.getPlayer(source.getTargets().getFirstTarget()); - if (targetedPlayer == null) { - return false; - } - - // AI hint to discard/sacrifice before die - Outcome aiOutcome = (targetedPlayer.getLife() <= 3 * 2) ? Outcome.Benefit : Outcome.Detriment; - - Set choiceSet = new HashSet<>(); - if (game.getBattlefield().count(StaticFilters.FILTER_CONTROLLED_PERMANENT_NON_LAND, targetedPlayer.getId(), source, game) > 0) { - choiceSet.add(SACRIFICE_CHOICE); - } - if (targetedPlayer.getHand().size() > 0) { - choiceSet.add(DISCARD_CHOICE); - } - choiceSet.add(LIFE_LOSS_CHOICE); - String chosen; - if (choiceSet.size() > 1) { - Choice choice = new ChoiceImpl(true); - choice.setChoices(choiceSet); - targetedPlayer.choose(aiOutcome, choice, game); - chosen = choice.getChoice(); - if (chosen == null) { - // on disconnect - chosen = LIFE_LOSS_CHOICE; - } - } else { - chosen = LIFE_LOSS_CHOICE; - } - switch (chosen) { - case SACRIFICE_CHOICE: - TargetSacrifice target = new TargetSacrifice(StaticFilters.FILTER_CONTROLLED_PERMANENT_NON_LAND); - targetedPlayer.choose(Outcome.Sacrifice, target, source, game); - Permanent chosenLand = game.getPermanent(target.getFirstTarget()); - return chosenLand != null && chosenLand.sacrifice(source, game); - case DISCARD_CHOICE: - return targetedPlayer.discard(1, false, false, source, game).size() > 0; - case LIFE_LOSS_CHOICE: - return targetedPlayer.loseLife(3, game, source, false) > 0; - } - return false; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/t/TormentOfScarabs.java b/Mage.Sets/src/mage/cards/t/TormentOfScarabs.java index 41621310d63..2b97347146a 100644 --- a/Mage.Sets/src/mage/cards/t/TormentOfScarabs.java +++ b/Mage.Sets/src/mage/cards/t/TormentOfScarabs.java @@ -2,8 +2,12 @@ package mage.cards.t; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.costs.OrCost; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.DoUnlessTargetPlayerOrTargetsControllerPaysEffect; +import mage.abilities.effects.common.LoseLifeTargetEffect; import mage.abilities.keyword.EnchantAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -11,21 +15,23 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; import mage.constants.TargetController; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.Target; -import mage.target.TargetPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.Predicates; import mage.target.TargetPlayer; import java.util.UUID; /** - * @author LevelX2 + * @author LevelX2, Susucr */ public final class TormentOfScarabs extends CardImpl { + private static final FilterControlledPermanent filter = new FilterControlledPermanent("nonland permanent"); + + static { + filter.add(Predicates.not(CardType.LAND.getPredicate())); + } + public TormentOfScarabs(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{B}"); @@ -40,7 +46,15 @@ public final class TormentOfScarabs extends CardImpl { // At the beginning of enchanted player's upkeep, that player loses 3 life unless they sacrifice a nonland permanent or discards a card. this.addAbility(new BeginningOfUpkeepTriggeredAbility( - new TormentOfScarabsEffect(), TargetController.ENCHANTED, false + new DoUnlessTargetPlayerOrTargetsControllerPaysEffect( + new LoseLifeTargetEffect(3), + new OrCost( + "sacrifice a nonland permanent or discard a card", + new SacrificeTargetCost(filter), + new DiscardCardCost() + ), + "Sacrifice a nonland permanent or discard a card to prevent losing 3 life?" + ), TargetController.ENCHANTED, false )); } @@ -52,48 +66,4 @@ public final class TormentOfScarabs extends CardImpl { public TormentOfScarabs copy() { return new TormentOfScarabs(this); } -} - -class TormentOfScarabsEffect extends OneShotEffect { - - TormentOfScarabsEffect() { - super(Outcome.LoseLife); - this.staticText = "that player loses 3 life unless they sacrifice a nonland permanent or discard a card"; - } - - private TormentOfScarabsEffect(final TormentOfScarabsEffect effect) { - super(effect); - } - - @Override - public TormentOfScarabsEffect copy() { - return new TormentOfScarabsEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player enchantedPlayer = game.getPlayer(getTargetPointer().getFirst(game, source)); - if (enchantedPlayer == null) { - return false; - } - int permanents = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_NON_LAND, enchantedPlayer.getId(), game); - if (permanents > 0 && enchantedPlayer.chooseUse(outcome, "Sacrifice a nonland permanent?", - "Otherwise you have to discard a card or lose 3 life.", "Sacrifice", "Discard or life loss", source, game)) { - Target target = new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_NON_LAND); - if (enchantedPlayer.choose(outcome, target, source, game)) { - Permanent permanent = game.getPermanent(target.getFirstTarget()); - if (permanent != null) { - permanent.sacrifice(source, game); - return true; - } - } - } - if (!enchantedPlayer.getHand().isEmpty() && enchantedPlayer.chooseUse(outcome, "Discard a card?", - "Otherwise you lose 3 life.", "Discard", "Lose 3 life", source, game)) { - enchantedPlayer.discardOne(false, false, source, game); - return true; - } - enchantedPlayer.loseLife(3, game, source, false); - return true; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/t/TormentOfVenom.java b/Mage.Sets/src/mage/cards/t/TormentOfVenom.java index 5ccc56ee3c7..d1be39d2508 100644 --- a/Mage.Sets/src/mage/cards/t/TormentOfVenom.java +++ b/Mage.Sets/src/mage/cards/t/TormentOfVenom.java @@ -1,7 +1,6 @@ package mage.cards.t; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; @@ -22,8 +21,9 @@ import mage.target.Target; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class TormentOfVenom extends CardImpl { @@ -46,6 +46,7 @@ public final class TormentOfVenom extends CardImpl { } } +// TODO: simplify using DoUnlessTargetPlayerOrTargetsControllerPaysEffect and OrCost class TormentOfVenomEffect extends OneShotEffect { TormentOfVenomEffect() { diff --git a/Mage.Sets/src/mage/sets/Bloomburrow.java b/Mage.Sets/src/mage/sets/Bloomburrow.java index a47da55f72f..7d0d4976114 100644 --- a/Mage.Sets/src/mage/sets/Bloomburrow.java +++ b/Mage.Sets/src/mage/sets/Bloomburrow.java @@ -88,6 +88,7 @@ public final class Bloomburrow extends ExpansionSet { cards.add(new SetCardInfo("Shrike Force", 31, Rarity.UNCOMMON, mage.cards.s.ShrikeForce.class)); cards.add(new SetCardInfo("Splash Lasher", 73, Rarity.UNCOMMON, mage.cards.s.SplashLasher.class)); cards.add(new SetCardInfo("Stargaze", 114, Rarity.UNCOMMON, mage.cards.s.Stargaze.class)); + cards.add(new SetCardInfo("Starseer Mentor", 233, Rarity.UNCOMMON, mage.cards.s.StarseerMentor.class)); cards.add(new SetCardInfo("Steampath Charger", 153, Rarity.COMMON, mage.cards.s.SteampathCharger.class)); cards.add(new SetCardInfo("Stormcatch Mentor", 234, Rarity.UNCOMMON, mage.cards.s.StormcatchMentor.class)); cards.add(new SetCardInfo("Sunshower Druid", 195, Rarity.COMMON, mage.cards.s.SunshowerDruid.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/blb/StarseerMentorTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/blb/StarseerMentorTest.java new file mode 100644 index 00000000000..6661422efdd --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/blb/StarseerMentorTest.java @@ -0,0 +1,231 @@ +package org.mage.test.cards.single.blb; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class StarseerMentorTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.s.StarseerMentor Starseer Mentor} {3}{W}{B} + * Creature — Bat Warlock + * Flying, vigilance + * At the beginning of your end step, if you gained or lost life this turn, target opponent loses 3 life unless they sacrifice a nonland permanent or discard a card. + * 3/5 + */ + private static final String mentor = "Starseer Mentor"; + + @Test + public void test_NoTrigger() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, mentor); + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertLife(playerB, 20); + } + + @Test + public void test_LifeGain_Trigger() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, mentor); + addCard(Zone.HAND, playerA, "Angel of Mercy"); // etb gains 5 life + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Angel of Mercy"); + + addTarget(playerA, playerB); // target for the trigger. + // playerB has no way to pay for the cost, so no choice. + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertLife(playerB, 20 - 3); + } + + @Test + public void test_LifeLost_Trigger() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, mentor); + addCard(Zone.HAND, playerA, "Serpent Warrior"); // etb loses 3 life + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Serpent Warrior"); + + addTarget(playerA, playerB); // target for the trigger. + // playerB has no way to pay for the cost, so no choice. + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertLife(playerB, 20 - 3); + } + + @Test + public void test_Trigger_CanDiscard_AndDiscard() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, mentor); + addCard(Zone.HAND, playerA, "Angel of Mercy"); // etb gains 5 life + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.HAND, playerB, "Abandon Hope"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Angel of Mercy"); + + addTarget(playerA, playerB); // target for the trigger. + setChoice(playerB, true); // choose to pay the cost to not lose life + setChoice(playerB, "Abandon Hope"); // Choose to discard + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertLife(playerB, 20); + assertGraveyardCount(playerB, "Abandon Hope", 1); + } + + @Test + public void test_Trigger_CanDiscard_AndDontDiscard() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, mentor); + addCard(Zone.HAND, playerA, "Angel of Mercy"); // etb gains 5 life + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.HAND, playerB, "Abandon Hope"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Angel of Mercy"); + + addTarget(playerA, playerB); // target for the trigger. + setChoice(playerB, false); // choose to not pay the cost to not lose life + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertLife(playerB, 20 - 3); + assertHandCount(playerB, "Abandon Hope", 1); + } + + @Test + public void test_Trigger_CanSacrifice_AndSacrifice() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, mentor); + addCard(Zone.HAND, playerA, "Angel of Mercy"); // etb gains 5 life + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.BATTLEFIELD, playerB, "Elite Vanguard"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Angel of Mercy"); + + addTarget(playerA, playerB); // target for the trigger. + setChoice(playerB, true); // choose to pay the cost to not lose life + setChoice(playerB, "Elite Vanguard"); // Choose to sacrifice + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertLife(playerB, 20); + assertGraveyardCount(playerB, "Elite Vanguard", 1); + } + + @Test + public void test_Trigger_CanSacrifice_AndDontSacrifice() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, mentor); + addCard(Zone.HAND, playerA, "Angel of Mercy"); // etb gains 5 life + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.BATTLEFIELD, playerB, "Elite Vanguard"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Angel of Mercy"); + + addTarget(playerA, playerB); // target for the trigger. + setChoice(playerB, false); // choose to not pay the cost to not lose life + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertLife(playerB, 20 - 3); + assertPermanentCount(playerB, "Elite Vanguard", 1); + } + + + @Test + public void test_Trigger_CanSacrificeOrDiscard_AndSacrifice() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, mentor); + addCard(Zone.HAND, playerA, "Angel of Mercy"); // etb gains 5 life + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.BATTLEFIELD, playerB, "Elite Vanguard"); + addCard(Zone.HAND, playerB, "Abandon Hope"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Angel of Mercy"); + + addTarget(playerA, playerB); // target for the trigger. + setChoice(playerB, true); // choose to pay the cost to not lose life + setChoice(playerB, true); // choose to pay the sacrifice cost + setChoice(playerB, "Elite Vanguard"); // Choose to sacrifice + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertLife(playerB, 20); + assertGraveyardCount(playerB, "Elite Vanguard", 1); + assertHandCount(playerB, "Abandon Hope", 1); + } + + @Test + public void test_Trigger_CanSacrificeOrDiscard_AndDiscard() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, mentor); + addCard(Zone.HAND, playerA, "Angel of Mercy"); // etb gains 5 life + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.BATTLEFIELD, playerB, "Elite Vanguard"); + addCard(Zone.HAND, playerB, "Abandon Hope"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Angel of Mercy"); + + addTarget(playerA, playerB); // target for the trigger. + setChoice(playerB, true); // choose to pay the cost to not lose life + setChoice(playerB, false); // choose to not pay the sacrifice cost (so will discard) + setChoice(playerB, "Abandon Hope"); // Choose to discard + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertLife(playerB, 20); + assertPermanentCount(playerB, "Elite Vanguard", 1); + assertGraveyardCount(playerB, "Abandon Hope", 1); + } + + @Test + public void test_Trigger_CanSacrificeOrDiscard_AndDontPay() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, mentor); + addCard(Zone.HAND, playerA, "Angel of Mercy"); // etb gains 5 life + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.BATTLEFIELD, playerB, "Elite Vanguard"); + addCard(Zone.HAND, playerB, "Abandon Hope"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Angel of Mercy"); + + addTarget(playerA, playerB); // target for the trigger. + setChoice(playerB, false); // choose to not pay the cost to not lose life + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertLife(playerB, 20 - 3); + assertPermanentCount(playerB, "Elite Vanguard", 1); + assertHandCount(playerB, "Abandon Hope", 1); + } +} diff --git a/Mage/src/main/java/mage/abilities/condition/common/YouLostLifeCondition.java b/Mage/src/main/java/mage/abilities/condition/common/YouLostLifeCondition.java new file mode 100644 index 00000000000..f42ce09bce8 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/condition/common/YouLostLifeCondition.java @@ -0,0 +1,37 @@ +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.IntCompareCondition; +import mage.constants.ComparisonType; +import mage.game.Game; +import mage.watchers.common.PlayerLostLifeWatcher; + +/** + * Needs PlayerLostLifeWatcher to work + * + * @author Susucr + */ +public class YouLostLifeCondition extends IntCompareCondition { + + /** + * "if you lost life this turn" + */ + public YouLostLifeCondition() { + super(ComparisonType.MORE_THAN, 0); + } + + public YouLostLifeCondition(ComparisonType type, int value) { + super(type, value); + } + + @Override + protected int getInputValue(Game game, Ability source) { + PlayerLostLifeWatcher watcher = game.getState().getWatcher(PlayerLostLifeWatcher.class); + return watcher == null ? 0 : watcher.getLifeLost(source.getControllerId()); + } + + @Override + public String toString() { + return "if you lost " + (value == 0 ? "" : (value + 1) + " or more ") + "life this turn"; + } +}