diff --git a/Mage.Sets/src/mage/cards/w/WhiteGloveGourmand.java b/Mage.Sets/src/mage/cards/w/WhiteGloveGourmand.java new file mode 100644 index 00000000000..4696620b69c --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WhiteGloveGourmand.java @@ -0,0 +1,119 @@ +package mage.cards.w; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.hint.ConditionHint; +import mage.abilities.hint.Hint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; +import mage.constants.WatcherScope; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.FoodToken; +import mage.game.permanent.token.HumanSoldierToken; +import mage.watchers.Watcher; + +import java.util.*; + +/** + * @author Susucr + */ +public final class WhiteGloveGourmand extends CardImpl { + + public WhiteGloveGourmand(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{B}"); + + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.NOBLE); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // When White Glove Gourmand enters the battlefield, create two 1/1 white Human Soldier creature tokens. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new HumanSoldierToken(), 2))); + + // At the beginning of your end step, if another Human died under your control this turn, create a Food token. + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new BeginningOfEndStepTriggeredAbility(new CreateTokenEffect(new FoodToken()), TargetController.YOU, false), + WhiteGloveGourmandCondition.instance, + "At the beginning of your end step, if another Human died under your control this turn, create a Food token." + ).addHint(WhiteGloveGourmandCondition.hint), new WhiteGloveGourmandWatcher()); + } + + private WhiteGloveGourmand(final WhiteGloveGourmand card) { + super(card); + } + + @Override + public WhiteGloveGourmand copy() { + return new WhiteGloveGourmand(this); + } +} + +enum WhiteGloveGourmandCondition implements Condition { + instance; + + static final Hint hint = new ConditionHint(instance); + + @Override + public boolean apply(Game game, Ability source) { + MageObjectReference mor = new MageObjectReference(source.getSourceObject(game), game); + return WhiteGloveGourmandWatcher.checkCondition(source.getControllerId(), mor, game); + } + + @Override + public String toString() { + return "another Human died under your control this turn"; + } +} + +class WhiteGloveGourmandWatcher extends Watcher { + + // player -> set of Human mor that dies under you control this turn. + private final Map> diedThisTurn = new HashMap<>(); + + WhiteGloveGourmandWatcher() { + super(WatcherScope.GAME); + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() != GameEvent.EventType.ZONE_CHANGE) { + return; + } + ZoneChangeEvent zce = (ZoneChangeEvent) event; + if (!zce.isDiesEvent()) { + return; + } + Permanent permanent = zce.getTarget(); + if (permanent != null && permanent.hasSubtype(SubType.HUMAN, game)) { + diedThisTurn.computeIfAbsent(event.getPlayerId(), k -> new HashSet<>()); + diedThisTurn.get(event.getPlayerId()).add(new MageObjectReference(permanent, game)); + } + } + + @Override + public void reset() { + super.reset(); + diedThisTurn.clear(); + } + + static boolean checkCondition(UUID playerId, MageObjectReference morSource, Game game) { + WhiteGloveGourmandWatcher watcher = game.getState().getWatcher(WhiteGloveGourmandWatcher.class); + return watcher != null && watcher + .diedThisTurn + .getOrDefault(playerId, Collections.emptySet()) + .stream() + .anyMatch(mor -> !mor.equals(morSource)); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/Fallout.java b/Mage.Sets/src/mage/sets/Fallout.java index cfe914d57ec..5cc0cd5d564 100644 --- a/Mage.Sets/src/mage/sets/Fallout.java +++ b/Mage.Sets/src/mage/sets/Fallout.java @@ -362,6 +362,7 @@ public final class Fallout extends ExpansionSet { cards.add(new SetCardInfo("Well Rested", 88, Rarity.UNCOMMON, mage.cards.w.WellRested.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Well Rested", 616, Rarity.UNCOMMON, mage.cards.w.WellRested.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Whirler Rogue", 181, Rarity.UNCOMMON, mage.cards.w.WhirlerRogue.class)); + cards.add(new SetCardInfo("White Glove Gourmand", 124, Rarity.UNCOMMON, mage.cards.w.WhiteGloveGourmand.class)); cards.add(new SetCardInfo("Wild Growth", 208, Rarity.COMMON, mage.cards.w.WildGrowth.class)); cards.add(new SetCardInfo("Windbrisk Heights", 315, Rarity.RARE, mage.cards.w.WindbriskHeights.class)); cards.add(new SetCardInfo("Winding Constrictor", 223, Rarity.UNCOMMON, mage.cards.w.WindingConstrictor.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/pip/WhiteGloveGourmandTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/pip/WhiteGloveGourmandTest.java new file mode 100644 index 00000000000..74164c3610b --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/pip/WhiteGloveGourmandTest.java @@ -0,0 +1,110 @@ +package org.mage.test.cards.single.pip; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class WhiteGloveGourmandTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.w.WhiteGloveGourmand White Glove Gourmand} {2}{W}{B} + * Creature — Human Noble + * When White Glove Gourmand enters the battlefield, create two 1/1 white Human Soldier creature tokens. + * At the beginning of your end step, if another Human died under your control this turn, create a Food token. + * 2/2 + */ + private static final String gourmand = "White Glove Gourmand"; + + @Test + public void test_Trigger_Simple() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, gourmand); + addCard(Zone.HAND, playerA, "Shrivel"); // All creatures get -1/-1 until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Scrubland", 6); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, gourmand, true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Shrivel"); + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertPermanentCount(playerA, "Food Token", 1); + } + + @Test + public void test_Trigger_Changeling() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, "Avian Changeling"); + addCard(Zone.BATTLEFIELD, playerA, "Scrubland", 6); + addCard(Zone.HAND, playerA, gourmand); + addCard(Zone.HAND, playerA, "Doom Blade"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Doom Blade", "Avian Changeling", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, gourmand); + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertPermanentCount(playerA, "Food Token", 1); + } + + @Test + public void test_NoTrigger_OpponentHuman_Die() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerB, "Avian Changeling"); + addCard(Zone.BATTLEFIELD, playerA, "Scrubland", 6); + addCard(Zone.HAND, playerA, gourmand); + addCard(Zone.HAND, playerA, "Doom Blade"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Doom Blade", "Avian Changeling", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, gourmand); + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertPermanentCount(playerA, "Food Token", 0); + } + + @Test + public void test_NoTrigger_NonHuman() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, "Goblin Piker"); + addCard(Zone.BATTLEFIELD, playerA, "Scrubland", 6); + addCard(Zone.HAND, playerA, gourmand); + addCard(Zone.HAND, playerA, "Doom Blade"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Doom Blade", "Goblin Piker", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, gourmand); + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertPermanentCount(playerA, "Food Token", 0); + } + + @Test + public void test_SelfDie_And_Reanimate() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + addCard(Zone.BATTLEFIELD, playerA, gourmand); + addCard(Zone.HAND, playerA, "Go for the Throat"); + addCard(Zone.HAND, playerA, "Reanimate"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Go for the Throat", gourmand, true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reanimate", gourmand); + + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertPermanentCount(playerA, "Food Token", 1); + } +}