diff --git a/Mage.Sets/src/mage/cards/b/BonecrusherGiant.java b/Mage.Sets/src/mage/cards/b/BonecrusherGiant.java index 8a756691c2..3e62dc9af2 100644 --- a/Mage.Sets/src/mage/cards/b/BonecrusherGiant.java +++ b/Mage.Sets/src/mage/cards/b/BonecrusherGiant.java @@ -1,16 +1,16 @@ package mage.cards.b; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.BecomesTargetTriggeredAbility; -import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.continuous.DamageCantBePreventedEffect; import mage.cards.AdventureCard; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SetTargetPointer; +import mage.constants.SubType; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; import mage.target.common.TargetAnyTarget; import java.util.UUID; @@ -34,7 +34,7 @@ public final class BonecrusherGiant extends AdventureCard { // Stomp // Damage can’t be prevented this turn. Stomp deals 2 damage to any target. - this.getSpellCard().getSpellAbility().addEffect(new StompEffect()); + this.getSpellCard().getSpellAbility().addEffect(new DamageCantBePreventedEffect(Duration.EndOfTurn, "Damage can't be prevented this turn", false, false)); this.getSpellCard().getSpellAbility().addEffect(new DamageTargetEffect(2)); this.getSpellCard().getSpellAbility().addTarget(new TargetAnyTarget()); } @@ -47,36 +47,4 @@ public final class BonecrusherGiant extends AdventureCard { public BonecrusherGiant copy() { return new BonecrusherGiant(this); } -} - -class StompEffect extends ReplacementEffectImpl { - - StompEffect() { - super(Duration.EndOfTurn, Outcome.Benefit); - staticText = "Damage can't be prevented this turn."; - } - - private StompEffect(final StompEffect effect) { - super(effect); - } - - @Override - public StompEffect copy() { - return new StompEffect(this); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - return true; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.PREVENT_DAMAGE; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - return true; - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/q/QuestingBeast.java b/Mage.Sets/src/mage/cards/q/QuestingBeast.java index 5ff8e7b120..4f8a5ff1fe 100644 --- a/Mage.Sets/src/mage/cards/q/QuestingBeast.java +++ b/Mage.Sets/src/mage/cards/q/QuestingBeast.java @@ -82,7 +82,7 @@ class QuestingBeastPreventionEffect extends ContinuousRuleModifyingEffectImpl { QuestingBeastPreventionEffect() { super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "Combat damage that would be dealt by creatures you control can't be prevented."; + staticText = "Combat damage that would be dealt by creatures you control can't be prevented"; } private QuestingBeastPreventionEffect(final QuestingBeastPreventionEffect effect) { diff --git a/Mage.Sets/src/mage/cards/s/StormwildCapridor.java b/Mage.Sets/src/mage/cards/s/StormwildCapridor.java index bdaa015513..ac782e8c43 100644 --- a/Mage.Sets/src/mage/cards/s/StormwildCapridor.java +++ b/Mage.Sets/src/mage/cards/s/StormwildCapridor.java @@ -80,8 +80,9 @@ class StormwildCapridorEffect extends PreventionEffectImpl { if (permanent != null) { permanent.addCounters(CounterType.P1P1.createInstance(preventionEffectData.getPreventedDamage()), source, game); } + return true; } - return preventionEffectData.isReplaced(); + return false; } @Override @@ -90,11 +91,8 @@ class StormwildCapridorEffect extends PreventionEffectImpl { || !super.applies(event, source, game)) { return false; } - if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.PREVENT_DAMAGE, source.getSourceId(), source.getControllerId()))) { - DamageCreatureEvent damageEvent = (DamageCreatureEvent) event; - return !damageEvent.isCombatDamage(); - } - return false; + DamageCreatureEvent damageEvent = (DamageCreatureEvent) event; + return !damageEvent.isCombatDamage(); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ConditionalPreventionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ConditionalPreventionTest.java index 2a7e23d40a..5f61921279 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ConditionalPreventionTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ConditionalPreventionTest.java @@ -135,7 +135,7 @@ public class ConditionalPreventionTest extends CardTestPlayerBase { } @Test - public void test_PrentableCombatDamage() { + public void test_PreventableCombatDamage() { // Prevent all damage that would be dealt to creatures. addCard(Zone.BATTLEFIELD, playerA, "Bubble Matrix", 1); addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); @@ -190,4 +190,77 @@ public class ConditionalPreventionTest extends CardTestPlayerBase { assertLife(playerA, 20); assertLife(playerB, 20 - 2); } + + @Test + public void test_PreventSomeDamage_Normal() { + // Kicker-Sacrifice a land. + // Prevent the next 3 damage that would be dealt this turn to any number of target creatures and/or players, divided as you choose. + // If Pollen Remedy was kicked, prevent the next 6 damage this way instead. + addCard(Zone.HAND, playerA, "Pollen Remedy", 1); // {W} + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.HAND, playerA, "Swamp", 1); // for kicker + // + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + + // add shield for 3 damage + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pollen Remedy"); + setChoice(playerA, "No"); // no kicker + addTargetAmount(playerA, "Balduvian Bears", 3); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("shield", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pollen Remedy", 1); + + // 6 damage to die (if no shield then can cast only 1 bolt) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + checkGraveyardCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 2); + checkGraveyardCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_PreventSomeDamage_Kicked() { + // Kicker-Sacrifice a land. + // Prevent the next 3 damage that would be dealt this turn to any number of target creatures and/or players, divided as you choose. + // If Pollen Remedy was kicked, prevent the next 6 damage this way instead. + addCard(Zone.HAND, playerA, "Pollen Remedy", 1); // {W} + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); // for kicker + // + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + + // add shield for 6 damage + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pollen Remedy"); + setChoice(playerA, "Yes"); // use kicker + setChoice(playerA, "Swamp"); // kicker cost + addTargetAmount(playerA, "Balduvian Bears", 6); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("shield", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pollen Remedy", 1); + + // 9 damage to die (if no shield then can cast only 1 bolt) + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Balduvian Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + checkGraveyardCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 3); + checkGraveyardCount("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/adventure/AdventureCardsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/adventure/AdventureCardsTest.java index 8271863f2b..92ba5af9a8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/adventure/AdventureCardsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/adventure/AdventureCardsTest.java @@ -650,4 +650,43 @@ public class AdventureCardsTest extends CardTestPlayerBase { execute(); assertAllCommandsUsed(); } + + @Test + public void test_BonecrusherGiant_Stopm() { + // bug with non working stopm: https://github.com/magefree/mage/issues/6915 + + // If noncombat damage would be dealt to Stormwild Capridor, prevent that damage. + // Put a +1/+1 counter on Stormwild Capridor for each 1 damage prevented this way. + addCard(Zone.BATTLEFIELD, playerA, "Stormwild Capridor@storm", 2); // 1/3 + // + addCard(Zone.HAND, playerA, "Lightning Bolt", 2); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + // + // Stomp {1}{R} + // Damage can’t be prevented this turn. Stomp deals 2 damage to any target. + addCard(Zone.HAND, playerA, "Bonecrusher Giant", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + + // prevent + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "@storm.1"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("prevent", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 1); + checkGraveyardCount("prevent", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "@storm.1", 0); + + // prepare protect by stomp + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Stomp"); + addTarget(playerA, playerB); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // can't prevent + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "@storm.2"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkGraveyardCount("can't prevent", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 2); + checkGraveyardCount("can't prevent", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "@storm.2", 1); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 5bf77cb250..016adfd48d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -2450,7 +2450,7 @@ public class TestPlayer implements Player { //Assert.fail("Wrong target"); } - this.chooseStrictModeFailed("target", game, getInfo(source, game) + "; " + getInfo(target)); + this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target)); return computerPlayer.chooseTarget(outcome, cards, target, source, game); } @@ -3760,7 +3760,7 @@ public class TestPlayer implements Player { } } - this.chooseStrictModeFailed("target", game, getInfo(source, game) + "; " + getInfo(target)); + this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target)); return computerPlayer.chooseTargetAmount(outcome, target, source, game); } diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index b594a1f1e9..506afe2b16 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -287,7 +287,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } } Assert.assertFalse("Wrong stop command on " + this.stopOnTurn + " / " + this.stopAtStep + " (" + this.stopAtStep.getIndex() + ")" - + " (found actions after stop on " + maxTurn + " / " + maxPhase + ")", + + " (found actions after stop on " + maxTurn + " / " + maxPhase + ")", (maxTurn > this.stopOnTurn) || (maxTurn == this.stopOnTurn && maxPhase > this.stopAtStep.getIndex())); if (!currentGame.isPaused()) { @@ -916,7 +916,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } Assert.assertNotNull("There is no such permanent under player's control, player=" + player.getName() + ", cardName=" + cardName, found); - + Assert.assertEquals(amount, found.getAbilities(currentGame).stream() .filter(a -> searchedAbility.isAssignableFrom(a.getClass())).collect(Collectors.toList()).size()); } @@ -1619,9 +1619,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * @param step * @param player * @param cardName - * @param targetName for modes you can add "mode=3" before target name, - * multiple targets can be seperated by ^, not target - * marks as TestPlayer.NO_TARGET + * @param targetName for modes you can add "mode=3" before target name; + * multiple targets can be seperated by ^; + * no target marks as TestPlayer.NO_TARGET; + * warning, do not support cards with target adjusters - use addTarget instead */ public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, String targetName) { //Assert.assertNotEquals("", cardName); diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/DamageCantBePreventedEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/DamageCantBePreventedEffect.java index 56d08fdbab..9259d2904a 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/DamageCantBePreventedEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/DamageCantBePreventedEffect.java @@ -8,6 +8,7 @@ import mage.game.Game; import mage.game.events.GameEvent; public class DamageCantBePreventedEffect extends ContinuousRuleModifyingEffectImpl { + public DamageCantBePreventedEffect(Duration duration, String staticText, boolean messageToUser, boolean messageToLog) { super(duration, Outcome.Benefit, messageToUser, messageToLog); this.staticText = staticText; diff --git a/Mage/src/main/java/mage/target/TargetImpl.java b/Mage/src/main/java/mage/target/TargetImpl.java index c7e656c70e..c800d552b5 100644 --- a/Mage/src/main/java/mage/target/TargetImpl.java +++ b/Mage/src/main/java/mage/target/TargetImpl.java @@ -12,14 +12,7 @@ import mage.game.events.GameEvent.EventType; import mage.players.Player; import mage.util.RandomUtil; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; +import java.util.*; /** * @author BetaSteward_at_googlemail.com @@ -112,9 +105,9 @@ public abstract class TargetImpl implements Target { StringBuilder sb = new StringBuilder(); sb.append("Select ").append(targetName); if (getMaxNumberOfTargets() > 0 && getMaxNumberOfTargets() != Integer.MAX_VALUE) { - sb.append(" (").append(targets.size()).append('/').append(getMaxNumberOfTargets()).append(')'); + sb.append(" (selected ").append(targets.size()).append(" of ").append(getMaxNumberOfTargets()).append(')'); } else { - sb.append(" (").append(targets.size()).append(')'); + sb.append(" (selected ").append(targets.size()).append(')'); } sb.append(suffix); return sb.toString();