[TDM] Implement Severance Priest

This commit is contained in:
theelk801 2025-04-10 15:52:11 -04:00
parent 61c771a905
commit 5ae4d7c0bc
4 changed files with 181 additions and 61 deletions

View file

@ -0,0 +1,98 @@
package mage.cards.s;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateXXTokenExiledEffectManaValueEffect;
import mage.abilities.keyword.DeathtouchAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.permanent.token.SpiritXXToken;
import mage.players.Player;
import mage.target.TargetCard;
import mage.target.common.TargetCardInHand;
import mage.target.common.TargetOpponent;
import mage.util.CardUtil;
import java.util.UUID;
/**
* @author TheElk801
*/
public final class SeverancePriest extends CardImpl {
public SeverancePriest(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{B}{G}");
this.subtype.add(SubType.DJINN);
this.subtype.add(SubType.CLERIC);
this.power = new MageInt(3);
this.toughness = new MageInt(3);
// Deathtouch
this.addAbility(DeathtouchAbility.getInstance());
// When this creature enters, target opponent reveals their hand. You may choose a nonland card from it. If you do, exile that card.
Ability ability = new EntersBattlefieldTriggeredAbility(new SeverancePriestEffect());
ability.addTarget(new TargetOpponent());
this.addAbility(ability);
// When this creature leaves the battlefield, the exiled card's owner creates an X/X white Spirit creature token, where X is the mana value of the exiled card.
this.addAbility(new LeavesBattlefieldTriggeredAbility(
new CreateXXTokenExiledEffectManaValueEffect(SpiritXXToken::new, "white Spirit")
));
}
private SeverancePriest(final SeverancePriest card) {
super(card);
}
@Override
public SeverancePriest copy() {
return new SeverancePriest(this);
}
}
class SeverancePriestEffect extends OneShotEffect {
SeverancePriestEffect() {
super(Outcome.Benefit);
staticText = "target opponent reveals their hand. You may choose " +
"a nonland card from it. If you do, exile that card";
}
private SeverancePriestEffect(final SeverancePriestEffect effect) {
super(effect);
}
@Override
public SeverancePriestEffect copy() {
return new SeverancePriestEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Player opponent = game.getPlayer(getTargetPointer().getFirst(game, source));
if (controller == null || opponent == null || opponent.getHand().isEmpty()) {
return false;
}
opponent.revealCards(source, opponent.getHand(), game);
TargetCard target = new TargetCardInHand(0, 1, StaticFilters.FILTER_CARD_NON_LAND);
controller.choose(Outcome.Discard, opponent.getHand(), target, source, game);
Card card = game.getCard(target.getFirstTarget());
return card != null && controller.moveCardsToExile(
card, source, game, true,
CardUtil.getExileZoneId(game, source),
CardUtil.getSourceName(game, source)
);
}
}

View file

