diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index 149d0ef226f..1ac0e3e83e5 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -20,13 +20,17 @@ package forge.ai; import com.google.common.base.Predicate; import com.google.common.collect.Lists; +import forge.ai.ability.AnimateAi; import forge.game.GameEntity; +import forge.game.ability.ApiType; import forge.game.card.Card; +import forge.game.card.CardFactory; import forge.game.card.CardLists; import forge.game.card.CounterType; import forge.game.combat.Combat; import forge.game.combat.CombatUtil; import forge.game.player.Player; +import forge.game.spellability.SpellAbility; import forge.game.trigger.Trigger; import forge.game.trigger.TriggerType; import forge.game.zone.ZoneType; @@ -82,7 +86,23 @@ public class AiAttackController { this.oppList = Lists.newArrayList(); this.oppList.addAll(this.defendingOpponent.getCreaturesInPlay()); this.myList = ai.getCreaturesInPlay(); - + Predicate canAnimate = new Predicate() { + @Override + public boolean apply(Card c) { + return !c.isCreature() && !c.isPlaneswalker(); + } + }; + for (Card c : CardLists.filter(this.defendingOpponent.getCardsIn(ZoneType.Battlefield), canAnimate)) { + for (SpellAbility sa : c.getSpellAbilities()) { + if (sa.getApi() == ApiType.Animate) { + if (ComputerUtilCost.canPayCost(sa, this.defendingOpponent)) { + Card animatedCopy = CardFactory.getCard(c.getPaperCard(), this.defendingOpponent); + AnimateAi.becomeAnimated(animatedCopy, sa); + this.oppList.add(animatedCopy); + } + } + } + } this.attackers = new ArrayList(); for (Card c : myList) { diff --git a/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java b/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java index 0e321bb4d8c..188a2dae7cf 100644 --- a/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java @@ -4,18 +4,28 @@ import com.google.common.collect.Iterables; import forge.ai.SpellAbilityAi; import forge.game.Game; +import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityUtils; import forge.game.card.Card; import forge.game.card.CardLists; import forge.game.card.CardPredicates; +import forge.game.card.CardUtil; import forge.game.phase.PhaseHandler; import forge.game.phase.PhaseType; import forge.game.player.Player; +import forge.game.replacement.ReplacementEffect; +import forge.game.replacement.ReplacementHandler; import forge.game.spellability.SpellAbility; import forge.game.spellability.TargetRestrictions; +import forge.game.staticability.StaticAbility; +import forge.game.trigger.Trigger; +import forge.game.trigger.TriggerHandler; import forge.game.zone.ZoneType; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Map; /** *

@@ -196,4 +206,257 @@ public class AnimateAi extends SpellAbilityAi { return false; } + public static void becomeAnimated(Card source, SpellAbility sa) { + //duplicating AnimateEffect.resolve + final Game game = sa.getActivatingPlayer().getGame(); + final Map svars = source.getSVars(); + final long timestamp = game.getNextTimestamp(); + + // AF specific sa + int power = -1; + if (sa.hasParam("Power")) { + power = AbilityUtils.calculateAmount(source, sa.getParam("Power"), sa); + } + int toughness = -1; + if (sa.hasParam("Toughness")) { + toughness = AbilityUtils.calculateAmount(source, sa.getParam("Toughness"), sa); + } + + final ArrayList types = new ArrayList(); + if (sa.hasParam("Types")) { + types.addAll(Arrays.asList(sa.getParam("Types").split(","))); + } + + final ArrayList removeTypes = new ArrayList(); + if (sa.hasParam("RemoveTypes")) { + removeTypes.addAll(Arrays.asList(sa.getParam("RemoveTypes").split(","))); + } + + // allow ChosenType - overrides anything else specified + if (types.contains("ChosenType")) { + types.clear(); + types.add(source.getChosenType()); + } + + final ArrayList keywords = new ArrayList(); + if (sa.hasParam("Keywords")) { + keywords.addAll(Arrays.asList(sa.getParam("Keywords").split(" & "))); + } + + final ArrayList removeKeywords = new ArrayList(); + if (sa.hasParam("RemoveKeywords")) { + removeKeywords.addAll(Arrays.asList(sa.getParam("RemoveKeywords").split(" & "))); + } + + final ArrayList hiddenKeywords = new ArrayList(); + if (sa.hasParam("HiddenKeywords")) { + hiddenKeywords.addAll(Arrays.asList(sa.getParam("HiddenKeywords").split(" & "))); + } + // allow SVar substitution for keywords + for (int i = 0; i < keywords.size(); i++) { + final String k = keywords.get(i); + if (svars.containsKey(k)) { + keywords.add(svars.get(k)); + keywords.remove(k); + } + } + + // colors to be added or changed to + String tmpDesc = ""; + if (sa.hasParam("Colors")) { + final String colors = sa.getParam("Colors"); + if (colors.equals("ChosenColor")) { + + tmpDesc = CardUtil.getShortColorsString(source.getChosenColor()); + } else { + tmpDesc = CardUtil.getShortColorsString(new ArrayList(Arrays.asList(colors.split(",")))); + } + } + final String finalDesc = tmpDesc; + + // abilities to add to the animated being + final ArrayList abilities = new ArrayList(); + if (sa.hasParam("Abilities")) { + abilities.addAll(Arrays.asList(sa.getParam("Abilities").split(","))); + } + + // replacement effects to add to the animated being + final ArrayList replacements = new ArrayList(); + if (sa.hasParam("Replacements")) { + replacements.addAll(Arrays.asList(sa.getParam("Replacements").split(","))); + } + + // triggers to add to the animated being + final ArrayList triggers = new ArrayList(); + if (sa.hasParam("Triggers")) { + triggers.addAll(Arrays.asList(sa.getParam("Triggers").split(","))); + } + + // static abilities to add to the animated being + final ArrayList stAbs = new ArrayList(); + if (sa.hasParam("staticAbilities")) { + stAbs.addAll(Arrays.asList(sa.getParam("staticAbilities").split(","))); + } + + // sVars to add to the animated being + final ArrayList sVars = new ArrayList(); + if (sa.hasParam("sVars")) { + sVars.addAll(Arrays.asList(sa.getParam("sVars").split(","))); + } + + //duplicating AnimateEffectBase.doAnimate + boolean removeSuperTypes = false; + boolean removeCardTypes = false; + boolean removeSubTypes = false; + boolean removeCreatureTypes = false; + + if (sa.hasParam("OverwriteTypes")) { + removeSuperTypes = true; + removeCardTypes = true; + removeSubTypes = true; + removeCreatureTypes = true; + } + + if (sa.hasParam("KeepSupertypes")) { + removeSuperTypes = false; + } + + if (sa.hasParam("KeepCardTypes")) { + removeCardTypes = false; + } + + if (sa.hasParam("RemoveSuperTypes")) { + removeSuperTypes = true; + } + + if (sa.hasParam("RemoveCardTypes")) { + removeCardTypes = true; + } + + if (sa.hasParam("RemoveSubTypes")) { + removeSubTypes = true; + } + + if (sa.hasParam("RemoveCreatureTypes")) { + removeCreatureTypes = true; + } + + if ((power != -1) || (toughness != -1)) { + source.addNewPT(power, toughness, timestamp); + } + + if (!types.isEmpty() || !removeTypes.isEmpty() || removeCreatureTypes) { + source.addChangedCardTypes(types, removeTypes, removeSuperTypes, removeCardTypes, removeSubTypes, + removeCreatureTypes, timestamp); + } + + source.addChangedCardKeywords(keywords, removeKeywords, sa.hasParam("RemoveAllAbilities"), timestamp); + + for (final String k : hiddenKeywords) { + source.addHiddenExtrinsicKeyword(k); + } + + source.addColor(finalDesc, !sa.hasParam("OverwriteColors"), true); + + //back to duplicating AnimateEffect.resolve + //TODO will all these abilities/triggers/replacements/etc. lead to memory leaks or unintended effects? + // remove abilities + final ArrayList removedAbilities = new ArrayList(); + boolean clearAbilities = sa.hasParam("OverwriteAbilities"); + boolean clearSpells = sa.hasParam("OverwriteSpells"); + boolean removeAll = sa.hasParam("RemoveAllAbilities"); + + if (clearAbilities || clearSpells || removeAll) { + for (final SpellAbility ab : source.getSpellAbilities()) { + if (removeAll || (ab.isAbility() && clearAbilities) + || (ab.isSpell() && clearSpells)) { + source.removeSpellAbility(ab); + removedAbilities.add(ab); + } + } + } + + // give abilities + final ArrayList addedAbilities = new ArrayList(); + if (abilities.size() > 0) { + for (final String s : abilities) { + final String actualAbility = source.getSVar(s); + final SpellAbility grantedAbility = AbilityFactory.getAbility(actualAbility, source); + addedAbilities.add(grantedAbility); + source.addSpellAbility(grantedAbility); + } + } + + // Grant triggers + final ArrayList addedTriggers = new ArrayList(); + if (triggers.size() > 0) { + for (final String s : triggers) { + final String actualTrigger = source.getSVar(s); + final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, source, false); + addedTriggers.add(source.addTrigger(parsedTrigger)); + } + } + + // give replacement effects + final ArrayList addedReplacements = new ArrayList(); + if (replacements.size() > 0) { + for (final String s : replacements) { + final String actualReplacement = source.getSVar(s); + final ReplacementEffect parsedReplacement = ReplacementHandler.parseReplacement(actualReplacement, source, false); + addedReplacements.add(source.addReplacementEffect(parsedReplacement)); + } + } + + // suppress triggers from the animated card + final ArrayList removedTriggers = new ArrayList(); + if (sa.hasParam("OverwriteTriggers") || removeAll) { + final List triggersToRemove = source.getTriggers(); + for (final Trigger trigger : triggersToRemove) { + trigger.setSuppressed(true); + removedTriggers.add(trigger); + } + } + + // give static abilities (should only be used by cards to give + // itself a static ability) + if (stAbs.size() > 0) { + for (final String s : stAbs) { + final String actualAbility = source.getSVar(s); + source.addStaticAbility(actualAbility); + } + } + + // give sVars + if (sVars.size() > 0) { + for (final String s : sVars) { + String actualsVar = source.getSVar(s); + String name = s; + if (actualsVar.startsWith("SVar:")) { + actualsVar = actualsVar.split("SVar:")[1]; + name = actualsVar.split(":")[0]; + actualsVar = actualsVar.split(":")[1]; + } + source.setSVar(name, actualsVar); + } + } + + // suppress static abilities from the animated card + final ArrayList removedStatics = new ArrayList(); + if (sa.hasParam("OverwriteStatics") || removeAll) { + final ArrayList staticsToRemove = source.getStaticAbilities(); + for (final StaticAbility stAb : staticsToRemove) { + stAb.setTemporarilySuppressed(true); + removedStatics.add(stAb); + } + } + + // suppress static abilities from the animated card + final ArrayList removedReplacements = new ArrayList(); + if (sa.hasParam("OverwriteReplacements") || removeAll) { + for (final ReplacementEffect re : source.getReplacementEffects()) { + re.setTemporarilySuppressed(true); + removedReplacements.add(re); + } + } + } }