mirror of
https://github.com/magefree/mage.git
synced 2025-12-22 03:22:00 -08:00
[MKM] Implement Fugitive Codebreaker
This commit is contained in:
parent
acf5f47fe7
commit
1ff8cf01cf
5 changed files with 153 additions and 4 deletions
98
Mage.Sets/src/mage/cards/f/FugitiveCodebreaker.java
Normal file
98
Mage.Sets/src/mage/cards/f/FugitiveCodebreaker.java
Normal file
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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("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("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("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("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("Fuss // Bother", 248, Rarity.UNCOMMON, mage.cards.f.FussBother.class));
|
||||||
cards.add(new SetCardInfo("Gadget Technician", 204, Rarity.COMMON, mage.cards.g.GadgetTechnician.class));
|
cards.add(new SetCardInfo("Gadget Technician", 204, Rarity.COMMON, mage.cards.g.GadgetTechnician.class));
|
||||||
|
|
|
||||||
|
|
@ -197,4 +197,27 @@ public class DisguiseTest extends CardTestPlayerBase {
|
||||||
assertLife(playerA, 20);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import mage.abilities.Ability;
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.common.TurnFaceUpAbility;
|
import mage.abilities.common.TurnFaceUpAbility;
|
||||||
import mage.abilities.costs.Cost;
|
import mage.abilities.costs.Cost;
|
||||||
|
import mage.abilities.costs.CostAdjuster;
|
||||||
import mage.abilities.costs.Costs;
|
import mage.abilities.costs.Costs;
|
||||||
import mage.abilities.costs.CostsImpl;
|
import mage.abilities.costs.CostsImpl;
|
||||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||||
|
|
@ -83,7 +84,22 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl {
|
||||||
this(createCosts(cost), objectReference, duration, faceDownType);
|
this(createCosts(cost), objectReference, duration, faceDownType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BecomesFaceDownCreatureEffect(Costs<Cost> turnFaceUpCosts, MageObjectReference objectReference, Duration duration, FaceDownType faceDownType) {
|
public BecomesFaceDownCreatureEffect(Costs<Cost> 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<Cost> turnFaceUpCosts, MageObjectReference objectReference, Duration duration, FaceDownType faceDownType, CostAdjuster costAdjuster) {
|
||||||
super(duration, Layer.CopyEffects_1, SubLayer.FaceDownEffects_1b, Outcome.BecomeCreature);
|
super(duration, Layer.CopyEffects_1, SubLayer.FaceDownEffects_1b, Outcome.BecomeCreature);
|
||||||
this.objectReference = objectReference;
|
this.objectReference = objectReference;
|
||||||
this.zoneChangeCounter = Integer.MIN_VALUE;
|
this.zoneChangeCounter = Integer.MIN_VALUE;
|
||||||
|
|
@ -91,7 +107,10 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl {
|
||||||
// add additional face up and information abilities
|
// add additional face up and information abilities
|
||||||
if (turnFaceUpCosts != null) {
|
if (turnFaceUpCosts != null) {
|
||||||
// face up for all
|
// 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) {
|
switch (faceDownType) {
|
||||||
case MORPHED:
|
case MORPHED:
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,14 @@ import mage.abilities.Ability;
|
||||||
import mage.abilities.SpellAbility;
|
import mage.abilities.SpellAbility;
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.costs.Cost;
|
import mage.abilities.costs.Cost;
|
||||||
|
import mage.abilities.costs.CostAdjuster;
|
||||||
import mage.abilities.costs.Costs;
|
import mage.abilities.costs.Costs;
|
||||||
import mage.abilities.costs.CostsImpl;
|
import mage.abilities.costs.CostsImpl;
|
||||||
import mage.abilities.costs.mana.GenericManaCost;
|
import mage.abilities.costs.mana.GenericManaCost;
|
||||||
import mage.abilities.costs.mana.ManaCost;
|
import mage.abilities.costs.mana.ManaCost;
|
||||||
import mage.abilities.effects.common.continuous.BecomesFaceDownCreatureEffect;
|
import mage.abilities.effects.common.continuous.BecomesFaceDownCreatureEffect;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
|
import mage.constants.Duration;
|
||||||
import mage.constants.SpellAbilityCastMode;
|
import mage.constants.SpellAbilityCastMode;
|
||||||
import mage.constants.SpellAbilityType;
|
import mage.constants.SpellAbilityType;
|
||||||
import mage.constants.TimingRule;
|
import mage.constants.TimingRule;
|
||||||
|
|
@ -68,6 +70,10 @@ public class DisguiseAbility extends SpellAbility {
|
||||||
protected Costs<Cost> disguiseCosts;
|
protected Costs<Cost> disguiseCosts;
|
||||||
|
|
||||||
public DisguiseAbility(Card card, Cost disguiseCost) {
|
public DisguiseAbility(Card card, Cost disguiseCost) {
|
||||||
|
this(card, disguiseCost, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DisguiseAbility(Card card, Cost disguiseCost, CostAdjuster costAdjuster) {
|
||||||
super(new GenericManaCost(3), card.getName());
|
super(new GenericManaCost(3), card.getName());
|
||||||
this.timing = TimingRule.SORCERY;
|
this.timing = TimingRule.SORCERY;
|
||||||
this.disguiseCosts = new CostsImpl<>();
|
this.disguiseCosts = new CostsImpl<>();
|
||||||
|
|
@ -77,13 +83,15 @@ public class DisguiseAbility extends SpellAbility {
|
||||||
|
|
||||||
// face down effect (hidden by default, visible in face down objects)
|
// face down effect (hidden by default, visible in face down objects)
|
||||||
Ability ability = new SimpleStaticAbility(new BecomesFaceDownCreatureEffect(
|
Ability ability = new SimpleStaticAbility(new BecomesFaceDownCreatureEffect(
|
||||||
this.disguiseCosts, BecomesFaceDownCreatureEffect.FaceDownType.DISGUISED));
|
this.disguiseCosts, null, Duration.WhileOnBattlefield,
|
||||||
|
BecomesFaceDownCreatureEffect.FaceDownType.DISGUISED, costAdjuster
|
||||||
|
));
|
||||||
ability.setWorksFaceDown(true);
|
ability.setWorksFaceDown(true);
|
||||||
ability.setRuleVisible(false);
|
ability.setRuleVisible(false);
|
||||||
addSubAbility(ability);
|
addSubAbility(ability);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DisguiseAbility(final DisguiseAbility ability) {
|
protected DisguiseAbility(final DisguiseAbility ability) {
|
||||||
super(ability);
|
super(ability);
|
||||||
this.disguiseCosts = ability.disguiseCosts; // can't be changed TODO: looks buggy, need research
|
this.disguiseCosts = ability.disguiseCosts; // can't be changed TODO: looks buggy, need research
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue