package forge.card.abilityFactory; import forge.*; import forge.card.cardFactory.CardFactoryUtil; import forge.card.spellability.*; import java.util.ArrayList; import java.util.HashMap; /** * <p>AbilityFactory_PreventDamage class.</p> * * @author Forge * @version $Id: $ */ public class AbilityFactory_PreventDamage { // Ex: A:SP$ PreventDamage | Cost$ W | Tgt$ TgtC | Amount$ 3 | SpellDescription$ Prevent the next 3 damage that would be dealt to target creature this turn. // http://www.slightlymagic.net/wiki/Forge_AbilityFactory#PreventDamage /** * <p>getAbilityPreventDamage.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @return a {@link forge.card.spellability.SpellAbility} object. */ public static SpellAbility getAbilityPreventDamage(final AbilityFactory af) { final SpellAbility abRegenerate = new Ability_Activated(af.getHostCard(), af.getAbCost(), af.getAbTgt()) { private static final long serialVersionUID = -6581723619801399347L; @Override public boolean canPlayAI() { return preventDamageCanPlayAI(af, this); } @Override public void resolve() { preventDamageResolve(af, this); af.getHostCard().setAbilityUsed(af.getHostCard().getAbilityUsed() + 1); } @Override public String getStackDescription() { return preventDamageStackDescription(af, this); } @Override public boolean doTrigger(boolean mandatory) { return doPreventDamageTriggerAI(af, this, mandatory); } };//Ability_Activated return abRegenerate; } /** * <p>getSpellPreventDamage.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @return a {@link forge.card.spellability.SpellAbility} object. */ public static SpellAbility getSpellPreventDamage(final AbilityFactory af) { final SpellAbility spRegenerate = new Spell(af.getHostCard(), af.getAbCost(), af.getAbTgt()) { private static final long serialVersionUID = -3899905398102316582L; @Override public boolean canPlayAI() { return preventDamageCanPlayAI(af, this); } @Override public void resolve() { preventDamageResolve(af, this); } @Override public String getStackDescription() { return preventDamageStackDescription(af, this); } }; // Spell return spRegenerate; } /** * <p>createDrawbackPreventDamage.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @return a {@link forge.card.spellability.SpellAbility} object. */ public static SpellAbility createDrawbackPreventDamage(final AbilityFactory af) { final SpellAbility dbRegen = new Ability_Sub(af.getHostCard(), af.getAbTgt()) { private static final long serialVersionUID = -2295483806708528744L; @Override public String getStackDescription() { return preventDamageStackDescription(af, this); } @Override public void resolve() { preventDamageResolve(af, this); } @Override public boolean chkAI_Drawback() { return true; } @Override public boolean doTrigger(boolean mandatory) { return doPreventDamageTriggerAI(af, this, mandatory); } }; return dbRegen; } /** * <p>preventDamageStackDescription.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @param sa a {@link forge.card.spellability.SpellAbility} object. * @return a {@link java.lang.String} object. */ private static String preventDamageStackDescription(AbilityFactory af, SpellAbility sa) { final HashMap<String, String> params = af.getMapParams(); StringBuilder sb = new StringBuilder(); Card host = af.getHostCard(); ArrayList<Object> tgts; if (sa.getTarget() == null) tgts = AbilityFactory.getDefinedObjects(sa.getSourceCard(), params.get("Defined"), sa); else tgts = sa.getTarget().getTargets(); if (sa instanceof Ability_Sub) sb.append(" "); else sb.append(host).append(" - "); sb.append("Prevent the next "); sb.append(params.get("Amount")); sb.append(" that would be dealt to "); for (int i = 0; i < tgts.size(); i++) { if (i != 0) sb.append(" "); Object o = tgts.get(i); if (o instanceof Card) { Card tgtC = (Card) o; if (tgtC.isFaceDown()) sb.append("Morph"); else sb.append(tgtC); } else sb.append(o.toString()); } sb.append(" this turn."); Ability_Sub abSub = sa.getSubAbility(); if (abSub != null) { sb.append(abSub.getStackDescription()); } return sb.toString(); } /** * <p>preventDamageCanPlayAI.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @param sa a {@link forge.card.spellability.SpellAbility} object. * @return a boolean. */ private static boolean preventDamageCanPlayAI(final AbilityFactory af, final SpellAbility sa) { final HashMap<String, String> params = af.getMapParams(); final Card hostCard = af.getHostCard(); boolean chance = false; // temporarily disabled until better AI if (af.getAbCost().getSacCost()) return false; if (af.getAbCost().getSubCounter()) if (af.getAbCost().getCounterType().equals(Counters.P1P1)) return false; if (af.getAbCost().getLifeCost()) return false; if (!ComputerUtil.canPayCost(sa)) return false; Target tgt = af.getAbTgt(); if (tgt == null) { // As far as I can tell these Defined Cards will only have one of them ArrayList<Object> objects = AbilityFactory.getDefinedObjects(sa.getSourceCard(), params.get("Defined"), sa); if (AllZone.getStack().size() > 0) { // check stack for something that will kill this } else { if (AllZone.getPhase().is(Constant.Phase.Combat_Declare_Blockers_InstantAbility)) { boolean flag = false; for (Object o : objects) { if (o instanceof Card) { Card c = (Card) o; flag |= CombatUtil.combatantWouldBeDestroyed(c); } else if (o instanceof Player) { Player p = (Player) o; flag |= (p.isComputer() && CombatUtil.lifeInDanger(AllZone.getCombat())); } } chance = flag; } else { // if nothing on the stack, and it's not declare blockers. no need to regen return false; } } } else if (AllZone.getStack().size() == 0 && AllZone.getPhase().is(Constant.Phase.Combat_Declare_Blockers_InstantAbility)) { tgt.resetTargets(); if (tgt.canTgtPlayer() && CombatUtil.wouldLoseLife(AllZone.getCombat()) && (CombatUtil.lifeInDanger(AllZone.getCombat()) || sa.isAbility())) { tgt.addTarget(AllZone.getComputerPlayer()); chance = true; } else { // filter AIs battlefield by what I can target CardList targetables = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer()); targetables = targetables.getValidCards(tgt.getValidTgts(), AllZone.getComputerPlayer(), hostCard); if (targetables.size() == 0) return false; CardList combatants = targetables.getType("Creature"); CardListUtil.sortByEvaluateCreature(combatants); for (Card c : combatants) { if (CombatUtil.combatantWouldBeDestroyed(c)) { tgt.addTarget(c); chance = true; break; } } } } Ability_Sub subAb = sa.getSubAbility(); if (subAb != null) chance &= subAb.chkAI_Drawback(); return chance; } /** * <p>doPreventDamageTriggerAI.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @param sa a {@link forge.card.spellability.SpellAbility} object. * @param mandatory a boolean. * @return a boolean. */ private static boolean doPreventDamageTriggerAI(final AbilityFactory af, final SpellAbility sa, boolean mandatory) { boolean chance = false; if (!ComputerUtil.canPayCost(sa)) return false; Target tgt = sa.getTarget(); if (tgt == null) { // If there's no target on the trigger, just say yes. chance = true; } else { chance = preventDamageMandatoryTarget(af, sa, mandatory); } Ability_Sub subAb = sa.getSubAbility(); if (subAb != null) chance &= subAb.doTrigger(mandatory); return chance; } /** * <p>preventDamageMandatoryTarget.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @param sa a {@link forge.card.spellability.SpellAbility} object. * @param mandatory a boolean. * @return a boolean. */ private static boolean preventDamageMandatoryTarget(AbilityFactory af, SpellAbility sa, boolean mandatory) { final Card hostCard = af.getHostCard(); Target tgt = sa.getTarget(); tgt.resetTargets(); // filter AIs battlefield by what I can target CardList targetables = AllZoneUtil.getCardsInPlay(); targetables = targetables.getValidCards(tgt.getValidTgts(), AllZone.getComputerPlayer(), hostCard); CardList compTargetables = targetables.getController(AllZone.getComputerPlayer()); if (targetables.size() == 0) return false; if (!mandatory && compTargetables.size() == 0) return false; if (compTargetables.size() > 0) { CardList combatants = compTargetables.getType("Creature"); CardListUtil.sortByEvaluateCreature(combatants); if (AllZone.getPhase().is(Constant.Phase.Combat_Declare_Blockers_InstantAbility)) { for (Card c : combatants) { if (CombatUtil.combatantWouldBeDestroyed(c)) { tgt.addTarget(c); return true; } } } // TODO see if something on the stack is about to kill something i can target tgt.addTarget(combatants.get(0)); return true; } tgt.addTarget(CardFactoryUtil.AI_getCheapestPermanent(targetables, hostCard, true)); return true; } /** * <p>preventDamageResolve.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @param sa a {@link forge.card.spellability.SpellAbility} object. */ private static void preventDamageResolve(final AbilityFactory af, final SpellAbility sa) { final HashMap<String, String> params = af.getMapParams(); int numDam = AbilityFactory.calculateAmount(af.getHostCard(), params.get("Amount"), sa); ArrayList<Object> tgts; if (sa.getTarget() == null) tgts = AbilityFactory.getDefinedObjects(sa.getSourceCard(), params.get("Defined"), sa); else tgts = sa.getTarget().getTargets(); boolean targeted = (af.getAbTgt() != null); for (Object o : tgts) { if (o instanceof Card) { Card c = (Card) o; if (AllZoneUtil.isCardInPlay(c) && (!targeted || CardFactoryUtil.canTarget(af.getHostCard(), c))) { c.addPreventNextDamage(numDam); } } else if (o instanceof Player) { Player p = (Player) o; if (!targeted || p.canTarget(af.getHostCard())) { p.addPreventNextDamage(numDam); } } } }//doResolve }