diff --git a/Mage.Sets/src/mage/cards/a/ActOnImpulse.java b/Mage.Sets/src/mage/cards/a/ActOnImpulse.java index a2f44ab166f..0f7dfa3ff87 100644 --- a/Mage.Sets/src/mage/cards/a/ActOnImpulse.java +++ b/Mage.Sets/src/mage/cards/a/ActOnImpulse.java @@ -1,20 +1,11 @@ package mage.cards.a; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; -import mage.cards.Card; +import mage.abilities.effects.common.ExileTop3MayPlayUntilEndOfTurnEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.game.Game; -import mage.players.Player; -import mage.target.targetpointer.FixedTargets; +import mage.constants.CardType; + +import java.util.UUID; /** * @@ -26,7 +17,7 @@ public final class ActOnImpulse extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}"); // Exile the top three cards of your library. Until end of turn, you may play cards exiled this way. - this.getSpellAbility().addEffect(new ActOnImpulseExileEffect()); + this.getSpellAbility().addEffect(new ExileTop3MayPlayUntilEndOfTurnEffect()); } public ActOnImpulse(final ActOnImpulse card) { @@ -39,45 +30,3 @@ public final class ActOnImpulse extends CardImpl { } } -class ActOnImpulseExileEffect extends OneShotEffect { - - public ActOnImpulseExileEffect() { - super(Outcome.Benefit); - this.staticText = "Exile the top three cards of your library. Until end of turn, you may play cards exiled this way"; - } - - public ActOnImpulseExileEffect(final ActOnImpulseExileEffect effect) { - super(effect); - } - - @Override - public ActOnImpulseExileEffect copy() { - return new ActOnImpulseExileEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (controller != null && sourceObject != null) { - Set cards = new HashSet<>(controller.getLibrary().getTopCards(game, 3)); - if (!cards.isEmpty()) { - controller.moveCardsToExile(cards, source, game, true, source.getSourceId(), sourceObject.getIdName()); - // remove cards that could not be moved to exile - for (Card card : cards) { - if (!Zone.EXILED.equals(game.getState().getZone(card.getId()))) { - cards.remove(card); - } - } - if (!cards.isEmpty()) { - ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, Duration.EndOfTurn); - effect.setTargetPointer(new FixedTargets(cards, game)); - game.addEffect(effect, source); - } - } - return true; - } - return false; - } - -} diff --git a/Mage.Sets/src/mage/cards/c/ChandraHeartOfFire.java b/Mage.Sets/src/mage/cards/c/ChandraHeartOfFire.java new file mode 100644 index 00000000000..dfd88af3421 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChandraHeartOfFire.java @@ -0,0 +1,135 @@ +package mage.cards.c; + +import mage.Mana; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.ExileTop3MayPlayUntilEndOfTurnEffect; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; +import mage.abilities.effects.common.discard.DiscardHandControllerEffect; +import mage.abilities.effects.mana.BasicManaEffect; +import mage.cards.*; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetAnyTarget; +import mage.target.common.TargetCardInLibrary; +import mage.target.targetpointer.FixedTargets; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * + * @author htrajan + */ +public final class ChandraHeartOfFire extends CardImpl { + + public ChandraHeartOfFire(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{3}{R}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.CHANDRA); + this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + + // +1: Discard your hand, then exile the top three cards of your library. Until end of turn, you may play cards exiled this way. + Ability ability = new LoyaltyAbility(new DiscardHandControllerEffect(), 1); + ability.addEffect(new ExileTop3MayPlayUntilEndOfTurnEffect() {{ + setText(", then " + getText(null)); + }}); + this.addAbility(ability); + + // +1: Chandra, Heart of Fire deals 2 damage to any target. + Ability damageAbility = new LoyaltyAbility(new DamageTargetEffect(2), 1); + damageAbility.addTarget(new TargetAnyTarget()); + this.addAbility(damageAbility); + + // −9: Search your graveyard and library for any number of red instant and/or sorcery spells, exile them, then shuffle your library. You may cast them this turn. Add six {R}. + Ability ultimateAbility = new LoyaltyAbility(new ChandraHeartOfFireUltimateEffect(), -9); + ultimateAbility.addEffect(new BasicManaEffect(Mana.RedMana(6)).setText("Add six {R}")); + this.addAbility(ultimateAbility); + } + + private ChandraHeartOfFire(final ChandraHeartOfFire card) { + super(card); + } + + @Override + public ChandraHeartOfFire copy() { + return new ChandraHeartOfFire(this); + } +} + +class ChandraHeartOfFireUltimateEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterCard(); + + static { + filter.add(new ColorPredicate(ObjectColor.RED)); + filter.add(Predicates.or(CardType.INSTANT.getPredicate(), CardType.SORCERY.getPredicate())); + } + + ChandraHeartOfFireUltimateEffect() { + super(Outcome.Benefit); + staticText = "Search your graveyard and library for any number of red instant and/or sorcery spells, exile them, then shuffle your library. You may cast them this turn"; + } + + private ChandraHeartOfFireUltimateEffect(ChandraHeartOfFireUltimateEffect effect) { + super(effect); + } + + @Override + public ChandraHeartOfFireUltimateEffect copy() { + return new ChandraHeartOfFireUltimateEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Set exiledCards = new HashSet<>(); + + filter.setMessage("red instant or sorcery in your graveyard"); + TargetCard target = new TargetCard(0, Integer.MAX_VALUE, Zone.GRAVEYARD, filter); + if (controller.choose(Outcome.Exile, controller.getGraveyard(), target, game)) { + List targets = target.getTargets(); + controller.moveCards(new CardsImpl(targets), Zone.EXILED, source, game); + exiledCards.addAll(targets.stream().map(game::getCard).collect(Collectors.toList())); + } + + filter.setMessage("red instant or sorcery in your library"); + Cards cardsInLibrary = new CardsImpl(); + cardsInLibrary.addAll(controller.getLibrary().getCards(game)); + target = new TargetCardInLibrary(0, Integer.MAX_VALUE, filter); + if (controller.choose(Outcome.Exile, cardsInLibrary, target, game)) { + List targets = target.getTargets(); + controller.moveCards(new CardsImpl(targets), Zone.EXILED, source, game); + exiledCards.addAll(targets.stream().map(game::getCard).collect(Collectors.toList())); + } + controller.shuffleLibrary(source, game); + + exiledCards.removeIf(card -> !Zone.EXILED.equals(game.getState().getZone(card.getId()))); + + if (!exiledCards.isEmpty()) { + ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, Duration.EndOfTurn); + effect.setTargetPointer(new FixedTargets(exiledCards, game)); + game.addEffect(effect, source); + } + + return true; + } + return false; + } + +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/c/ChandrasPyreling.java b/Mage.Sets/src/mage/cards/c/ChandrasPyreling.java new file mode 100644 index 00000000000..55f8778bd82 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChandrasPyreling.java @@ -0,0 +1,86 @@ +package mage.cards.c; + +import mage.MageInt; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.continuous.BoostSourceEffect; +import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; +import mage.abilities.keyword.DoubleStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.DamagedPlayerEvent; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class ChandrasPyreling extends CardImpl { + + public ChandrasPyreling(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}"); + + this.subtype.add(SubType.ELEMENTAL); + this.subtype.add(SubType.LIZARD); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // Whenever a source you control deals noncombat damage to an opponent, Chandra's Pyreling gets +1/+0 and gains double strike until end of turn. + this.addAbility(new ChandrasPyrelingAbility()); + } + + private ChandrasPyreling(final ChandrasPyreling card) { + super(card); + } + + @Override + public ChandrasPyreling copy() { + return new ChandrasPyreling(this); + } +} + +class ChandrasPyrelingAbility extends TriggeredAbilityImpl { + + private static final Effect effect = new BoostSourceEffect(1, 0, Duration.EndOfTurn); + + ChandrasPyrelingAbility() { + super(Zone.BATTLEFIELD, effect); + addEffect(new GainAbilitySourceEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn)); + } + + private ChandrasPyrelingAbility(ChandrasPyrelingAbility ability) { + super(ability); + addEffect(new GainAbilitySourceEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn)); + } + + @Override + public ChandrasPyrelingAbility copy() { + return new ChandrasPyrelingAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.DAMAGED_PLAYER; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + DamagedPlayerEvent damageEvent = (DamagedPlayerEvent) event; + return !damageEvent.isCombatDamage() + && game.getOpponents(controllerId).contains(event.getTargetId()) + && game.getControllerId(event.getSourceId()).equals(controllerId); + } + + @Override + public String getRule() { + return "Whenever a source you control deals noncombat damage to an opponent, {source} gets +1/+0 and gains double strike until end of turn."; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java index e8345dcebc6..945326709c4 100644 --- a/Mage.Sets/src/mage/sets/CoreSet2021.java +++ b/Mage.Sets/src/mage/sets/CoreSet2021.java @@ -55,7 +55,9 @@ public final class CoreSet2021 extends ExpansionSet { cards.add(new SetCardInfo("Chandra's Firemaw", 333, Rarity.RARE, mage.cards.c.ChandrasFiremaw.class)); cards.add(new SetCardInfo("Chandra's Incinerator", 136, Rarity.RARE, mage.cards.c.ChandrasIncinerator.class)); cards.add(new SetCardInfo("Chandra's Magmutt", 137, Rarity.COMMON, mage.cards.c.ChandrasMagmutt.class)); + cards.add(new SetCardInfo("Chandra's Pyreling", 138, Rarity.UNCOMMON, mage.cards.c.ChandrasPyreling.class)); cards.add(new SetCardInfo("Chandra, Flame's Catalyst", 332, Rarity.MYTHIC, mage.cards.c.ChandraFlamesCatalyst.class)); + cards.add(new SetCardInfo("Chandra, Heart of Fire", 135, Rarity.MYTHIC, mage.cards.c.ChandraHeartOfFire.class)); cards.add(new SetCardInfo("Chromatic Orrery", 228, Rarity.MYTHIC, mage.cards.c.ChromaticOrrery.class)); cards.add(new SetCardInfo("Colossal Dreadmaw", 176, Rarity.COMMON, mage.cards.c.ColossalDreadmaw.class)); cards.add(new SetCardInfo("Conclave Mentor", 216, Rarity.UNCOMMON, mage.cards.c.ConclaveMentor.class)); diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileTop3MayPlayUntilEndOfTurnEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileTop3MayPlayUntilEndOfTurnEffect.java new file mode 100644 index 00000000000..3ad21482f61 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/ExileTop3MayPlayUntilEndOfTurnEffect.java @@ -0,0 +1,56 @@ +package mage.abilities.effects.common; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffect; +import mage.cards.Card; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.target.targetpointer.FixedTargets; + +import java.util.HashSet; +import java.util.Set; + +public class ExileTop3MayPlayUntilEndOfTurnEffect extends OneShotEffect { + + public ExileTop3MayPlayUntilEndOfTurnEffect() { + super(Outcome.Benefit); + this.staticText = "exile the top three cards of your library. Until end of turn, you may play cards exiled this way"; + } + + public ExileTop3MayPlayUntilEndOfTurnEffect(final ExileTop3MayPlayUntilEndOfTurnEffect effect) { + super(effect); + } + + @Override + public ExileTop3MayPlayUntilEndOfTurnEffect copy() { + return new ExileTop3MayPlayUntilEndOfTurnEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (controller != null && sourceObject != null) { + Set cards = new HashSet<>(controller.getLibrary().getTopCards(game, 3)); + if (!cards.isEmpty()) { + controller.moveCardsToExile(cards, source, game, true, source.getSourceId(), sourceObject.getIdName()); + // remove cards that could not be moved to exile + cards.removeIf(card -> !Zone.EXILED.equals(game.getState().getZone(card.getId()))); + if (!cards.isEmpty()) { + ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, Duration.EndOfTurn); + effect.setTargetPointer(new FixedTargets(cards, game)); + game.addEffect(effect, source); + } + } + return true; + } + return false; + } + +}