mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 11:48:02 +00:00
- 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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -310,6 +310,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
|
||||
/**
|
||||
* Attach ai control preference.
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user