From 63239fe8e6191e8e80565075c5e309a6c62fce6a Mon Sep 17 00:00:00 2001 From: Daniel Bomar Date: Thu, 14 Apr 2022 08:43:12 -0500 Subject: [PATCH] [SNC] Implemented shield counter mechanic (#8830) * [SNC] Implemented shield counter mechanic * Rework shield counter to be a global replacement effect * Add unit test for shield counter Co-authored-by: Evan Kranzler --- .../src/mage/sets/StreetsOfNewCapenna.java | 2 +- .../cards/replacement/ShieldCounterTest.java | 68 +++++++++++++++++++ .../effects/keyword/ShieldCounterEffect.java | 61 +++++++++++++++++ Mage/src/main/java/mage/game/GameImpl.java | 4 ++ 4 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/replacement/ShieldCounterTest.java create mode 100644 Mage/src/main/java/mage/abilities/effects/keyword/ShieldCounterEffect.java diff --git a/Mage.Sets/src/mage/sets/StreetsOfNewCapenna.java b/Mage.Sets/src/mage/sets/StreetsOfNewCapenna.java index 7d870455bb..4086f782e8 100644 --- a/Mage.Sets/src/mage/sets/StreetsOfNewCapenna.java +++ b/Mage.Sets/src/mage/sets/StreetsOfNewCapenna.java @@ -12,7 +12,7 @@ import java.util.List; */ public final class StreetsOfNewCapenna extends ExpansionSet { - private static final List unfinished = Arrays.asList("Caldaia Strongarm", "Disciplined Duelist", "Elspeth Resplendent", "Falco Spara, Pactweaver", "Jaxis, the Troublemaker", "Mayhem Patrol", "Night Clubber", "Plasma Jockey", "Workshop Warchief", "Ziatora's Envoy"); + private static final List unfinished = Arrays.asList("Elspeth Resplendent", "Falco Spara, Pactweaver", "Jaxis, the Troublemaker"); private static final StreetsOfNewCapenna instance = new StreetsOfNewCapenna(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/ShieldCounterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/ShieldCounterTest.java new file mode 100644 index 0000000000..56a3c57588 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/ShieldCounterTest.java @@ -0,0 +1,68 @@ +package org.mage.test.cards.replacement; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author weirddan455 + */ +public class ShieldCounterTest extends CardTestPlayerBase { + + @Test + public void testNoncombatDamage() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, "Disciplined Duelist"); + addCard(Zone.HAND, playerA, "Lightning Bolt", 2); + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt"); + addTarget(playerA, "Disciplined Duelist"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertAllCommandsUsed(); + assertPermanentCount(playerA, "Disciplined Duelist", 1); + assertCounterCount("Disciplined Duelist", CounterType.SHIELD, 0); + } + + @Test + public void testCombatDamage() { + addCard(Zone.BATTLEFIELD, playerA, "Disciplined Duelist"); + addCard(Zone.BATTLEFIELD, playerB, "Hill Giant"); + setStrictChooseMode(true); + + attack(1, playerA, "Disciplined Duelist"); + block(1, playerB, "Hill Giant", "Disciplined Duelist"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertAllCommandsUsed(); + assertPermanentCount(playerA, "Disciplined Duelist", 1); + assertCounterCount("Disciplined Duelist", CounterType.SHIELD, 0); + assertGraveyardCount(playerB, "Hill Giant", 1); + } + + @Test + public void testDestroyEffect() { + addCard(Zone.BATTLEFIELD, playerA, "Disciplined Duelist"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + addCard(Zone.HAND, playerA, "Murder"); + setStrictChooseMode(true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Murder"); + addTarget(playerA, "Disciplined Duelist"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertAllCommandsUsed(); + assertPermanentCount(playerA, "Disciplined Duelist", 1); + assertCounterCount("Disciplined Duelist", CounterType.SHIELD, 0); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/keyword/ShieldCounterEffect.java b/Mage/src/main/java/mage/abilities/effects/keyword/ShieldCounterEffect.java new file mode 100644 index 0000000000..1670b714c0 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/keyword/ShieldCounterEffect.java @@ -0,0 +1,61 @@ +package mage.abilities.effects.keyword; + +import mage.abilities.Ability; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; + +/** + * + * @author weirddan455 + */ +public class ShieldCounterEffect extends ReplacementEffectImpl { + + public ShieldCounterEffect() { + super(Duration.Custom, Outcome.PreventDamage); + this.staticText = "If it would be dealt damage or destroyed, remove a shield counter from it instead"; + } + + private ShieldCounterEffect(final ShieldCounterEffect effect) { + super(effect); + } + + @Override + public ShieldCounterEffect copy() { + return new ShieldCounterEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null && permanent.getCounters(game).getCount(CounterType.SHIELD) > 0) { + permanent.removeCounters(CounterType.SHIELD.getName(), 1, source, game); + if (!game.isSimulation()) { + game.informPlayers("Removed a shield counter from " + permanent.getLogName()); + } + return true; + } + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + switch (event.getType()) { + case DAMAGE_PERMANENT: + case DESTROY_PERMANENT: + return true; + default: + return false; + } + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + Permanent permanent = game.getPermanent(event.getTargetId()); + return permanent != null && permanent.getCounters(game).getCount(CounterType.SHIELD) > 0; + } +} diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index 7729961158..0fc53f56a8 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -14,6 +14,7 @@ import mage.abilities.effects.Effect; import mage.abilities.effects.PreventionEffectData; import mage.abilities.effects.common.CopyEffect; import mage.abilities.effects.common.InfoEffect; +import mage.abilities.effects.keyword.ShieldCounterEffect; import mage.abilities.keyword.*; import mage.abilities.mana.DelayedTriggeredManaAbility; import mage.abilities.mana.TriggeredManaAbility; @@ -1130,6 +1131,9 @@ public abstract class GameImpl implements Game { return; } + // Apply shield counter mechanic from SNC + state.addAbility(new SimpleStaticAbility(Zone.ALL, new ShieldCounterEffect()), null); + // Handle companions Map playerCompanionMap = new HashMap<>(); for (Player player : state.getPlayers().values()) {