From 8b79053deb6d7d58e1e12a34af04d723a566c6a8 Mon Sep 17 00:00:00 2001 From: Susucre <34709007+Susucre@users.noreply.github.com> Date: Sat, 9 Sep 2023 21:31:49 +0200 Subject: [PATCH] [WOC] Implement Court of Garenbrig (#10970) * add DoubleCounterOnEachPermanentEffect * Clean text generation of ConditionalOneShotEffect to prevent "if if" * [WOC] Implement Court of Garenbrig * better rule generation * fix target, add tests --- .../src/mage/cards/c/CourtOfGarenbrig.java | 58 +++++++ Mage.Sets/src/mage/cards/k/KalonianHydra.java | 41 +---- .../mage/sets/WildsOfEldraineCommander.java | 1 + .../single/woc/CourtOfGarenbrigTest.java | 161 ++++++++++++++++++ .../DoubleCounterOnEachPermanentEffect.java | 51 ++++++ 5 files changed, 275 insertions(+), 37 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/c/CourtOfGarenbrig.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/woc/CourtOfGarenbrigTest.java create mode 100644 Mage/src/main/java/mage/abilities/effects/common/counter/DoubleCounterOnEachPermanentEffect.java diff --git a/Mage.Sets/src/mage/cards/c/CourtOfGarenbrig.java b/Mage.Sets/src/mage/cards/c/CourtOfGarenbrig.java new file mode 100644 index 00000000000..80aa562178e --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CourtOfGarenbrig.java @@ -0,0 +1,58 @@ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.common.MonarchIsSourceControllerCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.BecomesMonarchSourceEffect; +import mage.abilities.effects.common.counter.DistributeCountersEffect; +import mage.abilities.effects.common.counter.DoubleCounterOnEachPermanentEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.counters.CounterType; +import mage.filter.StaticFilters; +import mage.target.common.TargetCreaturePermanentAmount; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class CourtOfGarenbrig extends CardImpl { + + public CourtOfGarenbrig(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}{G}"); + + + // When Court of Garenbrig enters the battlefield, you become the monarch. + this.addAbility(new EntersBattlefieldTriggeredAbility(new BecomesMonarchSourceEffect())); + + // At the beginning of your upkeep, distribute two +1/+1 counters among up to two target creatures. Then if you're the monarch, double the number of +1/+1 counters on each creature you control. + Ability ability = new BeginningOfUpkeepTriggeredAbility( + new DistributeCountersEffect( + CounterType.P1P1, 2, false, "up to two target creatures" + ), TargetController.YOU, false + ); + TargetCreaturePermanentAmount target = new TargetCreaturePermanentAmount(2); + target.setMinNumberOfTargets(0); + target.setMaxNumberOfTargets(2); + ability.addTarget(target); + ability.addEffect(new ConditionalOneShotEffect( + new DoubleCounterOnEachPermanentEffect(CounterType.P1P1, StaticFilters.FILTER_CONTROLLED_CREATURE), + MonarchIsSourceControllerCondition.instance + ).concatBy("Then")); + this.addAbility(ability); + } + + private CourtOfGarenbrig(final CourtOfGarenbrig card) { + super(card); + } + + @Override + public CourtOfGarenbrig copy() { + return new CourtOfGarenbrig(this); + } +} diff --git a/Mage.Sets/src/mage/cards/k/KalonianHydra.java b/Mage.Sets/src/mage/cards/k/KalonianHydra.java index 6b05e977ede..7e7c6a3dba7 100644 --- a/Mage.Sets/src/mage/cards/k/KalonianHydra.java +++ b/Mage.Sets/src/mage/cards/k/KalonianHydra.java @@ -1,23 +1,18 @@ package mage.cards.k; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.common.counter.DoubleCounterOnEachPermanentEffect; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; import mage.counters.CounterType; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.permanent.Permanent; -import java.util.List; import java.util.UUID; /** @@ -41,7 +36,9 @@ public final class KalonianHydra extends CardImpl { ), "with four +1/+1 counters on it")); // Whenever Kalonian Hydra attacks, double the number of +1/+1 counters on each creature you control. - this.addAbility(new AttacksTriggeredAbility(new KalonianHydraEffect(), false)); + this.addAbility(new AttacksTriggeredAbility( + new DoubleCounterOnEachPermanentEffect(CounterType.P1P1, StaticFilters.FILTER_CONTROLLED_CREATURE), false + )); } private KalonianHydra(final KalonianHydra card) { @@ -53,33 +50,3 @@ public final class KalonianHydra extends CardImpl { return new KalonianHydra(this); } } - - -class KalonianHydraEffect extends OneShotEffect { - - public KalonianHydraEffect() { - super(Outcome.BoostCreature); - this.staticText = "double the number of +1/+1 counters on each creature you control"; - } - - private KalonianHydraEffect(final KalonianHydraEffect effect) { - super(effect); - } - - @Override - public KalonianHydraEffect copy() { - return new KalonianHydraEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - List permanents = game.getBattlefield().getActivePermanents(StaticFilters.FILTER_CONTROLLED_CREATURE_P1P1, source.getControllerId(), source, game); - for (Permanent permanent : permanents) { - int existingCounters = permanent.getCounters(game).getCount(CounterType.P1P1); - if (existingCounters > 0) { - permanent.addCounters(CounterType.P1P1.createInstance(existingCounters), source.getControllerId(), source, game); - } - } - return true; - } -} diff --git a/Mage.Sets/src/mage/sets/WildsOfEldraineCommander.java b/Mage.Sets/src/mage/sets/WildsOfEldraineCommander.java index 605a3295cfc..ad64e44c832 100644 --- a/Mage.Sets/src/mage/sets/WildsOfEldraineCommander.java +++ b/Mage.Sets/src/mage/sets/WildsOfEldraineCommander.java @@ -44,6 +44,7 @@ public final class WildsOfEldraineCommander extends ExpansionSet { cards.add(new SetCardInfo("Consider", 87, Rarity.COMMON, mage.cards.c.Consider.class)); cards.add(new SetCardInfo("Court of Ardenvale", 21, Rarity.RARE, mage.cards.c.CourtOfArdenvale.class)); cards.add(new SetCardInfo("Court of Embereth", 24, Rarity.RARE, mage.cards.c.CourtOfEmbereth.class)); + cards.add(new SetCardInfo("Court of Garenbrig", 25, Rarity.RARE, mage.cards.c.CourtOfGarenbrig.class)); cards.add(new SetCardInfo("Court of Vantress", 22, Rarity.RARE, mage.cards.c.CourtOfVantress.class)); cards.add(new SetCardInfo("Danitha Capashen, Paragon", 64, Rarity.UNCOMMON, mage.cards.d.DanithaCapashenParagon.class)); cards.add(new SetCardInfo("Darkwater Catacombs", 157, Rarity.RARE, mage.cards.d.DarkwaterCatacombs.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/woc/CourtOfGarenbrigTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/woc/CourtOfGarenbrigTest.java new file mode 100644 index 00000000000..c792adaca1a --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/woc/CourtOfGarenbrigTest.java @@ -0,0 +1,161 @@ +package org.mage.test.cards.single.woc; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.player.TestPlayer; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class CourtOfGarenbrigTest extends CardTestPlayerBase { + + /** + * Court of Garenbrig + * {1}{G}{G} + * Enchantment + * + * When Court of Garenbrig enters the battlefield, you become the monarch. + * At the beginning of your upkeep, distribute two +1/+1 counters among up to two target creatures. Then if you're the monarch, double the number of +1/+1 counters on each creature you control. + */ + private final String court = "Court of Garenbrig"; + private final String bears = "Grizzly Bears"; // 2/2 + private final String piker = "Goblin Piker"; // 2/1 + private final String beetle = "Bond Beetle"; // {G} 0/1, enters with a +1/+1 + + @Test + public void testNoTargetMonarch() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, court); + addCard(Zone.HAND, playerA, beetle); + addCard(Zone.BATTLEFIELD, playerA, bears); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, beetle, true); + addTarget(playerA, beetle); // beetle trigger + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, court); + + addTarget(playerA, TestPlayer.TARGET_SKIP); // no target for court trigger + setStopAt(3, PhaseStep.DRAW); + execute(); + + assertCounterCount(bears, CounterType.P1P1, 0); + assertCounterCount(beetle, CounterType.P1P1, 1 * 2); + } + + @Test + public void testNoTargetNotMonarch() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, court); + addCard(Zone.HAND, playerA, beetle); + addCard(Zone.BATTLEFIELD, playerA, bears); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + addCard(Zone.BATTLEFIELD, playerB, piker); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, beetle, true); + addTarget(playerA, beetle); // beetle trigger + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, court); + + attack(2, playerB, piker); + + addTarget(playerA, TestPlayer.TARGET_SKIP); // no target for court trigger + setStopAt(3, PhaseStep.DRAW); + execute(); + + assertCounterCount(bears, CounterType.P1P1, 0); + assertCounterCount(beetle, CounterType.P1P1, 1); + } + + @Test + public void testOneTargetMonarch() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, court); + addCard(Zone.HAND, playerA, beetle); + addCard(Zone.BATTLEFIELD, playerA, bears); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, beetle, true); + addTarget(playerA, beetle); // beetle trigger + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, court); + + addTarget(playerA, bears + "^X=2"); + setStopAt(3, PhaseStep.DRAW); + execute(); + + assertCounterCount(bears, CounterType.P1P1, 2 * 2); + assertCounterCount(beetle, CounterType.P1P1, 1 * 2); + } + + @Test + public void testOneTargetNotMonarch() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, court); + addCard(Zone.HAND, playerA, beetle); + addCard(Zone.BATTLEFIELD, playerA, bears); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + addCard(Zone.BATTLEFIELD, playerB, piker); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, beetle, true); + addTarget(playerA, beetle); // beetle trigger + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, court); + + attack(2, playerB, piker); + + addTarget(playerA, bears + "^X=2"); + setStopAt(3, PhaseStep.DRAW); + execute(); + + assertCounterCount(bears, CounterType.P1P1, 2); + assertCounterCount(beetle, CounterType.P1P1, 1); + } + + @Test + public void testTwoTargetMonarch() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, court); + addCard(Zone.HAND, playerA, beetle); + addCard(Zone.BATTLEFIELD, playerA, bears); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, beetle, true); + addTarget(playerA, beetle); // beetle trigger + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, court); + + addTarget(playerA, bears + "^X=1"); + addTarget(playerA, beetle + "^X=1"); + setStopAt(3, PhaseStep.DRAW); + execute(); + + assertCounterCount(bears, CounterType.P1P1, 1 * 2); + assertCounterCount(beetle, CounterType.P1P1, (1+1) * 2); + } + + @Test + public void testTwoTargetNotMonarch() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, court); + addCard(Zone.HAND, playerA, beetle); + addCard(Zone.BATTLEFIELD, playerA, bears); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + addCard(Zone.BATTLEFIELD, playerB, piker); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, beetle, true); + addTarget(playerA, beetle); // beetle trigger + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, court); + + attack(2, playerB, piker); + + addTarget(playerA, bears + "^X=1"); + addTarget(playerA, beetle + "^X=1"); + setStopAt(3, PhaseStep.DRAW); + execute(); + + assertCounterCount(bears, CounterType.P1P1, 1); + assertCounterCount(beetle, CounterType.P1P1, 1+1); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/counter/DoubleCounterOnEachPermanentEffect.java b/Mage/src/main/java/mage/abilities/effects/common/counter/DoubleCounterOnEachPermanentEffect.java new file mode 100644 index 00000000000..cf71cb93fdd --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/counter/DoubleCounterOnEachPermanentEffect.java @@ -0,0 +1,51 @@ +package mage.abilities.effects.common.counter; + + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; + +import java.util.List; + +/** + * @author Susucr + */ +public class DoubleCounterOnEachPermanentEffect extends OneShotEffect { + + private final CounterType counterType; + private final FilterPermanent filter; + + public DoubleCounterOnEachPermanentEffect(CounterType counterType, FilterPermanent filter) { + super(Outcome.BoostCreature); + this.counterType = counterType; + this.filter = filter.copy(); + this.staticText = "double the number of " + counterType.getName() + " counters on each " + filter.getMessage(); + } + + private DoubleCounterOnEachPermanentEffect(final DoubleCounterOnEachPermanentEffect effect) { + super(effect); + this.counterType = effect.counterType; + this.filter = effect.filter.copy(); + } + + @Override + public DoubleCounterOnEachPermanentEffect copy() { + return new DoubleCounterOnEachPermanentEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + List permanents = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source, game); + for (Permanent permanent : permanents) { + int existingCounters = permanent.getCounters(game).getCount(counterType); + if (existingCounters > 0) { + permanent.addCounters(counterType.createInstance(existingCounters), source.getControllerId(), source, game); + } + } + return true; + } +}