- [KHC] Added Cosmic Intervention, Ethereal Valkyrie, Hero of Bretagard, and Ranar, the Ever-Watchful.

This commit is contained in:
jeffwadsworth 2021-01-31 23:23:55 -06:00
parent 14f7c02b1f
commit 3b8e67c670
7 changed files with 816 additions and 6 deletions

View file

@ -0,0 +1,114 @@
package mage.cards.c;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.ExileTargetForSourceEffect;
import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect;
import mage.abilities.keyword.ForetellAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author jeffwadsworth
*/
public final class CosmicIntervention extends CardImpl {
public CosmicIntervention(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{W}");
// If a permanent you control would be put into a graveyard from the battlefield this turn, exile it instead. Return it to the battlefield under its owner's control at the beginning of the next end step.
this.getSpellAbility().addEffect(new CosmicInterventionReplacementEffect());
// Foretell 1W
this.addAbility(new ForetellAbility(this, "{1}{W}"));
}
public CosmicIntervention(final CosmicIntervention card) {
super(card);
}
@Override
public CosmicIntervention copy() {
return new CosmicIntervention(this);
}
}
class CosmicInterventionReplacementEffect extends ReplacementEffectImpl {
CosmicInterventionReplacementEffect() {
super(Duration.EndOfTurn, Outcome.Benefit);
staticText = "If a permanent you control would be put into a graveyard from the battlefield this turn, exile it instead. Return it to the battlefield under its owner's control at the beginning of the next end step";
}
private CosmicInterventionReplacementEffect(final CosmicInterventionReplacementEffect effect) {
super(effect);
}
@Override
public CosmicInterventionReplacementEffect copy() {
return new CosmicInterventionReplacementEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Permanent permanent = game.getPermanent(event.getTargetId());
if (permanent != null) {
Card card = game.getCard(event.getTargetId());
if (card != null) {
ExileTargetForSourceEffect exileEffect = new ExileTargetForSourceEffect();
exileEffect.setTargetPointer(new FixedTarget(card.getId(), game));
exileEffect.apply(game, source);
Effect returnEffect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(false, false);
returnEffect.setTargetPointer(new FixedTarget(card.getId(), game));
AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(returnEffect);
game.addDelayedTriggeredAbility(delayedAbility, source);
return true;
}
}
}
return false;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD
&& (((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD)) {
Player controller = game.getPlayer(source.getControllerId());
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());
if (permanent == null
|| controller == null
|| permanent.getControllerId() == controller.getId()) {
return true;
}
}
return false;
}
}

View file

