- Convert Animate Dead and Dance of the Dead to script (oh joyous day)

- Some small changes in AF_Animate to grant spells with the right timing
- Only move Auras into play if it's a cast spell
This commit is contained in:
Sol
2012-12-07 03:26:11 +00:00
parent 5c52d7209b
commit a82bf31519
6 changed files with 70 additions and 175 deletions

View File

@@ -1,7 +1,16 @@
Name:Animate Dead
ManaCost:1 B
Types:Enchantment Aura
Text:Enchant creature card in a graveyard\r\nWhen Animate Dead enters the battlefield, if it's on the battlefield, it loses "enchant creature card in a graveyard" and gains "enchant creature put onto the battlefield with Animate Dead." Return enchanted creature card to the battlefield under your control and attach Animate Dead to it. When Animate Dead leaves the battlefield, that creature's controller sacrifices it.
Text:no text
K:Enchant creature card in a graveyard
A:SP$ Attach | Cost$ 1 B | ValidTgts$ Creature | TgtZone$ Graveyard | AILogic$ Reanimate
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReanimate | TriggerDescription$ When CARDNAME enters the battlefield, if it's on the battlefield, it loses "enchant creature card in a graveyard" and gains "enchant creature put onto the battlefield with CARDNAME." Return enchanted creature card to the battlefield under your control and attach CARDNAME to it.
SVar:TrigReanimate:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Defined$ Enchanted | RememberChanged$ True | SubAbility$ DBAnimate
SVar:DBAnimate:DB$ Animate | Defined$ Self | OverwriteSpells$ True | Abilities$ NewAttach | Keywords$ Enchant creature put onto the battlefield with CARDNAME | RemoveKeywords$ Enchant creature card in a graveyard | Permanent$ True | SubAbility$ DBAttach
SVar:DBAttach:DB$ Attach | Defined$ Remembered
SVar:NewAttach:SP$ Attach | Cost$ 1 B | ValidTgts$ Creature.IsRemembered | AILogic$ Pump
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigSacrifice | TriggerDescription$ When Animate Dead leaves the battlefield, that creature's controller sacrifices it.
SVar:TrigSacrifice:DB$ Destroy | Sacrifice$ True | Defined$ Remembered
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ -1 | Description$ Enchanted creature gets -1/-0.
SVar:Rarity:Uncommon
SVar:Picture:http://resources.wizards.com/magic/cards/5e/en-us/card3823.jpg

View File

@@ -1,7 +1,16 @@
Name:Dance of the Dead
ManaCost:1 B
Types:Enchantment Aura
Text:Enchant creature card in a graveyard\r\n\r\nWhen CARDNAME enters the battlefield, if it's on the battlefield, it loses "enchant creature card in a graveyard" and gains "enchant creature put onto the battlefield with CARDNAME." Return enchanted creature card to the battlefield tapped under your control and attach CARDNAME to it. When CARDNAME leaves the battlefield, that creature's controller sacrifices it.
Text:no text
K:Enchant creature card in a graveyard
A:SP$ Attach | Cost$ 1 B | ValidTgts$ Creature | TgtZone$ Graveyard | AILogic$ Reanimate
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigReanimate | TriggerDescription$ When CARDNAME enters the battlefield, if it's on the battlefield, it loses "enchant creature card in a graveyard" and gains "enchant creature put onto the battlefield with CARDNAME." Return enchanted creature card to the battlefield under your control and attach CARDNAME to it.
SVar:TrigReanimate:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Defined$ Enchanted | RememberChanged$ True | SubAbility$ DBAnimate
SVar:DBAnimate:DB$ Animate | Defined$ Self | OverwriteSpells$ True | Abilities$ NewAttach | Keywords$ Enchant creature put onto the battlefield with CARDNAME | RemoveKeywords$ Enchant creature card in a graveyard | Permanent$ True | SubAbility$ DBAttach
SVar:DBAttach:DB$ Attach | Defined$ Remembered
SVar:NewAttach:SP$ Attach | Cost$ 1 B | ValidTgts$ Creature.IsRemembered | AILogic$ Pump
T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigSacrifice | TriggerDescription$ When Animate Dead leaves the battlefield, that creature's controller sacrifices it.
SVar:TrigSacrifice:DB$ Destroy | Sacrifice$ True | Defined$ Remembered
S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddPower$ 1 | AddToughness$ 1 | AddHiddenKeyword$ CARDNAME doesn't untap during your untap step. | Description$ Enchanted creature gets +1/+1 and doesn't untap during its controller's untap step.
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ EnchantedController | TriggerZones$ Battlefield | OptionalDecider$ EnchantedController | Execute$ TrigUntap | TriggerDescription$ At the beginning of the upkeep of enchanted creature's controller, that player may pay 1 B. If he or she does, untap that creature.
SVar:TrigUntap:AB$Untap | Cost$ 1 B | Defined$ Enchanted

View File

@@ -309,6 +309,34 @@ public class AttachAi extends SpellAiLogic {
return c;
}
/**
* Attach ai control preference.
*
* @param sa
* the sa
* @param list
* the list
* @param mandatory
* the mandatory
* @param attachSource
* the attach source
* @return the card
*/
private static Card attachAIReanimatePreference(final SpellAbility sa, final List<Card> list, final boolean mandatory,
final Card attachSource) {
// AI For choosing a Card to Animate.
// TODO Add some more restrictions for Reanimation Auras
final Card c = CardFactoryUtil.getBestCreatureAI(list);
// If Mandatory (brought directly into play without casting) gotta
// choose something
if (c == null && mandatory) {
return chooseLessPreferred(mandatory, list);
}
return c;
}
// Should generalize this code a bit since they all have similar structures
/**
@@ -786,6 +814,8 @@ public class AttachAi extends SpellAiLogic {
c = attachAIKeepTappedPreference(sa, prefList, mandatory, attachSource);
} else if ("Animate".equals(logic)) {
c = attachAIAnimatePreference(sa, prefList, mandatory, attachSource);
} else if ("Reanimate".equals(logic)) {
c = attachAIReanimatePreference(sa, prefList, mandatory, attachSource);
}
return c;

View File

@@ -147,6 +147,22 @@ public class AnimateEffect extends AnimateEffectBase {
final long colorTimestamp = doAnimate(c, sa, power, toughness, types, removeTypes,
finalDesc, keywords, removeKeywords, hiddenKeywords, timestamp);
// remove abilities
final ArrayList<SpellAbility> removedAbilities = new ArrayList<SpellAbility>();
boolean clearAbilities = sa.hasParam("OverwriteAbilities");
boolean clearSpells = sa.hasParam("OverwriteSpells");
boolean removeAll = sa.hasParam("RemoveAllAbilities");
if (clearAbilities || clearSpells || removeAll) {
for (final SpellAbility ab : c.getSpellAbilities()) {
if (removeAll || (ab.isAbility() && clearAbilities) ||
(ab.isSpell() && clearSpells)) {
c.removeSpellAbility(ab);
removedAbilities.add(ab);
}
}
}
// give abilities
final ArrayList<SpellAbility> addedAbilities = new ArrayList<SpellAbility>();
if (abilities.size() > 0) {
@@ -159,17 +175,6 @@ public class AnimateEffect extends AnimateEffectBase {
}
}
// remove abilities
final ArrayList<SpellAbility> removedAbilities = new ArrayList<SpellAbility>();
if (sa.hasParam("OverwriteAbilities") || sa.hasParam("RemoveAllAbilities")) {
for (final SpellAbility ab : c.getSpellAbilities()) {
if (ab.isAbility()) {
c.removeSpellAbility(ab);
removedAbilities.add(ab);
}
}
}
// Grant triggers
final ArrayList<Trigger> addedTriggers = new ArrayList<Trigger>();
if (triggers.size() > 0) {
@@ -182,7 +187,7 @@ public class AnimateEffect extends AnimateEffectBase {
// suppress triggers from the animated card
final ArrayList<Trigger> removedTriggers = new ArrayList<Trigger>();
if (sa.hasParam("OverwriteTriggers") || sa.hasParam("RemoveAllAbilities")) {
if (sa.hasParam("OverwriteTriggers") || removeAll) {
final List<Trigger> triggersToRemove = c.getTriggers();
for (final Trigger trigger : triggersToRemove) {
trigger.setSuppressed(true);
@@ -209,7 +214,7 @@ public class AnimateEffect extends AnimateEffectBase {
// suppress static abilities from the animated card
final ArrayList<StaticAbility> removedStatics = new ArrayList<StaticAbility>();
if (sa.hasParam("OverwriteStatics") || sa.hasParam("RemoveAllAbilities")) {
if (sa.hasParam("OverwriteStatics") || removeAll) {
final ArrayList<StaticAbility> staticsToRemove = c.getStaticAbilities();
for (final StaticAbility stAb : staticsToRemove) {
stAb.setTemporarilySuppressed(true);
@@ -219,7 +224,7 @@ public class AnimateEffect extends AnimateEffectBase {
// suppress static abilities from the animated card
final ArrayList<ReplacementEffect> removedReplacements = new ArrayList<ReplacementEffect>();
if (sa.hasParam("OverwriteReplacements") || sa.hasParam("RemoveAllAbilities")) {
if (sa.hasParam("OverwriteReplacements") || removeAll) {
final ArrayList<ReplacementEffect> replacementsToRemove = c.getReplacementEffects();
for (final ReplacementEffect re : replacementsToRemove) {
re.setTemporarilySuppressed(true);

View File

@@ -25,7 +25,7 @@ public class AttachEffect extends SpellEffect {
*/
@Override
public void resolve(SpellAbility sa) {
if (sa.getSourceCard().isAura()) {
if (sa.getSourceCard().isAura() && sa.isSpell()) {
// The Spell_Permanent (Auras) version of this AF needs to
// move the card into play before Attaching

View File

@@ -436,164 +436,6 @@ class CardFactoryAuras {
card.addSpellAbility(spell);
} // *************** END ************ END **************************
// *************** START *********** START **************************
else if (cardName.equals("Animate Dead") || cardName.equals("Dance of the Dead")) {
final Card[] targetC = new Card[1];
// need to override what happens when this is cast.
final SpellPermanent animate = new SpellPermanent(card) {
private static final long serialVersionUID = 7126615291288065344L;
public List<Card> getCreturesInGrave() {
// This includes creatures Animate Dead can't enchant once
// in play.
// The human may try to Animate them, the AI will not.
return CardLists.filter(Singletons.getModel().getGame().getCardsIn(ZoneType.Graveyard), Presets.CREATURES);
}
@Override
public boolean canPlay() {
return super.canPlay() && (this.getCreturesInGrave().size() != 0);
}
@Override
public boolean canPlayAI() {
List<Card> cList = this.getCreturesInGrave();
// AI will only target something that will stick in play.
cList = CardLists.getTargetableCards(cList, this);
if (cList.size() == 0) {
return false;
}
final Card c = CardFactoryUtil.getBestCreatureAI(cList);
this.setTargetCard(c);
final boolean playable = (2 < c.getNetAttack()) && (2 < c.getNetDefense()) && super.canPlayAI();
return playable;
} // canPlayAI
@Override
public void resolve() {
targetC[0] = this.getTargetCard();
super.resolve();
}
}; // addSpellAbility
// Target AbCost and Restriction are set here to get this working as
// expected
final Target tgt = new Target(card, "Select a creature in a graveyard", "Creature".split(","));
tgt.setZone(ZoneType.Graveyard);
animate.setTarget(tgt);
final Cost cost = new Cost(card, "1 B", false);
animate.setPayCosts(cost);
animate.getRestrictions().setZone(ZoneType.Hand);
final Ability attach = new Ability(card, "0") {
@Override
public void resolve() {
final PlayerZone play = card.getController().getZone(ZoneType.Battlefield);
// Animate Dead got destroyed before its ability resolved
if (!play.contains(card)) {
return;
}
final Card animated = targetC[0];
final Zone grave = Singletons.getModel().getGame().getZoneOf(animated);
if (!grave.is(ZoneType.Graveyard)) {
// Animated Creature got removed before ability resolved
Singletons.getModel().getGame().getAction().sacrifice(card, null);
return;
}
// Bring creature onto the battlefield under your control
// (should trigger etb Abilities)
animated.addController(card.getController());
Singletons.getModel().getGame().getAction().moveToPlay(animated, card.getController());
if (cardName.equals("Dance of the Dead")) {
animated.tap();
}
card.enchantEntity(animated); // Attach before Targeting so
// detach Command will trigger
if (CardFactoryUtil.hasProtectionFrom(card, animated)) {
// Animated a creature with protection
Singletons.getModel().getGame().getAction().sacrifice(card, null);
return;
}
// Everything worked out perfectly.
}
}; // Ability
final Command attachCmd = new Command() {
private static final long serialVersionUID = 3595188622377350327L;
@Override
public void execute() {
if (targetC[0] != null) {
//too slow - must be done immediately
//otherwise before attach is resolved state effect kills aura as it has no target...
// AllZone.getStack().addSimultaneousStackEntry(attach);
//this seems to work, but I'm not 100% sure of possible side effects (hopefully none)
attach.resolve();
} else {
// note: this should be a state-based action, but it doesn't work currently.
// I don't know if that because it's hard-coded or what, but this fixes
// these cards being put on the battlefield not attached to anything.
Singletons.getModel().getGame().getAction().moveToGraveyard(card);
}
}
};
final Ability detach = new Ability(card, "0") {
@Override
public void resolve() {
final Card c = targetC[0];
final PlayerZone play = card.getController().getZone(ZoneType.Battlefield);
if (play.contains(c)) {
Singletons.getModel().getGame().getAction().sacrifice(c, null);
}
}
}; // Detach
final Command detachCmd = new Command() {
private static final long serialVersionUID = 2425333033834543422L;
@Override
public void execute() {
final Card c = targetC[0];
final PlayerZone play = card.getController().getZone(ZoneType.Battlefield);
if (play.contains(c)) {
Singletons.getModel().getGame().getStack().addSimultaneousStackEntry(detach);
}
}
};
card.addSpellAbility(animate);
final StringBuilder sbA = new StringBuilder();
sbA.append("Attaching ").append(cardName).append(" to creature in graveyard.");
attach.setStackDescription(sbA.toString());
card.addComesIntoPlayCommand(attachCmd);
final StringBuilder sbD = new StringBuilder();
sbD.append(cardName).append(" left play. Sacrificing creature if still around.");
detach.setStackDescription(sbD.toString());
card.addLeavesPlayCommand(detachCmd);
card.addUnEnchantCommand(detachCmd);
} // *************** END ************ END **************************
// *************** START *********** START **************************
else if (CardFactoryUtil.hasKeyword(card, "enchant") != -1) {
final int n = CardFactoryUtil.hasKeyword(card, "enchant");
if (n != -1) {