From dfd3e5e10d2d55b2b95afb8cecd3e605d373fca6 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Wed, 23 Apr 2025 14:20:46 -0400 Subject: [PATCH] [WHO] Implement Sycorax Commander --- .../src/mage/cards/e/EnsnaredByTheMara.java | 80 +++++----------- .../src/mage/cards/s/SycoraxCommander.java | 92 +++++++++++++++++++ .../src/mage/cards/t/TheDalekEmperor.java | 41 ++------- Mage.Sets/src/mage/sets/DoctorWho.java | 8 +- .../FaceVillainousChoiceOpponentsEffect.java | 45 +++++++++ 5 files changed, 172 insertions(+), 94 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/s/SycoraxCommander.java create mode 100644 Mage/src/main/java/mage/abilities/effects/common/FaceVillainousChoiceOpponentsEffect.java diff --git a/Mage.Sets/src/mage/cards/e/EnsnaredByTheMara.java b/Mage.Sets/src/mage/cards/e/EnsnaredByTheMara.java index 180cda0c375..1da6eb431b6 100644 --- a/Mage.Sets/src/mage/cards/e/EnsnaredByTheMara.java +++ b/Mage.Sets/src/mage/cards/e/EnsnaredByTheMara.java @@ -1,33 +1,34 @@ package mage.cards.e; -import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.Cards; -import mage.cards.CardImpl; -import mage.cards.CardsImpl; -import mage.cards.CardSetInfo; +import mage.abilities.effects.common.FaceVillainousChoiceOpponentsEffect; +import mage.cards.*; import mage.choices.FaceVillainousChoice; import mage.choices.VillainousChoice; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; import mage.game.Game; import mage.players.Player; -import mage.MageObject; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author padfoot */ public final class EnsnaredByTheMara extends CardImpl { + private static final FaceVillainousChoice choice = new FaceVillainousChoice( + Outcome.PlayForFree, new EnsnaredByTheMaraFirstChoice(), new EnsnaredByTheMaraSecondChoice() + ); + public EnsnaredByTheMara(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}{R}"); - // Each opponent faces a villainous choice -- They exile cards from the top of their library until they exile a nonland card, then you may cast that card without paying its mana cost, or that player exiles the top four cards of their library and Ensnared by the Mara deals damage equal to the total mana value of those exiled cards to that player. - this.getSpellAbility().addEffect(new EnsnaredByTheMaraEffect()); + this.getSpellAbility().addEffect(new FaceVillainousChoiceOpponentsEffect(choice)); } private EnsnaredByTheMara(final EnsnaredByTheMara card) { @@ -40,45 +41,15 @@ public final class EnsnaredByTheMara extends CardImpl { } } -class EnsnaredByTheMaraEffect extends OneShotEffect { - - private static final FaceVillainousChoice choice = new FaceVillainousChoice( - Outcome.PlayForFree, new EnsnaredByTheMaraFirstChoice(), new EnsnaredByTheMaraSecondChoice() - ); - - EnsnaredByTheMaraEffect() { - super(Outcome.Benefit); - staticText = "each opponent " + choice.generateRule(); - } - - private EnsnaredByTheMaraEffect(final EnsnaredByTheMaraEffect effect) { - super(effect); - } - - @Override - public EnsnaredByTheMaraEffect copy() { - return new EnsnaredByTheMaraEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - for (UUID playerId : game.getOpponents(source.getControllerId())) { - Player player = game.getPlayer(playerId); - choice.faceChoice(player, game, source); - } - return true; - } -} - class EnsnaredByTheMaraFirstChoice extends VillainousChoice { EnsnaredByTheMaraFirstChoice() { - super("They exile cards from the top of their library until they exile a nonland card, then you may cast that card without paying its mana cost","Exile a card for {controller} to cast for free"); + super("They exile cards from the top of their library until they exile a nonland card, then you may cast that card without paying its mana cost", "Exile a card for {controller} to cast for free"); } @Override public boolean doChoice(Player player, Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - for (Card card : player.getLibrary().getCards(game)) { + for (Card card : player.getLibrary().getCards(game)) { player.moveCards(card, Zone.EXILED, source, game); if (!card.isLand(game)) { CardUtil.castSpellWithAttributesForFree(controller, source, game, card); @@ -91,20 +62,19 @@ class EnsnaredByTheMaraFirstChoice extends VillainousChoice { class EnsnaredByTheMaraSecondChoice extends VillainousChoice { EnsnaredByTheMaraSecondChoice() { - super("that player exiles the top four cards of their library and {this} deals damage equal to the total mana value of those exiled cards to that player","exile four cards and take damage"); + super("that player exiles the top four cards of their library and {this} deals damage equal to the total mana value of those exiled cards to that player", "exile four cards and take damage"); } @Override public boolean doChoice(Player player, Game game, Ability source) { - int totalManaValue = 0; - Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 4)); - player.moveCards(cards, Zone.EXILED, source, game); - totalManaValue = cards - .getCards(game) - .stream() - .mapToInt(MageObject::getManaValue) - .sum(); - player.damage(totalManaValue, source, game); - return true; + Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 4)); + player.moveCards(cards, Zone.EXILED, source, game); + int totalManaValue = cards + .getCards(game) + .stream() + .mapToInt(MageObject::getManaValue) + .sum(); + player.damage(totalManaValue, source, game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/s/SycoraxCommander.java b/Mage.Sets/src/mage/cards/s/SycoraxCommander.java new file mode 100644 index 00000000000..db301e02c29 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SycoraxCommander.java @@ -0,0 +1,92 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.FaceVillainousChoiceOpponentsEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.keyword.HasteAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.choices.FaceVillainousChoice; +import mage.choices.VillainousChoice; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SycoraxCommander extends CardImpl { + + private static final FaceVillainousChoice choice = new FaceVillainousChoice( + Outcome.Discard, new SycoraxCommanderFirstChoice(), new SycoraxCommanderSecondChoice() + ); + + public SycoraxCommander(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{R}"); + + this.subtype.add(SubType.ALIEN); + this.subtype.add(SubType.SOLDIER); + this.power = new MageInt(4); + this.toughness = new MageInt(2); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Sanctified Rules of Combat -- When Sycorax Commander enters the battlefield, each opponent faces a villainous choice -- That opponent discards all the cards in their hand, then draws that many cards minus one, or Sycorax Commander deals damage to that player equal to the number of cards in their hand. + this.addAbility(new EntersBattlefieldTriggeredAbility( + new FaceVillainousChoiceOpponentsEffect(choice) + ).withFlavorWord("Sanctified Rules of Combat")); + } + + private SycoraxCommander(final SycoraxCommander card) { + super(card); + } + + @Override + public SycoraxCommander copy() { + return new SycoraxCommander(this); + } +} + +class SycoraxCommanderFirstChoice extends VillainousChoice { + + SycoraxCommanderFirstChoice() { + super( + "That opponent discards all the cards in their hand, then draws that many cards minus one", + "Discard your hand and draw one less card" + ); + } + + @Override + public boolean doChoice(Player player, Game game, Ability source) { + int discarded = player.discard(player.getHand(), false, source, game).size(); + if (discarded > 0) { + player.drawCards(discarded - 1, source, game); + } + return true; + } +} + +class SycoraxCommanderSecondChoice extends VillainousChoice { + + SycoraxCommanderSecondChoice() { + super( + "{this} deals damage to that player equal to the number of cards in their hand", + "Take damage equal to the number of cards in your hand" + ); + } + + @Override + public boolean doChoice(Player player, Game game, Ability source) { + return player.damage(player.getHand().size(), source, game) > 0; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TheDalekEmperor.java b/Mage.Sets/src/mage/cards/t/TheDalekEmperor.java index 9aebe165240..d077bd75e9d 100644 --- a/Mage.Sets/src/mage/cards/t/TheDalekEmperor.java +++ b/Mage.Sets/src/mage/cards/t/TheDalekEmperor.java @@ -2,15 +2,15 @@ package mage.cards.t; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AffinityEffect; +import mage.abilities.effects.common.FaceVillainousChoiceOpponentsEffect; import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; import mage.abilities.hint.Hint; import mage.abilities.hint.ValueHint; import mage.abilities.keyword.HasteAbility; +import mage.abilities.triggers.BeginningOfCombatTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.choices.FaceVillainousChoice; @@ -33,6 +33,9 @@ public final class TheDalekEmperor extends CardImpl { private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.DALEK, "Daleks"); private static final Hint hint = new ValueHint("Daleks you control", new PermanentsOnBattlefieldCount(filter)); + private static final FaceVillainousChoice choice = new FaceVillainousChoice( + Outcome.Sacrifice, new TheDalekEmperorFirstChoice(), new TheDalekEmperorSecondChoice() + ); public TheDalekEmperor(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}{B}{R}"); @@ -51,9 +54,7 @@ public final class TheDalekEmperor extends CardImpl { ))); // At the beginning of combat on your turn, each opponent faces a villainous choice -- That player sacrifices a creature they control, or you create a 3/3 black Dalek artifact creature token with menace. - this.addAbility(new BeginningOfCombatTriggeredAbility( - new TheDalekEmperorEffect() - )); + this.addAbility(new BeginningOfCombatTriggeredAbility(new FaceVillainousChoiceOpponentsEffect(choice))); } private TheDalekEmperor(final TheDalekEmperor card) { @@ -66,36 +67,6 @@ public final class TheDalekEmperor extends CardImpl { } } -class TheDalekEmperorEffect extends OneShotEffect { - - private static final FaceVillainousChoice choice = new FaceVillainousChoice( - Outcome.Sacrifice, new TheDalekEmperorFirstChoice(), new TheDalekEmperorSecondChoice() - ); - - TheDalekEmperorEffect() { - super(Outcome.Benefit); - staticText = "each opponent " + choice.generateRule(); - } - - private TheDalekEmperorEffect(final TheDalekEmperorEffect effect) { - super(effect); - } - - @Override - public TheDalekEmperorEffect copy() { - return new TheDalekEmperorEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - for (UUID playerId : game.getOpponents(source.getControllerId())) { - Player player = game.getPlayer(playerId); - choice.faceChoice(player, game, source); - } - return true; - } -} - class TheDalekEmperorFirstChoice extends VillainousChoice { TheDalekEmperorFirstChoice() { diff --git a/Mage.Sets/src/mage/sets/DoctorWho.java b/Mage.Sets/src/mage/sets/DoctorWho.java index bed2ce0c8f7..44f1079a3bc 100644 --- a/Mage.Sets/src/mage/sets/DoctorWho.java +++ b/Mage.Sets/src/mage/sets/DoctorWho.java @@ -803,10 +803,10 @@ public final class DoctorWho extends ExpansionSet { cards.add(new SetCardInfo("Swamp", 201, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swords to Plowshares", 212, Rarity.UNCOMMON, mage.cards.s.SwordsToPlowshares.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swords to Plowshares", 803, Rarity.UNCOMMON, mage.cards.s.SwordsToPlowshares.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Sycorax Commander", 1036, Rarity.RARE, mage.cards.s.SycoraxCommander.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Sycorax Commander", 161, Rarity.RARE, mage.cards.s.SycoraxCommander.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Sycorax Commander", 445, Rarity.RARE, mage.cards.s.SycoraxCommander.class, NON_FULL_USE_VARIOUS)); - //cards.add(new SetCardInfo("Sycorax Commander", 766, Rarity.RARE, mage.cards.s.SycoraxCommander.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sycorax Commander", 1036, Rarity.RARE, mage.cards.s.SycoraxCommander.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sycorax Commander", 161, Rarity.RARE, mage.cards.s.SycoraxCommander.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sycorax Commander", 445, Rarity.RARE, mage.cards.s.SycoraxCommander.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sycorax Commander", 766, Rarity.RARE, mage.cards.s.SycoraxCommander.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Talisman of Conviction", 247, Rarity.UNCOMMON, mage.cards.t.TalismanOfConviction.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Talisman of Conviction", 838, Rarity.UNCOMMON, mage.cards.t.TalismanOfConviction.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Talisman of Creativity", 248, Rarity.UNCOMMON, mage.cards.t.TalismanOfCreativity.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage/src/main/java/mage/abilities/effects/common/FaceVillainousChoiceOpponentsEffect.java b/Mage/src/main/java/mage/abilities/effects/common/FaceVillainousChoiceOpponentsEffect.java new file mode 100644 index 00000000000..17e802be8e1 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/FaceVillainousChoiceOpponentsEffect.java @@ -0,0 +1,45 @@ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.choices.FaceVillainousChoice; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public class FaceVillainousChoiceOpponentsEffect extends OneShotEffect { + + private final FaceVillainousChoice choice; + + public FaceVillainousChoiceOpponentsEffect(FaceVillainousChoice choice) { + super(Outcome.Benefit); + this.choice = choice; + staticText = "each opponent " + choice.generateRule(); + } + + private FaceVillainousChoiceOpponentsEffect(final FaceVillainousChoiceOpponentsEffect effect) { + super(effect); + this.choice = effect.choice; + } + + @Override + public FaceVillainousChoiceOpponentsEffect copy() { + return new FaceVillainousChoiceOpponentsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (UUID playerId : game.getOpponents(source.getControllerId())) { + Player player = game.getPlayer(playerId); + if (player != null) { + choice.faceChoice(player, game, source); + } + } + return true; + } +}