@ -0,0 +1,324 @@
package mage.cards.e;
import java.util.UUID;
import mage.MageInt;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbility;
import mage.abilities.SpellAbility;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.costs.Cost;
import mage.abilities.costs.Costs;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.ForetellAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.ModalDoubleFacesCard;
import mage.cards.SplitCard;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SpellAbilityCastMode;
import mage.constants.SpellAbilityType;
import mage.constants.SubLayer;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.game.ExileZone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetCardInHand;
import mage.util.CardUtil;
/**
* @author jeffwadsworth
*/
public final class EtherealValkyrie extends CardImpl {
public EtherealValkyrie(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}{U}");
this.subtype.add(SubType.SPIRIT);
this.subtype.add(SubType.ANGEL);
this.power = new MageInt(4);
this.toughness = new MageInt(4);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Whenever Ethereal Valkyrie enters the battlefield or attacks, draw a card, then exile a card from your hand face down. It becomes foretold. Its foretell cost is its mana cost reduced by {2}.
this.addAbility(new EtherealValkyrieTriggeredAbility(new EtherealValkyrieEffect()));
}
private EtherealValkyrie(final EtherealValkyrie card) {
super(card);
}
@Override
public EtherealValkyrie copy() {
return new EtherealValkyrie(this);
}
}
class EtherealValkyrieTriggeredAbility extends TriggeredAbilityImpl {
EtherealValkyrieTriggeredAbility(Effect effect) {
super(Zone.BATTLEFIELD, effect, false);
}
EtherealValkyrieTriggeredAbility(final EtherealValkyrieTriggeredAbility ability) {
super(ability);
}
@Override
public EtherealValkyrieTriggeredAbility copy() {
return new EtherealValkyrieTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD
|| event.getType() == GameEvent.EventType.ATTACKER_DECLARED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Permanent p = game.getPermanent(event.getSourceId());
Permanent pETB = game.getPermanent(event.getTargetId());
if (p != null
&& p.getId() == sourceId) {
return true;
}
if (pETB != null
&& pETB.getId() == sourceId) {
return true;
}
return false;
}
@Override
public String getRule() {
return "Whenever {this} enters the battlefield or attacks, " + super.getRule();
}
}
class EtherealValkyrieEffect extends OneShotEffect {
public EtherealValkyrieEffect() {
super(Outcome.Benefit);
this.staticText = "draw a card, then exile a card from your hand face down. It becomes foretold. Its foretell cost is its mana cost reduced by {2}";
}
public EtherealValkyrieEffect(final EtherealValkyrieEffect effect) {
super(effect);
}
@Override
public EtherealValkyrieEffect copy() {
return new EtherealValkyrieEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
controller.drawCards(1, source, game);
TargetCardInHand targetCard = new TargetCardInHand(new FilterCard("card to exile face down. It becomes foretold."));
if (controller.chooseTarget(Outcome.Benefit, targetCard, source, game)) {
Card exileCard = game.getCard(targetCard.getFirstTarget());
if (exileCard == null) {
return false;
}
String foretellCost = CardUtil.reduceCost(exileCard.getSpellAbility().getManaCostsToPay(), 2).getText();
game.getState().setValue(exileCard.getId().toString() + "Foretell Cost", foretellCost);
game.getState().setValue(exileCard.getId().toString() + "Foretell Turn Number", game.getTurnNum());
UUID exileId = CardUtil.getExileZoneId(exileCard.getId().toString() + "foretellAbility", game);
controller.moveCardsToExile(exileCard, source, game, true, exileId, " Foretell Turn Number: " + game.getTurnNum());
exileCard.setFaceDown(true, game);
ForetellAbility foretellAbility = new ForetellAbility(exileCard, foretellCost);
foretellAbility.setSourceId(exileCard.getId());
foretellAbility.setControllerId(exileCard.getOwnerId());
game.getState().addOtherAbility(exileCard, foretellAbility);
foretellAbility.activate(game, true);
game.addEffect(new ForetellAddCostEffect(new MageObjectReference(exileCard, game)), source);
return true;
}
}
return false;
}
}
class ForetellAddCostEffect extends ContinuousEffectImpl {
private final MageObjectReference mor;
public ForetellAddCostEffect(MageObjectReference mor) {
super(Duration.EndOfGame, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility);
this.mor = mor;
staticText = "Foretold card";
}
public ForetellAddCostEffect(final ForetellAddCostEffect effect) {
super(effect);
this.mor = effect.mor;
}
@Override
public boolean apply(Game game, Ability source) {
Card card = mor.getCard(game);
if (card != null
&& game.getState().getZone(card.getId()) == Zone.EXILED) {
String foretellCost = (String) game.getState().getValue(card.getId().toString() + "Foretell Cost");
Ability ability = new ForetellCostAbility(foretellCost);
ability.setSourceId(card.getId());
ability.setControllerId(source.getControllerId());
game.getState().addOtherAbility(card, ability);
} else {
discard();
}
return true;
}
@Override
public ForetellAddCostEffect copy() {
return new ForetellAddCostEffect(this);
}
}
class ForetellCostAbility extends SpellAbility {
private String abilityName;
private SpellAbility spellAbilityToResolve;
public ForetellCostAbility(String foretellCost) {
super(null, "Testing", Zone.EXILED, SpellAbilityType.BASE_ALTERNATE, SpellAbilityCastMode.NORMAL);
this.setAdditionalCostsRuleVisible(false);
this.name = "Foretell " + foretellCost;
this.addCost(new ManaCostsImpl(foretellCost));
}
public ForetellCostAbility(final ForetellCostAbility ability) {
super(ability);
this.spellAbilityType = ability.spellAbilityType;
this.abilityName = ability.abilityName;
this.spellAbilityToResolve = ability.spellAbilityToResolve;
}
@Override
public ActivatedAbility.ActivationStatus canActivate(UUID playerId, Game game) {
if (super.canActivate(playerId, game).canActivate()) {
Card card = game.getCard(getSourceId());
if (card != null) {
// Card must be in the exile zone
if (game.getState().getZone(card.getId()) != Zone.EXILED) {
return ActivatedAbility.ActivationStatus.getFalse();
}
// Card must be Foretold
if (game.getState().getValue(card.getId().toString() + "Foretell Turn Number") == null
&& game.getState().getValue(card.getId().toString() + "foretellAbility") == null) {
return ActivatedAbility.ActivationStatus.getFalse();
}
// Can't be cast if the turn it was Foretold is the same
if ((int) game.getState().getValue(card.getId().toString() + "Foretell Turn Number") == game.getTurnNum()) {
return ActivatedAbility.ActivationStatus.getFalse();
}
// Check that the card is actually in the exile zone (ex: Oblivion Ring exiles it after it was Foretold, etc)
UUID exileId = (UUID) game.getState().getValue(card.getId().toString() + "foretellAbility");
ExileZone exileZone = game.getState().getExile().getExileZone(exileId);
if (exileZone != null
&& exileZone.isEmpty()) {
return ActivatedAbility.ActivationStatus.getFalse();
}
if (card instanceof SplitCard) {
if (((SplitCard) card).getLeftHalfCard().getName().equals(abilityName)) {
return ((SplitCard) card).getLeftHalfCard().getSpellAbility().canActivate(playerId, game);
} else if (((SplitCard) card).getRightHalfCard().getName().equals(abilityName)) {
return ((SplitCard) card).getRightHalfCard().getSpellAbility().canActivate(playerId, game);
}
} else if (card instanceof ModalDoubleFacesCard) {
if (((ModalDoubleFacesCard) card).getLeftHalfCard().getName().equals(abilityName)) {
return ((ModalDoubleFacesCard) card).getLeftHalfCard().getSpellAbility().canActivate(playerId, game);
} else if (((ModalDoubleFacesCard) card).getRightHalfCard().getName().equals(abilityName)) {
return ((ModalDoubleFacesCard) card).getRightHalfCard().getSpellAbility().canActivate(playerId, game);
}
}
return card.getSpellAbility().canActivate(playerId, game);
}
}
return ActivatedAbility.ActivationStatus.getFalse();
}
@Override
public SpellAbility getSpellAbilityToResolve(Game game) {
Card card = game.getCard(getSourceId());
if (card != null) {
if (spellAbilityToResolve == null) {
SpellAbility spellAbilityCopy = null;
if (card instanceof SplitCard) {
if (((SplitCard) card).getLeftHalfCard().getName().equals(abilityName)) {
spellAbilityCopy = ((SplitCard) card).getLeftHalfCard().getSpellAbility().copy();
} else if (((SplitCard) card).getRightHalfCard().getName().equals(abilityName)) {
spellAbilityCopy = ((SplitCard) card).getRightHalfCard().getSpellAbility().copy();
}
} else if (card instanceof ModalDoubleFacesCard) {
if (((ModalDoubleFacesCard) card).getLeftHalfCard().getName().equals(abilityName)) {
spellAbilityCopy = ((ModalDoubleFacesCard) card).getLeftHalfCard().getSpellAbility().copy();
} else if (((ModalDoubleFacesCard) card).getRightHalfCard().getName().equals(abilityName)) {
spellAbilityCopy = ((ModalDoubleFacesCard) card).getRightHalfCard().getSpellAbility().copy();
}
} else {
spellAbilityCopy = card.getSpellAbility().copy();
}
if (spellAbilityCopy == null) {
return null;
}
spellAbilityCopy.setId(this.getId());
spellAbilityCopy.getManaCosts().clear();
spellAbilityCopy.getManaCostsToPay().clear();
spellAbilityCopy.getCosts().addAll(this.getCosts().copy());
spellAbilityCopy.addCost(this.getManaCosts().copy());
spellAbilityCopy.setSpellAbilityCastMode(this.getSpellAbilityCastMode());
spellAbilityToResolve = spellAbilityCopy;
}
}
return spellAbilityToResolve;
}
@Override
public Costs<Cost> getCosts() {
if (spellAbilityToResolve == null) {
return super.getCosts();
}
return spellAbilityToResolve.getCosts();
}
@Override
public ForetellCostAbility copy() {
return new ForetellCostAbility(this);
}
@Override
public String getRule(boolean all) {
return "";
}
/**
* Used for split card in PlayerImpl method:
* getOtherUseableActivatedAbilities
*
* @param abilityName
*/
public void setAbilityName(String abilityName) {
this.abilityName = abilityName;
}
}

