diff --git a/Mage.Sets/src/mage/cards/g/GluttonousHellkite.java b/Mage.Sets/src/mage/cards/g/GluttonousHellkite.java new file mode 100644 index 00000000000..0ec591dbd27 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GluttonousHellkite.java @@ -0,0 +1,91 @@ +package mage.cards.g; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.GetXValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.CastSourceTriggeredAbility; +import mage.abilities.effects.common.SacrificeAllEffect; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +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; + +/** + * @author JayDi85 + */ +public final class GluttonousHellkite extends CardImpl { + + public GluttonousHellkite(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{X}{X}{B}{R}{G}"); + + this.subtype.add(SubType.DRAGON); + this.power = new MageInt(3); + this.toughness = new MageInt(3); + + // When you cast this spell, each player sacrifices X creatures. Gluttonous Hellkite enters the battlefield with two +1/+1 counters on it for each creature sacrificed this way. + this.addAbility(new CastSourceTriggeredAbility(new SacrificeAllEffect(GetXValue.instance, StaticFilters.FILTER_PERMANENT_CREATURES))); + Ability ability = new EntersBattlefieldAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance(0), GluttonousHellkiteDynamicValue.instance, true), + "with two +1/+1 counters on it for each creature sacrificed this way"); + ability.addHint(new ValueHint("Will get +1/+1 counters on ETB", GluttonousHellkiteDynamicValue.instance)); + //ability.setRuleVisible(false); + this.addAbility(ability); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + } + + private GluttonousHellkite(final GluttonousHellkite card) { + super(card); + } + + @Override + public GluttonousHellkite copy() { + return new GluttonousHellkite(this); + } +} + +enum GluttonousHellkiteDynamicValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + List list = SacrificeAllEffect.getSacrificedPermanentsList(sourceAbility.getSourceId(), game, false); + if (list == null) { + return 0; + } + return list.size() * 2; + } + + @Override + public GluttonousHellkiteDynamicValue copy() { + return this; + } + + @Override + public String toString() { + return "X"; + } + + @Override + public String getMessage() { + return "will get counters"; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/ModernHorizons3Commander.java b/Mage.Sets/src/mage/sets/ModernHorizons3Commander.java index 6ebcedf7b36..bc21033183d 100644 --- a/Mage.Sets/src/mage/sets/ModernHorizons3Commander.java +++ b/Mage.Sets/src/mage/sets/ModernHorizons3Commander.java @@ -138,6 +138,7 @@ public final class ModernHorizons3Commander extends ExpansionSet { cards.add(new SetCardInfo("Glimmer of Genius", 187, Rarity.UNCOMMON, mage.cards.g.GlimmerOfGenius.class)); cards.add(new SetCardInfo("Glimmerpost", 346, Rarity.COMMON, mage.cards.g.Glimmerpost.class)); cards.add(new SetCardInfo("Goldspan Dragon", 212, Rarity.MYTHIC, mage.cards.g.GoldspanDragon.class)); + cards.add(new SetCardInfo("Gluttonous Hellkite", 73, Rarity.RARE, mage.cards.g.GluttonousHellkite.class)); cards.add(new SetCardInfo("Gonti's Aether Heart", 294, Rarity.MYTHIC, mage.cards.g.GontisAetherHeart.class)); cards.add(new SetCardInfo("Grapple with the Past", 230, Rarity.COMMON, mage.cards.g.GrappleWithThePast.class)); cards.add(new SetCardInfo("Graveshifter", 198, Rarity.UNCOMMON, mage.cards.g.Graveshifter.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/GluttonousHellkiteTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/GluttonousHellkiteTest.java new file mode 100644 index 00000000000..9558b74aecc --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/GluttonousHellkiteTest.java @@ -0,0 +1,147 @@ +package org.mage.test.cards.single.mh3; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class GluttonousHellkiteTest extends CardTestPlayerBase { + + @Test + public void test_CastWithoutSac_X0() { + // When you cast this spell, each player sacrifices X creatures. Gluttonous Hellkite enters the battlefield + // with two +1/+1 counters on it for each creature sacrificed this way. + addCard(Zone.HAND, playerA, "Gluttonous Hellkite", 1); // {X}{X}{B}{R}{G} + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gluttonous Hellkite"); + setChoice(playerA, "X=0"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Gluttonous Hellkite", 1); + assertCounterCount(playerA, "Gluttonous Hellkite", CounterType.P1P1, 0); + } + + @Test + public void test_CastWithoutSac_NothingToSac() { + // When you cast this spell, each player sacrifices X creatures. Gluttonous Hellkite enters the battlefield + // with two +1/+1 counters on it for each creature sacrificed this way. + addCard(Zone.HAND, playerA, "Gluttonous Hellkite", 1); // {X}{X}{B}{R}{G} + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1 * 2); // fox X + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gluttonous Hellkite"); + setChoice(playerA, "X=1"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Gluttonous Hellkite", 1); + assertCounterCount(playerA, "Gluttonous Hellkite", CounterType.P1P1, 0); + } + + @Test + public void test_CastWithoutSac_CounterTrigger() { + // When you cast this spell, each player sacrifices X creatures. Gluttonous Hellkite enters the battlefield + // with two +1/+1 counters on it for each creature sacrificed this way. + addCard(Zone.HAND, playerA, "Gluttonous Hellkite", 1); // {X}{X}{B}{R}{G} + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1 * 2); // fox X + // + // Ultimate Sacrifice -- {1}{U}, Sacrifice Adric: Counter target activated or triggered ability. + addCard(Zone.BATTLEFIELD, playerB, "Adric, Mathematical Genius", 1); + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + // + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + addCard(Zone.BATTLEFIELD, playerB, "Augmenting Automaton", 1); + + // cast with sac, but B counter it + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gluttonous Hellkite"); + setChoice(playerA, "X=1"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Ultimate Sacrifice"); + addTarget(playerB, "stack ability (When you cast this spell, each player sacrifices"); // counter sac + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + // no sac due counter + assertPermanentCount(playerA, "Gluttonous Hellkite", 1); + assertCounterCount(playerA, "Gluttonous Hellkite", CounterType.P1P1, 0); + } + + @Test + public void test_CastWithSac_LackingToSac() { + // When you cast this spell, each player sacrifices X creatures. Gluttonous Hellkite enters the battlefield + // with two +1/+1 counters on it for each creature sacrificed this way. + addCard(Zone.HAND, playerA, "Gluttonous Hellkite", 1); // {X}{X}{B}{R}{G} + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2 * 2); // fox X + // + addCard(Zone.BATTLEFIELD, playerA, "Balduvian Bears", 1); + addCard(Zone.BATTLEFIELD, playerB, "Augmenting Automaton", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gluttonous Hellkite"); + setChoice(playerA, "X=2"); + setChoice(playerA, "Balduvian Bears"); // sac + setChoice(playerB, "Augmenting Automaton"); // sac + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Gluttonous Hellkite", 1); + assertCounterCount(playerA, "Gluttonous Hellkite", CounterType.P1P1, 2 + 2); // bear from A + aug from B + } + + @Test + public void test_CastWithSac_SacFullAndBlink() { + addCustomEffect_BlinkTarget(playerA); + + // When you cast this spell, each player sacrifices X creatures. Gluttonous Hellkite enters the battlefield + // with two +1/+1 counters on it for each creature sacrificed this way. + addCard(Zone.HAND, playerA, "Gluttonous Hellkite", 1); // {X}{X}{B}{R}{G} + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2 * 3); // fox X + // + addCard(Zone.BATTLEFIELD, playerB, "Balduvian Bears", 2); + addCard(Zone.BATTLEFIELD, playerB, "Augmenting Automaton", 2); + + // first cast with counters + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gluttonous Hellkite"); + setChoice(playerA, "X=3"); + setChoice(playerB, "Balduvian Bears", 2); // sac + setChoice(playerB, "Augmenting Automaton", 1); // sac + + // blinked without counters + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("before blink", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gluttonous Hellkite", 1); + checkPermanentCounters("before blink", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gluttonous Hellkite", CounterType.P1P1, 2 + 2 + 2); // 2x bears and 1x aug from B + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "target blink", "Gluttonous Hellkite"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Gluttonous Hellkite", 1); + assertCounterCount(playerA, "Gluttonous Hellkite", CounterType.P1P1, 0); // 0 after blink + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/SacrificeAllEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SacrificeAllEffect.java index 99c0bc6cfad..6b637d76dbd 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/SacrificeAllEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/SacrificeAllEffect.java @@ -12,12 +12,10 @@ import mage.players.Player; import mage.target.common.TargetSacrifice; import mage.util.CardUtil; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; +import java.util.*; /** - * @author BetaSteward_at_googlemail.com + * @author BetaSteward_at_googlemail.com, JayDi85 */ public class SacrificeAllEffect extends OneShotEffect { @@ -25,6 +23,8 @@ public class SacrificeAllEffect extends OneShotEffect { private final FilterPermanent filter; private final boolean onlyOpponents; + private static final String VALUE_KEY = "sacrificeAllEffect_permanentsList"; + /** * Each player sacrifices a permanent * @@ -52,9 +52,6 @@ public class SacrificeAllEffect extends OneShotEffect { this(amount, filter, false); } - /** - * Internal use for this and SacrificeOpponentsEffect - */ protected SacrificeAllEffect(DynamicValue amount, FilterPermanent filter, boolean onlyOpponents) { super(Outcome.Sacrifice); this.amount = amount; @@ -99,15 +96,32 @@ public class SacrificeAllEffect extends OneShotEffect { } perms.addAll(target.getTargets()); } + + List sacraficedPermanents = new ArrayList<>(); for (UUID permID : perms) { Permanent permanent = game.getPermanent(permID); - if (permanent != null) { - permanent.sacrifice(source, game); + if (permanent != null && permanent.sacrifice(source, game)) { + sacraficedPermanents.add(permanent.copy()); } } + saveSacrificedPermanentsList(source.getSourceId(), game, sacraficedPermanents); + return true; } + public static void saveSacrificedPermanentsList(UUID sourceObjectId, Game game, List list) { + game.getState().setValue(CardUtil.getCardZoneString(VALUE_KEY, sourceObjectId, game), list); + } + + /** + * Get detailed list of sacrificed permanents + * + * @param previous if you need to look in detailed list on battlefield, then use previous param to find data from a stack moment + */ + public static List getSacrificedPermanentsList(UUID sourceObjectId, Game game, boolean previous) { + return (List) game.getState().getValue(CardUtil.getCardZoneString(VALUE_KEY, sourceObjectId, game, previous)); + } + private void setText() { StringBuilder sb = new StringBuilder(); sb.append(onlyOpponents ? "each opponent sacrifices " : "each player sacrifices ");