Refactor Maestros Ascendancy

This commit is contained in:
tool4EvEr
2024-05-13 16:38:11 +02:00
parent 9b54ecfe12
commit 26ef0966ea
12 changed files with 45 additions and 50 deletions

View File

@@ -319,7 +319,11 @@ public final class GameActionUtil {
newSA.getMapParams().put("ValidAfterStack", o.getAbility().getParam("ValidAfterStack")); newSA.getMapParams().put("ValidAfterStack", o.getAbility().getParam("ValidAfterStack"));
} }
if (o.getAbility().hasParam("RaiseCost")) { if (o.getAbility().hasParam("RaiseCost")) {
newSA.getMapParams().put("RaiseCost", Integer.toString(AbilityUtils.calculateAmount(host, o.getAbility().getParam("RaiseCost"), o.getAbility()))); String raise = o.getAbility().getParam("RaiseCost");
if (o.getAbility().hasSVar(raise)) {
raise = Integer.toString(AbilityUtils.calculateAmount(host, raise, o.getAbility()));
}
newSA.getMapParams().put("RaiseCost", raise);
} }
final SpellAbilityRestriction sar = newSA.getRestrictions(); final SpellAbilityRestriction sar = newSA.getRestrictions();

View File

@@ -449,13 +449,13 @@ public class PlayEffect extends SpellAbilityEffect {
if (sa.hasParam("ReplaceGraveyard")) { if (sa.hasParam("ReplaceGraveyard")) {
if (!sa.hasParam("ReplaceGraveyardValid") if (!sa.hasParam("ReplaceGraveyardValid")
|| tgtSA.isValid(sa.getParam("ReplaceGraveyardValid").split(","), controller, source, sa)) { || tgtSA.isValid(sa.getParam("ReplaceGraveyardValid").split(","), controller, source, sa)) {
addReplaceGraveyardEffect(tgtCard, sa, tgtSA, sa.getParam("ReplaceGraveyard"), moveParams); addReplaceGraveyardEffect(tgtCard, source, sa, tgtSA, sa.getParam("ReplaceGraveyard"));
} }
} }
// For Illusionary Mask effect // For Illusionary Mask effect
if (sa.hasParam("ReplaceIlluMask")) { if (sa.hasParam("ReplaceIlluMask")) {
addIllusionaryMaskReplace(tgtCard, sa, moveParams); addIllusionaryMaskReplace(tgtCard, sa);
} }
// Add controlled by player to target SA so when the spell is resolving, the controller would be changed again // Add controlled by player to target SA so when the spell is resolving, the controller would be changed again
@@ -500,8 +500,7 @@ public class PlayEffect extends SpellAbilityEffect {
} }
} }
protected void addReplaceGraveyardEffect(Card c, SpellAbility sa, SpellAbility tgtSA, String zone, Map<AbilityKey, Object> moveParams) { public static void addReplaceGraveyardEffect(Card c, Card hostCard, SpellAbility sa, SpellAbility tgtSA, String zone) {
final Card hostCard = sa.getHostCard();
final Game game = hostCard.getGame(); final Game game = hostCard.getGame();
final Player controller = sa.getActivatingPlayer(); final Player controller = sa.getActivatingPlayer();
final String name = hostCard.getName() + "'s Effect"; final String name = hostCard.getName() + "'s Effect";
@@ -535,7 +534,7 @@ public class PlayEffect extends SpellAbilityEffect {
game.getAction().moveToCommand(eff, sa); game.getAction().moveToCommand(eff, sa);
} }
protected void addIllusionaryMaskReplace(Card c, SpellAbility sa, Map<AbilityKey, Object> moveParams) { protected void addIllusionaryMaskReplace(Card c, SpellAbility sa) {
final Card hostCard = sa.getHostCard(); final Card hostCard = sa.getHostCard();
final Game game = hostCard.getGame(); final Game game = hostCard.getGame();
final Player controller = sa.getActivatingPlayer(); final Player controller = sa.getActivatingPlayer();

View File

@@ -3777,8 +3777,8 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
} }
return result; return result;
} }
public final void setMayPlay(final Player player, final boolean withoutManaCost, final Cost altManaCost, final boolean altIsAdditional, final boolean withFlash, final boolean grantZonePermissions, final StaticAbility sta) { public final void setMayPlay(final Player player, final boolean withoutManaCost, final Cost altManaCost, final boolean withFlash, final boolean grantZonePermissions, final StaticAbility sta) {
this.mayPlay.put(sta, new CardPlayOption(player, sta, withoutManaCost, altManaCost, altIsAdditional, withFlash, grantZonePermissions)); this.mayPlay.put(sta, new CardPlayOption(player, sta, withoutManaCost, altManaCost, withFlash, grantZonePermissions));
this.updateMayPlay(); this.updateMayPlay();
} }
public final void removeMayPlay(final StaticAbility sta) { public final void removeMayPlay(final StaticAbility sta) {

View File

@@ -3954,7 +3954,7 @@ public class CardFactoryUtil {
String effect = "Mode$ AttackVigilance | ValidCard$ Card.Self | Secondary$ True | Description$ Vigilance (" + inst.getReminderText() + ")"; String effect = "Mode$ AttackVigilance | ValidCard$ Card.Self | Secondary$ True | Description$ Vigilance (" + inst.getReminderText() + ")";
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic)); inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));
} else if (keyword.equals("MayFlashSac")) { } else if (keyword.equals("MayFlashSac")) {
String effect = "Mode$ Continuous | EffectZone$ All | Affected$ Card.Self | Secondary$ True | MayPlay$ True" String effect = "Mode$ Continuous | EffectZone$ All | Affected$ Card.Self | Secondary$ True | MayPlay$ True | MayPlayDontGrantZonePermissions$ True"
+ " | MayPlayNotSorcerySpeed$ True | MayPlayWithFlash$ True | MayPlayText$ Sacrifice at the next cleanup step" + " | MayPlayNotSorcerySpeed$ True | MayPlayWithFlash$ True | MayPlayText$ Sacrifice at the next cleanup step"
+ " | AffectedZone$ Exile,Graveyard,Hand,Library,Stack | Description$ " + inst.getReminderText(); + " | AffectedZone$ Exile,Graveyard,Hand,Library,Stack | Description$ " + inst.getReminderText();
inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic)); inst.addStaticAbility(StaticAbility.create(effect, state.getCard(), state, intrinsic));