View file

@ -0,0 +1,195 @@
package mage.cards.h;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.SourceMatchesFilterCondition;
import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.continuous.AddCardSubTypeSourceEffect;
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.IndestructibleAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
import mage.filter.predicate.Predicate;
/**
* @author jeffwadsworth
*/
public final class HeroOfBretagard extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent();
private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent();
static {
filter.add(test1.instance);
filter2.add(test2.instance);
}
public HeroOfBretagard(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}");
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.WARRIOR);
this.power = new MageInt(1);
this.toughness = new MageInt(1);
// Whenever you exile one or more cards from your hand and/or permanents from the battlefield, put that many +1/+1 counters on Hero of Bretagard.
this.addAbility(new HeroOfBretagardTriggeredAbility(new HeroOfBretagardEffect()));
// As long as Hero of Bretagard has five or more counters on it, it has flying and is an Angel in addition to its other types.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
new ConditionalContinuousEffect(new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield),
new SourceMatchesFilterCondition(filter),
"As long as Hero of Bretagard has five or more counters on it, it has flying ")));
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
new ConditionalContinuousEffect(new AddCardSubTypeSourceEffect(Duration.WhileOnBattlefield, SubType.ANGEL), new SourceMatchesFilterCondition(filter),
"and is an Angel in addition to its other types.")));
// As long as Hero of Bretagard has ten or more counters on it, it has indestructible and is a God in addition to its other types.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
new ConditionalContinuousEffect(new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield),
new SourceMatchesFilterCondition(filter2),
"As long as Hero of Bretagard has ten or more counters on it, it has indestructible ")));
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
new ConditionalContinuousEffect(new AddCardSubTypeSourceEffect(Duration.WhileOnBattlefield, SubType.GOD), new SourceMatchesFilterCondition(filter2),
"and is a God in addition to its other types.")));
}
private HeroOfBretagard(final HeroOfBretagard card) {
super(card);
}
@Override
public HeroOfBretagard copy() {
return new HeroOfBretagard(this);
}
}
class HeroOfBretagardTriggeredAbility extends TriggeredAbilityImpl {
HeroOfBretagardTriggeredAbility(Effect effect) {
super(Zone.BATTLEFIELD, effect, false);
}
HeroOfBretagardTriggeredAbility(final HeroOfBretagardTriggeredAbility ability) {
super(ability);
}
@Override
public HeroOfBretagardTriggeredAbility copy() {
return new HeroOfBretagardTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
if (zEvent != null
&& Zone.HAND == zEvent.getFromZone()
&& Zone.EXILED == zEvent.getToZone()
&& zEvent.getTargetId() != null) {
Card card = game.getCard(zEvent.getTargetId());
if (card != null) {
UUID cardOwnerId = card.getOwnerId();
if (cardOwnerId != null
&& card.isOwnedBy(getControllerId())) {
this.getEffects().get(0).setValue("number", 1);
return true;
}
}
}
if (zEvent != null
&& Zone.BATTLEFIELD == zEvent.getFromZone()
&& Zone.EXILED == zEvent.getToZone()
&& zEvent.getTargetId() != null) {
Card card = game.getCard(zEvent.getTargetId());
if (card != null) {
UUID cardOwnerId = card.getOwnerId();
if (cardOwnerId != null
&& card.isOwnedBy(getControllerId())) {
this.getEffects().get(0).setValue("number", 1);
return true;
}
}
}
return false;
}
@Override
public String getRule() {
return "Whenever you exile one or more cards from your hand and/or permanents from the battlefield, put that many +1/+1 counters on {this}";
}
}
class HeroOfBretagardEffect extends OneShotEffect {
public HeroOfBretagardEffect() {
super(Outcome.Benefit);
}
public HeroOfBretagardEffect(final HeroOfBretagardEffect effect) {
super(effect);
}
@Override
public HeroOfBretagardEffect copy() {
return new HeroOfBretagardEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
return new AddCountersSourceEffect(CounterType.P1P1.createInstance((Integer) this.getValue("number"))).apply(game, source);
}
}
enum test1 implements Predicate<Permanent> {
instance;
@Override
public boolean apply(Permanent input, Game game) {
return input.getCounters(game).values().stream().anyMatch(counter -> counter.getCount() > 4);
}
@Override
public String toString() {
return "any counter";
}
}
enum test2 implements Predicate<Permanent> {
instance;
@Override
public boolean apply(Permanent input, Game game) {
return input.getCounters(game).values().stream().anyMatch(counter -> counter.getCount() > 9);
}
@Override
public String toString() {
return "any counter";
}
}

