package forge.card.abilityFactory;
import forge.*;
import forge.card.cardFactory.CardFactoryUtil;
import forge.card.spellability.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Random;
/**
* <p>AbilityFactory_Destroy class.</p>
*
* @author Forge
* @version $Id: $
*/
public class AbilityFactory_Destroy {
// An AbilityFactory subclass for destroying permanents
// *********************************************************************************
// ************************** DESTROY **********************************************
// *********************************************************************************
/**
* <p>createAbilityDestroy.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createAbilityDestroy(final AbilityFactory af) {
final SpellAbility abDestroy = new Ability_Activated(af.getHostCard(), af.getAbCost(), af.getAbTgt()) {
private static final long serialVersionUID = -4153613567150919283L;
@Override
public String getStackDescription() {
return destroyStackDescription(af, this);
}
@Override
public boolean canPlayAI() {
return destroyCanPlayAI(af, this);
}
@Override
public void resolve() {
destroyResolve(af, this);
}
@Override
public boolean doTrigger(boolean mandatory) {
return destroyDoTriggerAI(af, this, mandatory);
}
};
return abDestroy;
}
/**
* <p>createSpellDestroy.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createSpellDestroy(final AbilityFactory af) {
final SpellAbility spDestroy = new Spell(af.getHostCard(), af.getAbCost(), af.getAbTgt()) {
private static final long serialVersionUID = -317810567632846523L;
@Override
public String getStackDescription() {
return destroyStackDescription(af, this);
}
@Override
public boolean canPlayAI() {
return destroyCanPlayAI(af, this);
}
@Override
public void resolve() {
destroyResolve(af, this);
}
};
return spDestroy;
}
/**
* <p>createDrawbackDestroy.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.Ability_Sub} object.
*/
public static Ability_Sub createDrawbackDestroy(final AbilityFactory af) {
final Ability_Sub dbDestroy = new Ability_Sub(af.getHostCard(), af.getAbTgt()) {
private static final long serialVersionUID = -4153613567150919283L;
@Override
public String getStackDescription() {
return destroyStackDescription(af, this);
}
@Override
public boolean chkAI_Drawback() {
return false;
}
@Override
public void resolve() {
destroyResolve(af, this);
}
@Override
public boolean doTrigger(boolean mandatory) {
return destroyDoTriggerAI(af, this, mandatory);
}
};
return dbDestroy;
}
/**
* <p>destroyCanPlayAI.</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 destroyCanPlayAI(final AbilityFactory af, final SpellAbility sa) {
// AI needs to be expanded, since this function can be pretty complex based on what the expected targets could be
Random r = MyRandom.random;
Cost abCost = sa.getPayCosts();
Target abTgt = sa.getTarget();
final Card source = sa.getSourceCard();
HashMap<String, String> params = af.getMapParams();
final boolean noRegen = params.containsKey("NoRegen");
CardList list;
list = AllZoneUtil.getPlayerCardsInPlay(AllZone.getHumanPlayer());
list = list.getTargetableCards(source);
if (abTgt != null) {
list = list.getValidCards(abTgt.getValidTgts(), source.getController(), source);
list = list.getNotKeyword("Indestructible");
// If NoRegen is not set, filter out creatures that have a regeneration shield
if (!noRegen) {
// TODO: filter out things that could regenerate in response? might be tougher?
list = list.filter(new CardListFilter() {
public boolean addCard(Card c) {
return (c.getShield() == 0 && !ComputerUtil.canRegenerate(c));
}
});
}
if (list.size() == 0)
return false;
}
if (abCost != null) {
// AI currently disabled for some costs
if (abCost.getSacCost() && !abCost.getSacThis()) {
//only sacrifice something that's supposed to be sacrificed
String sacType = abCost.getSacType();
CardList typeList = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer());
typeList = typeList.getValidCards(sacType.split(","), source.getController(), source);
if (ComputerUtil.getCardPreference(source, "SacCost", typeList) == null)
return false;
}
if (abCost.getLifeCost()) {
if (AllZone.getComputerPlayer().getLife() - abCost.getLifeAmount() < 4)
return false;
}
if (abCost.getDiscardCost()) return false;
if (abCost.getSubCounter()) {
// OK
}
}
if (!ComputerUtil.canPayCost(sa))
return false;
// prevent run-away activations - first time will always return true
boolean chance = r.nextFloat() <= Math.pow(.6667, source.getAbilityUsed());
// Targeting
if (abTgt != null) {
abTgt.resetTargets();
// target loop
while (abTgt.getNumTargeted() < abTgt.getMaxTargets(sa.getSourceCard(), sa)) {
if (list.size() == 0) {
if (abTgt.getNumTargeted() < abTgt.getMinTargets(sa.getSourceCard(), sa) || abTgt.getNumTargeted() == 0) {
abTgt.resetTargets();
return false;
} else {
// TODO is this good enough? for up to amounts?
break;
}
}
Card choice = null;
if (list.getNotType("Creature").size() == 0)
choice = CardFactoryUtil.AI_getBestCreature(list); //if the targets are only creatures, take the best
else
choice = CardFactoryUtil.AI_getMostExpensivePermanent(list, af.getHostCard(), true);
if (choice == null) { // can't find anything left
if (abTgt.getNumTargeted() < abTgt.getMinTargets(sa.getSourceCard(), sa) || abTgt.getNumTargeted() == 0) {
abTgt.resetTargets();
return false;
} else {
// TODO is this good enough? for up to amounts?
break;
}
}
list.remove(choice);
abTgt.addTarget(choice);
}
} else {
return false;
}
Ability_Sub subAb = sa.getSubAbility();
if (subAb != null)
chance &= subAb.chkAI_Drawback();
return ((r.nextFloat() < .6667) && chance);
}
/**
* <p>destroyDoTriggerAI.</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 destroyDoTriggerAI(final AbilityFactory af, SpellAbility sa, boolean mandatory) {
if (!ComputerUtil.canPayCost(sa))
return false;
Target tgt = sa.getTarget();
final Card source = sa.getSourceCard();
HashMap<String, String> params = af.getMapParams();
final boolean noRegen = params.containsKey("NoRegen");
if (tgt != null) {
CardList list;
list = AllZoneUtil.getCardsInPlay();
list = list.getTargetableCards(source);
list = list.getValidCards(tgt.getValidTgts(), source.getController(), source);
if (list.size() == 0 || list.size() < tgt.getMinTargets(sa.getSourceCard(), sa))
return false;
tgt.resetTargets();
CardList preferred = list.getNotKeyword("Indestructible");
preferred = list.getController(AllZone.getHumanPlayer());
// If NoRegen is not set, filter out creatures that have a regeneration shield
if (!noRegen) {
// TODO: filter out things that could regenerate in response? might be tougher?
preferred = preferred.filter(new CardListFilter() {
public boolean addCard(Card c) {
return c.getShield() == 0;
}
});
}
for (Card c : preferred)
list.remove(c);
while (tgt.getNumTargeted() < tgt.getMaxTargets(sa.getSourceCard(), sa)) {
if (preferred.size() == 0) {
if (tgt.getNumTargeted() == 0 || tgt.getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa)) {
if (!mandatory) {
tgt.resetTargets();
return false;
} else
break;
} else {
break;
}
} else {
Card c;
if (preferred.getNotType("Creature").size() == 0) {
c = CardFactoryUtil.AI_getBestCreature(preferred);
} else if (preferred.getNotType("Land").size() == 0) {
c = CardFactoryUtil.AI_getBestLand(preferred);
} else {
c = CardFactoryUtil.AI_getMostExpensivePermanent(preferred, source, false);
}
tgt.addTarget(c);
preferred.remove(c);
}
}
while (tgt.getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa)) {
if (list.size() == 0) {
break;
} else {
Card c;
if (list.getNotType("Creature").size() == 0) {
c = CardFactoryUtil.AI_getWorstCreature(list);
} else {
c = CardFactoryUtil.AI_getCheapestPermanent(list, source, false);
}
tgt.addTarget(c);
list.remove(c);
}
}
if (tgt.getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa))
return false;
} else {
if (!mandatory)
return false;
}
Ability_Sub subAb = sa.getSubAbility();
if (subAb != null)
return subAb.doTrigger(mandatory);
return true;
}
/**
* <p>destroyStackDescription.</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 destroyStackDescription(final AbilityFactory af, SpellAbility sa) {
HashMap<String, String> params = af.getMapParams();
final boolean noRegen = params.containsKey("NoRegen");
StringBuilder sb = new StringBuilder();
Card host = af.getHostCard();
String conditionDesc = params.get("ConditionDescription");
if (conditionDesc != null)
sb.append(conditionDesc).append(" ");
ArrayList<Card> tgtCards;
Target tgt = af.getAbTgt();
if (tgt != null)
tgtCards = tgt.getTargetCards();
else {
tgtCards = AbilityFactory.getDefinedCards(sa.getSourceCard(), params.get("Defined"), sa);
}
if (sa instanceof Ability_Sub)
sb.append(" ");
else
sb.append(host).append(" - ");
sb.append("Destroy ");
Iterator<Card> it = tgtCards.iterator();
while (it.hasNext()) {
Card tgtC = it.next();
if (tgtC.isFaceDown()) sb.append("Morph ").append("(").append(tgtC.getUniqueNumber()).append(")");
else sb.append(tgtC);
if (it.hasNext()) sb.append(", ");
}
if (noRegen) {
sb.append(". ");
if (tgtCards.size() == 1)
sb.append("It");
else
sb.append("They");
sb.append(" can't be regenerated");
}
sb.append(".");
Ability_Sub abSub = sa.getSubAbility();
if (abSub != null) {
sb.append(abSub.getStackDescription());
}
return sb.toString();
}
/**
* <p>destroyResolve.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
*/
private static void destroyResolve(final AbilityFactory af, final SpellAbility sa) {
HashMap<String, String> params = af.getMapParams();
final boolean noRegen = params.containsKey("NoRegen");
Card card = sa.getSourceCard();
ArrayList<Card> tgtCards;
Target tgt = af.getAbTgt();
if (tgt != null)
tgtCards = tgt.getTargetCards();
else {
tgtCards = AbilityFactory.getDefinedCards(sa.getSourceCard(), params.get("Defined"), sa);
}
for (Card tgtC : tgtCards) {
if (AllZoneUtil.isCardInPlay(tgtC) && (tgt == null || CardFactoryUtil.canTarget(card, tgtC))) {
if (noRegen)
AllZone.getGameAction().destroyNoRegeneration(tgtC);
else
AllZone.getGameAction().destroy(tgtC);
}
}
}
// *********************************************************************************
// ************************ DESTROY ALL ********************************************
// *********************************************************************************
/**
* <p>createAbilityDestroyAll.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createAbilityDestroyAll(final AbilityFactory af) {
final SpellAbility abDestroyAll = new Ability_Activated(af.getHostCard(), af.getAbCost(), af.getAbTgt()) {
private static final long serialVersionUID = -1376444173137861437L;
final HashMap<String, String> params = af.getMapParams();
final boolean noRegen = params.containsKey("NoRegen");
@Override
public String getStackDescription() {
return destroyAllStackDescription(af, this, noRegen);
}
@Override
public boolean canPlayAI() {
return destroyAllCanPlayAI(af, this, noRegen);
}
@Override
public void resolve() {
destroyAllResolve(af, this, noRegen);
}
@Override
public boolean doTrigger(boolean mandatory) {
return destroyAllCanPlayAI(af, this, noRegen);
}
};
return abDestroyAll;
}
/**
* <p>createSpellDestroyAll.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createSpellDestroyAll(final AbilityFactory af) {
final SpellAbility spDestroyAll = new Spell(af.getHostCard(), af.getAbCost(), af.getAbTgt()) {
private static final long serialVersionUID = -3712659336576469102L;
final HashMap<String, String> params = af.getMapParams();
final boolean noRegen = params.containsKey("NoRegen");
@Override
public String getStackDescription() {
if (params.containsKey("SpellDescription"))
return af.getHostCard().getName() + " - " + params.get("SpellDescription");
else
return destroyAllStackDescription(af, this, noRegen);
}
@Override
public boolean canPlayAI() {
return destroyAllCanPlayAI(af, this, noRegen);
}
@Override
public void resolve() {
destroyAllResolve(af, this, noRegen);
}
};
return spDestroyAll;
}
/**
* <p>createDrawbackDestroyAll.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createDrawbackDestroyAll(final AbilityFactory af) {
final SpellAbility dbDestroyAll = new Ability_Sub(af.getHostCard(), af.getAbTgt()) {
private static final long serialVersionUID = -242160421677518351L;
final HashMap<String, String> params = af.getMapParams();
final boolean noRegen = params.containsKey("NoRegen");
@Override
public String getStackDescription() {
if (params.containsKey("SpellDescription"))
return af.getHostCard().getName() + " - " + params.get("SpellDescription");
else
return destroyAllStackDescription(af, this, noRegen);
}
@Override
public void resolve() {
destroyAllResolve(af, this, noRegen);
}
@Override
public boolean chkAI_Drawback() {
return true;
}
@Override
public boolean doTrigger(boolean mandatory) {
// TODO Auto-generated method stub
return false;
}
};
return dbDestroyAll;
}
/**
* <p>destroyAllStackDescription.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @param noRegen a boolean.
* @return a {@link java.lang.String} object.
*/
private static String destroyAllStackDescription(final AbilityFactory af, SpellAbility sa, boolean noRegen) {
StringBuilder sb = new StringBuilder();
String name = af.getHostCard().getName();
HashMap<String, String> params = af.getMapParams();
String conditionDesc = params.get("ConditionDescription");
if (conditionDesc != null)
sb.append(conditionDesc).append(" ");
ArrayList<Card> tgtCards;
Target tgt = af.getAbTgt();
if (tgt != null)
tgtCards = tgt.getTargetCards();
else {
tgtCards = new ArrayList<Card>();
tgtCards.add(sa.getSourceCard());
}
sb.append(name).append(" - Destroy permanents.");
if (noRegen) sb.append(" They can't be regenerated");
Ability_Sub abSub = sa.getSubAbility();
if (abSub != null) {
sb.append(abSub.getStackDescription());
}
return sb.toString();
}
/**
* <p>destroyAllCanPlayAI.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @param noRegen a boolean.
* @return a boolean.
*/
private static boolean destroyAllCanPlayAI(final AbilityFactory af, final SpellAbility sa, final boolean noRegen) {
// AI needs to be expanded, since this function can be pretty complex based on what the expected targets could be
Random r = MyRandom.random;
Cost abCost = sa.getPayCosts();
final Card source = sa.getSourceCard();
final HashMap<String, String> params = af.getMapParams();
String Valid = "";
if (params.containsKey("ValidCards"))
Valid = params.get("ValidCards");
if (Valid.contains("X") && source.getSVar("X").equals("Count$xPaid")) {
// Set PayX here to maximum value.
int xPay = ComputerUtil.determineLeftoverMana(sa);
source.setSVar("PayX", Integer.toString(xPay));
Valid = Valid.replace("X", Integer.toString(xPay));
}
CardList humanlist = AllZoneUtil.getPlayerCardsInPlay(AllZone.getHumanPlayer());
CardList computerlist = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer());
humanlist = humanlist.getValidCards(Valid.split(","), source.getController(), source);
computerlist = computerlist.getValidCards(Valid.split(","), source.getController(), source);
humanlist = humanlist.getNotKeyword("Indestructible");
computerlist = computerlist.getNotKeyword("Indestructible");
if (abCost != null) {
// AI currently disabled for some costs
if (abCost.getSacCost()) {
//OK
}
if (abCost.getLifeCost()) {
if (AllZone.getComputerPlayer().getLife() - abCost.getLifeAmount() < 4)
return false;
}
if (abCost.getDiscardCost()) ;//OK
if (abCost.getSubCounter()) {
// OK
}
}
if (!ComputerUtil.canPayCost(sa))
return false;
// prevent run-away activations - first time will always return true
boolean chance = r.nextFloat() <= Math.pow(.6667, source.getAbilityUsed());
// if only creatures are affected evaluate both lists and pass only if human creatures are more valuable
if (humanlist.getNotType("Creature").size() == 0 && computerlist.getNotType("Creature").size() == 0) {
if (CardFactoryUtil.evaluateCreatureList(computerlist) + 200 >= CardFactoryUtil.evaluateCreatureList(humanlist))
return false;
}//only lands involved
else if (humanlist.getNotType("Land").size() == 0 && computerlist.getNotType("Land").size() == 0) {
if (CardFactoryUtil.evaluatePermanentList(computerlist) + 1 >= CardFactoryUtil.evaluatePermanentList(humanlist))
return false;
} // otherwise evaluate both lists by CMC and pass only if human permanents are more valuable
else if (CardFactoryUtil.evaluatePermanentList(computerlist) + 3 >= CardFactoryUtil.evaluatePermanentList(humanlist))
return false;
Ability_Sub subAb = sa.getSubAbility();
if (subAb != null)
chance &= subAb.chkAI_Drawback();
return ((r.nextFloat() < .9667) && chance);
}
/**
* <p>destroyAllResolve.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @param noRegen a boolean.
*/
private static void destroyAllResolve(final AbilityFactory af, final SpellAbility sa, final boolean noRegen) {
HashMap<String, String> params = af.getMapParams();
Card card = sa.getSourceCard();
String Valid = "";
if (params.containsKey("ValidCards"))
Valid = params.get("ValidCards");
// Ugh. If calculateAmount needs to be called with DestroyAll it _needs_ to use the X variable
// We really need a better solution to this
if (Valid.contains("X"))
Valid = Valid.replace("X", Integer.toString(AbilityFactory.calculateAmount(card, "X", sa)));
CardList list = AllZoneUtil.getCardsInPlay();
list = AbilityFactory.filterListByType(list, Valid, sa);
boolean remDestroyed = params.containsKey("RememberDestroyed");
if (remDestroyed)
card.clearRemembered();
if (noRegen) {
for (int i = 0; i < list.size(); i++)
if (AllZone.getGameAction().destroyNoRegeneration(list.get(i)) && remDestroyed)
card.addRemembered(list.get(i));
} else {
for (int i = 0; i < list.size(); i++)
if (AllZone.getGameAction().destroy(list.get(i)) && remDestroyed)
card.addRemembered(list.get(i));
}
}
}//end class AbilityFactory_Destroy