From 4065e2e9358256aeb6f996d798e940f507cdf540 Mon Sep 17 00:00:00 2001 From: Bobby McCann <88658699+bobby-mccann@users.noreply.github.com> Date: Thu, 13 Jul 2023 00:39:52 +0100 Subject: [PATCH] [LTR] Implement Bill Ferny, Bree Swindler (#10575) --- .../mage/cards/b/BillFernyBreeSwindler.java | 103 ++++++++++++++++++ .../TheLordOfTheRingsTalesOfMiddleEarth.java | 1 + .../single/ltr/BillFernyBreeSwindlerTest.java | 64 +++++++++++ 3 files changed, 168 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/b/BillFernyBreeSwindler.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/BillFernyBreeSwindlerTest.java diff --git a/Mage.Sets/src/mage/cards/b/BillFernyBreeSwindler.java b/Mage.Sets/src/mage/cards/b/BillFernyBreeSwindler.java new file mode 100644 index 00000000000..1b9dbbea771 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BillFernyBreeSwindler.java @@ -0,0 +1,103 @@ +package mage.cards.b; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.BecomesBlockedAllTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.RemoveFromCombatSourceEffect; +import mage.abilities.effects.common.continuous.GainControlTargetEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.TreasureToken; +import mage.target.common.TargetControlledPermanent; +import mage.target.common.TargetOpponent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * + * @author bobby-mccann + */ +public final class BillFernyBreeSwindler extends CardImpl { + + public BillFernyBreeSwindler(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.HUMAN); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Whenever Bill Ferny, Bree Swindler becomes blocked, choose one -- + Ability ability = new BecomesBlockedAllTriggeredAbility( + // * Create a Treasure token. + new CreateTokenEffect(new TreasureToken()), false + ); + ability.addMode( + // * Target opponent gains control of target Horse you control. If they do, remove Bill Ferny from combat and create three Treasure tokens. + new Mode(new BillFernyEffect()) + .addTarget(new TargetOpponent()) + .addTarget(new TargetControlledPermanent( + new FilterControlledPermanent(SubType.HORSE) + )) + ); + this.addAbility(ability); + } + + private BillFernyBreeSwindler(final BillFernyBreeSwindler card) { + super(card); + } + + @Override + public BillFernyBreeSwindler copy() { + return new BillFernyBreeSwindler(this); + } +} + +class BillFernyEffect extends OneShotEffect { + + private static final Effect create3TreasureTokens = new CreateTokenEffect(new TreasureToken(), 3); + private static final Effect removeFromCombat = new RemoveFromCombatSourceEffect(); + + public BillFernyEffect() { + super(Outcome.Benefit); + this.staticText = "Target opponent gains control of target Horse you control. If they do, remove Bill Ferny from combat and create three Treasure tokens."; + } + + private BillFernyEffect(BillFernyEffect effect) { + super(effect); + } + + @Override + public BillFernyEffect copy() { + return new BillFernyEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); + if (permanent == null) { + return false; + } + UUID opponentToGainControl = targetPointer.getFirst(game, source); + game.addEffect(new GainControlTargetEffect( + Duration.Custom, true, opponentToGainControl + ).setTargetPointer(new FixedTarget(permanent.getId(), game)), source); + game.getState().processAction(game); + if (permanent.isControlledBy(opponentToGainControl)) { + removeFromCombat.apply(game, source); + create3TreasureTokens.apply(game, source); + return true; + } + return false; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/TheLordOfTheRingsTalesOfMiddleEarth.java b/Mage.Sets/src/mage/sets/TheLordOfTheRingsTalesOfMiddleEarth.java index d9fe8fc62a7..f4f968972d4 100644 --- a/Mage.Sets/src/mage/sets/TheLordOfTheRingsTalesOfMiddleEarth.java +++ b/Mage.Sets/src/mage/sets/TheLordOfTheRingsTalesOfMiddleEarth.java @@ -33,6 +33,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet { cards.add(new SetCardInfo("Battle-Scarred Goblin", 115, Rarity.COMMON, mage.cards.b.BattleScarredGoblin.class)); cards.add(new SetCardInfo("Bilbo's Ring", 298, Rarity.RARE, mage.cards.b.BilbosRing.class)); cards.add(new SetCardInfo("Bilbo, Retired Burglar", 196, Rarity.UNCOMMON, mage.cards.b.BilboRetiredBurglar.class)); + cards.add(new SetCardInfo("Bill Ferny, Bree Swindler", 42, Rarity.UNCOMMON, mage.cards.b.BillFernyBreeSwindler.class)); cards.add(new SetCardInfo("Bill the Pony", 3, Rarity.UNCOMMON, mage.cards.b.BillThePony.class)); cards.add(new SetCardInfo("Birthday Escape", 43, Rarity.COMMON, mage.cards.b.BirthdayEscape.class)); cards.add(new SetCardInfo("Bitter Downfall", 77, Rarity.UNCOMMON, mage.cards.b.BitterDownfall.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/BillFernyBreeSwindlerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/BillFernyBreeSwindlerTest.java new file mode 100644 index 00000000000..5c8aca89e91 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ltr/BillFernyBreeSwindlerTest.java @@ -0,0 +1,64 @@ +package org.mage.test.cards.single.ltr; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class BillFernyBreeSwindlerTest extends CardTestPlayerBase { + private static final String bill = "Bill Ferny, Bree Swindler"; + private static final String horse = "Armored Warhorse"; + @Test + public void giveAHorseTest() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, bill); + addCard(Zone.BATTLEFIELD, playerA, horse); + addCard(Zone.BATTLEFIELD, playerA, "Horseshoe Crab"); // not a horse + + addCard(Zone.BATTLEFIELD, playerB, "Iron Golem"); // has to block + + attack(1, playerA, bill); + setModeChoice(playerA, "2"); + addTarget(playerA, playerB); + addTarget(playerA, horse); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerB, horse, 1); + assertPermanentCount(playerA, bill, 1); + assertPermanentCount(playerA, "Treasure Token", 3); + } + + @Test + public void phantasmalImageTest() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, bill); + addCard(Zone.BATTLEFIELD, playerA, horse); + addCard(Zone.BATTLEFIELD, playerA, "Horseshoe Crab"); // not a horse + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.HAND, playerA, "Phantasmal Image"); + + addCard(Zone.BATTLEFIELD, playerB, "Iron Golem"); // has to block + + // Create a copy of the horse, but it gets sacrificed when it becomes a target: + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Phantasmal Image"); + setChoice(playerA, "Yes"); + setChoice(playerA, horse); + + attack(1, playerA, bill); + setModeChoice(playerA, "2"); + addTarget(playerA, playerB); + addTarget(playerA, horse+"[only copy]"); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerB, horse, 0); // Opponent never got the horse + assertPermanentCount(playerA, bill, 0); // Died to Iron Golem + assertPermanentCount(playerA, "Treasure Token", 0); // No treasure tokens created + assertPermanentCount(playerA, horse, 1); // Normal horse still remains + } +} \ No newline at end of file