diff --git a/Mage.Client/src/main/resources/card-pictures-tok.txt b/Mage.Client/src/main/resources/card-pictures-tok.txt index 6c46c6c064..d3426689e9 100644 --- a/Mage.Client/src/main/resources/card-pictures-tok.txt +++ b/Mage.Client/src/main/resources/card-pictures-tok.txt @@ -154,7 +154,7 @@ |Generate|TOK:5ED|Serf|||SerfToken| |Generate|TOK:5ED|Snake|||SerpentGeneratorSnakeToken| |Generate|TOK:5ED|Thrull|||BreedingPitBlackInsectToken| -|Generate|TOK:6ED|Cat|||WaitingInTheWeedsCatToken| +|Generate|TOK:6ED|Cat|||GreenCatToken| |Generate|TOK:6ED|Citizen|||CitizenToken| |Generate|TOK:6ED|Djinn|||DjinnToken| |Generate|TOK:6ED|Goblin|||GoblinToken| @@ -1147,7 +1147,7 @@ |Generate|TOK:THS|Soldier|1||SoldierToken| |Generate|TOK:THS|Soldier|2||SoldierToken| |Generate|TOK:TMP|Beast|||CarnivoreToken| -|Generate|TOK:TMP|Hound|||HoundToken| +|Generate|TOK:TMP|Dog|||GreenDogToken| |Generate|TOK:TMP|Pegasus|||PegasusToken| |Generate|TOK:TMP|Reflection|||ReflectionToken| |Generate|TOK:TMP|Saproling|||SaprolingToken| diff --git a/Mage.Sets/src/mage/cards/b/BasriDevotedPaladin.java b/Mage.Sets/src/mage/cards/b/BasriDevotedPaladin.java new file mode 100644 index 0000000000..c484ec5995 --- /dev/null +++ b/Mage.Sets/src/mage/cards/b/BasriDevotedPaladin.java @@ -0,0 +1,113 @@ +package mage.cards.b; + +import mage.abilities.Ability; +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; +import mage.abilities.effects.common.continuous.BoostControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.effects.common.counter.AddCountersTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.VigilanceAbility; +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.counters.CounterType; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class BasriDevotedPaladin extends CardImpl { + + public BasriDevotedPaladin(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{4}{W}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.BASRI); + this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + + // +1: Put a +1/+1 counter on up to one target creature. It gains vigilance until end of turn. + Ability ability = new LoyaltyAbility(new AddCountersTargetEffect( + CounterType.P1P1.createInstance() + ).setText("Put a +1/+1 counter on up to one target creature"), 1); + ability.addEffect(new GainAbilityTargetEffect( + VigilanceAbility.getInstance(), Duration.EndOfTurn + ).setText("It gains vigilance until end of turn")); + ability.addTarget(new TargetCreaturePermanent(0, 1)); + this.addAbility(ability); + + // −1: Whenever a creature attacks this turn, put a +1/+1 counter on it. + this.addAbility(new LoyaltyAbility(new CreateDelayedTriggeredAbilityEffect(new BasriDevotedPaladinTriggeredAbility()), -1)); + + // −6: Creatures you control get +2/+2 and gain flying until end of turn. + Effect effect = new BoostControlledEffect(2, 2, Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURE); + effect.setText("Creatures you control get +2/+2"); + LoyaltyAbility ultimateAbility = new LoyaltyAbility(effect, -6); + effect = new GainAbilityControlledEffect(FlyingAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURE); + effect.setText("and gain flying until end of turn"); + ultimateAbility.addEffect(effect); + this.addAbility(ultimateAbility); + } + + private BasriDevotedPaladin(final BasriDevotedPaladin card) { + super(card); + } + + @Override + public BasriDevotedPaladin copy() { + return new BasriDevotedPaladin(this); + } +} + +class BasriDevotedPaladinTriggeredAbility extends DelayedTriggeredAbility { + + public BasriDevotedPaladinTriggeredAbility() { + super(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), Duration.EndOfTurn, false); + } + + public BasriDevotedPaladinTriggeredAbility(BasriDevotedPaladinTriggeredAbility ability) { + super(ability); + } + + @Override + public BasriDevotedPaladinTriggeredAbility copy() { + return new BasriDevotedPaladinTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ATTACKER_DECLARED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent permanent = game.getPermanent(event.getSourceId()); + if (permanent != null) { + for (Effect effect : getEffects()) { + effect.setTargetPointer(new FixedTarget(permanent, game)); + } + return true; + } + return false; + } + + @Override + public String getRule() { + return "Whenever a creature attacks this turn, put a +1/+1 counter on it."; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/c/ChandraFlamesCatalyst.java b/Mage.Sets/src/mage/cards/c/ChandraFlamesCatalyst.java new file mode 100644 index 0000000000..9c78e245a3 --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ChandraFlamesCatalyst.java @@ -0,0 +1,75 @@ +package mage.cards.c; + +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.effects.CastCardFromGraveyardThenExileItEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.continuous.CastFromHandWithoutPayingManaCostEffect; +import mage.abilities.effects.common.discard.DiscardHandControllerEffect; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.target.common.TargetCardInYourGraveyard; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class ChandraFlamesCatalyst extends CardImpl { + + private static final FilterCard filter = new FilterCard(); + + static { + filter.add(new ColorPredicate(ObjectColor.RED)); + filter.add(Predicates.or(CardType.INSTANT.getPredicate(), CardType.SORCERY.getPredicate())); + } + + public ChandraFlamesCatalyst(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{4}{R}{R}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.CHANDRA); + this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + + // +1: Chandra, Flame's Catalyst deals 3 damage to each opponent. + this.addAbility(new LoyaltyAbility(new DamagePlayersEffect(3, TargetController.OPPONENT), 1)); + + // −2: You may cast target red instant or sorcery card from your graveyard. If that spell would be put into your graveyard this turn, exile it instead. + CastCardFromGraveyardThenExileItEffect minusEffect = new CastCardFromGraveyardThenExileItEffect(); + minusEffect.setText("You may cast target red instant or sorcery card from your graveyard. If that spell would be put into your graveyard this turn, exile it instead"); + Ability ability = new LoyaltyAbility(minusEffect, -2); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); + + // −8: Discard your hand, then draw seven cards. Until end of turn, you may cast spells from your hand without paying their mana costs. + Effect discardHandEffect = new DiscardHandControllerEffect(); + Effect drawEffect = new DrawCardSourceControllerEffect(7); + drawEffect.setText(", then draw seven cards"); + Effect castSpellsFromHandEffect = new CastFromHandWithoutPayingManaCostEffect( + StaticFilters.FILTER_CARD_NON_LAND, true, Duration.EndOfTurn); + castSpellsFromHandEffect.setText("Until end of turn, you may cast spells from your hand without paying their mana costs"); + Ability ultimateAbility = new LoyaltyAbility(discardHandEffect, -8); + ultimateAbility.addEffect(drawEffect); + ultimateAbility.addEffect(castSpellsFromHandEffect); + this.addAbility(ultimateAbility); + } + + private ChandraFlamesCatalyst(final ChandraFlamesCatalyst card) { + super(card); + } + + @Override + public ChandraFlamesCatalyst copy() { + return new ChandraFlamesCatalyst(this); + } +} diff --git a/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java b/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java new file mode 100644 index 0000000000..614653fe34 --- /dev/null +++ b/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java @@ -0,0 +1,107 @@ +package mage.cards.g; + +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.DamageAsThoughNotBlockedAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageWithPowerFromOneToAnotherTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityControlledEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; +import mage.constants.*; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class GarrukSavageHerald extends CardImpl { + + public GarrukSavageHerald(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{4}{G}{G}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.GARRUK); + this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(5)); + + // +1: Reveal the top card of your library. If it's a creature card, put it into your hand. Otherwise, put it on the bottom of your library. + this.addAbility(new LoyaltyAbility(new GarrukSavageHeraldEffect(), 1)); + + // −2: Target creature you control deals damage equal to its power to another target creature. + DamageWithPowerFromOneToAnotherTargetEffect effect = new DamageWithPowerFromOneToAnotherTargetEffect(); + effect.setText("Target creature you control deals damage equal to its power to another target creature"); + + Ability minusAbility = new LoyaltyAbility(effect, -2); + TargetControlledCreaturePermanent controlledCreature = new TargetControlledCreaturePermanent(); + controlledCreature.setTargetTag(1); + minusAbility.addTarget(controlledCreature); + + FilterCreaturePermanent filter = new FilterCreaturePermanent("Another creature: damage dealt to"); + filter.add(new AnotherTargetPredicate(2)); + TargetCreaturePermanent anotherTargetCreature = new TargetCreaturePermanent(filter); + minusAbility.addTarget(anotherTargetCreature); + + this.addAbility(minusAbility); + + // −7: Until end of turn, creatures you control gain "You may have this creature assign its combat damage as though it weren't blocked." + ContinuousEffect ultimateEffect = new GainAbilityControlledEffect(DamageAsThoughNotBlockedAbility.getInstance(), Duration.EndOfTurn); + ultimateEffect.setText("Until end of turn, creatures you control gain \"You may have this creature assign its combat damage as though it weren't blocked.\""); + this.addAbility(new LoyaltyAbility(ultimateEffect, -7)); + } + + private GarrukSavageHerald(final GarrukSavageHerald card) { + super(card); + } + + @Override + public GarrukSavageHerald copy() { + return new GarrukSavageHerald(this); + } +} + +class GarrukSavageHeraldEffect extends OneShotEffect { + + GarrukSavageHeraldEffect() { + super(Outcome.Benefit); + staticText = "reveal the top card of your library. If it's a creature card, " + + "put it into your hand. Otherwise, put it on the bottom of your library"; + } + + private GarrukSavageHeraldEffect(final GarrukSavageHeraldEffect effect) { + super(effect); + } + + @Override + public GarrukSavageHeraldEffect copy() { + return new GarrukSavageHeraldEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Card card = player.getLibrary().getFromTop(game); + if (card == null) { + return false; + } + player.revealCards(source, new CardsImpl(card), game); + if (card.isCreature()) { + return player.moveCards(card, Zone.HAND, source, game); + } else { + return player.putCardsOnBottomOfLibrary(card, game, source, false); + } + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/j/JaceTelepathUnbound.java b/Mage.Sets/src/mage/cards/j/JaceTelepathUnbound.java index e9504663f3..cb756e9150 100644 --- a/Mage.Sets/src/mage/cards/j/JaceTelepathUnbound.java +++ b/Mage.Sets/src/mage/cards/j/JaceTelepathUnbound.java @@ -5,32 +5,19 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; -import mage.abilities.effects.AsThoughEffectImpl; -import mage.abilities.effects.ContinuousEffect; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.*; import mage.abilities.effects.common.GetEmblemEffect; import mage.abilities.effects.common.continuous.BoostTargetEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.AsThoughEffectType; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.SuperType; -import mage.constants.Zone; import mage.filter.common.FilterInstantOrSorceryCard; -import mage.game.Game; import mage.game.command.emblems.JaceTelepathUnboundEmblem; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; -import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetpointer.FixedTarget; /** * @@ -57,7 +44,9 @@ public final class JaceTelepathUnbound extends CardImpl { this.addAbility(ability); // -3: You may cast target instant or sorcery card from your graveyard this turn. If that card would be put into your graveyard this turn, exile it instead. - ability = new LoyaltyAbility(new JaceTelepathUnboundEffect(), -3); + CastCardFromGraveyardThenExileItEffect minusEffect = new CastCardFromGraveyardThenExileItEffect(); + minusEffect.setText("You may cast target instant or sorcery card from your graveyard this turn. If that card would be put into your graveyard this turn, exile it instead"); + ability = new LoyaltyAbility(minusEffect, -3); ability.addTarget(new TargetCardInYourGraveyard(new FilterInstantOrSorceryCard())); this.addAbility(ability); @@ -74,104 +63,3 @@ public final class JaceTelepathUnbound extends CardImpl { return new JaceTelepathUnbound(this); } } - -class JaceTelepathUnboundEffect extends OneShotEffect { - - JaceTelepathUnboundEffect() { - super(Outcome.Benefit); - this.staticText = "You may cast target instant or sorcery card from your graveyard this turn. If that card would be put into your graveyard this turn, exile it instead"; - } - - JaceTelepathUnboundEffect(final JaceTelepathUnboundEffect effect) { - super(effect); - } - - @Override - public JaceTelepathUnboundEffect copy() { - return new JaceTelepathUnboundEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Card card = game.getCard(this.getTargetPointer().getFirst(game, source)); - if (card != null) { - ContinuousEffect effect = new JaceTelepathUnboundCastFromGraveyardEffect(); - effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); - game.addEffect(effect, source); - effect = new JaceTelepathUnboundReplacementEffect(card.getId()); - game.addEffect(effect, source); - return true; - } - return false; - } -} - -class JaceTelepathUnboundCastFromGraveyardEffect extends AsThoughEffectImpl { - - JaceTelepathUnboundCastFromGraveyardEffect() { - super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); - } - - JaceTelepathUnboundCastFromGraveyardEffect(final JaceTelepathUnboundCastFromGraveyardEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public JaceTelepathUnboundCastFromGraveyardEffect copy() { - return new JaceTelepathUnboundCastFromGraveyardEffect(this); - } - - @Override - public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - return objectId.equals(this.getTargetPointer().getFirst(game, source)) && affectedControllerId.equals(source.getControllerId()); - } -} - -class JaceTelepathUnboundReplacementEffect extends ReplacementEffectImpl { - - private final UUID cardId; - - JaceTelepathUnboundReplacementEffect(UUID cardId) { - super(Duration.EndOfTurn, Outcome.Exile); - this.cardId = cardId; - staticText = "If that card would be put into your graveyard this turn, exile it instead"; - } - - JaceTelepathUnboundReplacementEffect(final JaceTelepathUnboundReplacementEffect effect) { - super(effect); - this.cardId = effect.cardId; - } - - @Override - public JaceTelepathUnboundReplacementEffect copy() { - return new JaceTelepathUnboundReplacementEffect(this); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Player controller = game.getPlayer(source.getControllerId()); - Card card = game.getCard(this.cardId); - if (controller != null && card != null) { - controller.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, Zone.STACK, true); - 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) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - return zEvent.getToZone() == Zone.GRAVEYARD - && zEvent.getTargetId().equals(this.cardId); - } -} diff --git a/Mage.Sets/src/mage/cards/l/LilianaDeathMage.java b/Mage.Sets/src/mage/cards/l/LilianaDeathMage.java new file mode 100644 index 0000000000..45c189ded9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/l/LilianaDeathMage.java @@ -0,0 +1,117 @@ +package mage.cards.l; + +import mage.abilities.Ability; +import mage.abilities.LoyaltyAbility; +import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.LoseLifeTargetControllerEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInYourGraveyard; +import mage.target.common.TargetCreaturePermanent; +import mage.target.common.TargetOpponent; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class LilianaDeathMage extends CardImpl { + + public LilianaDeathMage(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{4}{B}{B}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.LILIANA); + this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4)); + + // +1: Return up to one target creature card from your graveyard to your hand. + Ability plusAbility = new LoyaltyAbility(new LilianaDeathMagePlusEffect(), 1); + plusAbility.addTarget(new TargetCardInYourGraveyard(0, 1, StaticFilters.FILTER_CARD_CREATURE)); + this.addAbility(plusAbility); + + // −3: Destroy target creature. Its controller loses 2 life. + Ability minusAbility = new LoyaltyAbility(new DestroyTargetEffect(), -3); + minusAbility.addTarget(new TargetCreaturePermanent()); + minusAbility.addEffect(new LoseLifeTargetControllerEffect(2)); + this.addAbility(minusAbility); + + // −7: Target opponent loses 2 life for each creature card in their graveyard. + Ability ultimateAbility = new LoyaltyAbility(new LilianaDeathMageUltimateEffect(), -7); + ultimateAbility.addTarget(new TargetOpponent()); + this.addAbility(ultimateAbility); + } + + private LilianaDeathMage(final LilianaDeathMage card) { + super(card); + } + + @Override + public LilianaDeathMage copy() { + return new LilianaDeathMage(this); + } +} + +class LilianaDeathMagePlusEffect extends OneShotEffect { + + LilianaDeathMagePlusEffect() { + super(Outcome.Benefit); + staticText = "Return up to one target creature card from your graveyard to your hand"; + } + + private LilianaDeathMagePlusEffect(LilianaDeathMagePlusEffect effect) { + super(effect); + } + + @Override + public LilianaDeathMagePlusEffect copy() { + return new LilianaDeathMagePlusEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null || !player.chooseUse(Outcome.Benefit, "Return a creature card from your graveyard to your hand?", source, game)) { + return false; + } + Card card = game.getCard(source.getTargets().get(0).getFirstTarget()); + if (card == null) { + return false; + } + return player.moveCards(card, Zone.HAND, source, game); + } +} + +class LilianaDeathMageUltimateEffect extends OneShotEffect { + + LilianaDeathMageUltimateEffect() { + super(Outcome.Damage); + staticText = "Target opponent loses 2 life for each creature card in their graveyard"; + } + + private LilianaDeathMageUltimateEffect(LilianaDeathMageUltimateEffect effect) { + super(effect); + } + + @Override + public LilianaDeathMageUltimateEffect copy() { + return new LilianaDeathMageUltimateEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player opponent = game.getPlayer(source.getFirstTarget()); + if (opponent != null) { + int amount = opponent.getGraveyard().count(StaticFilters.FILTER_CARD_CREATURE, game); + opponent.loseLife(amount * 2, game, false); + } + return true; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/m/MongrelPack.java b/Mage.Sets/src/mage/cards/m/MongrelPack.java index 732f0c97ac..94d40ab16d 100644 --- a/Mage.Sets/src/mage/cards/m/MongrelPack.java +++ b/Mage.Sets/src/mage/cards/m/MongrelPack.java @@ -11,7 +11,7 @@ import mage.constants.TurnPhase; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.permanent.token.DogToken; +import mage.game.permanent.token.GreenDogToken; import java.util.UUID; @@ -44,7 +44,7 @@ public final class MongrelPack extends CardImpl { class MongrelPackAbility extends ZoneChangeTriggeredAbility { MongrelPackAbility() { - super(Zone.BATTLEFIELD, Zone.GRAVEYARD, new CreateTokenEffect(new DogToken(), 4), "When {this} dies during combat, ", false); + super(Zone.BATTLEFIELD, Zone.GRAVEYARD, new CreateTokenEffect(new GreenDogToken(), 4), "When {this} dies during combat, ", false); } private MongrelPackAbility(MongrelPackAbility ability) { diff --git a/Mage.Sets/src/mage/cards/r/RinAndSeriInseparable.java b/Mage.Sets/src/mage/cards/r/RinAndSeriInseparable.java new file mode 100644 index 0000000000..21789c8869 --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RinAndSeriInseparable.java @@ -0,0 +1,91 @@ +package mage.cards.r; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +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.CreateTokenEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.hint.ValueHint; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; +import mage.filter.FilterSpell; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.game.permanent.token.GreenCatToken; +import mage.game.permanent.token.WhiteDogToken; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * + * @author htrajan + */ +public final class RinAndSeriInseparable extends CardImpl { + + private static final FilterSpell dogSpellFilter = new FilterSpell("a Dog spell"); + private static final FilterSpell catSpellFilter = new FilterSpell("a Cat spell"); + + private static final FilterPermanent dogPermanentFilter = new FilterControlledCreaturePermanent("Dogs you control"); + private static final FilterPermanent catPermanentFilter = new FilterControlledCreaturePermanent("Cats you control"); + + static { + dogSpellFilter.add(SubType.DOG.getPredicate()); + catSpellFilter.add(SubType.CAT.getPredicate()); + + dogPermanentFilter.add(SubType.DOG.getPredicate()); + catPermanentFilter.add(SubType.CAT.getPredicate()); + } + + public RinAndSeriInseparable(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{R}{G}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.DOG); + this.subtype.add(SubType.CAT); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Whenever you cast a Dog spell, create a 1/1 green Cat creature token. + this.addAbility(new SpellCastControllerTriggeredAbility( + new CreateTokenEffect(new GreenCatToken()), dogSpellFilter, false + )); + + // Whenever you cast a Cat spell, create a 1/1 white Dog creature token. + this.addAbility(new SpellCastControllerTriggeredAbility( + new CreateTokenEffect(new WhiteDogToken()), catSpellFilter, false + )); + + // {R}{G}{W}: Rin and Seri, Inseparable deals damage to any target equal to the number of Dogs you control. You gain life equal to the number of Cats you control. + DynamicValue dogCount = new PermanentsOnBattlefieldCount(dogPermanentFilter); + Effect damageEffect = new DamageTargetEffect(dogCount); + damageEffect.setText("{source} deals damage to any target equal to the number of Dogs you control"); + DynamicValue catCount = new PermanentsOnBattlefieldCount(catPermanentFilter); + Effect lifeGainEffect = new GainLifeEffect(catCount); + lifeGainEffect.setText("You gain life equal to the number of Cats you control"); + Ability ability = new SimpleActivatedAbility(damageEffect, new ManaCostsImpl("{R}{G}{W}")); + ability.addEffect(lifeGainEffect); + ability.addTarget(new TargetAnyTarget()); + ability.addHint(new ValueHint("Dogs you control", dogCount)); + ability.addHint(new ValueHint("Cats you control", catCount)); + this.addAbility(ability); + } + + private RinAndSeriInseparable(final RinAndSeriInseparable card) { + super(card); + } + + @Override + public RinAndSeriInseparable copy() { + return new RinAndSeriInseparable(this); + } +} diff --git a/Mage.Sets/src/mage/cards/w/WaitingInTheWeeds.java b/Mage.Sets/src/mage/cards/w/WaitingInTheWeeds.java index d7e6015625..114c0fa813 100644 --- a/Mage.Sets/src/mage/cards/w/WaitingInTheWeeds.java +++ b/Mage.Sets/src/mage/cards/w/WaitingInTheWeeds.java @@ -14,7 +14,7 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.permanent.token.Token; -import mage.game.permanent.token.WaitingInTheWeedsCatToken; +import mage.game.permanent.token.GreenCatToken; import mage.players.Player; /** @@ -68,7 +68,7 @@ class WaitingInTheWeedsEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Token token = new WaitingInTheWeedsCatToken(); + Token token = new GreenCatToken(); int amount = game.getBattlefield().getAllActivePermanents(filter, playerId, game).size(); token.putOntoBattlefield(amount, game, source.getSourceId(), playerId); } diff --git a/Mage.Sets/src/mage/sets/CoreSet2021.java b/Mage.Sets/src/mage/sets/CoreSet2021.java index 974055a407..ce980c6f23 100644 --- a/Mage.Sets/src/mage/sets/CoreSet2021.java +++ b/Mage.Sets/src/mage/sets/CoreSet2021.java @@ -36,16 +36,20 @@ public final class CoreSet2021 extends ExpansionSet { cards.add(new SetCardInfo("Azusa, Lost but Seeking", 173, Rarity.RARE, mage.cards.a.AzusaLostButSeeking.class)); cards.add(new SetCardInfo("Bad Deal", 89, Rarity.UNCOMMON, mage.cards.b.BadDeal.class)); cards.add(new SetCardInfo("Baneslayer Angel", 4, Rarity.MYTHIC, mage.cards.b.BaneslayerAngel.class)); + cards.add(new SetCardInfo("Basri, Devoted Paladin", 320, Rarity.MYTHIC, mage.cards.b.BasriDevotedPaladin.class)); + cards.add(new SetCardInfo("Chandra, Flame's Catalyst", 332, Rarity.MYTHIC, mage.cards.c.ChandraFlamesCatalyst.class)); cards.add(new SetCardInfo("Chandra's Firemaw", 333, Rarity.RARE, mage.cards.c.ChandrasFiremaw.class)); cards.add(new SetCardInfo("Containment Priest", 314, Rarity.RARE, mage.cards.c.ContainmentPriest.class)); cards.add(new SetCardInfo("Double Vision", 142, Rarity.RARE, mage.cards.d.DoubleVision.class)); cards.add(new SetCardInfo("Fierce Empath", 181, Rarity.UNCOMMON, mage.cards.f.FierceEmpath.class)); cards.add(new SetCardInfo("Gadrak, the Crown-Scourge", 146, Rarity.RARE, mage.cards.g.GadrakTheCrownScourge.class)); + cards.add(new SetCardInfo("Garruk, Savage Herald", 336, Rarity.MYTHIC, mage.cards.g.GarrukSavageHerald.class)); cards.add(new SetCardInfo("Grim Tutor", 103, Rarity.MYTHIC, mage.cards.g.GrimTutor.class)); cards.add(new SetCardInfo("Historian of Zhalfir", 325, Rarity.UNCOMMON, mage.cards.h.HistorianOfZhalfir.class)); cards.add(new SetCardInfo("Indulging Patrician", 219, Rarity.UNCOMMON, mage.cards.i.IndulgingPatrician.class)); cards.add(new SetCardInfo("Island", 310, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jeskai Elder", 53, Rarity.UNCOMMON, mage.cards.j.JeskaiElder.class)); + cards.add(new SetCardInfo("Liliana, Death Mage", 328, Rarity.MYTHIC, mage.cards.l.LilianaDeathMage.class)); cards.add(new SetCardInfo("Liliana's Scrounger", 330, Rarity.UNCOMMON, mage.cards.l.LilianasScrounger.class)); cards.add(new SetCardInfo("Llanowar Visionary", 193, Rarity.COMMON, mage.cards.l.LlanowarVisionary.class)); cards.add(new SetCardInfo("Mangara, the Diplomat", 27, Rarity.MYTHIC, mage.cards.m.MangaraTheDiplomat.class)); @@ -54,6 +58,7 @@ public final class CoreSet2021 extends ExpansionSet { cards.add(new SetCardInfo("Peer into the Abyss", 117, Rarity.RARE, mage.cards.p.PeerIntoTheAbyss.class)); cards.add(new SetCardInfo("Quirion Dryad", 198, Rarity.UNCOMMON, mage.cards.q.QuirionDryad.class)); cards.add(new SetCardInfo("Rain of Revelation", 61, Rarity.UNCOMMON, mage.cards.r.RainOfRevelation.class)); + cards.add(new SetCardInfo("Rin and Seri, Inseparable", 278, Rarity.MYTHIC, mage.cards.r.RinAndSeriInseparable.class)); cards.add(new SetCardInfo("Runed Halo", 32, Rarity.RARE, mage.cards.r.RunedHalo.class)); cards.add(new SetCardInfo("Shipwreck Dowser", 71, Rarity.UNCOMMON, mage.cards.s.ShipwreckDowser.class)); cards.add(new SetCardInfo("Sigiled Contender", 323, Rarity.UNCOMMON, mage.cards.s.SigiledContender.class)); diff --git a/Mage/src/main/java/mage/abilities/effects/CastCardFromGraveyardThenExileItEffect.java b/Mage/src/main/java/mage/abilities/effects/CastCardFromGraveyardThenExileItEffect.java new file mode 100644 index 0000000000..c17f6fae73 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/CastCardFromGraveyardThenExileItEffect.java @@ -0,0 +1,115 @@ +package mage.abilities.effects; + +import mage.abilities.Ability; +import mage.cards.Card; +import mage.constants.AsThoughEffectType; +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.players.Player; +import mage.target.targetpointer.FixedTarget; + +import java.util.UUID; + +public class CastCardFromGraveyardThenExileItEffect extends OneShotEffect { + + public CastCardFromGraveyardThenExileItEffect() { + super(Outcome.Benefit); + } + + CastCardFromGraveyardThenExileItEffect(final CastCardFromGraveyardThenExileItEffect effect) { + super(effect); + } + + @Override + public CastCardFromGraveyardThenExileItEffect copy() { + return new CastCardFromGraveyardThenExileItEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Card card = game.getCard(this.getTargetPointer().getFirst(game, source)); + if (card != null) { + ContinuousEffect effect = new CastCardFromGraveyardEffect(); + effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game))); + game.addEffect(effect, source); + effect = new ExileReplacementEffect(card.getId()); + game.addEffect(effect, source); + return true; + } + return false; + } +} + +class CastCardFromGraveyardEffect extends AsThoughEffectImpl { + + CastCardFromGraveyardEffect() { + super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.EndOfTurn, Outcome.Benefit); + this.staticText = "You may cast target card from your graveyard"; + } + + CastCardFromGraveyardEffect(final CastCardFromGraveyardEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return true; + } + + @Override + public CastCardFromGraveyardEffect copy() { + return new CastCardFromGraveyardEffect(this); + } + + @Override + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + return objectId.equals(this.getTargetPointer().getFirst(game, source)) && affectedControllerId.equals(source.getControllerId()); + } +} + +class ExileReplacementEffect extends ReplacementEffectImpl { + + private final UUID cardId; + + ExileReplacementEffect(UUID cardId) { + super(Duration.EndOfTurn, Outcome.Exile); + this.cardId = cardId; + this.staticText = "If that card would be put into your graveyard this turn, exile it instead"; + } + + ExileReplacementEffect(final ExileReplacementEffect effect) { + super(effect); + this.cardId = effect.cardId; + } + + @Override + public ExileReplacementEffect copy() { + return new ExileReplacementEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + Card card = game.getCard(this.cardId); + if (controller != null && card != null) { + return controller.moveCards(card, Zone.EXILED, source, game); + } + 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) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + return zEvent.getToZone() == Zone.GRAVEYARD + && zEvent.getTargetId().equals(this.cardId); + } +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/CastFromHandWithoutPayingManaCostEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/CastFromHandWithoutPayingManaCostEffect.java index a330989828..c72a601922 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/CastFromHandWithoutPayingManaCostEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/CastFromHandWithoutPayingManaCostEffect.java @@ -28,10 +28,14 @@ public class CastFromHandWithoutPayingManaCostEffect extends ContinuousEffectImp } public CastFromHandWithoutPayingManaCostEffect(FilterCard filter, boolean fromHand) { - super(Duration.WhileOnBattlefield, Outcome.Detriment); + this(filter, fromHand, Duration.WhileOnBattlefield); + } + + public CastFromHandWithoutPayingManaCostEffect(FilterCard filter, boolean fromHand, Duration duration) { + super(duration, Outcome.Detriment); this.filter = filter; this.fromHand = fromHand; - staticText = "You may cast " + filter.getMessage() + this.staticText = "You may cast " + filter.getMessage() + (fromHand ? " from your hand" : "") + " without paying their mana costs"; } diff --git a/Mage/src/main/java/mage/constants/SubType.java b/Mage/src/main/java/mage/constants/SubType.java index 7f0c89242f..7724b09214 100644 --- a/Mage/src/main/java/mage/constants/SubType.java +++ b/Mage/src/main/java/mage/constants/SubType.java @@ -391,6 +391,7 @@ public enum SubType { ARLINN("Arlinn", SubTypeSet.PlaneswalkerType), ASHIOK("Ashiok", SubTypeSet.PlaneswalkerType), AURRA("Aurra", SubTypeSet.PlaneswalkerType, true), // Star Wars + BASRI("Basri", SubTypeSet.PlaneswalkerType), BOLAS("Bolas", SubTypeSet.PlaneswalkerType), CALIX("Calix", SubTypeSet.PlaneswalkerType), CHANDRA("Chandra", SubTypeSet.PlaneswalkerType), diff --git a/Mage/src/main/java/mage/game/permanent/token/WaitingInTheWeedsCatToken.java b/Mage/src/main/java/mage/game/permanent/token/GreenCatToken.java similarity index 60% rename from Mage/src/main/java/mage/game/permanent/token/WaitingInTheWeedsCatToken.java rename to Mage/src/main/java/mage/game/permanent/token/GreenCatToken.java index d7d78e4205..da32a283f9 100644 --- a/Mage/src/main/java/mage/game/permanent/token/WaitingInTheWeedsCatToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/GreenCatToken.java @@ -9,9 +9,9 @@ import mage.constants.SubType; * * @author spjspj */ -public final class WaitingInTheWeedsCatToken extends TokenImpl { +public final class GreenCatToken extends TokenImpl { - public WaitingInTheWeedsCatToken() { + public GreenCatToken() { super("Cat", "1/1 green Cat creature token"); cardType.add(CardType.CREATURE); color.setGreen(true); @@ -20,11 +20,11 @@ public final class WaitingInTheWeedsCatToken extends TokenImpl { toughness = new MageInt(1); } - public WaitingInTheWeedsCatToken(final WaitingInTheWeedsCatToken token) { + public GreenCatToken(final GreenCatToken token) { super(token); } - public WaitingInTheWeedsCatToken copy() { - return new WaitingInTheWeedsCatToken(this); + public GreenCatToken copy() { + return new GreenCatToken(this); } } diff --git a/Mage/src/main/java/mage/game/permanent/token/DogToken.java b/Mage/src/main/java/mage/game/permanent/token/GreenDogToken.java similarity index 66% rename from Mage/src/main/java/mage/game/permanent/token/DogToken.java rename to Mage/src/main/java/mage/game/permanent/token/GreenDogToken.java index a229350513..dddd731733 100644 --- a/Mage/src/main/java/mage/game/permanent/token/DogToken.java +++ b/Mage/src/main/java/mage/game/permanent/token/GreenDogToken.java @@ -7,9 +7,9 @@ import mage.constants.SubType; /** * @author spjspj */ -public final class DogToken extends TokenImpl { +public final class GreenDogToken extends TokenImpl { - public DogToken() { + public GreenDogToken() { super("Dog", "1/1 green Dog creature token"); cardType.add(CardType.CREATURE); subtype.add(SubType.DOG); @@ -19,11 +19,11 @@ public final class DogToken extends TokenImpl { toughness = new MageInt(1); } - private DogToken(final DogToken token) { + private GreenDogToken(final GreenDogToken token) { super(token); } - public DogToken copy() { - return new DogToken(this); + public GreenDogToken copy() { + return new GreenDogToken(this); } } diff --git a/Mage/src/main/java/mage/game/permanent/token/WhiteDogToken.java b/Mage/src/main/java/mage/game/permanent/token/WhiteDogToken.java new file mode 100644 index 0000000000..d9742c0fc7 --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/WhiteDogToken.java @@ -0,0 +1,31 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.constants.CardType; +import mage.constants.SubType; + +/** + * + * @author htrajan + */ +public final class WhiteDogToken extends TokenImpl { + + public WhiteDogToken() { + super("Dog", "1/1 white Dog creature token"); + cardType.add(CardType.CREATURE); + subtype.add(SubType.DOG); + + color.setWhite(true); + power = new MageInt(1); + toughness = new MageInt(1); + } + + private WhiteDogToken(final WhiteDogToken token) { + super(token); + } + + public WhiteDogToken copy() { + return new WhiteDogToken(this); + } + +}