diff --git a/Mage.Sets/src/mage/cards/f/FugitiveCodebreaker.java b/Mage.Sets/src/mage/cards/f/FugitiveCodebreaker.java new file mode 100644 index 00000000000..e21d8c6fc0d --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FugitiveCodebreaker.java @@ -0,0 +1,98 @@ +package mage.cards.f; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility; +import mage.abilities.costs.CostAdjuster; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.discard.DiscardHandControllerEffect; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.DisguiseAbility; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.ProwessAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class FugitiveCodebreaker extends CardImpl { + + public FugitiveCodebreaker(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.GOBLIN); + this.subtype.add(SubType.ROGUE); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Prowess + this.addAbility(new ProwessAbility()); + + // Haste + this.addAbility(HasteAbility.getInstance()); + + // Disguise {5}{R}. This cost is reduced by {1} for each instant and sorcery card in your graveyard. + this.addAbility(new FugitiveCodebreakerDisguiseAbility(this)); + + // When Fugitive Codebreaker is turned face up, discard your hand, then draw three cards. + Ability ability = new TurnedFaceUpSourceTriggeredAbility(new DiscardHandControllerEffect()); + ability.addEffect(new DrawCardSourceControllerEffect(3).concatBy(", then")); + this.addAbility(ability); + } + + private FugitiveCodebreaker(final FugitiveCodebreaker card) { + super(card); + } + + @Override + public FugitiveCodebreaker copy() { + return new FugitiveCodebreaker(this); + } +} + +class FugitiveCodebreakerDisguiseAbility extends DisguiseAbility { + + static final DynamicValue xValue = new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY_FROM_YOUR_GRAVEYARD); + private static final Hint hint = new ValueHint("instant and/or sorcery in your graveyard", xValue); + + FugitiveCodebreakerDisguiseAbility(Card card) { + super(card, new ManaCostsImpl<>("{5}{R}"), FugitiveCodebreakerAdjuster.instance); + addHint(hint); + } + + private FugitiveCodebreakerDisguiseAbility(final FugitiveCodebreakerDisguiseAbility ability) { + super(ability); + } + + @Override + public FugitiveCodebreakerDisguiseAbility copy() { + return new FugitiveCodebreakerDisguiseAbility(this); + } + + @Override + public String getRule() { + return "Disguise {5}{R}. This cost is reduced by {1} for each instant and sorcery card in your graveyard."; + } +} + +enum FugitiveCodebreakerAdjuster implements CostAdjuster { + instance; + + @Override + public void adjustCosts(Ability ability, Game game) { + CardUtil.reduceCost(ability, FugitiveCodebreakerDisguiseAbility.xValue.calculate(game, ability, null)); + } +} diff --git a/Mage.Sets/src/mage/sets/MurdersAtKarlovManor.java b/Mage.Sets/src/mage/sets/MurdersAtKarlovManor.java index a8a72a9e01e..52af4072c47 100644 --- a/Mage.Sets/src/mage/sets/MurdersAtKarlovManor.java +++ b/Mage.Sets/src/mage/sets/MurdersAtKarlovManor.java @@ -126,6 +126,7 @@ public final class MurdersAtKarlovManor extends ExpansionSet { cards.add(new SetCardInfo("Forest", 276, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Forum Familiar", 16, Rarity.UNCOMMON, mage.cards.f.ForumFamiliar.class)); cards.add(new SetCardInfo("Frantic Scapegoat", 126, Rarity.UNCOMMON, mage.cards.f.FranticScapegoat.class)); + cards.add(new SetCardInfo("Fugitive Codebreaker", 127, Rarity.RARE, mage.cards.f.FugitiveCodebreaker.class)); cards.add(new SetCardInfo("Furtive Courier", 59, Rarity.UNCOMMON, mage.cards.f.FurtiveCourier.class)); cards.add(new SetCardInfo("Fuss // Bother", 248, Rarity.UNCOMMON, mage.cards.f.FussBother.class)); cards.add(new SetCardInfo("Gadget Technician", 204, Rarity.COMMON, mage.cards.g.GadgetTechnician.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DisguiseTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DisguiseTest.java index 9dc6a6efee3..5a168aaca66 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DisguiseTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DisguiseTest.java @@ -197,4 +197,27 @@ public class DisguiseTest extends CardTestPlayerBase { assertLife(playerA, 20); } + @Test + public void testCostAdjuster() { + /* Fugitive Codebreaker {1}{R} + * Creature — Goblin Rogue + * Prowess, haste + * Disguise {5}{R}. This cost is reduced by {1} for each instant and sorcery card in your graveyard. + * When Fugitive Codebreaker is turned face up, discard your hand, then draw three cards. + */ + String codebreaker = "Fugitive Codebreaker"; + addCard(Zone.HAND, playerA, codebreaker); + addCard(Zone.GRAVEYARD, playerA, "Lightning Bolt", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3 + 3); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, codebreaker + " using Disguise", true); + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{5}{R}:"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertHandCount(playerA, 3); + } + } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java index 472bd01489e..9cdc4c220f9 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java @@ -7,6 +7,7 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.TurnFaceUpAbility; import mage.abilities.costs.Cost; +import mage.abilities.costs.CostAdjuster; import mage.abilities.costs.Costs; import mage.abilities.costs.CostsImpl; import mage.abilities.costs.mana.ManaCostsImpl; @@ -83,7 +84,22 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl { this(createCosts(cost), objectReference, duration, faceDownType); } - public BecomesFaceDownCreatureEffect(Costs turnFaceUpCosts, MageObjectReference objectReference, Duration duration, FaceDownType faceDownType) { + public BecomesFaceDownCreatureEffect(Costs cost, MageObjectReference objectReference, Duration duration, FaceDownType faceDownType) { + this(createCosts(cost), objectReference, duration, faceDownType, null); + } + + public BecomesFaceDownCreatureEffect(Cost cost, MageObjectReference objectReference, Duration duration, FaceDownType faceDownType, CostAdjuster costAdjuster) { + this(createCosts(cost), objectReference, duration, faceDownType, costAdjuster); + } + + /** + * @param turnFaceUpCosts costs for the turn face up ability + * @param objectReference + * @param duration + * @param faceDownType type of face down (morph, disguise, manifest, etc...) + * @param costAdjuster optional costAdjuster for the turn face up ability + */ + public BecomesFaceDownCreatureEffect(Costs turnFaceUpCosts, MageObjectReference objectReference, Duration duration, FaceDownType faceDownType, CostAdjuster costAdjuster) { super(duration, Layer.CopyEffects_1, SubLayer.FaceDownEffects_1b, Outcome.BecomeCreature); this.objectReference = objectReference; this.zoneChangeCounter = Integer.MIN_VALUE; @@ -91,7 +107,10 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl { // add additional face up and information abilities if (turnFaceUpCosts != null) { // face up for all - this.additionalAbilities.add(new TurnFaceUpAbility(turnFaceUpCosts, faceDownType == FaceDownType.MEGAMORPHED)); + this.additionalAbilities.add( + new TurnFaceUpAbility(turnFaceUpCosts, faceDownType == FaceDownType.MEGAMORPHED) + .setCostAdjuster(costAdjuster) + ); switch (faceDownType) { case MORPHED: diff --git a/Mage/src/main/java/mage/abilities/keyword/DisguiseAbility.java b/Mage/src/main/java/mage/abilities/keyword/DisguiseAbility.java index b93d5e313fd..69c8f47b248 100644 --- a/Mage/src/main/java/mage/abilities/keyword/DisguiseAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/DisguiseAbility.java @@ -4,12 +4,14 @@ import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.Cost; +import mage.abilities.costs.CostAdjuster; import mage.abilities.costs.Costs; import mage.abilities.costs.CostsImpl; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCost; import mage.abilities.effects.common.continuous.BecomesFaceDownCreatureEffect; import mage.cards.Card; +import mage.constants.Duration; import mage.constants.SpellAbilityCastMode; import mage.constants.SpellAbilityType; import mage.constants.TimingRule; @@ -68,6 +70,10 @@ public class DisguiseAbility extends SpellAbility { protected Costs disguiseCosts; public DisguiseAbility(Card card, Cost disguiseCost) { + this(card, disguiseCost, null); + } + + public DisguiseAbility(Card card, Cost disguiseCost, CostAdjuster costAdjuster) { super(new GenericManaCost(3), card.getName()); this.timing = TimingRule.SORCERY; this.disguiseCosts = new CostsImpl<>(); @@ -77,13 +83,15 @@ public class DisguiseAbility extends SpellAbility { // face down effect (hidden by default, visible in face down objects) Ability ability = new SimpleStaticAbility(new BecomesFaceDownCreatureEffect( - this.disguiseCosts, BecomesFaceDownCreatureEffect.FaceDownType.DISGUISED)); + this.disguiseCosts, null, Duration.WhileOnBattlefield, + BecomesFaceDownCreatureEffect.FaceDownType.DISGUISED, costAdjuster + )); ability.setWorksFaceDown(true); ability.setRuleVisible(false); addSubAbility(ability); } - private DisguiseAbility(final DisguiseAbility ability) { + protected DisguiseAbility(final DisguiseAbility ability) { super(ability); this.disguiseCosts = ability.disguiseCosts; // can't be changed TODO: looks buggy, need research }