View file

@ -0,0 +1,149 @@
package mage.cards.r;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.ForetellAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.game.Game;
import java.util.UUID;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.keyword.VigilanceAbility;
import mage.cards.Card;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeGroupEvent;
import mage.game.permanent.token.SpiritWhiteToken;
import mage.watchers.common.ForetoldWatcher;
/**
* @author jeffwadsworth
*/
public final class RanarTheEverWatchful extends CardImpl {
public RanarTheEverWatchful(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{U}");
this.addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.SPIRIT);
this.subtype.add(SubType.WARRIOR);
this.power = new MageInt(2);
this.toughness = new MageInt(3);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Vigilance
this.addAbility(VigilanceAbility.getInstance());
// The first card you foretell each turn costs 0 to foretell
Ability ability = new SimpleStaticAbility(new RanarTheEverWatchfulCostReductionEffect());
this.addAbility(ability);
// Whenever you exile one or more cards from your hand and/or permanents from the battlefield, create a 1/1 white Spirit creature token with flying.
this.addAbility(new RanarTheEverWatchfulTriggeredAbility(new CreateTokenEffect(new SpiritWhiteToken())));
}
private RanarTheEverWatchful(final RanarTheEverWatchful card) {
super(card);
}
@Override
public RanarTheEverWatchful copy() {
return new RanarTheEverWatchful(this);
}
}
class RanarTheEverWatchfulCostReductionEffect extends CostModificationEffectImpl {
RanarTheEverWatchfulCostReductionEffect() {
super(Duration.WhileOnBattlefield, Outcome.Neutral, CostModificationType.REDUCE_COST);
staticText = "The first card you foretell each turn costs 0 to foretell";
}
private RanarTheEverWatchfulCostReductionEffect(RanarTheEverWatchfulCostReductionEffect effect) {
super(effect);
}
@Override
public RanarTheEverWatchfulCostReductionEffect copy() {
return new RanarTheEverWatchfulCostReductionEffect(this);
}
@Override
public boolean apply(Game game, Ability source, Ability abilityToModify) {
abilityToModify.getManaCostsToPay().clear();
abilityToModify.getManaCostsToPay().addAll(new ManaCostsImpl<>("{0}"));
return true;
}
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
ForetoldWatcher watcher = game.getState().getWatcher(ForetoldWatcher.class);
return (watcher != null
&& watcher.countNumberForetellThisTurn() == 0
&& abilityToModify.isControlledBy(source.getControllerId())
&& abilityToModify instanceof ForetellAbility);
}
}
class RanarTheEverWatchfulTriggeredAbility extends TriggeredAbilityImpl {
RanarTheEverWatchfulTriggeredAbility(Effect effect) {
super(Zone.BATTLEFIELD, effect, false);
}
RanarTheEverWatchfulTriggeredAbility(final RanarTheEverWatchfulTriggeredAbility ability) {
super(ability);
}
@Override
public RanarTheEverWatchfulTriggeredAbility copy() {
return new RanarTheEverWatchfulTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ZONE_CHANGE_GROUP;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
ZoneChangeGroupEvent zEvent = (ZoneChangeGroupEvent) event;
if (zEvent != null
&& Zone.HAND == zEvent.getFromZone()
&& Zone.EXILED == zEvent.getToZone()
&& zEvent.getCards() != null) {
for (Card card : zEvent.getCards()) {
if (card != null) {
UUID cardOwnerId = card.getOwnerId();
if (cardOwnerId != null
&& card.isOwnedBy(getControllerId())) {
return true;
}
}
}
}
if (zEvent != null
&& Zone.BATTLEFIELD == zEvent.getFromZone()
&& Zone.EXILED == zEvent.getToZone()
&& zEvent.getCards() != null) {
return true;
}
return false;
}
@Override
public String getRule() {
return "Whenever you exile one or more cards from your hand and/or permanents from the battlefield, " + super.getRule();
}
}

