- 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

@@ -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) {