diff --git a/Mage.Sets/src/mage/cards/l/LavaballTrap.java b/Mage.Sets/src/mage/cards/l/LavaballTrap.java index 787bfb2055..39fc399138 100644 --- a/Mage.Sets/src/mage/cards/l/LavaballTrap.java +++ b/Mage.Sets/src/mage/cards/l/LavaballTrap.java @@ -15,6 +15,7 @@ import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.common.TargetLandPermanent; import mage.watchers.common.PermanentsEnteredBattlefieldWatcher; @@ -57,9 +58,10 @@ enum LavaballTrapCondition implements Condition { @Override public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); PermanentsEnteredBattlefieldWatcher watcher = game.getState().getWatcher(PermanentsEnteredBattlefieldWatcher.class); - if (watcher != null) { - for (UUID opponentId : game.getOpponents(source.getControllerId())) { + if (watcher != null && controller != null) { + for (UUID opponentId : game.getOpponents(controller.getId())) { List permanents = watcher.getThisTurnEnteringPermanents(opponentId); if (permanents != null) { int count = 0; diff --git a/Mage.Sets/src/mage/cards/s/SmugglersShare.java b/Mage.Sets/src/mage/cards/s/SmugglersShare.java new file mode 100644 index 0000000000..13c9c7b0c7 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SmugglersShare.java @@ -0,0 +1,137 @@ +package mage.cards.s; + +import java.util.List; +import java.util.UUID; + +import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.TreasureToken; +import mage.players.Player; +import mage.watchers.common.CardsAmountDrawnThisTurnWatcher; +import mage.watchers.common.PermanentsEnteredBattlefieldWatcher; + +/** + * One way or another, everyone gets paid what they're owed. + * Based on Gadrak, the Crown Scourge, Hoard Hauler, Lavaball Trap, Walker Of The Grove, Widespread Thieving, and Wound Reflection + * + * @author Slanman3755 + */ +public final class SmugglersShare extends CardImpl { + + public SmugglersShare(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}"); + + // At the beginning of each end step, draw a card for each opponent who drew two or more cards this turn, then + // create a Treasure token for each opponent who had two or more lands enter the battlefield under their control + // this turn. + Ability ability = new BeginningOfEndStepTriggeredAbility(new DrawCardSourceControllerEffect(SmugglersShareDrawValue.instance), TargetController.EACH_PLAYER, false); + ability.addEffect((new CreateTokenEffect(new TreasureToken(), SmugglersShareTreasureValue.instance)).concatBy(", then")); + ability.addWatcher(new CardsAmountDrawnThisTurnWatcher()); + ability.addWatcher(new PermanentsEnteredBattlefieldWatcher()); + this.addAbility(ability); + } + + private SmugglersShare(final SmugglersShare card) { + super(card); + } + + @Override + public SmugglersShare copy() { + return new SmugglersShare(this); + } +} + +enum SmugglersShareDrawValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability source, Effect effect) { + Player controller = game.getPlayer(source.getControllerId()); + CardsAmountDrawnThisTurnWatcher watcher = game.getState().getWatcher(CardsAmountDrawnThisTurnWatcher.class); + if (watcher == null || controller == null) { + return 0; + } + int drawCount = 0; + for (UUID opponentId : game.getOpponents(controller.getId())) { + Player opponent = game.getPlayer(opponentId); + if (opponent != null && watcher.getAmountCardsDrawn(opponentId) >= 2) { + drawCount++; + } + } + return drawCount; + } + + @Override + public SmugglersShareDrawValue copy() { + return instance; + } + + @Override + public String getMessage() { + return "opponent who drew two or more cards this turn"; + } + + @Override + public String toString() { + return "1"; + } +} + +enum SmugglersShareTreasureValue implements DynamicValue { + instance; + + @Override + public int calculate(Game game, Ability source, Effect effect) { + Player controller = game.getPlayer(source.getControllerId()); + PermanentsEnteredBattlefieldWatcher watcher = game.getState().getWatcher(PermanentsEnteredBattlefieldWatcher.class); + if (watcher == null || controller == null) { + return 0; + } + int treasureCount = 0; + for (UUID opponentId : game.getOpponents(controller.getId())) { + Player opponent = game.getPlayer(opponentId); + if (opponent == null) { + continue; + } + + List enteredPermanents = watcher.getThisTurnEnteringPermanents(opponentId); + if (enteredPermanents == null) { + continue; + } + + int enteredLandCount = 0; + for (Permanent permanent : enteredPermanents) { + if (permanent.isLand(game)) enteredLandCount++; + } + if (enteredLandCount >= 2) { + treasureCount++; + } + } + return treasureCount; + } + + @Override + public SmugglersShareTreasureValue copy() { + return instance; + } + + @Override + public String getMessage() { + return "opponent who had two or more lands enter the battlefield under their control this turn"; + } + + @Override + public String toString() { + return "1"; + } +} diff --git a/Mage.Sets/src/mage/sets/NewCapennaCommander.java b/Mage.Sets/src/mage/sets/NewCapennaCommander.java index 495eb353b3..a7fd94bb2c 100644 --- a/Mage.Sets/src/mage/sets/NewCapennaCommander.java +++ b/Mage.Sets/src/mage/sets/NewCapennaCommander.java @@ -268,6 +268,7 @@ public final class NewCapennaCommander extends ExpansionSet { cards.add(new SetCardInfo("Skyship Plunderer", 232, Rarity.UNCOMMON, mage.cards.s.SkyshipPlunderer.class)); cards.add(new SetCardInfo("Slippery Bogbonder", 312, Rarity.RARE, mage.cards.s.SlipperyBogbonder.class)); cards.add(new SetCardInfo("Smoldering Marsh", 428, Rarity.RARE, mage.cards.s.SmolderingMarsh.class)); + cards.add(new SetCardInfo("Smuggler's Share", 21, Rarity.RARE, mage.cards.s.SmugglersShare.class)); cards.add(new SetCardInfo("Sol Ring", 379, Rarity.UNCOMMON, mage.cards.s.SolRing.class)); cards.add(new SetCardInfo("Solemn Simulacrum", 380, Rarity.RARE, mage.cards.s.SolemnSimulacrum.class)); cards.add(new SetCardInfo("Spellbinding Soprano", 53, Rarity.RARE, mage.cards.s.SpellbindingSoprano.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ncc/SmugglersShareTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ncc/SmugglersShareTest.java new file mode 100644 index 0000000000..747be86a0f --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ncc/SmugglersShareTest.java @@ -0,0 +1,302 @@ +package org.mage.test.cards.single.ncc; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommander4Players; + +/** + * + * @author Slanman3755 + */ +public class SmugglersShareTest extends CardTestCommander4Players { + /* + Smuggler's Share {2}{W} + Enchantment + + At the beginning of each end step, draw a card for each opponent who drew two or more cards this turn, then + create a Treasure token for each opponent who had two or more lands enter the battlefield under their control + this turn. + */ + String smugglersShare = "Smuggler's Share"; + + /* + Cultivate {2}{G} + Sorcery + + Search your library for up to two basic land cards, reveal those cards, put one onto the battlefield tapped and + the other into your hand, then shuffle. + */ + String cultivate = "Cultivate"; + + /* + Forest + Basic Land - Forest + + ({T}: Add {G}.) + */ + String forest = "Forest"; + + /* + Harmonize {2}{G}{G} + Sorcery + + Draw three cards. + */ + String harmonize = "Harmonize"; + + /* + Harrow {2}{G} + Instant + + As an additional cost to cast this spell, sacrifice a land. + Search your library for up to two basic land cards, put them onto the battlefield, then shuffle. + */ + String harrow = "Harrow"; + + /* + Chemister's Insight {3}{U} + Instant + + Draw two cards. + Jump-start + */ + String chemistersInsight = "Chemister's Insight"; + + /* + Island + Basic Land - Island + + ({T}: Add {U}.) + */ + String island = "Island"; + + /* + Plains + Basic Land - Plains + + ({T}: Add {W}.) + */ + String plains = "Plains"; + + /* + Treasure + Token Artifact - Treasure + + {T}, Sacrifice this artifact: Add one mana of any color. + */ + String treasureToken = "Treasure Token"; + + /** + * Test with two players: + * A with Cultivate + * B with Smuggler's Share + * + * A plays Cultivate, B gets 1 Treasure token + */ + @Test + public void testTreasure() { + removeAllCardsFromLibrary(playerA); + + addCard(Zone.BATTLEFIELD, playerB, smugglersShare, 1); + addCard(Zone.BATTLEFIELD, playerA, forest, 3); + addCard(Zone.LIBRARY, playerA, forest, 3); + addCard(Zone.HAND, playerA, cultivate, 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, cultivate); + addTarget(playerA, forest + "^" + forest); + setChoice(playerA, forest); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, forest); + + setStopAt(1, PhaseStep.CLEANUP); + execute(); + assertAllCommandsUsed(); + + // 2 lands entered the battlefield under opponent's control, create Treasure token. + assertPermanentCount(playerB, treasureToken, 1); + } + + /** + * Test with two players: + * A with Harmonize + * B with Smuggler's Share + * + * A plays Harmonize, B draws 1 card + */ + @Test + public void testDraw() { + assertHandCount(playerB, 0); + + removeAllCardsFromLibrary(playerA); + + addCard(Zone.BATTLEFIELD, playerB, smugglersShare, 1); + addCard(Zone.BATTLEFIELD, playerA, forest, 4); + addCard(Zone.LIBRARY, playerA, forest, 4); + addCard(Zone.HAND, playerA, harmonize, 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, harmonize); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, forest); + + setStopAt(1, PhaseStep.CLEANUP); + execute(); + assertAllCommandsUsed(); + + // More than 2 cards were drawn by an opponent, draw a card. + assertHandCount(playerB, 1); + } + + /** + * Test with two players: + * A with Harmonize and Cultivate + * B with Smuggler's Share + * + * A plays Harmonize, B draws 1 card + */ + @Test + public void testDrawAndTreasure() { + assertHandCount(playerB, 0); + + removeAllCardsFromLibrary(playerA); + + addCard(Zone.BATTLEFIELD, playerA, forest, 6); + addCard(Zone.BATTLEFIELD, playerB, smugglersShare, 1); + + addCard(Zone.LIBRARY, playerA, forest, 6); + addCard(Zone.HAND, playerA, cultivate, 1); + addCard(Zone.HAND, playerA, harmonize, 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, cultivate); + addTarget(playerA, forest + "^" + forest); + setChoice(playerA, forest); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, forest); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, harmonize); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + setStopAt(1, PhaseStep.CLEANUP); + execute(); + assertAllCommandsUsed(); + + // 2 lands entered the battlefield under opponent's control, create Treasure token. + assertPermanentCount(playerB, treasureToken, 1); + + // More than 2 cards were drawn by an opponent, draw a card. + assertHandCount(playerB, 1); + } + + /** + * Test with four players: + * A with Harmonize and Cultivate + * B with Chemister's Insight + * C with Harrow + * D with Smuggler's Share + * + * A plays Harmonize, B draws 1 card + */ + @Test + public void testMultipleOpponents() { + assertHandCount(playerB, 0); + + removeAllCardsFromLibrary(playerA); + addCard(Zone.LIBRARY, playerA, forest, 6); + + removeAllCardsFromLibrary(playerB); + addCard(Zone.LIBRARY, playerB, island, 2); + + removeAllCardsFromLibrary(playerC); + addCard(Zone.LIBRARY, playerC, forest, 2); + + addCard(Zone.BATTLEFIELD, playerA, forest, 6); + addCard(Zone.BATTLEFIELD, playerB, island, 4); + addCard(Zone.BATTLEFIELD, playerC, forest, 4); + addCard(Zone.BATTLEFIELD, playerD, smugglersShare, 1); + + addCard(Zone.HAND, playerA, cultivate, 1); + addCard(Zone.HAND, playerA, harmonize, 1); + addCard(Zone.HAND, playerB, chemistersInsight, 1); + addCard(Zone.HAND, playerC, harrow, 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, cultivate); + addTarget(playerA, forest + "^" + forest); + setChoice(playerA, forest); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, forest); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, harmonize); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, chemistersInsight); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerC, harrow); + setChoice(playerC, forest); + addTarget(playerC, forest + "^" + forest); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + setStopAt(1, PhaseStep.CLEANUP); + execute(); + assertAllCommandsUsed(); + + // 2 lands entered the battlefield for 2 different opponents, create 2 Treasure tokens. + assertPermanentCount(playerD, treasureToken, 2); + + // More than 2 cards were drawn by 2 opponents, draw 2 cards. + assertHandCount(playerD, 2); + } + + /** + * Test with two players: + * A with Smuggler's Share + * B with Harrow and Chemister's Insight + * + * B plays Harrow and Chemister's Insight, A plays Smuggler's Share, A draws 1 card and creates 1 Treasure token + */ + @Test + public void testDrawAndTreasureEarly() { + assertHandCount(playerA, 0); + + + addCard(Zone.BATTLEFIELD, playerB, forest, 4); + addCard(Zone.BATTLEFIELD, playerB, island, 4); + + addCard(Zone.BATTLEFIELD, playerA, plains, 3); + + removeAllCardsFromLibrary(playerB); + addCard(Zone.LIBRARY, playerB, forest, 5); + + addCard(Zone.HAND, playerB, harrow, 1); + addCard(Zone.HAND, playerB, chemistersInsight, 1); + + addCard(Zone.HAND, playerA, smugglersShare, 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, harrow); + setChoice(playerB, forest); + addTarget(playerB, forest + "^" + forest); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, chemistersInsight); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, smugglersShare); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + setStopAt(1, PhaseStep.PRECOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + + // 1 card drawn for turn + assertHandCount(playerA, 1); + // No treasure tokens created yet + assertPermanentCount(playerA, treasureToken, 0); + + setStopAt(1, PhaseStep.CLEANUP); + execute(); + assertAllCommandsUsed(); + + // 2 lands entered the battlefield under opponent's control, create Treasure token. + assertPermanentCount(playerA, treasureToken, 1); + + // More than 2 cards were drawn by an opponent, draw a card. + assertHandCount(playerA, 2); + } +}