View File

@@ -23,12 +23,11 @@ public final class CardPlayOption {
private final boolean withFlash; private final boolean withFlash;
private final boolean grantsZonePermissions; private final boolean grantsZonePermissions;
private final Cost altManaCost; private final Cost altManaCost;
private final boolean altIsAdditional;
public CardPlayOption(final Player player, final StaticAbility sta, final boolean withoutManaCost, final Cost altManaCost, final boolean altIsAdditional, final boolean withFlash, final boolean grantZonePermissions) { public CardPlayOption(final Player player, final StaticAbility sta, final boolean withoutManaCost, final Cost altManaCost, final boolean withFlash, final boolean grantZonePermissions) {
this(player, sta, withoutManaCost ? PayManaCost.NO : PayManaCost.YES, altManaCost, altIsAdditional, withFlash, grantZonePermissions); this(player, sta, withoutManaCost ? PayManaCost.NO : PayManaCost.YES, altManaCost, withFlash, grantZonePermissions);
} }
private CardPlayOption(final Player player, final StaticAbility sta, final PayManaCost payManaCost, final Cost altManaCost, final boolean altIsAdditional, final boolean withFlash, private CardPlayOption(final Player player, final StaticAbility sta, final PayManaCost payManaCost, final Cost altManaCost, final boolean withFlash,
final boolean grantZonePermissions) { final boolean grantZonePermissions) {
this.player = player; this.player = player;
this.sta = sta; this.sta = sta;
@@ -36,7 +35,6 @@ public final class CardPlayOption {
this.withFlash = withFlash; this.withFlash = withFlash;
this.grantsZonePermissions = grantZonePermissions; this.grantsZonePermissions = grantZonePermissions;
this.altManaCost = altManaCost; this.altManaCost = altManaCost;
this.altIsAdditional = altIsAdditional;
} }
@@ -102,14 +100,9 @@ public final class CardPlayOption {
switch (getPayManaCost()) { switch (getPayManaCost()) {
case YES: case YES:
if (altManaCost != null) { if (altManaCost != null) {
if (altIsAdditional) {
String desc = sta.getParam("Description");
sb.append(" (").append(desc, desc.indexOf("by "), desc.indexOf("."));
} else {
String insteadCost = getFormattedAltManaCost(); String insteadCost = getFormattedAltManaCost();
insteadCost = insteadCost.replace("Pay ",""); insteadCost = insteadCost.replace("Pay ","");
sb.append(" (by paying ").append(insteadCost).append(" instead of paying its mana cost"); sb.append(" (by paying ").append(insteadCost).append(" instead of paying its mana cost");
}
if (isWithFlash()) { if (isWithFlash()) {
sb.append(" and as though it has flash"); sb.append(" and as though it has flash");
} }
@@ -120,6 +113,10 @@ public final class CardPlayOption {
} else if (isIgnoreManaCostColor()) { } else if (isIgnoreManaCostColor()) {
sb.append(" (may spend mana as though it were mana of any color to cast it)"); sb.append(" (may spend mana as though it were mana of any color to cast it)");
} }
if (sta.hasParam("RaiseCost")) {
String desc = sta.getParam("Description");
sb.append(" (").append(desc, desc.indexOf("by ") + desc.indexOf("pay "), desc.indexOf(".")).append(")");
}
break; break;
case NO: case NO:
sb.append(" (without paying its mana cost"); sb.append(" (without paying its mana cost");

View File

@@ -84,15 +84,16 @@ public class CostAdjustment {
} }
} }
} }
if (sa.hasParam("RaiseCost")) { if (sa.hasParam("RaiseCost")) {
String raise = sa.getParam("RaiseCost"); String raise = sa.getParam("RaiseCost");
ManaCost mc; Cost inc;
if (sa.hasSVar(raise)) { if (sa.hasSVar(raise)) {
mc = ManaCost.get(AbilityUtils.calculateAmount(host, raise, sa)); inc = new Cost(ManaCost.get(AbilityUtils.calculateAmount(host, raise, sa)), false);
} else { } else {
mc = new ManaCost(new ManaCostParser(raise)); inc = new Cost(raise, false);
} }
result.add(new Cost(mc, false)); result.add(inc);
} }
// Raise cost // Raise cost

View File

@@ -938,15 +938,11 @@ public final class StaticAbilityContinuous {
if (controllerMayPlay && (mayPlayLimit == null || stAb.getMayPlayTurn() < mayPlayLimit)) { if (controllerMayPlay && (mayPlayLimit == null || stAb.getMayPlayTurn() < mayPlayLimit)) {
String mayPlayAltCost = mayPlayAltManaCost; String mayPlayAltCost = mayPlayAltManaCost;
boolean additional = mayPlayAltCost != null && mayPlayAltCost.contains("RegularCost");
if (mayPlayAltCost != null) { if (mayPlayAltCost != null) {
if (mayPlayAltCost.contains("ConvertedManaCost")) { if (mayPlayAltCost.contains("ConvertedManaCost")) {
final String costcmc = Integer.toString(affectedCard.getCMC()); final String costcmc = Integer.toString(affectedCard.getCMC());
mayPlayAltCost = mayPlayAltCost.replace("ConvertedManaCost", costcmc); mayPlayAltCost = mayPlayAltCost.replace("ConvertedManaCost", costcmc);
} else if (additional) {
final String regCost = affectedCard.getManaCost().getShortString();
mayPlayAltCost = mayPlayAltManaCost.replace("RegularCost", regCost);
} }
} }
@@ -954,7 +950,7 @@ public final class StaticAbilityContinuous {
AbilityUtils.getDefinedPlayers(affectedCard, params.get("MayPlayPlayer"), stAb).get(0) : AbilityUtils.getDefinedPlayers(affectedCard, params.get("MayPlayPlayer"), stAb).get(0) :
controller; controller;
affectedCard.setMayPlay(mayPlayController, mayPlayWithoutManaCost, affectedCard.setMayPlay(mayPlayController, mayPlayWithoutManaCost,
mayPlayAltCost != null ? new Cost(mayPlayAltCost, false, affectedCard.equals(hostCard)) : null, additional, mayPlayWithFlash, mayPlayAltCost != null ? new Cost(mayPlayAltCost, false, affectedCard.equals(hostCard)) : null, mayPlayWithFlash,
mayPlayGrantZonePermissions, stAb); mayPlayGrantZonePermissions, stAb);
// If the MayPlay effect only affected itself, check if it is in graveyard and give other player who cast Shaman's Trance MayPlay // If the MayPlay effect only affected itself, check if it is in graveyard and give other player who cast Shaman's Trance MayPlay
@@ -962,7 +958,7 @@ public final class StaticAbilityContinuous {
for (final Player p : game.getPlayers()) { for (final Player p : game.getPlayers()) {
if (p.hasKeyword("Shaman's Trance") && mayPlayController != p) { if (p.hasKeyword("Shaman's Trance") && mayPlayController != p) {
affectedCard.setMayPlay(p, mayPlayWithoutManaCost, affectedCard.setMayPlay(p, mayPlayWithoutManaCost,
mayPlayAltCost != null ? new Cost(mayPlayAltCost, false) : null, additional, mayPlayAltCost != null ? new Cost(mayPlayAltCost, false) : null,
mayPlayWithFlash, mayPlayGrantZonePermissions, stAb); mayPlayWithFlash, mayPlayGrantZonePermissions, stAb);
} }
} }

View File

@@ -40,6 +40,7 @@ import forge.game.*;
import forge.game.ability.AbilityKey; import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType; import forge.game.ability.ApiType;
import forge.game.ability.effects.PlayEffect;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardCopyService; import forge.game.card.CardCopyService;
import forge.game.event.EventValueChangeType; import forge.game.event.EventValueChangeType;
@@ -85,7 +86,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
private final List<Card> thisTurnCast = Lists.newArrayList(); private final List<Card> thisTurnCast = Lists.newArrayList();
private List<Card> lastTurnCast = Lists.newArrayList(); private List<Card> lastTurnCast = Lists.newArrayList();
private final List<SpellAbility> abilitiesActivatedThisTurn = Lists.newArrayList(); private final List<SpellAbility> thisTurnActivated = Lists.newArrayList();
private Card curResolvingCard = null; private Card curResolvingCard = null;
private final Map<String, List<GameCommand>> commandList = Maps.newHashMap(); private final Map<String, List<GameCommand>> commandList = Maps.newHashMap();
@@ -493,6 +494,9 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
if (sp.isSpell() && sp.getMayPlay() != null) { if (sp.isSpell() && sp.getMayPlay() != null) {
sp.getMayPlay().incMayPlayTurn(); sp.getMayPlay().incMayPlayTurn();
if (sp.getMayPlay().hasParam("ReplaceGraveyard")) {
PlayEffect.addReplaceGraveyardEffect(sp.getHostCard(), sp.getMayPlay().getHostCard(), sp, sp, sp.getMayPlay().getParam("ReplaceGraveyard"));
}
} }
si = si == null ? new SpellAbilityStackInstance(sp, id) : si; si = si == null ? new SpellAbilityStackInstance(sp, id) : si;
@@ -503,7 +507,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
Set<Integer> sources = new TreeSet<>(); Set<Integer> sources = new TreeSet<>();
for (SpellAbilityStackInstance s : stack) { for (SpellAbilityStackInstance s : stack) {
if (s.isSpell()) { if (s.isSpell()) {
distinctSources += 1; distinctSources++;
} else { } else {
sources.add(s.getSourceCard().getId()); sources.add(s.getSourceCard().getId());
} }
@@ -895,11 +899,14 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
public final List<Card> getSpellsCastThisTurn() { public final List<Card> getSpellsCastThisTurn() {
return thisTurnCast; return thisTurnCast;
} }
public final List<Card> getSpellsCastLastTurn() {
return lastTurnCast;
}
public final void onNextTurn() { public final void onNextTurn() {
final Player active = game.getPhaseHandler().getPlayerTurn(); final Player active = game.getPhaseHandler().getPlayerTurn();
game.getStackZone().resetCardsAddedThisTurn(); game.getStackZone().resetCardsAddedThisTurn();
this.abilitiesActivatedThisTurn.clear(); this.thisTurnActivated.clear();
if (thisTurnCast.isEmpty()) { if (thisTurnCast.isEmpty()) {
lastTurnCast = Lists.newArrayList(); lastTurnCast = Lists.newArrayList();
active.resetSpellCastSinceBegOfYourLastTurn(); active.resetSpellCastSinceBegOfYourLastTurn();
@@ -914,17 +921,13 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
game.updateStackForView(); game.updateStackForView();
} }
public final List<Card> getSpellsCastLastTurn() {
return lastTurnCast;
}
public void addAbilityActivatedThisTurn(SpellAbility sa, final Card source) { public void addAbilityActivatedThisTurn(SpellAbility sa, final Card source) {
source.addAbilityActivated(sa); source.addAbilityActivated(sa);
abilitiesActivatedThisTurn.add(sa.copy(CardCopyService.getLKICopy(source), true)); thisTurnActivated.add(sa.copy(CardCopyService.getLKICopy(source), true));
} }
public List<SpellAbility> getAbilityActivatedThisTurn() { public List<SpellAbility> getAbilityActivatedThisTurn() {
return abilitiesActivatedThisTurn; return thisTurnActivated;
} }
public final void addCastCommand(final String valid, final GameCommand c) { public final void addCastCommand(final String valid, final GameCommand c) {

View File

@@ -6,7 +6,7 @@ K:Flying
K:Trample K:Trample
K:etbCounter:SHIELD:1 K:etbCounter:SHIELD:1
S:Mode$ Continuous | Affected$ Card.TopLibrary+YouOwn | AffectedZone$ Library | MayLookAt$ You | Description$ You may look at the top card of your library any time. S:Mode$ Continuous | Affected$ Card.TopLibrary+YouOwn | AffectedZone$ Library | MayLookAt$ You | Description$ You may look at the top card of your library any time.
S:Mode$ Continuous | Affected$ Card.TopLibrary+YouOwn+nonLand | AffectedZone$ Library | MayPlay$ True | MayPlayAltManaCost$ RegularCost RemoveAnyCounter<1/Any/Creature.YouCtrl/a creature you control> | Description$ You may cast spells from the top of your library by removing a counter from a creature you control in addition to paying their other costs. S:Mode$ Continuous | Affected$ Card.TopLibrary+YouOwn+nonLand | AffectedZone$ Library | MayPlay$ True | RaiseCost$ RemoveAnyCounter<1/Any/Creature.YouCtrl/a creature you control> | Description$ You may cast spells from the top of your library by removing a counter from a creature you control in addition to paying their other costs.
DeckHas:Ability$Counters DeckHas:Ability$Counters
DeckHints:Ability$Counters|Proliferate DeckHints:Ability$Counters|Proliferate
Oracle:Flying, trample\nFalco Spara, Pactweaver enters the battlefield with a shield counter on it.\nYou may look at the top card of your library any time.\nYou may cast spells from the top of your library by removing a counter from a creature you control in addition to paying their other costs. Oracle:Flying, trample\nFalco Spara, Pactweaver enters the battlefield with a shield counter on it.\nYou may look at the top card of your library any time.\nYou may cast spells from the top of your library by removing a counter from a creature you control in addition to paying their other costs.

View File

@@ -3,7 +3,5 @@ ManaCost:1 U B R
Types:Legendary Creature Human Wizard Types:Legendary Creature Human Wizard
PT:3/4 PT:3/4
K:Flying K:Flying
S:Mode$ Continuous | Affected$ Instant.YouCtrl,Sorcery.YouCtrl | Condition$ PlayerTurn | MayPlay$ True | MayPlayLimit$ 1 | EffectZone$ Battlefield | AffectedZone$ Graveyard | Description$ During each of your turns, you may cast an instant or sorcery spell from your graveyard. If a spell cast this way would be put into your graveyard, exile it instead. S:Mode$ Continuous | Affected$ Instant.YouCtrl,Sorcery.YouCtrl | Condition$ PlayerTurn | ReplaceGraveyard$ Exile | MayPlay$ True | MayPlayLimit$ 1 | EffectZone$ Battlefield | AffectedZone$ Graveyard | Description$ During each of your turns, you may cast an instant or sorcery spell from your graveyard. If a spell cast this way would be put into your graveyard, exile it instead.
R:Event$ Moved | ValidLKI$ Card.CastSa Spell.MayPlaySource | Origin$ Stack | Destination$ Graveyard | ReplaceWith$ MoveExile
SVar:MoveExile:DB$ ChangeZone | Defined$ ReplacedCard | Origin$ Stack | Destination$ Exile
Oracle:Flying\nDuring each of your turns, you may cast an instant or sorcery spell from your graveyard. If a spell cast this way would be put into your graveyard, exile it instead. Oracle:Flying\nDuring each of your turns, you may cast an instant or sorcery spell from your graveyard. If a spell cast this way would be put into your graveyard, exile it instead.

View File

@@ -1,7 +1,5 @@
Name:Maestros Ascendancy Name:Maestros Ascendancy
ManaCost:U B R ManaCost:U B R
Types:Enchantment Types:Enchantment
S:Mode$ Continuous | Affected$ Instant.YouCtrl,Sorcery.YouCtrl | Condition$ PlayerTurn | MayPlay$ True | MayPlayAltManaCost$ RegularCost Sac<1/Creature> | MayPlayLimit$ 1 | EffectZone$ Battlefield | AffectedZone$ Graveyard | Description$ Once during each of your turns, you may cast an instant or sorcery spell from your graveyard by sacrificing a creature in addition to paying its other costs. If a spell cast this way would be put into your graveyard, exile it instead. S:Mode$ Continuous | Affected$ Instant.YouCtrl,Sorcery.YouCtrl | Condition$ PlayerTurn | ReplaceGraveyard$ Exile | MayPlay$ True | RaiseCost$ Sac<1/Creature> | MayPlayLimit$ 1 | EffectZone$ Battlefield | AffectedZone$ Graveyard | Description$ Once during each of your turns, you may cast an instant or sorcery spell from your graveyard by sacrificing a creature in addition to paying its other costs. If a spell cast this way would be put into your graveyard, exile it instead.
R:Event$ Moved | ValidLKI$ Card.CastSa Spell.MayPlaySource | Origin$ Stack | Destination$ Graveyard | ReplaceWith$ MoveExile
SVar:MoveExile:DB$ ChangeZone | Defined$ ReplacedCard | Origin$ Stack | Destination$ Exile
Oracle:Once during each of your turns, you may cast an instant or sorcery spell from your graveyard by sacrificing a creature in addition to paying its other costs. If a spell cast this way would be put into your graveyard, exile it instead. Oracle:Once during each of your turns, you may cast an instant or sorcery spell from your graveyard by sacrificing a creature in addition to paying its other costs. If a spell cast this way would be put into your graveyard, exile it instead.

View File

@@ -4,7 +4,6 @@ Types:Creature Human Cleric
PT:1/1 PT:1/1
K:A deck can have any number of cards named CARDNAME. K:A deck can have any number of cards named CARDNAME.
A:AB$ ChangeZone | Cost$ B Sac<6/Creature.namedShadowborn Apostle/creatures named Shadowborn Apostle> | Origin$ Library | Destination$ Battlefield | ChangeType$ Creature.Demon | ChangeNum$ 1 | SpellDescription$ Search your library for a Demon creature card, put it onto the battlefield, then shuffle. A:AB$ ChangeZone | Cost$ B Sac<6/Creature.namedShadowborn Apostle/creatures named Shadowborn Apostle> | Origin$ Library | Destination$ Battlefield | ChangeType$ Creature.Demon | ChangeNum$ 1 | SpellDescription$ Search your library for a Demon creature card, put it onto the battlefield, then shuffle.
DeckNeeds:Name$Shadowborn Apostle DeckNeeds:Name$Shadowborn Apostle & Type$Demon
DeckNeeds:Type$Demon
DeckHints:Name$Shadowborn Demon DeckHints:Name$Shadowborn Demon
Oracle:A deck can have any number of cards named Shadowborn Apostle.\n{B}, Sacrifice six creatures named Shadowborn Apostle: Search your library for a Demon creature card, put it onto the battlefield, then shuffle. Oracle:A deck can have any number of cards named Shadowborn Apostle.\n{B}, Sacrifice six creatures named Shadowborn Apostle: Search your library for a Demon creature card, put it onto the battlefield, then shuffle.