package forge.card.abilityFactory;
import forge.*;
import forge.card.cardFactory.CardFactoryUtil;
import forge.card.spellability.*;
import java.util.*;
/**
* <p>AbilityFactory_Debuff class.</p>
*
* @author Forge
* @version $Id: $
*/
public class AbilityFactory_Debuff {
// *************************************************************************
// ***************************** Debuff ************************************
// *************************************************************************
/**
* <p>createAbilityDebuff.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createAbilityDebuff(final AbilityFactory af) {
final SpellAbility abDebuff = new Ability_Activated(af.getHostCard(), af.getAbCost(), af.getAbTgt()) {
private static final long serialVersionUID = 3536198601841771383L;
@Override
public String getStackDescription() {
return debuffStackDescription(af, this);
}
@Override
public boolean canPlayAI() {
return debuffCanPlayAI(af, this);
}
@Override
public void resolve() {
debuffResolve(af, this);
}
@Override
public boolean doTrigger(boolean mandatory) {
return debuffTriggerAI(af, this, mandatory);
}
};
return abDebuff;
}
/**
* <p>createSpellDebuff.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createSpellDebuff(final AbilityFactory af) {
final SpellAbility spDebuff = new Spell(af.getHostCard(), af.getAbCost(), af.getAbTgt()) {
private static final long serialVersionUID = -54573740774322697L;
@Override
public String getStackDescription() {
return debuffStackDescription(af, this);
}
@Override
public boolean canPlayAI() {
return debuffCanPlayAI(af, this);
}
@Override
public void resolve() {
debuffResolve(af, this);
}
};
return spDebuff;
}
/**
* <p>createDrawbackDebuff.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createDrawbackDebuff(final AbilityFactory af) {
final SpellAbility dbDebuff = new Ability_Sub(af.getHostCard(), af.getAbTgt()) {
private static final long serialVersionUID = -4728590185604233229L;
@Override
public String getStackDescription() {
return debuffStackDescription(af, this);
}
@Override
public void resolve() {
debuffResolve(af, this);
}
@Override
public boolean chkAI_Drawback() {
return debuffDrawbackAI(af, this);
}
@Override
public boolean doTrigger(boolean mandatory) {
return debuffTriggerAI(af, this, mandatory);
}
};
return dbDebuff;
}
/**
* <p>getKeywords.</p>
*
* @param params a {@link java.util.HashMap} object.
* @return a {@link java.util.ArrayList} object.
*/
private static ArrayList<String> getKeywords(HashMap<String, String> params) {
ArrayList<String> kws = new ArrayList<String>();
if (params.containsKey("Keywords")) {
kws.addAll(Arrays.asList(params.get("Keywords").split(" & ")));
}
return kws;
}
/**
* <p>debuffStackDescription.</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 debuffStackDescription(AbilityFactory af, SpellAbility sa) {
HashMap<String, String> params = af.getMapParams();
Card host = af.getHostCard();
ArrayList<String> kws = getKeywords(params);
StringBuilder sb = new StringBuilder();
ArrayList<Card> tgtCards;
Target tgt = af.getAbTgt();
if (tgt != null)
tgtCards = tgt.getTargetCards();
else
tgtCards = AbilityFactory.getDefinedCards(sa.getSourceCard(), params.get("Defined"), sa);
if (tgtCards.size() > 0) {
if (sa instanceof Ability_Sub)
sb.append(" ");
else
sb.append(host).append(" - ");
Iterator<Card> it = tgtCards.iterator();
while (it.hasNext()) {
Card tgtC = it.next();
if (tgtC.isFaceDown()) sb.append("Morph");
else sb.append(tgtC);
if (it.hasNext()) sb.append(" ");
}
sb.append(" loses ");
/*
Iterator<String> kwit = kws.iterator();
while(it.hasNext()) {
String kw = kwit.next();
sb.append(kw);
if(it.hasNext()) sb.append(" ");
}*/
sb.append(kws);
if (!params.containsKey("Permanent")) {
sb.append(" until end of turn");
}
sb.append(".");
}
Ability_Sub abSub = sa.getSubAbility();
if (abSub != null) {
sb.append(abSub.getStackDescription());
}
return sb.toString();
}
/**
* <p>debuffCanPlayAI.</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 debuffCanPlayAI(final AbilityFactory af, final SpellAbility sa) {
// if there is no target and host card isn't in play, don't activate
if (af.getAbTgt() == null && !AllZoneUtil.isCardInPlay(af.getHostCard()))
return false;
// temporarily disabled until AI is improved
if (af.getAbCost().getSacCost() && sa.getSourceCard().isCreature()) return false;
if (af.getAbCost().getLifeCost()) {
return false;
}
if (af.getAbCost().getSubCounter()) {
// instead of never removing counters, we will have a random possibility of failure.
// all the other tests still need to pass if a counter will be removed
Counters count = af.getAbCost().getCounterType();
double chance = .66;
if (count.equals(Counters.P1P1)) { // 10% chance to remove +1/+1 to pump
chance = .1;
} else if (count.equals(Counters.CHARGE)) { // 50% chance to remove charge to pump
chance = .5;
}
Random r = MyRandom.random;
if (r.nextFloat() > chance)
return false;
}
if (!ComputerUtil.canPayCost(sa))
return false;
HashMap<String, String> params = af.getMapParams();
SpellAbility_Restriction restrict = sa.getRestrictions();
// Phase Restrictions
if (AllZone.getStack().size() == 0 && AllZone.getPhase().isBefore(Constant.Phase.Combat_Begin)) {
// Instant-speed pumps should not be cast outside of combat when the stack is empty
if (!AbilityFactory.isSorcerySpeed(sa))
return false;
}
int activations = restrict.getNumberTurnActivations();
int sacActivations = restrict.getActivationNumberSacrifice();
//don't risk sacrificing a creature just to pump it
if (sacActivations != -1 && activations >= (sacActivations - 1)) {
return false;
}
if (af.getAbTgt() == null || !af.getAbTgt().doesTarget()) {
ArrayList<Card> cards = AbilityFactory.getDefinedCards(sa.getSourceCard(), params.get("Defined"), sa);
if (cards.size() == 0)
return false;
} else
return debuffTgtAI(af, sa, getKeywords(params), false);
return false;
}
/**
* <p>debuffDrawbackAI.</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 debuffDrawbackAI(AbilityFactory af, SpellAbility sa) {
HashMap<String, String> params = af.getMapParams();
if (af.getAbTgt() == null || !af.getAbTgt().doesTarget()) {
//TODO - copied from AF_Pump.pumpDrawbackAI() - what should be here?
} else
return debuffTgtAI(af, sa, getKeywords(params), false);
return true;
}//debuffDrawbackAI()
/**
* <p>debuffTgtAI.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @param kws a {@link java.util.ArrayList} object.
* @param mandatory a boolean.
* @return a boolean.
*/
private static boolean debuffTgtAI(AbilityFactory af, SpellAbility sa, ArrayList<String> kws, boolean mandatory) {
//this would be for evasive things like Flying, Unblockable, etc
if (!mandatory && AllZone.getPhase().isAfter(Constant.Phase.Combat_Declare_Blockers_InstantAbility))
return false;
Target tgt = af.getAbTgt();
tgt.resetTargets();
CardList list = getCurseCreatures(af, sa, kws);
list = list.getValidCards(tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getSourceCard());
//several uses here:
//1. make human creatures lose evasion when they are attacking
//2. make human creatures lose Flying/Horsemanship/Shadow/etc. when Comp is attacking
//3. remove Indestructible keyword so it can be destroyed?
//3a. remove Persist?
if (list.isEmpty())
return mandatory && debuffMandatoryTarget(af, sa, mandatory);
while (tgt.getNumTargeted() < tgt.getMaxTargets(sa.getSourceCard(), sa)) {
Card t = null;
//boolean goodt = false;
if (list.isEmpty()) {
if (tgt.getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa) || tgt.getNumTargeted() == 0) {
if (mandatory)
return debuffMandatoryTarget(af, sa, mandatory);
tgt.resetTargets();
return false;
} else {
// TODO is this good enough? for up to amounts?
break;
}
}
t = CardFactoryUtil.AI_getBestCreature(list);
tgt.addTarget(t);
list.remove(t);
}
return true;
}//pumpTgtAI()
/**
* <p>getCurseCreatures.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @param kws a {@link java.util.ArrayList} object.
* @return a {@link forge.CardList} object.
*/
private static CardList getCurseCreatures(AbilityFactory af, SpellAbility sa, final ArrayList<String> kws) {
Card hostCard = af.getHostCard();
CardList list = AllZoneUtil.getCreaturesInPlay(AllZone.getHumanPlayer());
list = list.filter(AllZoneUtil.getCanTargetFilter(hostCard));
if (!list.isEmpty()) {
list = list.filter(new CardListFilter() {
public boolean addCard(Card c) {
return c.hasAnyKeyword(kws); // don't add duplicate negative keywords
}
});
}
return list;
}//getCurseCreatures()
/**
* <p>debuffMandatoryTarget.</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 debuffMandatoryTarget(AbilityFactory af, SpellAbility sa, boolean mandatory) {
CardList list = AllZoneUtil.getCardsInPlay();
Target tgt = sa.getTarget();
list = list.getValidCards(tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getSourceCard());
if (list.size() < tgt.getMinTargets(sa.getSourceCard(), sa)) {
tgt.resetTargets();
return false;
}
// Remove anything that's already been targeted
for (Card c : tgt.getTargetCards())
list.remove(c);
CardList pref = list.getController(AllZone.getHumanPlayer());
CardList forced = list.getController(AllZone.getComputerPlayer());
Card source = sa.getSourceCard();
while (tgt.getNumTargeted() < tgt.getMaxTargets(source, sa)) {
if (pref.isEmpty())
break;
Card c;
if (pref.getNotType("Creature").size() == 0)
c = CardFactoryUtil.AI_getBestCreature(pref);
else
c = CardFactoryUtil.AI_getMostExpensivePermanent(pref, source, true);
pref.remove(c);
tgt.addTarget(c);
}
while (tgt.getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa)) {
if (forced.isEmpty())
break;
//TODO - if forced targeting, just pick something without the given keyword
Card c;
if (forced.getNotType("Creature").size() == 0)
c = CardFactoryUtil.AI_getWorstCreature(forced);
else
c = CardFactoryUtil.AI_getCheapestPermanent(forced, source, true);
forced.remove(c);
tgt.addTarget(c);
}
if (tgt.getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa)) {
tgt.resetTargets();
return false;
}
return true;
}//pumpMandatoryTarget()
/**
* <p>debuffTriggerAI.</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 debuffTriggerAI(final AbilityFactory af, final SpellAbility sa, boolean mandatory) {
if (!ComputerUtil.canPayCost(sa))
return false;
HashMap<String, String> params = af.getMapParams();
ArrayList<String> kws = getKeywords(params);
if (sa.getTarget() == null) {
if (mandatory)
return true;
} else {
return debuffTgtAI(af, sa, kws, mandatory);
}
return true;
}
/**
* <p>debuffResolve.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
*/
private static void debuffResolve(final AbilityFactory af, final SpellAbility sa) {
HashMap<String, String> params = af.getMapParams();
Card host = af.getHostCard();
ArrayList<String> kws = getKeywords(params);
ArrayList<Card> tgtCards;
Target tgt = af.getAbTgt();
if (tgt != null)
tgtCards = tgt.getTargetCards();
else
tgtCards = AbilityFactory.getDefinedCards(host, params.get("Defined"), sa);
for (final Card tgtC : tgtCards) {
final ArrayList<String> hadIntrinsic = new ArrayList<String>();
if (AllZoneUtil.isCardInPlay(tgtC) && CardFactoryUtil.canTarget(host, tgtC)) {
for (String kw : kws) {
if (tgtC.getIntrinsicKeyword().contains(kw)) hadIntrinsic.add(kw);
tgtC.removeIntrinsicKeyword(kw);
tgtC.removeExtrinsicKeyword(kw);
}
}
if (!params.containsKey("Permanent")) {
AllZone.getEndOfTurn().addUntil(new Command() {
private static final long serialVersionUID = 5387486776282932314L;
public void execute() {
if (AllZoneUtil.isCardInPlay(tgtC)) {
for (String kw : hadIntrinsic) {
tgtC.addIntrinsicKeyword(kw);
}
}
}
});
}
}
}//debuffResolve
// *************************************************************************
// ***************************** DebuffAll *********************************
// *************************************************************************
/**
* <p>createAbilityDebuffAll.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
* @since 1.0.15
*/
public static SpellAbility createAbilityDebuffAll(final AbilityFactory af) {
final SpellAbility abDebuffAll = new Ability_Activated(af.getHostCard(), af.getAbCost(), af.getAbTgt()) {
private static final long serialVersionUID = -1977027530713097149L;
@Override
public boolean canPlayAI() {
return debuffAllCanPlayAI(af, this);
}
@Override
public String getStackDescription() {
return debuffAllStackDescription(af, this);
}
@Override
public void resolve() {
debuffAllResolve(af, this);
}
@Override
public boolean doTrigger(boolean mandatory) {
return debuffAllTriggerAI(af, this, mandatory);
}
};//SpellAbility
return abDebuffAll;
}
/**
* <p>createSpellDebuffAll.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
* @since 1.0.15
*/
public static SpellAbility createSpellDebuffAll(final AbilityFactory af) {
SpellAbility spDebuffAll = new Spell(af.getHostCard(), af.getAbCost(), af.getAbTgt()) {
private static final long serialVersionUID = 399707924254248213L;
@Override
public boolean canPlayAI() {
return debuffAllCanPlayAI(af, this);
}
@Override
public String getStackDescription() {
return debuffAllStackDescription(af, this);
}
@Override
public void resolve() {
debuffAllResolve(af, this);
}
};//SpellAbility
return spDebuffAll;
}
/**
* <p>createDrawbackDebuffAll.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
* @since 1.0.15
*/
public static SpellAbility createDrawbackDebuffAll(final AbilityFactory af) {
SpellAbility dbDebuffAll = new Ability_Sub(af.getHostCard(), af.getAbTgt()) {
private static final long serialVersionUID = 3262199296469706708L;
@Override
public String getStackDescription() {
return debuffAllStackDescription(af, this);
}
@Override
public void resolve() {
debuffAllResolve(af, this);
}
@Override
public boolean chkAI_Drawback() {
return debuffAllChkDrawbackAI(af, this);
}
@Override
public boolean doTrigger(boolean mandatory) {
return debuffAllTriggerAI(af, this, mandatory);
}
};//SpellAbility
return dbDebuffAll;
}
/**
* <p>debuffAllCanPlayAI.</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 debuffAllCanPlayAI(final AbilityFactory af, SpellAbility sa) {
String valid = "";
Random r = MyRandom.random;
final Card source = sa.getSourceCard();
Card hostCard = af.getHostCard();
HashMap<String, String> params = af.getMapParams();
boolean chance = r.nextFloat() <= Math.pow(.6667, source.getAbilityUsed()); //to prevent runaway activations
if (params.containsKey("ValidCards")) {
valid = params.get("ValidCards");
}
CardList comp = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer());
comp = comp.getValidCards(valid, hostCard.getController(), hostCard);
CardList human = AllZoneUtil.getPlayerCardsInPlay(AllZone.getHumanPlayer());
human = human.getValidCards(valid, hostCard.getController(), hostCard);
//TODO - add blocking situations here also
//only count creatures that can attack
human = human.filter(new CardListFilter() {
public boolean addCard(Card c) {
return CombatUtil.canAttack(c);
}
});
//don't use DebuffAll after Combat_Begin until AI is improved
if (AllZone.getPhase().isAfter(Constant.Phase.Combat_Begin))
return false;
if (comp.size() > human.size())
return false;
return (r.nextFloat() < .6667) && chance;
}//debuffAllCanPlayAI()
/**
* <p>debuffAllResolve.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
*/
private static void debuffAllResolve(AbilityFactory af, SpellAbility sa) {
HashMap<String, String> params = af.getMapParams();
Card hostCard = af.getHostCard();
ArrayList<String> kws = getKeywords(params);
String valid = "";
if (params.containsKey("ValidCards"))
valid = params.get("ValidCards");
CardList list = AllZoneUtil.getCardsInPlay();
list = list.getValidCards(valid.split(","), hostCard.getController(), hostCard);
for (final Card tgtC : list) {
final ArrayList<String> hadIntrinsic = new ArrayList<String>();
if (AllZoneUtil.isCardInPlay(tgtC) && CardFactoryUtil.canTarget(hostCard, tgtC)) {
for (String kw : kws) {
if (tgtC.getIntrinsicKeyword().contains(kw)) hadIntrinsic.add(kw);
tgtC.removeIntrinsicKeyword(kw);
tgtC.removeExtrinsicKeyword(kw);
}
}
if (!params.containsKey("Permanent")) {
AllZone.getEndOfTurn().addUntil(new Command() {
private static final long serialVersionUID = 7486231071095628674L;
public void execute() {
if (AllZoneUtil.isCardInPlay(tgtC)) {
for (String kw : hadIntrinsic) {
tgtC.addIntrinsicKeyword(kw);
}
}
}
});
}
}
}//debuffAllResolve()
/**
* <p>debuffAllTriggerAI.</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 debuffAllTriggerAI(AbilityFactory af, SpellAbility sa, boolean mandatory) {
if (!ComputerUtil.canPayCost(sa))
return false;
return true;
}
/**
* <p>debuffAllChkDrawbackAI.</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 debuffAllChkDrawbackAI(AbilityFactory af, SpellAbility sa) {
return true;
}
/**
* <p>debuffAllStackDescription.</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 debuffAllStackDescription(AbilityFactory af, SpellAbility sa) {
HashMap<String, String> params = af.getMapParams();
StringBuilder sb = new StringBuilder();
String desc = "";
if (params.containsKey("SpellDescription")) {
desc = params.get("SpellDescription");
} else if (params.containsKey("DebuffAllDescription")) {
desc = params.get("DebuffAllDescription");
}
if (sa instanceof Ability_Sub)
sb.append(" ");
else
sb.append(sa.getSourceCard()).append(" - ");
sb.append(desc);
Ability_Sub abSub = sa.getSubAbility();
if (abSub != null) {
sb.append(abSub.getStackDescription());
}
return sb.toString();
}//debuffAllStackDescription()
}//end class AbilityFactory_Debuff