View file

@ -39,6 +39,7 @@ public final class KaldheimCommander extends ExpansionSet {
cards.add(new SetCardInfo("Cloudgoat Ranger", 21, Rarity.UNCOMMON, mage.cards.c.CloudgoatRanger.class));
cards.add(new SetCardInfo("Command Tower", 108, Rarity.COMMON, mage.cards.c.CommandTower.class));
cards.add(new SetCardInfo("Commander's Sphere", 99, Rarity.COMMON, mage.cards.c.CommandersSphere.class));
cards.add(new SetCardInfo("Cosmic Intervention", 3, Rarity.RARE, mage.cards.c.CosmicIntervention.class));
cards.add(new SetCardInfo("Crown of Skemfar", 13, Rarity.RARE, mage.cards.c.CrownOfSkemfar.class));
cards.add(new SetCardInfo("Cryptic Caves", 109, Rarity.UNCOMMON, mage.cards.c.CrypticCaves.class));
cards.add(new SetCardInfo("Cultivator of Blades", 55, Rarity.RARE, mage.cards.c.CultivatorOfBlades.class));
@ -53,6 +54,7 @@ public final class KaldheimCommander extends ExpansionSet {
cards.add(new SetCardInfo("Elvish Rejuvenator", 60, Rarity.COMMON, mage.cards.e.ElvishRejuvenator.class));
cards.add(new SetCardInfo("Empyrean Eagle", 85, Rarity.UNCOMMON, mage.cards.e.EmpyreanEagle.class));
cards.add(new SetCardInfo("End-Raze Forerunners", 61, Rarity.RARE, mage.cards.e.EndRazeForerunners.class));
cards.add(new SetCardInfo("Ethereal Valkyrie", 16, Rarity.RARE, mage.cards.e.EtherealValkyrie.class));
cards.add(new SetCardInfo("Evangel of Heliod", 23, Rarity.UNCOMMON, mage.cards.e.EvangelOfHeliod.class));
cards.add(new SetCardInfo("Eyeblight Cullers", 48, Rarity.COMMON, mage.cards.e.EyeblightCullers.class));
cards.add(new SetCardInfo("Eyeblight Massacre", 49, Rarity.UNCOMMON, mage.cards.e.EyeblightMassacre.class));
@ -67,6 +69,7 @@ public final class KaldheimCommander extends ExpansionSet {
cards.add(new SetCardInfo("Golgari Guildgate", 111, Rarity.COMMON, mage.cards.g.GolgariGuildgate.class));
cards.add(new SetCardInfo("Golgari Rot Farm", 112, Rarity.UNCOMMON, mage.cards.g.GolgariRotFarm.class));
cards.add(new SetCardInfo("Harvest Season", 63, Rarity.RARE, mage.cards.h.HarvestSeason.class));
cards.add(new SetCardInfo("Hero of Bretagard", 4, Rarity.RARE, mage.cards.h.HeroOfBretagard.class));
cards.add(new SetCardInfo("Imperious Perfect", 64, Rarity.RARE, mage.cards.i.ImperiousPerfect.class));
cards.add(new SetCardInfo("Inspired Sphinx", 40, Rarity.MYTHIC, mage.cards.i.InspiredSphinx.class));
cards.add(new SetCardInfo("Jagged-Scar Archers", 65, Rarity.UNCOMMON, mage.cards.j.JaggedScarArchers.class));
@ -100,6 +103,7 @@ public final class KaldheimCommander extends ExpansionSet {
cards.add(new SetCardInfo("Pride of the Perfect", 52, Rarity.UNCOMMON, mage.cards.p.PrideOfThePerfect.class));
cards.add(new SetCardInfo("Prowess of the Fair", 53, Rarity.UNCOMMON, mage.cards.p.ProwessOfTheFair.class));
cards.add(new SetCardInfo("Putrefy", 91, Rarity.UNCOMMON, mage.cards.p.Putrefy.class));
cards.add(new SetCardInfo("Ranar the Ever-Watchful", 2, Rarity.RARE, mage.cards.r.RanarTheEverWatchful.class));
cards.add(new SetCardInfo("Reclamation Sage", 72, Rarity.UNCOMMON, mage.cards.r.ReclamationSage.class));
cards.add(new SetCardInfo("Restoration Angel", 31, Rarity.COMMON, mage.cards.r.RestorationAngel.class));
cards.add(new SetCardInfo("Return to Dust", 32, Rarity.UNCOMMON, mage.cards.r.ReturnToDust.class));

View file

@ -17,7 +17,7 @@ public enum ForetoldCondition implements Condition {
public boolean apply(Game game, Ability source) {
ForetoldWatcher watcher = game.getState().getWatcher(ForetoldWatcher.class);
if (watcher != null) {
return watcher.foretoldSpellWasCast(source.getSourceId());
return watcher.cardWasForetold(source.getSourceId());
}
return false;
}
@ -26,4 +26,4 @@ public enum ForetoldCondition implements Condition {
public String toString() {
return "this spell was foretold";
}
}
}

View file

@ -8,6 +8,8 @@ package mage.watchers.common;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import mage.abilities.keyword.ForetellAbility;
import mage.cards.Card;
import mage.constants.WatcherScope;
import mage.constants.Zone;
import mage.game.Game;
@ -21,9 +23,9 @@ import mage.watchers.Watcher;
* @author jeffwadsworth
*/
public class ForetoldWatcher extends Watcher {
// If a card was Foretold during a turn, this list stores it. Cleared at the end of the turn.
// If foretell was activated or a card was Foretold by the controller this turn, this list stores it. Cleared at the end of the turn.
private final Set<UUID> foretellCardsThisTurn = new HashSet<>();
private final Set<UUID> foretoldCardsThisTurn = new HashSet<>();
public ForetoldWatcher() {
@ -32,10 +34,19 @@ public class ForetoldWatcher extends Watcher {
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.TAKEN_SPECIAL_ACTION) {
Card card = game.getCard(event.getSourceId());
if (card != null
&& card.getAbilities(game).containsClass(ForetellAbility.class)
&& controllerId == event.getPlayerId()) {
foretellCardsThisTurn.add(card.getId());
}
}
if (event.getType() == GameEvent.EventType.SPELL_CAST
&& event.getZone() == Zone.EXILED) {
Spell spell = (Spell) game.getObject(event.getTargetId());
if (spell != null) {
if (spell != null
&& controllerId == event.getPlayerId()) {
UUID exileId = CardUtil.getExileZoneId(spell.getSourceId().toString() + "foretellAbility", game);
if (exileId != null) {
foretoldCardsThisTurn.add(spell.getSourceId());
@ -44,13 +55,26 @@ public class ForetoldWatcher extends Watcher {
}
}
public boolean foretoldSpellWasCast(UUID sourceId) {
public boolean cardUsedForetell(UUID sourceId) {
return foretellCardsThisTurn.contains(sourceId);
}
public boolean cardWasForetold(UUID sourceId) {
return foretoldCardsThisTurn.contains(sourceId);
}
public int countNumberForetellThisTurn() {
return foretellCardsThisTurn.size();
}
public int countNumberForetoldThisTurn() {
return foretoldCardsThisTurn.size();
}
@Override
public void reset() {
super.reset();
foretellCardsThisTurn.clear();
foretoldCardsThisTurn.clear();
}
}