@ -1,29 +1,24 @@
package mage.cards.s;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateXXTokenExiledEffectManaValueEffect;
import mage.abilities.effects.common.ExileTargetForSourceEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.constants.CardType;
import mage.constants.ComparisonType;
import mage.constants.SubType;
import mage.constants.TargetController;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterNonlandPermanent;
import mage.filter.predicate.mageobject.ManaValuePredicate;
import mage.filter.predicate.permanent.TokenPredicate;
import mage.game.ExileZone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.CustomIllusionToken;
import mage.target.TargetPermanent;
import mage.util.CardUtil;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
/**
@ -55,7 +50,9 @@ public final class SkyclaveApparition extends CardImpl {
this.addAbility(ability);
// When Skyclave Apparition leaves the battlefield, the exiled card's owner creates an X/X blue Illusion creature token, where X is the converted mana cost of the exiled card.
this.addAbility(new LeavesBattlefieldTriggeredAbility(new SkyclaveApparitionEffect(), false));
this.addAbility(new LeavesBattlefieldTriggeredAbility(
new CreateXXTokenExiledEffectManaValueEffect(CustomIllusionToken::new, "blue Illusion")
));
}
private SkyclaveApparition(final SkyclaveApparition card) {
@ -67,50 +64,3 @@ public final class SkyclaveApparition extends CardImpl {
return new SkyclaveApparition(this);
}
}
class SkyclaveApparitionEffect extends OneShotEffect {
SkyclaveApparitionEffect() {
super(Outcome.Benefit);
staticText = "the exiled card's owner creates an X/X blue Illusion creature token, " +
"where X is the mana value of the exiled card";
}
private SkyclaveApparitionEffect(final SkyclaveApparitionEffect effect) {
super(effect);
}
@Override
public SkyclaveApparitionEffect copy() {
return new SkyclaveApparitionEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanentLeftBattlefield = (Permanent) getValue("permanentLeftBattlefield");
ExileZone exile = game.getExile().getExileZone(
CardUtil.getExileZoneId(game, source.getSourceId(), permanentLeftBattlefield.getZoneChangeCounter(game))
);
if (exile == null || exile.isEmpty()) {
return false;
}
// From ZNR Release Notes:
// https://magic.wizards.com/en/articles/archive/feature/zendikar-rising-release-notes-2020-09-10
// If Skyclave Apparition's first ability exiled more than one card owned by a single player,
// that player creates a token with power and toughness equal to the sum of those cards' converted mana costs.
// If the first ability exiled cards owned by more than one player, each of those players creates a token
// with power and toughness equal to the sum of the converted mana costs of all cards exiled by the first ability.
Set<UUID> owners = new HashSet<>();
int totalCMC = exile
.getCards(game)
.stream()
.filter(Objects::nonNull)
.map(card -> owners.add(card.getOwnerId()) ? card : card)
.mapToInt(MageObject::getManaValue)
.sum();
for (UUID playerId : owners) {
new CustomIllusionToken(totalCMC).putOntoBattlefield(1, game, source, playerId);
}
return true;
}
}

View file

@ -1,8 +1,5 @@
package mage.sets;
import java.util.Arrays;
import java.util.List;
import mage.cards.ExpansionSet;
import mage.constants.Rarity;
import mage.constants.SetType;
@ -222,6 +219,7 @@ public final class TarkirDragonstorm extends ExpansionSet {
cards.add(new SetCardInfo("Scavenger Regent", 90, Rarity.RARE, mage.cards.s.ScavengerRegent.class));
cards.add(new SetCardInfo("Scoured Barrens", 267, Rarity.COMMON, mage.cards.s.ScouredBarrens.class));
cards.add(new SetCardInfo("Seize Opportunity", 119, Rarity.COMMON, mage.cards.s.SeizeOpportunity.class));
cards.add(new SetCardInfo("Severance Priest", 222, Rarity.RARE, mage.cards.s.SeverancePriest.class));
cards.add(new SetCardInfo("Shiko, Paragon of the Way", 223, Rarity.MYTHIC, mage.cards.s.ShikoParagonOfTheWay.class));
cards.add(new SetCardInfo("Shock Brigade", 120, Rarity.COMMON, mage.cards.s.ShockBrigade.class));
cards.add(new SetCardInfo("Shocking Sharpshooter", 121, Rarity.UNCOMMON, mage.cards.s.ShockingSharpshooter.class));

View file

@ -0,0 +1,74 @@
package mage.abilities.effects.common;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.game.ExileZone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.Token;
import mage.util.CardUtil;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
/**
* @author TheElk801
*/
public class CreateXXTokenExiledEffectManaValueEffect extends OneShotEffect {
private final Function<Integer, Token> tokenMaker;
public CreateXXTokenExiledEffectManaValueEffect(Function<Integer, Token> tokenMaker, String description) {
super(Outcome.Benefit);
this.tokenMaker = tokenMaker;
staticText = "the exiled card's owner creates an X/X " + description +
"creature token, where X is the mana value of the exiled card";
}
private CreateXXTokenExiledEffectManaValueEffect(final CreateXXTokenExiledEffectManaValueEffect effect) {
super(effect);
this.tokenMaker = effect.tokenMaker;
}
@Override
public CreateXXTokenExiledEffectManaValueEffect copy() {
return new CreateXXTokenExiledEffectManaValueEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanentLeftBattlefield = (Permanent) getValue("permanentLeftBattlefield");
ExileZone exile = game.getExile().getExileZone(
CardUtil.getExileZoneId(game, source.getSourceId(), permanentLeftBattlefield.getZoneChangeCounter(game))
);
if (exile == null || exile.isEmpty()) {
return false;
}
// From ZNR Release Notes:
// https://magic.wizards.com/en/articles/archive/feature/zendikar-rising-release-notes-2020-09-10
// If Skyclave Apparition's first ability exiled more than one card owned by a single player,
// that player creates a token with power and toughness equal to the sum of those cards' converted mana costs.
// If the first ability exiled cards owned by more than one player, each of those players creates a token
// with power and toughness equal to the sum of the converted mana costs of all cards exiled by the first ability.
Set<UUID> owners = new HashSet<>();
int totalCMC = exile
.getCards(game)
.stream()
.filter(Objects::nonNull)
.map(card -> {
owners.add(card.getOwnerId());
return card;
})
.mapToInt(MageObject::getManaValue)
.sum();
for (UUID playerId : owners) {
tokenMaker.apply(totalCMC).putOntoBattlefield(1, game, source, playerId);
}
return true;
}
}