mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 03:38:01 +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:
@@ -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;
|
||||
|
||||
@@ -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