From c942592c3b72ce4ec6e4eceead03192e19762fdf Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 6 Jul 2015 13:36:23 +0200 Subject: [PATCH] * Dash - Fixed that the creature was returned to hand from dash also if it left battlefield before. --- .../sets/returntoravnica/AngelOfSerenity.java | 11 ++-- .../sets/returntoravnica/UltimatePrice.java | 2 - .../cards/abilities/keywords/DashTest.java | 62 +++++++++++++------ .../cards/abilities/keywords/RenownTest.java | 27 ++++++++ .../mage/abilities/keyword/DashAbility.java | 44 ++++++++++--- 5 files changed, 112 insertions(+), 34 deletions(-) diff --git a/Mage.Sets/src/mage/sets/returntoravnica/AngelOfSerenity.java b/Mage.Sets/src/mage/sets/returntoravnica/AngelOfSerenity.java index e59ab2b006f..06879b41d58 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/AngelOfSerenity.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/AngelOfSerenity.java @@ -67,7 +67,6 @@ public class AngelOfSerenity extends CardImpl { this.power = new MageInt(5); this.toughness = new MageInt(6); - // Flying this.addAbility(FlyingAbility.getInstance()); @@ -75,7 +74,7 @@ public class AngelOfSerenity extends CardImpl { this.addAbility(new AngelOfSerenityTriggeredAbility()); // When Angel of Serenity leaves the battlefield, return the exiled cards to their owners' hands. - this.addAbility(new LeavesBattlefieldTriggeredAbility(new AngelOfSerenityLeaveEffect(), false )); + this.addAbility(new LeavesBattlefieldTriggeredAbility(new AngelOfSerenityLeaveEffect(), false)); } public AngelOfSerenity(final AngelOfSerenity card) { @@ -104,7 +103,7 @@ class AngelOfSerenityTriggeredAbility extends ZoneChangeTriggeredAbility { getTargets().clear(); FilterCreaturePermanent filter = new FilterCreaturePermanent("up to three other target creatures"); filter.add(new AnotherPredicate()); - TargetCreaturePermanent target1 = new TargetCreaturePermanent(0,3, filter, false); + TargetCreaturePermanent target1 = new TargetCreaturePermanent(0, 3, filter, false); game.getPlayer(getControllerId()).chooseTarget(Outcome.Exile, target1, this, game); if (target1.getTargets().size() > 0) { getTargets().add(target1); @@ -112,8 +111,8 @@ class AngelOfSerenityTriggeredAbility extends ZoneChangeTriggeredAbility { } int leftTargets = 3 - target1.getTargets().size(); if (leftTargets > 0) { - FilterCard filter2 = new FilterCreatureCard("up to " + leftTargets + " target creature card" + (leftTargets > 1?"s":"") +" from graveyards"); - TargetCardInGraveyard target2 = new TargetCardInGraveyard(0,leftTargets, filter2); + FilterCard filter2 = new FilterCreatureCard("up to " + leftTargets + " target creature card" + (leftTargets > 1 ? "s" : "") + " from graveyards"); + TargetCardInGraveyard target2 = new TargetCardInGraveyard(0, leftTargets, filter2); game.getPlayer(getControllerId()).chooseTarget(Outcome.Exile, target2, this, game); if (target2.getTargets().size() > 0) { getTargets().add(target2); @@ -160,7 +159,7 @@ class AngelOfSerenityEnterEffect extends OneShotEffect { } } - } else if (target instanceof TargetCardInGraveyard){ + } else if (target instanceof TargetCardInGraveyard) { for (UUID cardId : target.getTargets()) { Card card = game.getCard(cardId); if (card != null) { diff --git a/Mage.Sets/src/mage/sets/returntoravnica/UltimatePrice.java b/Mage.Sets/src/mage/sets/returntoravnica/UltimatePrice.java index 168ad1e8f70..2c2e3a4af0e 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/UltimatePrice.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/UltimatePrice.java @@ -34,7 +34,6 @@ import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.mageobject.MonocoloredPredicate; -import mage.target.Target; import mage.target.common.TargetCreaturePermanent; /** @@ -53,7 +52,6 @@ public class UltimatePrice extends CardImpl { super(ownerId, 82, "Ultimate Price", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{1}{B}"); this.expansionSetCode = "RTR"; - // Destroy target monocolored creature. this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); this.getSpellAbility().addEffect(new DestroyTargetEffect()); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DashTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DashTest.java index 5305854a07a..e5f3b6f0167 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DashTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DashTest.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package org.mage.test.cards.abilities.keywords; import mage.constants.PhaseStep; @@ -40,26 +39,25 @@ import org.mage.test.serverside.base.CardTestPlayerBase; public class DashTest extends CardTestPlayerBase { /** - * 702.108. Dash - * 702.108a Dash represents three abilities: two static abilities that function while the card with dash is - * on the stack, one of which may create a delayed triggered ability, and a static ability that - * functions while the object with dash is on the battlefield. “Dash [cost]” means “You may cast - * this card by paying [cost] rather that its mana cost,” “If this spell’s dash cost was paid, return the - * permanent this spell becomes to its owner’s hand at the beginning of the next end step,” and “As - * long as this permanent’s dash cost was paid, it has haste.” Paying a card’s dash cost follows the - * rules for paying alternative costs in rules 601.2b and 601.2e–g. - * - */ - - /** - * Screamreach Brawler - * Creature — Orc Berserker 2/3, 2R (3) - * Dash {1}{R} (You may cast this spell for its dash cost. If you do, it - * gains haste, and it's returned from the battlefield to its owner's hand - * at the beginning of the next end step.) + * 702.108. Dash 702.108a Dash represents three abilities: two static + * abilities that function while the card with dash is on the stack, one of + * which may create a delayed triggered ability, and a static ability that + * functions while the object with dash is on the battlefield. “Dash [cost]” + * means “You may cast this card by paying [cost] rather that its mana + * cost,” “If this spell’s dash cost was paid, return the permanent this + * spell becomes to its owner’s hand at the beginning of the next end step,” + * and “As long as this permanent’s dash cost was paid, it has haste.” + * Paying a card’s dash cost follows the rules for paying alternative costs + * in rules 601.2b and 601.2e–g. + * + */ + /** + * Screamreach Brawler Creature — Orc Berserker 2/3, 2R (3) Dash {1}{R} (You + * may cast this spell for its dash cost. If you do, it gains haste, and + * it's returned from the battlefield to its owner's hand at the beginning + * of the next end step.) * */ - @Test public void testDash() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); @@ -96,4 +94,30 @@ public class DashTest extends CardTestPlayerBase { } + /** + * Also dash returns creatures to your hand at end of turn even if they died + * that turn. + */ + @Test + public void testDashedCreatureDiesInCombat() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.HAND, playerA, "Screamreach Brawler"); // 2/3 + + addCard(Zone.BATTLEFIELD, playerB, "Geist of the Moors", 1); // 3/1 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Screamreach Brawler"); + setChoice(playerA, "Yes"); + attack(1, playerA, "Screamreach Brawler"); + block(1, playerB, "Geist of the Moors", "Screamreach Brawler"); + + setStopAt(2, PhaseStep.UNTAP); + execute(); + + assertLife(playerB, 20); + assertPermanentCount(playerA, "Screamreach Brawler", 0); + assertHandCount(playerA, "Screamreach Brawler", 0); + assertGraveyardCount(playerA, "Screamreach Brawler", 1); + assertGraveyardCount(playerB, "Geist of the Moors", 1); + + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RenownTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RenownTest.java index 533c50b39b4..16637cc14cd 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RenownTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/RenownTest.java @@ -211,4 +211,31 @@ public class RenownTest extends CardTestPlayerBase { } + /** + * Ability doesn't trigger when renowned. ("Whenever an opponent casts a + * noncreature spell, if ~ is renowned, ~ deals 2 damage to that player.") + */ + @Test + public void testScabClanBerserker() { + // Renown 1 + // Whenever an opponent casts a noncreature spell, if Scab-Clan Berserker is renowned, Scab-Clan Berserker deals 2 damage to that player. + addCard(Zone.BATTLEFIELD, playerA, "Scab-Clan Berserker"); // 2/2 {1}{R}{R} + + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.HAND, playerB, "Lightning Bolt"); + + attack(3, playerA, "Scab-Clan Berserker"); // 1 damage + castSpell(3, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", playerA); + + setStopAt(3, PhaseStep.END_TURN); + execute(); + + Permanent berserker = getPermanent("Scab-Clan Berserker", playerA); + Assert.assertEquals("has has renown", true, berserker.isRenown()); + assertPowerToughness(playerA, "Scab-Clan Berserker", 3, 3); + + assertLife(playerA, 17); // Lightning Bolt + assertLife(playerB, 16); // 2 from attack 2 from triggered ability + + } } diff --git a/Mage/src/mage/abilities/keyword/DashAbility.java b/Mage/src/mage/abilities/keyword/DashAbility.java index 4072e1a8563..4a2d96170f0 100644 --- a/Mage/src/mage/abilities/keyword/DashAbility.java +++ b/Mage/src/mage/abilities/keyword/DashAbility.java @@ -31,6 +31,7 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; import mage.abilities.SpellAbility; import mage.abilities.StaticAbility; import mage.abilities.common.EntersBattlefieldAbility; @@ -44,7 +45,7 @@ import mage.abilities.costs.Costs; import mage.abilities.costs.CostsImpl; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; -import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; import mage.cards.Card; @@ -76,12 +77,9 @@ public class DashAbility extends StaticAbility implements AlternativeSourceCosts Ability ability = new EntersBattlefieldAbility( new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.Custom, false), DashedCondition.getInstance(), false, "", ""); - Effect effect = new ReturnToHandTargetEffect(); - effect.setText("return the dashed creature from the battlefield to its owner's hand"); - effect.setTargetPointer(new FixedTarget(card.getId())); - ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), false)); + ability.addEffect(new DashAddDelayedTriggeredAbilityEffect()); addSubAbility(ability); - + } public DashAbility(final DashAbility ability) { @@ -134,7 +132,7 @@ public class DashAbility extends StaticAbility implements AlternativeSourceCosts this.resetDash(); for (AlternativeCost2 dashCost : alternativeSourceCosts) { if (dashCost.canPay(ability, sourceId, controllerId, game) - && player.chooseUse(Outcome.Benefit, new StringBuilder(KEYWORD).append(" the creature for ").append(dashCost.getText(true)).append(" ?").toString(), ability, game)) { + && player.chooseUse(Outcome.Benefit, KEYWORD + " the creature for " + dashCost.getText(true) + " ?", ability, game)) { activateDash(dashCost, game); ability.getManaCostsToPay().clear(); ability.getCosts().clear(); @@ -209,3 +207,35 @@ public class DashAbility extends StaticAbility implements AlternativeSourceCosts return alterCosts; } } + +class DashAddDelayedTriggeredAbilityEffect extends OneShotEffect { + + public DashAddDelayedTriggeredAbilityEffect() { + super(Outcome.Benefit); + this.staticText = "return the dashed creature from the battlefield to its owner's hand"; + } + + public DashAddDelayedTriggeredAbilityEffect(final DashAddDelayedTriggeredAbilityEffect effect) { + super(effect); + } + + @Override + public DashAddDelayedTriggeredAbilityEffect copy() { + return new DashAddDelayedTriggeredAbilityEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Effect effect = new ReturnToHandTargetEffect(); + effect.setText("return the dashed creature from the battlefield to its owner's hand"); + effect.setTargetPointer(new FixedTarget(source.getSourceId())); + // init target pointer now because the dashed creature will only be returned from current zone + effect.getTargetPointer().init(game, source); + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + delayedAbility.setSourceObject(source.getSourceObject(game), game); + game.addDelayedTriggeredAbility(delayedAbility); + return false; + } +}