diff --git a/Mage.Sets/src/mage/cards/k/KetramoseTheNewDawn.java b/Mage.Sets/src/mage/cards/k/KetramoseTheNewDawn.java new file mode 100644 index 00000000000..f87abbbf1db --- /dev/null +++ b/Mage.Sets/src/mage/cards/k/KetramoseTheNewDawn.java @@ -0,0 +1,113 @@ +package mage.cards.k; + +import mage.MageInt; +import mage.abilities.BatchTriggeredAbility; +import mage.abilities.TriggeredAbility; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.CardsInExileCondition; +import mage.abilities.dynamicvalue.common.CardsInExileCount; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.LoseLifeSourceControllerEffect; +import mage.abilities.effects.common.combat.CantAttackBlockUnlessConditionSourceEffect; +import mage.abilities.keyword.IndestructibleAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeBatchEvent; +import mage.game.events.ZoneChangeEvent; + +import java.util.UUID; + +/** + * @author Jmlundeen + */ +public final class KetramoseTheNewDawn extends CardImpl { + + public KetramoseTheNewDawn(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{B}"); + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.GOD); + + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Menace + this.addAbility(new MenaceAbility()); + + // Lifelink + this.addAbility(LifelinkAbility.getInstance()); + + // Indestructible + this.addAbility(IndestructibleAbility.getInstance()); + + // Ketramose can't attack or block unless there are seven or more cards in exile. + this.addAbility(new SimpleStaticAbility( + new CantAttackBlockUnlessConditionSourceEffect(new CardsInExileCondition(ComparisonType.MORE_THAN, 6, CardsInExileCount.ALL)) + .setText("{this} can't attack or block unless there are seven or more cards in exile") + ).addHint(CardsInExileCount.ALL.getHint())); + + // Whenever one or more cards are put into exile from graveyards and/or the battlefield during your turn, you draw a card and lose 1 life. + this.addAbility(new KetramoseTriggeredAbility()); + + } + + private KetramoseTheNewDawn(final KetramoseTheNewDawn card) { + super(card); + } + + @Override + public KetramoseTheNewDawn copy() { + return new KetramoseTheNewDawn(this); + } + +} +class KetramoseTriggeredAbility extends TriggeredAbilityImpl implements BatchTriggeredAbility { + KetramoseTriggeredAbility() { + super(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), false); + this.addEffect(new LoseLifeSourceControllerEffect(1)); + } + + private KetramoseTriggeredAbility(final KetramoseTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE_BATCH; + } + + @Override + public boolean checkEvent(ZoneChangeEvent event, Game game) { + if (event.getToZone() != Zone.EXILED) { + return false; + } + return event.getFromZone() == Zone.GRAVEYARD || event.getFromZone() == Zone.BATTLEFIELD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return game.getActivePlayerId().equals(getControllerId()) + && !getFilteredEvents((ZoneChangeBatchEvent) event, game).isEmpty(); + } + + @Override + public TriggeredAbility copy() + { + return new KetramoseTriggeredAbility(this); + } + + @Override + public String getRule() { + return "Whenever one or more cards are put into exile from graveyards" + + " and/or the battlefield during your turn, you draw a card and lose 1 life."; + } +} diff --git a/Mage.Sets/src/mage/sets/Aetherdrift.java b/Mage.Sets/src/mage/sets/Aetherdrift.java index dfcef6ecbec..5b3bee14ac7 100644 --- a/Mage.Sets/src/mage/sets/Aetherdrift.java +++ b/Mage.Sets/src/mage/sets/Aetherdrift.java @@ -131,6 +131,10 @@ public final class Aetherdrift extends ExpansionSet { cards.add(new SetCardInfo("Jungle Hollow", 256, Rarity.COMMON, mage.cards.j.JungleHollow.class)); cards.add(new SetCardInfo("Kalakscion, Hunger Tyrant", 93, Rarity.UNCOMMON, mage.cards.k.KalakscionHungerTyrant.class)); cards.add(new SetCardInfo("Keen Buccaneer", 48, Rarity.COMMON, mage.cards.k.KeenBuccaneer.class)); + cards.add(new SetCardInfo("Ketramose, the New Dawn", 209, Rarity.MYTHIC, mage.cards.k.KetramoseTheNewDawn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ketramose, the New Dawn", 350, Rarity.MYTHIC, mage.cards.k.KetramoseTheNewDawn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ketramose, the New Dawn", 482, Rarity.MYTHIC, mage.cards.k.KetramoseTheNewDawn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ketramose, the New Dawn", 549, Rarity.MYTHIC, mage.cards.k.KetramoseTheNewDawn.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Kickoff Celebrations", 135, Rarity.COMMON, mage.cards.k.KickoffCelebrations.class)); cards.add(new SetCardInfo("Lagorin, Soul of Alacria", 211, Rarity.UNCOMMON, mage.cards.l.LagorinSoulOfAlacria.class)); cards.add(new SetCardInfo("Leonin Surveyor", 18, Rarity.COMMON, mage.cards.l.LeoninSurveyor.class)); diff --git a/Mage/src/main/java/mage/abilities/condition/common/CardsInExileCondition.java b/Mage/src/main/java/mage/abilities/condition/common/CardsInExileCondition.java new file mode 100644 index 00000000000..6fa1ba75005 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/condition/common/CardsInExileCondition.java @@ -0,0 +1,34 @@ +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.abilities.dynamicvalue.common.CardsInExileCount; +import mage.constants.ComparisonType; +import mage.game.Game; + + +public class CardsInExileCondition implements Condition +{ + private final ComparisonType type; + private final int count; + private CardsInExileCount cardsInExileCount; + + public CardsInExileCondition(ComparisonType type, int count) + { + this(type, count, CardsInExileCount.YOU); + } + + public CardsInExileCondition(ComparisonType type, int count, CardsInExileCount cardsInExileCount) + { + this.type = type; + this.count = count; + this.cardsInExileCount = cardsInExileCount; + } + + @Override + public boolean apply(Game game, Ability source) + { + int exileCards = cardsInExileCount.calculate(game, source, null); + return ComparisonType.compare(exileCards, type, count); + } +} diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardsInExileCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardsInExileCount.java new file mode 100644 index 00000000000..6db633c0c2f --- /dev/null +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/CardsInExileCount.java @@ -0,0 +1,110 @@ +package mage.abilities.dynamicvalue.common; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.hint.Hint; +import mage.cards.Card; +import mage.constants.CardType; +import mage.game.Game; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author JayDi85 + */ +public enum CardsInExileCount implements DynamicValue { + YOU("you"), + ALL("all players"), + OPPONENTS("your opponents'"); + + private final String message; + private final CardsInExileHint hint; + + CardsInExileCount(String message) { + this.message = "The number of cards owned by " + message + " in exile"; + this.hint = new CardsInExileHint(this); + } + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return getExileCards(game, sourceAbility) + .mapToInt(x -> 1) + .sum(); + } + + @Override + public CardsInExileCount copy() { + return this; + } + + @Override + public String toString() { + return "X"; + } + + @Override + public String getMessage() { + return message; + } + + public Hint getHint() { + return hint; + } + + public Stream getExileCards(Game game, Ability ability) { + Collection playerIds; + switch (this) { + case YOU: + playerIds = Collections.singletonList(ability.getControllerId()); + break; + case OPPONENTS: + playerIds = game.getOpponents(ability.getControllerId()); + break; + case ALL: + playerIds = game.getState().getPlayersInRange(ability.getControllerId(), game); + break; + default: + throw new IllegalArgumentException("Wrong code usage: miss implementation for " + this); + } + return playerIds.stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .map(player -> game.getExile().getAllCards(game, player.getId())) + .flatMap(Collection::stream) + .filter(Objects::nonNull); + } +} + +class CardsInExileHint implements Hint { + + CardsInExileCount value; + + CardsInExileHint(CardsInExileCount value) { + this.value = value; + } + + private CardsInExileHint(final CardsInExileHint hint) { + this.value = hint.value; + } + + @Override + public String getText(Game game, Ability ability) { + int count = value.getExileCards(game, ability) + .mapToInt(x -> 1) + .sum(); + + return this.value.getMessage() + ": " + count; + } + + @Override + public CardsInExileHint copy() { + return new CardsInExileHint(this); + } +} \ No newline at end of file