implement [FIC] Edgar, Master Machinist (#13676)

This commit is contained in:
Susucre 2025-05-30 15:11:43 +02:00 committed by GitHub
parent 0ac37617d1
commit ba395c8385
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 580 additions and 244 deletions

View file

@ -2,7 +2,7 @@ package mage.cards.a;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.CastFromGraveyardOnceEachTurnAbility;
import mage.abilities.common.CastFromGraveyardOnceDuringEachOfYourTurnAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.effects.common.DrawDiscardControllerEffect;
@ -24,14 +24,13 @@ import mage.game.permanent.Permanent;
import java.util.UUID;
/**
*
* @author justinjohnson14
*/
public final class ArcadeGannon extends CardImpl {
private static final FilterCard filter = new FilterCard("an artifact or Human spell from your graveyard with mana value less than or equal to the number of quest counters on {this}");
static{
static {
filter.add(Predicates.or(
CardType.ARTIFACT.getPredicate(),
SubType.HUMAN.getPredicate()
@ -41,7 +40,7 @@ public final class ArcadeGannon extends CardImpl {
public ArcadeGannon(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{U}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.DOCTOR);
@ -49,12 +48,12 @@ public final class ArcadeGannon extends CardImpl {
this.toughness = new MageInt(3);
// {T}: Draw a card, then discard a card. Put a quest counter on Arcade Gannon.
Ability ability = (new SimpleActivatedAbility(new DrawDiscardControllerEffect(1,1), new TapSourceCost()));
Ability ability = (new SimpleActivatedAbility(new DrawDiscardControllerEffect(1, 1), new TapSourceCost()));
ability.addEffect(new AddCountersSourceEffect(CounterType.QUEST.createInstance(1)));
this.addAbility(ability);
// For Auld Lang Syne -- Once during each of your turns, you may cast an artifact or Human spell from your graveyard with mana value less than or equal to the number of quest counters on Arcade Gannon.
this.addAbility(new CastFromGraveyardOnceEachTurnAbility(filter).withFlavorWord("For Auld Lang Syne"));
this.addAbility(new CastFromGraveyardOnceDuringEachOfYourTurnAbility(filter).withFlavorWord("For Auld Lang Syne"));
}
private ArcadeGannon(final ArcadeGannon card) {

View file

@ -1,26 +1,27 @@
package mage.cards.b;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.AttacksWithCreaturesTriggeredAbility;
import mage.abilities.common.CastFromGraveyardOnceEachTurnAbility;
import mage.abilities.common.CastFromGraveyardOnceDuringEachOfYourTurnAbility;
import mage.abilities.costs.CompositeCost;
import mage.abilities.costs.common.DiscardCardCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.DoIfCostPaid;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.cards.Card;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.Predicate;
import mage.game.Game;
import mage.watchers.common.CardsPutIntoGraveyardWatcher;
import java.util.UUID;
/**
* @author balazskristof
*/
@ -36,7 +37,7 @@ public final class BanonTheReturnersLeader extends CardImpl {
public BanonTheReturnersLeader(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{W}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.CLERIC);
@ -45,7 +46,7 @@ public final class BanonTheReturnersLeader extends CardImpl {
this.toughness = new MageInt(3);
// Pray -- Once during each of your turns, you may cast a creature spell from among cards in your graveyard that were put there from anywhere other than the battlefield this turn.
Ability ability = new CastFromGraveyardOnceEachTurnAbility(filter).withFlavorWord("Pray");
Ability ability = new CastFromGraveyardOnceDuringEachOfYourTurnAbility(filter).withFlavorWord("Pray");
ability.addWatcher(new CardsPutIntoGraveyardWatcher());
this.addAbility(ability);
// Whenever you attack, you may pay {1} and discard a card. If you do, draw a card.

View file

@ -12,7 +12,7 @@ import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.SetTargetPointer;
import mage.counters.CounterType;
import mage.filter.common.FilterCreatureSpell;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.events.EntersTheBattlefieldEvent;
import mage.game.events.GameEvent;
@ -28,8 +28,6 @@ import java.util.UUID;
*/
public final class CommunalBrewing extends CardImpl {
private static final FilterCreatureSpell filter = new FilterCreatureSpell("a creature spell");
public CommunalBrewing(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}");
@ -41,7 +39,11 @@ public final class CommunalBrewing extends CardImpl {
// Whenever you cast a creature spell, that creature enters with X additional +1/+1
// counters on it, where X is the number of ingredient counters on Communal Brewing.
this.addAbility(new SpellCastControllerTriggeredAbility(new CommunalBrewingCountersEffect(), filter, false, SetTargetPointer.SPELL));
this.addAbility(new SpellCastControllerTriggeredAbility(
new CommunalBrewingCountersEffect(),
StaticFilters.FILTER_SPELL_A_CREATURE,
false, SetTargetPointer.SPELL
));
}
private CommunalBrewing(final CommunalBrewing card) {

View file

@ -1,7 +1,7 @@
package mage.cards.d;
import mage.MageInt;
import mage.abilities.common.CastFromGraveyardOnceEachTurnAbility;
import mage.abilities.common.CastFromGraveyardOnceDuringEachOfYourTurnAbility;
import mage.abilities.keyword.LifelinkAbility;
import mage.abilities.keyword.TrampleAbility;
import mage.abilities.keyword.VigilanceAbility;
@ -21,6 +21,7 @@ import java.util.UUID;
public final class DanithaNewBenaliasLight extends CardImpl {
private static final FilterCard filter = new FilterCard("an Aura or Equipment spell");
static {
filter.add(Predicates.or(
SubType.AURA.getPredicate(), SubType.EQUIPMENT.getPredicate()
@ -46,7 +47,7 @@ public final class DanithaNewBenaliasLight extends CardImpl {
this.addAbility(LifelinkAbility.getInstance());
// Once during each of your turns, you may cast an Aura or Equipment spell from your graveyard.
this.addAbility(new CastFromGraveyardOnceEachTurnAbility(filter));
this.addAbility(new CastFromGraveyardOnceDuringEachOfYourTurnAbility(filter));
}
private DanithaNewBenaliasLight(final DanithaNewBenaliasLight card) {

View file

@ -0,0 +1,55 @@
package mage.cards.e;
import mage.MageIdentifier;
import mage.MageInt;
import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.common.CastFromGraveyardOnceDuringEachOfYourTurnAbility;
import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.common.continuous.BoostSourceEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.filter.FilterCard;
import mage.filter.common.FilterArtifactCard;
import java.util.UUID;
/**
* @author Susucr
*/
public final class EdgarMasterMachinist extends CardImpl {
private static final FilterCard filter = new FilterArtifactCard("an artifact spell");
public EdgarMasterMachinist(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{W}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.ARTIFICER);
this.subtype.add(SubType.NOBLE);
this.power = new MageInt(2);
this.toughness = new MageInt(4);
// Once during each of your turns, you may cast an artifact spell from your graveyard. If you cast a spell this way, that artifact enters tapped.
this.addAbility(new CastFromGraveyardOnceDuringEachOfYourTurnAbility(filter, MageIdentifier.OnceOnYourTurnCastFromGraveyardEntersTapped));
// Tools -- Whenever Edgar attacks, it gets +X/+0 until end of turn, where X is the greatest mana value among artifacts you control.
this.addAbility(new AttacksTriggeredAbility(
new BoostSourceEffect(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_ARTIFACTS, StaticValue.get(0), Duration.EndOfTurn, "it")
).withFlavorWord("Tools").addHint(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_ARTIFACTS.getHint()));
}
private EdgarMasterMachinist(final EdgarMasterMachinist card) {
super(card);
}
@Override
public EdgarMasterMachinist copy() {
return new EdgarMasterMachinist(this);
}
}

View file

@ -1,7 +1,7 @@
package mage.cards.g;
import mage.MageInt;
import mage.abilities.common.CastFromGraveyardOnceEachTurnAbility;
import mage.abilities.common.CastFromGraveyardOnceDuringEachOfYourTurnAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.common.MillCardsControllerEffect;
import mage.cards.CardImpl;
@ -9,17 +9,18 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.filter.FilterCard;
import mage.filter.common.FilterCreatureCard;
import java.util.UUID;
/**
*
* @author fireshoes
*/
public final class GisaAndGeralf extends CardImpl {
private static final FilterCreatureCard filter = new FilterCreatureCard("a Zombie creature spell");
private static final FilterCard filter = new FilterCreatureCard("a Zombie creature spell");
static {
filter.add(SubType.ZOMBIE.getPredicate());
}
@ -36,7 +37,7 @@ public final class GisaAndGeralf extends CardImpl {
this.addAbility(new EntersBattlefieldTriggeredAbility(new MillCardsControllerEffect(4)));
// Once during each of your turns, you may cast a Zombie creature spell from your graveyard
this.addAbility(new CastFromGraveyardOnceEachTurnAbility(filter));
this.addAbility(new CastFromGraveyardOnceDuringEachOfYourTurnAbility(filter));
}
private GisaAndGeralf(final GisaAndGeralf card) {

View file

@ -1,7 +1,6 @@
package mage.cards.h;
import java.util.UUID;
import mage.ObjectColor;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.common.SpellCastControllerTriggeredAbility;
@ -13,27 +12,23 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SuperType;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.FilterSpell;
import mage.filter.StaticFilters;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.ColorPredicate;
import java.util.UUID;
/**
*
* @author fireshoes
*/
public final class HazoretsMonument extends CardImpl {
private static final FilterCard filter = new FilterCard("Red creature spells");
private static final FilterSpell filter2 = new FilterSpell("a creature spell");
static {
filter.add(Predicates.and(new ColorPredicate(ObjectColor.RED), CardType.CREATURE.getPredicate()));
}
static {
filter2.add(CardType.CREATURE.getPredicate());
}
public HazoretsMonument(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
@ -44,7 +39,10 @@ public final class HazoretsMonument extends CardImpl {
this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 1)));
// Whenever you cast a creature spell, you may discard a card. If you do, draw a card.
this.addAbility(new SpellCastControllerTriggeredAbility(new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new DiscardCardCost()), filter2, false));
this.addAbility(new SpellCastControllerTriggeredAbility(
new DoIfCostPaid(new DrawCardSourceControllerEffect(1), new DiscardCardCost()),
StaticFilters.FILTER_SPELL_A_CREATURE, false
));
}
private HazoretsMonument(final HazoretsMonument card) {

View file

@ -3,14 +3,13 @@ package mage.cards.k;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.common.CastFromGraveyardOnceEachTurnAbility;
import mage.abilities.common.CastFromGraveyardOnceDuringEachOfYourTurnAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.StaticFilters;
import mage.filter.common.FilterCreatureCard;
import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
@ -18,13 +17,10 @@ import mage.util.CardUtil;
import java.util.UUID;
/**
*
* @author emerald000
*/
public final class KaradorGhostChieftain extends CardImpl {
private static final FilterCreatureCard filter = new FilterCreatureCard("a creature spell");
public KaradorGhostChieftain(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}{B}{G}");
this.supertype.add(SuperType.LEGENDARY);
@ -39,7 +35,7 @@ public final class KaradorGhostChieftain extends CardImpl {
new KaradorGhostChieftainCostReductionEffect()));
// Once during each of your turns, you may cast a creature spell from your graveyard.
this.addAbility(new CastFromGraveyardOnceEachTurnAbility(filter));
this.addAbility(new CastFromGraveyardOnceDuringEachOfYourTurnAbility(StaticFilters.FILTER_CARD_A_CREATURE_SPELL));
}
private KaradorGhostChieftain(final KaradorGhostChieftain card) {

View file

@ -1,7 +1,6 @@
package mage.cards.k;
import java.util.UUID;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
@ -12,26 +11,23 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SuperType;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.FilterSpell;
import mage.filter.StaticFilters;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.ColorPredicate;
import mage.target.common.TargetCreaturePermanent;
import java.util.UUID;
/**
*
* @author fireshoes
*/
public final class KefnetsMonument extends CardImpl {
private static final FilterCard filter = new FilterCard("Blue creature spells");
private static final FilterSpell filter2 = new FilterSpell("a creature spell");
static {
filter.add(Predicates.and(new ColorPredicate(ObjectColor.BLUE), CardType.CREATURE.getPredicate()));
filter2.add(CardType.CREATURE.getPredicate());
}
public KefnetsMonument(UUID ownerId, CardSetInfo setInfo) {
@ -43,7 +39,10 @@ public final class KefnetsMonument extends CardImpl {
this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 1)));
// Whenever you cast a creature spell, target creature an opponent controls doesn't untap during its controller's next untap step.
Ability ability = new SpellCastControllerTriggeredAbility(new DontUntapInControllersNextUntapStepTargetEffect(), filter2, false);
Ability ability = new SpellCastControllerTriggeredAbility(
new DontUntapInControllersNextUntapStepTargetEffect(),
StaticFilters.FILTER_SPELL_A_CREATURE, false
);
ability.addTarget(new TargetCreaturePermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE));
this.addAbility(ability);
}

View file

@ -1,7 +1,7 @@
package mage.cards.k;
import mage.MageInt;
import mage.abilities.common.CastFromGraveyardOnceEachTurnAbility;
import mage.abilities.common.CastFromGraveyardOnceDuringEachOfYourTurnAbility;
import mage.abilities.common.EntersBattlefieldOneOrMoreTriggeredAbility;
import mage.abilities.costs.Cost;
import mage.abilities.costs.common.ExileFromGraveCost;
@ -12,7 +12,6 @@ import mage.constants.*;
import mage.counters.CounterType;
import mage.filter.FilterCard;
import mage.filter.StaticFilters;
import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.mageobject.AnotherPredicate;
import mage.game.Game;
import mage.game.events.ZoneChangeEvent;
@ -23,21 +22,19 @@ import mage.target.common.TargetCardInYourGraveyard;
import java.util.UUID;
/**
*
* @author Jmlundeen
*/
public final class KotisSibsigChampion extends CardImpl {
private static final FilterCreatureCard filter = new FilterCreatureCard("a creature spell");
private static final FilterCard filter2 = new FilterCard("other cards");
private static final FilterCard filter = new FilterCard("other cards");
static {
filter2.add(AnotherPredicate.instance);
filter.add(AnotherPredicate.instance);
}
public KotisSibsigChampion(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{G}{U}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.ZOMBIE);
this.subtype.add(SubType.WARRIOR);
@ -45,9 +42,9 @@ public final class KotisSibsigChampion extends CardImpl {
this.toughness = new MageInt(3);
// Once during each of your turns, you may cast a creature spell from your graveyard by exiling three other cards from your graveyard in addition to paying its other costs.
Cost cost = new ExileFromGraveCost(new TargetCardInYourGraveyard(3, filter2));
Cost cost = new ExileFromGraveCost(new TargetCardInYourGraveyard(3, filter));
cost.setText(cost.getText().replace("exile", "exiling"));
this.addAbility(new CastFromGraveyardOnceEachTurnAbility(filter, cost));
this.addAbility(new CastFromGraveyardOnceDuringEachOfYourTurnAbility(StaticFilters.FILTER_CARD_A_CREATURE_SPELL, cost));
// Whenever one or more creatures you control enter, if one or more of them entered from a graveyard or was cast from a graveyard, put two +1/+1 counters on Kotis.
this.addAbility(new KotisSibsigTriggeredAbility());

View file

@ -2,7 +2,7 @@ package mage.cards.l;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.common.CastFromGraveyardOnceEachTurnAbility;
import mage.abilities.common.CastFromGraveyardOnceDuringEachOfYourTurnAbility;
import mage.abilities.keyword.CompanionAbility;
import mage.abilities.keyword.CompanionCondition;
import mage.abilities.keyword.LifelinkAbility;
@ -13,8 +13,10 @@ import mage.constants.CardType;
import mage.constants.ComparisonType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.filter.FilterCard;
import mage.filter.common.FilterPermanentCard;
import mage.filter.predicate.mageobject.ManaValuePredicate;
import mage.filter.predicate.mageobject.PermanentPredicate;
import java.util.Set;
import java.util.UUID;
@ -24,9 +26,10 @@ import java.util.UUID;
*/
public final class LurrusOfTheDreamDen extends CardImpl {
private static final FilterPermanentCard filter = new FilterPermanentCard("a permanent spell with mana value 2 or less");
private static final FilterCard filter = new FilterPermanentCard("a permanent spell with mana value 2 or less");
static {
filter.add(PermanentPredicate.instance);
filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3));
}
@ -46,7 +49,7 @@ public final class LurrusOfTheDreamDen extends CardImpl {
this.addAbility(LifelinkAbility.getInstance());
// During each of your turns, you may cast one permanent spell with converted mana cost 2 or less from your graveyard.
this.addAbility(new CastFromGraveyardOnceEachTurnAbility(filter));
this.addAbility(new CastFromGraveyardOnceDuringEachOfYourTurnAbility(filter));
}
private LurrusOfTheDreamDen(final LurrusOfTheDreamDen card) {

View file

@ -1,45 +1,38 @@
package mage.cards.m;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.MaxSpeedAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.common.SpellCastControllerTriggeredAbility;
import mage.abilities.common.delayed.CopyNextSpellDelayedTriggeredAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.CopyStackObjectEffect;
import mage.abilities.effects.common.CopyTargetStackObjectEffect;
import mage.abilities.effects.common.DoIfCostPaid;
import mage.abilities.effects.common.continuous.SetBasePowerSourceEffect;
import mage.constants.*;
import mage.abilities.keyword.StartYourEnginesAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.filter.FilterPermanent;
import mage.constants.*;
import mage.filter.FilterSpell;
import mage.filter.StaticFilters;
import mage.filter.common.FilterControlledPermanent;
import mage.filter.common.FilterArtifactSpell;
import java.util.UUID;
/**
*
* @author Jmlundeen
*/
public final class MendicantCoreGuidelight extends CardImpl {
private static final FilterSpell filter = new FilterSpell("an artifact spell");
private static final FilterSpell filter = new FilterArtifactSpell("an artifact spell");
private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_PERMANENT_ARTIFACTS);
static {
filter.add(CardType.ARTIFACT.getPredicate());
}
public MendicantCoreGuidelight(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{W}{U}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.ROBOT);
this.power = new MageInt(0);
@ -53,7 +46,7 @@ public final class MendicantCoreGuidelight extends CardImpl {
// Max speed -- Whenever you cast an artifact spell, you may pay {1}. If you do, copy it.
Effect copyEffect = new CopyTargetStackObjectEffect(true)
.setText("copy it. <i>(The copy becomes a token.)</i>");
Effect doIfEffect = new DoIfCostPaid(copyEffect,new ManaCostsImpl<>("{1}"));
Effect doIfEffect = new DoIfCostPaid(copyEffect, new ManaCostsImpl<>("{1}"));
Ability ability = new SpellCastControllerTriggeredAbility(doIfEffect, filter, false, SetTargetPointer.SPELL);
this.addAbility(new MaxSpeedAbility(ability));
}

View file

@ -1,7 +1,6 @@
package mage.cards.o;
import java.util.UUID;
import mage.ObjectColor;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.common.SpellCastControllerTriggeredAbility;
@ -11,28 +10,24 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SuperType;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.FilterSpell;
import mage.filter.StaticFilters;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.ColorPredicate;
import mage.game.permanent.token.WarriorVigilantToken;
import java.util.UUID;
/**
*
* @author fireshoes
*/
public final class OketrasMonument extends CardImpl {
private static final FilterCard filter = new FilterCard("White creature spells");
private static final FilterSpell filter2 = new FilterSpell("a creature spell");
static {
filter.add(Predicates.and(new ColorPredicate(ObjectColor.WHITE), CardType.CREATURE.getPredicate()));
}
static {
filter2.add(CardType.CREATURE.getPredicate());
}
public OketrasMonument(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
@ -43,7 +38,10 @@ public final class OketrasMonument extends CardImpl {
this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 1)));
// Whenever you cast a creature spell, create a 1/1 white Warrior creature token with vigilance.
this.addAbility(new SpellCastControllerTriggeredAbility(new CreateTokenEffect(new WarriorVigilantToken()), filter2, false));
this.addAbility(new SpellCastControllerTriggeredAbility(
new CreateTokenEffect(new WarriorVigilantToken()),
StaticFilters.FILTER_SPELL_A_CREATURE, false
));
}
private OketrasMonument(final OketrasMonument card) {

View file

@ -1,7 +1,7 @@
package mage.cards.r;
import mage.MageInt;
import mage.abilities.common.CastFromGraveyardOnceEachTurnAbility;
import mage.abilities.common.CastFromGraveyardOnceDuringEachOfYourTurnAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.effects.common.MillCardsEachPlayerEffect;
@ -39,7 +39,7 @@ public final class RaulTroubleShooter extends CardImpl {
this.toughness = new MageInt(4);
// Once during each of your turns, you may cast a spell from among cards in your graveyard that were milled this turn.
this.addAbility(new CastFromGraveyardOnceEachTurnAbility(filter), new CardsMilledWatcher());
this.addAbility(new CastFromGraveyardOnceDuringEachOfYourTurnAbility(filter), new CardsMilledWatcher());
// {T}: Each player mills a card.
this.addAbility(new SimpleActivatedAbility(

View file

@ -1,6 +1,5 @@
package mage.cards.r;
import java.util.UUID;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
@ -15,30 +14,25 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SuperType;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.FilterSpell;
import mage.filter.StaticFilters;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.ColorPredicate;
import mage.target.common.TargetControlledCreaturePermanent;
import java.util.UUID;
/**
*
* @author fireshoes
*/
public final class RhonassMonument extends CardImpl {
private static final FilterCard filter = new FilterCard("Green creature spells");
private static final FilterSpell filter2 = new FilterSpell("a creature spell");
static {
filter.add(Predicates.and(new ColorPredicate(ObjectColor.GREEN), CardType.CREATURE.getPredicate()));
}
static {
filter2.add(CardType.CREATURE.getPredicate());
}
public RhonassMonument(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
@ -49,7 +43,7 @@ public final class RhonassMonument extends CardImpl {
// Whenever you cast a creature spell, target creature you control gets +2/+2 and gains trample until end of turn.
Ability ability = new SpellCastControllerTriggeredAbility(new BoostTargetEffect(2, 2, Duration.EndOfTurn)
.setText("target creature you control gets +2/+2"), filter2, false);
.setText("target creature you control gets +2/+2"), StaticFilters.FILTER_SPELL_A_CREATURE, false);
Effect effect = new GainAbilityTargetEffect(TrampleAbility.getInstance(), Duration.EndOfTurn);
effect.setText("and gains trample until end of turn");
ability.addEffect(effect);

View file

@ -1,7 +1,7 @@
package mage.cards.r;
import mage.MageInt;
import mage.abilities.common.CastFromGraveyardOnceEachTurnAbility;
import mage.abilities.common.CastFromGraveyardOnceDuringEachOfYourTurnAbility;
import mage.abilities.common.DiesSourceTriggeredAbility;
import mage.abilities.common.SpellCastControllerTriggeredAbility;
import mage.abilities.effects.common.ExileSourceEffect;
@ -50,7 +50,7 @@ public final class RivazOfTheClaw extends CardImpl {
this.addAbility(new ConditionalAnyColorManaAbility(2, new ConditionalSpellManaBuilder(manaAbilityFilter)));
// Once during each of your turns, you may cast a Dragon creature spell from your graveyard.
this.addAbility(new CastFromGraveyardOnceEachTurnAbility(staticAbilityFilter));
this.addAbility(new CastFromGraveyardOnceDuringEachOfYourTurnAbility(staticAbilityFilter));
// Whenever you cast a Dragon creature spell from your graveyard, it gains "When this creature dies, exile it."
this.addAbility(new SpellCastControllerTriggeredAbility(

View file

@ -129,6 +129,8 @@ public final class FinalFantasyCommander extends ExpansionSet {
cards.add(new SetCardInfo("Dragonskull Summit", 387, Rarity.RARE, mage.cards.d.DragonskullSummit.class));
cards.add(new SetCardInfo("Drowned Catacomb", 388, Rarity.RARE, mage.cards.d.DrownedCatacomb.class));
cards.add(new SetCardInfo("Duskshell Crawler", 301, Rarity.COMMON, mage.cards.d.DuskshellCrawler.class));
cards.add(new SetCardInfo("Edgar, Master Machinist", 169, Rarity.RARE, mage.cards.e.EdgarMasterMachinist.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Edgar, Master Machinist", 80, Rarity.RARE, mage.cards.e.EdgarMasterMachinist.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Elena, Turk Recruit", 133, Rarity.RARE, mage.cards.e.ElenaTurkRecruit.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Elena, Turk Recruit", 18, Rarity.RARE, mage.cards.e.ElenaTurkRecruit.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Endless Detour", 324, Rarity.RARE, mage.cards.e.EndlessDetour.class));

View file

@ -0,0 +1,180 @@
package org.mage.test.cards.single.fic;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author Susucr
*/
public class EdgarMasterMachinistTest extends CardTestPlayerBase {
/**
* {@link mage.cards.e.EdgarMasterMachinist Edgar, Master Machinist} {2}{R}{W}
* Legendary Creature Human Artificer Noble
* Once during each of your turns, you may cast an artifact spell from your graveyard. If you cast a spell this way, that artifact enters tapped.
* Tools Whenever Edgar attacks, it gets +X/+0 until end of turn, where X is the greatest mana value among artifacts you control.
* 2/4
*/
private static final String edgar = "Edgar, Master Machinist";
@Test
public void test_cast_from_yard() {
addCard(Zone.BATTLEFIELD, playerA, edgar);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
addCard(Zone.GRAVEYARD, playerA, "Golgari Signet");
addCard(Zone.GRAVEYARD, playerA, "Elite Vanguard");
checkPlayableAbility("can not cast Elite Vanguard", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Elite Vanguard", false);
checkPlayableAbility("can cast Golgari Signet", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Golgari Signet", true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Golgari Signet");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertPermanentCount(playerA, "Golgari Signet", 1);
assertTappedCount("Plains", true, 2);
assertTappedCount("Golgari Signet", true, 1);
}
@Test
public void test_tapped_effect_wait_for_cleanup() {
// test to make sure the discarding of the "enters tapped effect" only happens when the spell leave the stack
addCard(Zone.BATTLEFIELD, playerA, edgar);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
addCard(Zone.GRAVEYARD, playerA, "Golgari Signet");
addCard(Zone.HAND, playerA, "Lightning Bolt");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Golgari Signet");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
setStrictChooseMode(true);
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertPermanentCount(playerA, "Golgari Signet", 1);
assertTappedCount("Mountain", true, 3);
assertTappedCount("Golgari Signet", true, 1);
}
@Test
public void test_cast_limits() {
addCard(Zone.BATTLEFIELD, playerA, edgar);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
addCard(Zone.GRAVEYARD, playerA, "Golgari Signet");
addCard(Zone.GRAVEYARD, playerA, "Bear Trap"); // has flash
addCard(Zone.GRAVEYARD, playerB, "Orzhov Signet");
checkPlayableAbility("can not cast opponent Orzhov Signet", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Orzhov Signet", false);
checkPlayableAbility("can cast Golgari Signet", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Golgari Signet", true);
checkPlayableAbility("can cast Bear Trap", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Bear Trap", true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Golgari Signet");
checkPlayableAbility("can not cast 2 per turn", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Cast Bear Trap", false);
checkPlayableAbility("can not cast opponent Orzhov Signet", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Orzhov Signet", false);
checkPlayableAbility("opp can not cast Orzhov Signet", 2, PhaseStep.PRECOMBAT_MAIN, playerB, "Cast Orzhov Signet", false);
checkPlayableAbility("can not cast Bear Trap on opp turn", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Bear Trap", false);
castSpell(3, PhaseStep.UPKEEP, playerA, "Bear Trap");
setStrictChooseMode(true);
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertPermanentCount(playerA, "Golgari Signet", 1);
assertPermanentCount(playerA, "Bear Trap", 1);
assertTappedCount("Bear Trap", true, 1);
}
@Test
public void test_mdfc() {
addCard(Zone.BATTLEFIELD, playerA, edgar);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
addCard(Zone.GRAVEYARD, playerA, "Halvar, God of Battle");
checkPlayableAbility("can not cast Halvar, God of Battle", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Halvar, God of Battle", false);
checkPlayableAbility("can cast Sword of the Realms", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Sword of the Realms", true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sword of the Realms");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertPermanentCount(playerA, "Sword of the Realms", 1);
assertTappedCount("Plains", true, 2);
assertTappedCount("Sword of the Realms", true, 1);
}
@Test
public void test_adventure() {
addCard(Zone.BATTLEFIELD, playerA, edgar);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
addCard(Zone.GRAVEYARD, playerA, "Horn of Valhalla");
checkPlayableAbility("can not cast Ysgard's Call", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Ysgard's Call", false);
checkPlayableAbility("can cast Horn of Valhalla", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Horn of Valhalla", true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Horn of Valhalla");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertPermanentCount(playerA, "Horn of Valhalla", 1);
assertTappedCount("Plains", true, 2);
assertTappedCount("Horn of Valhalla", true, 1);
}
@Test
public void test_remand_recast() {
addCard(Zone.BATTLEFIELD, playerA, edgar);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
addCard(Zone.BATTLEFIELD, playerB, "Island", 2);
addCard(Zone.HAND, playerB, "Remand");
addCard(Zone.GRAVEYARD, playerA, "Golgari Signet");
addCard(Zone.GRAVEYARD, playerA, "Elite Vanguard");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Golgari Signet");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Remand", "Golgari Signet", "Golgari Signet");
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Golgari Signet");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertPermanentCount(playerA, "Golgari Signet", 1);
assertGraveyardCount(playerB, "Remand", 1);
assertTappedCount("Plains", true, 4);
assertTappedCount("Golgari Signet", false, 1);
}
@Test
public void test_blink() {
addCard(Zone.BATTLEFIELD, playerA, edgar);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
addCard(Zone.HAND, playerA, "Cloudshift", 1);
addCard(Zone.GRAVEYARD, playerA, "Memnite");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Memnite");
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
checkPermanentTapped("Memnite entered tapped", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Memnite", true, 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cloudshift", "Memnite");
setStrictChooseMode(true);
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertGraveyardCount(playerA, "Cloudshift", 1);
assertTappedCount("Memnite", false, 1);
}
}

View file

@ -19,7 +19,8 @@ public enum MageIdentifier {
// e.g. [[Johann, Apprentice Sorcerer]]
// "Once each turn, you may cast an instant or sorcery spell from the top of your library."
//
CastFromGraveyardOnceWatcher,
OnceOnYourTurnCastFromGraveyard,
OnceOnYourTurnCastFromGraveyardEntersTapped,
OnceEachTurnCastWatcher,
HaukensInsightWatcher,
IntrepidPaleontologistWatcher,

View file

@ -0,0 +1,190 @@
package mage.abilities.common;
import mage.MageIdentifier;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.costs.Cost;
import mage.abilities.costs.Costs;
import mage.abilities.costs.CostsImpl;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.common.replacement.MorEnteringTappedEffect;
import mage.cards.Card;
import mage.constants.*;
import mage.filter.FilterCard;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.game.stack.Spell;
import mage.players.Player;
import mage.watchers.Watcher;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
/**
* Once during each of your turns, you may cast... from your graveyard
* <p>
* See Lurrus of the Dream Den and Rivaz of the Claw
*
* @author weirddan455, Susucr
*/
public class CastFromGraveyardOnceDuringEachOfYourTurnAbility extends SimpleStaticAbility {
public CastFromGraveyardOnceDuringEachOfYourTurnAbility(FilterCard filter) {
this(filter, (Cost) null);
}
public CastFromGraveyardOnceDuringEachOfYourTurnAbility(FilterCard filter, Cost additionalCost) {
this(filter, additionalCost, MageIdentifier.OnceOnYourTurnCastFromGraveyard);
}
public CastFromGraveyardOnceDuringEachOfYourTurnAbility(FilterCard filter, MageIdentifier mageIdentifier) {
this(filter, null, mageIdentifier);
}
public CastFromGraveyardOnceDuringEachOfYourTurnAbility(FilterCard filter, Cost additionalCost, MageIdentifier mageIdentifier) {
super(new CastFromGraveyardOnceEffect(filter, additionalCost, mageIdentifier));
this.addWatcher(new CastFromGraveyardOnceWatcher());
switch (mageIdentifier) {
case OnceOnYourTurnCastFromGraveyard:
case OnceOnYourTurnCastFromGraveyardEntersTapped:
this.setIdentifier(mageIdentifier);
break;
default:
throw new IllegalArgumentException("Wrong code usage: only specific MageIdentifier are currently supported");
}
}
private CastFromGraveyardOnceDuringEachOfYourTurnAbility(final CastFromGraveyardOnceDuringEachOfYourTurnAbility ability) {
super(ability);
}
@Override
public CastFromGraveyardOnceDuringEachOfYourTurnAbility copy() {
return new CastFromGraveyardOnceDuringEachOfYourTurnAbility(this);
}
}
class CastFromGraveyardOnceEffect extends AsThoughEffectImpl {
private final FilterCard filter;
private final Cost additionalCost;
private final MageIdentifier mageIdentifier;
CastFromGraveyardOnceEffect(FilterCard filter, Cost additionalCost, MageIdentifier mageIdentifier) {
super(AsThoughEffectType.CAST_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit);
this.filter = filter;
this.staticText = "Once during each of your turns, you may cast " + filter.getMessage()
+ (filter.getMessage().contains("your graveyard") ? "" : " from your graveyard")
+ (additionalCost == null ? "" : " by " + additionalCost.getText() + " in addition to paying its other costs.");
this.additionalCost = additionalCost;
this.mageIdentifier = mageIdentifier;
}
private CastFromGraveyardOnceEffect(final CastFromGraveyardOnceEffect effect) {
super(effect);
this.filter = effect.filter;
this.additionalCost = effect.additionalCost;
this.mageIdentifier = effect.mageIdentifier;
}
@Override
public CastFromGraveyardOnceEffect copy() {
return new CastFromGraveyardOnceEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
throw new IllegalArgumentException("Wrong code usage: can't call applies method on empty affectedAbility");
}
@Override
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
Player controller = game.getPlayer(source.getControllerId());
Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game);
CastFromGraveyardOnceWatcher watcher = game.getState().getWatcher(CastFromGraveyardOnceWatcher.class);
Card cardToCast = game.getCard(objectId);
if (controller == null || sourcePermanent == null || watcher == null || cardToCast == null
|| !game.isActivePlayer(playerId) // only during your turn
|| !source.isControlledBy(playerId) // only you may cast
|| !Zone.GRAVEYARD.equals(game.getState().getZone(objectId)) // from graveyard
|| !cardToCast.getOwnerId().equals(playerId) // only your graveyard
|| !(affectedAbility instanceof SpellAbility) // characteristics to check
|| watcher.abilityUsed(new MageObjectReference(sourcePermanent, game)) // once per turn
) {
return false;
}
SpellAbility spellAbility = (SpellAbility) affectedAbility;
Card cardToCheck = spellAbility.getCharacteristics(game);
if (spellAbility.getManaCosts().isEmpty()) {
return false;
}
Set<MageIdentifier> allowedToBeCastNow = spellAbility.spellCanBeActivatedNow(playerId, game);
if (!allowedToBeCastNow.contains(MageIdentifier.Default)
|| !filter.match(cardToCheck, playerId, source, game)) {
return false;
}
if (additionalCost != null) {
Costs<Cost> costs = new CostsImpl<>();
costs.add(additionalCost);
controller.setCastSourceIdWithAlternateMana(
objectId, spellAbility.getManaCosts(),
costs, mageIdentifier);
}
return true;
}
}
class CastFromGraveyardOnceWatcher extends Watcher {
// TODO: we might want to store (approver, approving ability) instead on the odd chance there
// is more than one such ability on a given approver. (event.getApprovingObject() has
// the exact ability, but not sure its id is stable enough.)
// Set of each approver that approved casting a spell this turn (and is thus done for the turn)
private final Set<MageObjectReference> usedFrom = new HashSet<>();
CastFromGraveyardOnceWatcher() {
super(WatcherScope.GAME);
}
@Override
public void watch(GameEvent event, Game game) {
if (!GameEvent.EventType.SPELL_CAST.equals(event.getType())) {
return;
}
if (event.hasApprovingIdentifier(MageIdentifier.OnceOnYourTurnCastFromGraveyard)) {
usedFrom.add(event.getApprovingObject().getApprovingMageObjectReference());
return;
}
if (event.hasApprovingIdentifier(MageIdentifier.OnceOnYourTurnCastFromGraveyardEntersTapped)) {
usedFrom.add(event.getApprovingObject().getApprovingMageObjectReference());
// The cast (most likely permanent) spell enters the battlefield tapped.
Spell target = game.getSpell(event.getTargetId());
if (target != null) {
MageObjectReference mor = new MageObjectReference(target, game);
game.getState().addEffect(
new MorEnteringTappedEffect(mor),
event.getApprovingObject().getApprovingAbility() // ability that approved the cast is the source of the tapping.
);
}
return;
}
}
@Override
public void reset() {
super.reset();
usedFrom.clear();
}
boolean abilityUsed(MageObjectReference mor) {
return usedFrom.contains(mor);
}
}

View file

@ -1,150 +0,0 @@
package mage.abilities.common;
import mage.MageIdentifier;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.costs.Cost;
import mage.abilities.costs.Costs;
import mage.abilities.costs.CostsImpl;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.cards.Card;
import mage.constants.*;
import mage.filter.FilterCard;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.watchers.Watcher;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
/**
* Once during each of your turns, you may cast... from your graveyard
* <p>
* See Lurrus of the Dream Den and Rivaz of the Claw
*
* @author weirddan455
*/
public class CastFromGraveyardOnceEachTurnAbility extends SimpleStaticAbility {
public CastFromGraveyardOnceEachTurnAbility(FilterCard filter) {
this(filter, null);
}
public CastFromGraveyardOnceEachTurnAbility(FilterCard filter, Cost additionalCost) {
super(new CastFromGraveyardOnceEffect(filter, additionalCost));
this.addWatcher(new CastFromGraveyardOnceWatcher());
this.setIdentifier(MageIdentifier.CastFromGraveyardOnceWatcher);
}
private CastFromGraveyardOnceEachTurnAbility(final CastFromGraveyardOnceEachTurnAbility ability) {
super(ability);
}
@Override
public CastFromGraveyardOnceEachTurnAbility copy() {
return new CastFromGraveyardOnceEachTurnAbility(this);
}
}
class CastFromGraveyardOnceEffect extends AsThoughEffectImpl {
private final FilterCard filter;
private final Cost additionalCost;
CastFromGraveyardOnceEffect(FilterCard filter, Cost additionalCost) {
super(AsThoughEffectType.CAST_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit);
this.filter = filter;
this.staticText = "Once during each of your turns, you may cast " + filter.getMessage()
+ (filter.getMessage().contains("your graveyard") ? "" : " from your graveyard")
+ (additionalCost == null ? "" : " by " + additionalCost.getText() + " in addition to paying its other costs.");
this.additionalCost = additionalCost;
}
private CastFromGraveyardOnceEffect(final CastFromGraveyardOnceEffect effect) {
super(effect);
this.filter = effect.filter;
this.additionalCost = effect.additionalCost;
}
@Override
public CastFromGraveyardOnceEffect copy() {
return new CastFromGraveyardOnceEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
throw new IllegalArgumentException("Wrong code usage: can't call applies method on empty affectedAbility");
}
@Override
public boolean applies(UUID objectId, Ability affectedAbility, Ability source, Game game, UUID playerId) {
Player controller = game.getPlayer(source.getControllerId());
Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game);
CastFromGraveyardOnceWatcher watcher = game.getState().getWatcher(CastFromGraveyardOnceWatcher.class);
Card cardToCast = game.getCard(objectId);
if (controller == null || sourcePermanent == null || watcher == null || cardToCast == null) {
return false;
}
if (game.isActivePlayer(playerId) // only during your turn
&& source.isControlledBy(playerId) // only you may cast
&& Zone.GRAVEYARD.equals(game.getState().getZone(objectId)) // from graveyard
&& cardToCast.getOwnerId().equals(playerId) // only your graveyard
&& affectedAbility instanceof SpellAbility // characteristics to check
&& watcher.abilityNotUsed(new MageObjectReference(sourcePermanent, game)) // once per turn
) {
SpellAbility spellAbility = (SpellAbility) affectedAbility;
Card cardToCheck = spellAbility.getCharacteristics(game);
if (spellAbility.getManaCosts().isEmpty()) {
return false;
}
Set<MageIdentifier> allowedToBeCastNow = spellAbility.spellCanBeActivatedNow(playerId, game);
if (allowedToBeCastNow.contains(MageIdentifier.Default)) {
boolean matched = filter.match(cardToCheck, playerId, source, game);
if (matched && additionalCost != null) {
Costs<Cost> costs = new CostsImpl<>();
costs.add(additionalCost);
controller.setCastSourceIdWithAlternateMana(objectId, spellAbility.getManaCosts(),
costs, MageIdentifier.CastFromGraveyardOnceWatcher);
}
return matched;
}
}
return false;
}
}
class CastFromGraveyardOnceWatcher extends Watcher {
private final Set<MageObjectReference> usedFrom = new HashSet<>();
CastFromGraveyardOnceWatcher() {
super(WatcherScope.GAME);
}
@Override
public void watch(GameEvent event, Game game) {
if (GameEvent.EventType.SPELL_CAST.equals(event.getType())
&& event.hasApprovingIdentifier(MageIdentifier.CastFromGraveyardOnceWatcher)) {
usedFrom.add(event.getApprovingObject().getApprovingMageObjectReference());
}
}
@Override
public void reset() {
super.reset();
usedFrom.clear();
}
boolean abilityNotUsed(MageObjectReference mor) {
return !usedFrom.contains(mor);
}
}

View file

@ -0,0 +1,69 @@
package mage.abilities.effects.common.replacement;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.events.EntersTheBattlefieldEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.game.stack.Spell;
/**
* Used to affect a spell on the stack.
* The permanent it may become enters tapped.
* <p>
* Short-lived replacement effect, auto-cleanup if the mor is no longer a spell.
*
* @author Susucr
*/
public class MorEnteringTappedEffect extends ReplacementEffectImpl {
private final MageObjectReference mor;
public MorEnteringTappedEffect(MageObjectReference mor) {
super(Duration.OneUse, Outcome.Tap);
this.staticText = "That permanent enters the battlefield tapped";
this.mor = mor;
}
private MorEnteringTappedEffect(final MorEnteringTappedEffect effect) {
super(effect);
this.mor = effect.mor;
}
@Override
public MorEnteringTappedEffect copy() {
return new MorEnteringTappedEffect(this);
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
Spell spell = game.getSpell(event.getSourceId());
Spell morSpell = mor.getSpell(game);
if (morSpell == null) {
// cleanup if something other than resolving happens to the spell.
discard();
return false;
}
return spell != null
&& morSpell.getSourceId() == spell.getSourceId()
&& morSpell.getZoneChangeCounter(game) == spell.getZoneChangeCounter(game);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget();
if (permanent != null) {
permanent.setTapped(true);
}
return false;
}
}

View file

@ -97,6 +97,13 @@ public final class StaticFilters {
FILTER_CARD_CREATURE_A.setLockedFilter(true);
}
// for checks on cards to be cast as "a creature spell", this is a FilterCard, but the text is about spell
public static final FilterCreatureCard FILTER_CARD_A_CREATURE_SPELL = new FilterCreatureCard("a creature spell");
static {
FILTER_CARD_A_CREATURE_SPELL.setLockedFilter(true);
}
public static final FilterCreatureCard FILTER_CARD_CREATURE_YOUR_HAND = new FilterCreatureCard("a creature card from your hand");
static {