diff --git a/Mage.Sets/src/mage/cards/c/CosimaGodOfTheVoyage.java b/Mage.Sets/src/mage/cards/c/CosimaGodOfTheVoyage.java new file mode 100644 index 0000000000..74d6c3f90c --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/CosimaGodOfTheVoyage.java @@ -0,0 +1,295 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.DealsDamageToAPlayerAllTriggeredAbility; +import mage.abilities.effects.AsThoughEffectImpl; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.CrewAbility; +import mage.cards.*; +import mage.constants.*; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.players.Player; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * @author TheElk801 + */ +public final class CosimaGodOfTheVoyage extends ModalDoubleFacesCard { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.VEHICLE, "a Vehicle you control"); + + public CosimaGodOfTheVoyage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.GOD}, "{2}{U}", + "The Omenkeel", new CardType[]{CardType.ARTIFACT}, new SubType[]{SubType.VEHICLE}, "{1}{U}" + ); + + // 1. + // Cosima, God of the Voyage + // Legendary Creature - God + this.getLeftHalfCard().addSuperType(SuperType.LEGENDARY); + this.getLeftHalfCard().setPT(new MageInt(2), new MageInt(4)); + + // At the beginning of your upkeep, you may exile Cosima. If you do, it gains "Whenever a land enters the battlefield under your control, if Cosima is exiled, you may put a voyage counter on it. If you don't, return Cosima to the battlefield with X +1/+1 counters on it and draw X cards, where X is the number of voyage counters on it. + this.getLeftHalfCard().addAbility(new BeginningOfUpkeepTriggeredAbility( + new CosimaGodOfTheVoyageEffect(), TargetController.YOU, true + )); + + // 2. + // The Omenkeel + // Legendary Artifact - Vehicle + this.getRightHalfCard().addSuperType(SuperType.LEGENDARY); + this.getRightHalfCard().setPT(new MageInt(3), new MageInt(3)); + + // Whenever a Vehicle you control deals combat damage to a player, that player exiles that many cards from the top of their library. You may play lands from among those cards for as long as they remain exiled. + this.getRightHalfCard().addAbility(new DealsDamageToAPlayerAllTriggeredAbility( + new TheOmenkeelEffect(), filter, false, + SetTargetPointer.PLAYER, true, true + )); + + // Crew 1 + this.getRightHalfCard().addAbility(new CrewAbility(1)); + } + + private CosimaGodOfTheVoyage(final CosimaGodOfTheVoyage card) { + super(card); + } + + @Override + public CosimaGodOfTheVoyage copy() { + return new CosimaGodOfTheVoyage(this); + } +} + +class CosimaGodOfTheVoyageEffect extends OneShotEffect { + + CosimaGodOfTheVoyageEffect() { + super(Outcome.Benefit); + staticText = "exile {this}. If you do, it gains " + + "\"Whenever a land enters the battlefield under your control, if {this} is exiled, " + + "you may put a voyage counter on it. If you don't, return {this} to the battlefield " + + "with X +1/+1 counters on it and draw X cards, where X is the number of voyage counters on it.\""; + } + + private CosimaGodOfTheVoyageEffect(final CosimaGodOfTheVoyageEffect effect) { + super(effect); + } + + @Override + public CosimaGodOfTheVoyageEffect copy() { + return new CosimaGodOfTheVoyageEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (player == null || permanent == null) { + return false; + } + Card card = game.getCard(permanent.getId()); + player.moveCards(permanent, Zone.EXILED, source, game); + if (card == null || game.getState().getZone(card.getId()) != Zone.EXILED) { + return true; + } + game.addEffect(new CosimaGodOfTheVoyageGainAbilityEffect(new MageObjectReference(card, game)), source); + return true; + } +} + +class CosimaGodOfTheVoyageGainAbilityEffect extends ContinuousEffectImpl { + + private final MageObjectReference mor; + + CosimaGodOfTheVoyageGainAbilityEffect(MageObjectReference mor) { + super(Duration.Custom, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + this.mor = mor; + } + + private CosimaGodOfTheVoyageGainAbilityEffect(final CosimaGodOfTheVoyageGainAbilityEffect effect) { + super(effect); + this.mor = effect.mor; + } + + @Override + public CosimaGodOfTheVoyageGainAbilityEffect copy() { + return new CosimaGodOfTheVoyageGainAbilityEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Card card = mor.getCard(game); + if (card != null && game.getState().getZone(card.getId()) == Zone.EXILED) { + Ability ability = new CosimaGodOfTheVoyageTriggeredAbility(); + ability.setSourceId(card.getId()); + ability.setControllerId(source.getSourceId()); + game.getState().addAbility(ability, card); + game.getState().addOtherAbility(card, ability); + } else { + discard(); + } + return true; + } +} + +class CosimaGodOfTheVoyageTriggeredAbility extends TriggeredAbilityImpl { + + CosimaGodOfTheVoyageTriggeredAbility() { + super(Zone.EXILED, new CosimaGodOfTheVoyageReturnEffect()); + } + + private CosimaGodOfTheVoyageTriggeredAbility(final CosimaGodOfTheVoyageTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent permanent = game.getPermanent(event.getTargetId()); + return permanent != null && permanent.isLand() && permanent.isControlledBy(getControllerId()); + } + + @Override + public boolean checkInterveningIfClause(Game game) { + return game.getState().getZone(getSourceId()) == Zone.EXILED; + } + + @Override + public CosimaGodOfTheVoyageTriggeredAbility copy() { + return new CosimaGodOfTheVoyageTriggeredAbility(this); + } + + @Override + public String getRule() { + return "Whenever a land enters the battlefield under your control, if {this} is exiled, " + + "you may put a voyage counter on it. If you don't, return {this} to the battlefield " + + "with X +1/+1 counters on it and draw X cards, where X is the number of voyage counters on it."; + } +} + +class CosimaGodOfTheVoyageReturnEffect extends OneShotEffect { + + CosimaGodOfTheVoyageReturnEffect() { + super(Outcome.Benefit); + } + + private CosimaGodOfTheVoyageReturnEffect(final CosimaGodOfTheVoyageReturnEffect effect) { + super(effect); + } + + @Override + public CosimaGodOfTheVoyageReturnEffect copy() { + return new CosimaGodOfTheVoyageReturnEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Card card = game.getCard(source.getSourceId()); + if (player == null || card == null) { + return false; + } + if (player.chooseUse(outcome, "Add a voyage counter?", source, game) + && card.addCounters(CounterType.VOYAGE.createInstance(), player.getId(), source, game)) { + return true; + } + int counterCount = card.getCounters(game).getCount(CounterType.VOYAGE); + player.moveCards(card, Zone.BATTLEFIELD, source, game); + if (counterCount < 1) { + return true; + } + player.drawCards(counterCount, source, game); + Permanent permanent = game.getPermanent(card.getId()); + if (permanent != null) { + permanent.addCounters(CounterType.P1P1.createInstance(counterCount), player.getId(), source, game); + } + return true; + } +} + +class TheOmenkeelEffect extends OneShotEffect { + + TheOmenkeelEffect() { + super(Outcome.Benefit); + staticText = "that player exiles that many cards from the top of their library. " + + "You may play lands from among those cards for as long as they remain exiled."; + } + + private TheOmenkeelEffect(final TheOmenkeelEffect effect) { + super(effect); + } + + @Override + public TheOmenkeelEffect copy() { + return new TheOmenkeelEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getFirstTarget()); + int damage = (Integer) getValue("damage"); + if (player == null || damage < 1) { + return false; + } + Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, damage)); + player.moveCards(cards, Zone.EXILED, source, game); + cards.removeIf(uuid -> game.getState().getZone(uuid) != Zone.EXILED); + cards.removeIf(uuid -> !game.getCard(uuid).isLand()); + game.addEffect(new TheOmenkeelPlayFromExileEffect(cards, game), source); + return true; + } +} + +class TheOmenkeelPlayFromExileEffect extends AsThoughEffectImpl { + + private final Set morSet = new HashSet<>(); + + TheOmenkeelPlayFromExileEffect(Cards cards, Game game) { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.Benefit); + this.morSet.addAll(cards.stream().map(uuid -> new MageObjectReference(uuid, game)).collect(Collectors.toSet())); + } + + private TheOmenkeelPlayFromExileEffect(final TheOmenkeelPlayFromExileEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public TheOmenkeelPlayFromExileEffect copy() { + return new TheOmenkeelPlayFromExileEffect(this); + } + + @Override + public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { + if (!source.isControlledBy(affectedControllerId) + || game.getState().getZone(sourceId) != Zone.EXILED) { + return false; + } + Card card = game.getCard(sourceId); + return card != null + && card.isLand() + && morSet.stream().anyMatch(mor -> mor.refersTo(card, game)); + } +} diff --git a/Mage.Sets/src/mage/sets/Kaldheim.java b/Mage.Sets/src/mage/sets/Kaldheim.java index 452c0fc429..4eb7ef7bfb 100644 --- a/Mage.Sets/src/mage/sets/Kaldheim.java +++ b/Mage.Sets/src/mage/sets/Kaldheim.java @@ -91,6 +91,7 @@ public final class Kaldheim extends ExpansionSet { cards.add(new SetCardInfo("Cleaving Reaper", 376, Rarity.RARE, mage.cards.c.CleavingReaper.class)); cards.add(new SetCardInfo("Codespell Cleric", 7, Rarity.COMMON, mage.cards.c.CodespellCleric.class)); cards.add(new SetCardInfo("Colossal Plow", 236, Rarity.UNCOMMON, mage.cards.c.ColossalPlow.class)); + cards.add(new SetCardInfo("Cosima, God of the Voyage", 50, Rarity.RARE, mage.cards.c.CosimaGodOfTheVoyage.class)); cards.add(new SetCardInfo("Cosmos Elixir", 237, Rarity.RARE, mage.cards.c.CosmosElixir.class)); cards.add(new SetCardInfo("Craven Hulk", 127, Rarity.COMMON, mage.cards.c.CravenHulk.class)); cards.add(new SetCardInfo("Crippling Fear", 82, Rarity.RARE, mage.cards.c.CripplingFear.class)); diff --git a/Mage/src/main/java/mage/abilities/common/DealsDamageToAPlayerAllTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DealsDamageToAPlayerAllTriggeredAbility.java index 4c662d195c..1de8a51fa5 100644 --- a/Mage/src/main/java/mage/abilities/common/DealsDamageToAPlayerAllTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DealsDamageToAPlayerAllTriggeredAbility.java @@ -58,30 +58,28 @@ public class DealsDamageToAPlayerAllTriggeredAbility extends TriggeredAbilityImp @Override public boolean checkTrigger(GameEvent event, Game game) { - if (!onlyCombat || ((DamagedPlayerEvent) event).isCombatDamage()) { - Permanent permanent = game.getPermanent(event.getSourceId()); - if (permanent != null && filter.match(permanent, getSourceId(), getControllerId(), game)) { - for (Effect effect : this.getEffects()) { - effect.setValue("damage", event.getAmount()); - effect.setValue("sourceId", event.getSourceId()); - if (affectsDefendingPlayer) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); - continue; - } - switch (setTargetPointer) { - case PLAYER: - effect.setTargetPointer(new FixedTarget(permanent.getControllerId())); - break; - case PERMANENT: - effect.setTargetPointer(new FixedTarget(permanent.getId(), permanent.getZoneChangeCounter(game))); - break; - } - - } - return true; + if (onlyCombat && !((DamagedPlayerEvent) event).isCombatDamage()) { + return false; + } + Permanent permanent = game.getPermanent(event.getSourceId()); + if (permanent == null || !filter.match(permanent, getSourceId(), getControllerId(), game)) { + return false; + } + this.getEffects().setValue("damage", event.getAmount()); + this.getEffects().setValue("sourceId", event.getSourceId()); + if (affectsDefendingPlayer) { + this.getEffects().setTargetPointer(new FixedTarget(event.getTargetId())); + } else { + switch (setTargetPointer) { + case PLAYER: + this.getEffects().setTargetPointer(new FixedTarget(permanent.getControllerId())); + break; + case PERMANENT: + this.getEffects().setTargetPointer(new FixedTarget(permanent, game)); + break; } } - return false; + return true; } @Override diff --git a/Mage/src/main/java/mage/counters/CounterType.java b/Mage/src/main/java/mage/counters/CounterType.java index ed90e4ba7f..fbe987305f 100644 --- a/Mage/src/main/java/mage/counters/CounterType.java +++ b/Mage/src/main/java/mage/counters/CounterType.java @@ -164,6 +164,7 @@ public enum CounterType { VIGILANCE("vigilance"), VITALITY("vitality"), VORTEX("vortex"), + VOYAGE("voyage"), WAGE("wage"), WINCH("winch"), WIND("wind"),