package forge.card.abilityFactory;
import forge.*;
import forge.card.cardFactory.CardFactoryUtil;
import forge.card.spellability.*;
import forge.gui.GuiUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Random;
/**
* <p>AbilityFactory_PermanentState class.</p>
*
* @author Forge
* @version $Id: $
*/
public class AbilityFactory_PermanentState {
// ****************************************
// ************** Untap *******************
// ****************************************
/**
* <p>createAbilityUntap.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createAbilityUntap(final AbilityFactory af) {
final SpellAbility abUntap = new Ability_Activated(af.getHostCard(), af.getAbCost(), af.getAbTgt()) {
private static final long serialVersionUID = 5445572699000471299L;
@Override
public String getStackDescription() {
return untapStackDescription(af, this);
}
@Override
public boolean canPlayAI() {
return untapCanPlayAI(af, this);
}
@Override
public void resolve() {
untapResolve(af, this);
}
@Override
public boolean doTrigger(boolean mandatory) {
return untapTrigger(af, this, mandatory);
}
};
return abUntap;
}
/**
* <p>createSpellUntap.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createSpellUntap(final AbilityFactory af) {
final SpellAbility spUntap = new Spell(af.getHostCard(), af.getAbCost(), af.getAbTgt()) {
private static final long serialVersionUID = -4990932993654533449L;
@Override
public String getStackDescription() {
return untapStackDescription(af, this);
}
@Override
public boolean canPlayAI() {
return untapCanPlayAI(af, this);
}
@Override
public void resolve() {
untapResolve(af, this);
}
};
return spUntap;
}
/**
* <p>createDrawbackUntap.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createDrawbackUntap(final AbilityFactory af) {
final SpellAbility dbUntap = new Ability_Sub(af.getHostCard(), af.getAbTgt()) {
private static final long serialVersionUID = -4990932993654533449L;
@Override
public String getStackDescription() {
return untapStackDescription(af, this);
}
@Override
public void resolve() {
untapResolve(af, this);
}
@Override
public boolean chkAI_Drawback() {
return untapPlayDrawbackAI(af, this);
}
@Override
public boolean doTrigger(boolean mandatory) {
return untapTrigger(af, this, mandatory);
}
};
return dbUntap;
}
/**
* <p>untapStackDescription.</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 untapStackDescription(AbilityFactory af, SpellAbility sa) {
// when getStackDesc is called, just build exactly what is happening
StringBuilder sb = new StringBuilder();
final HashMap<String, String> params = af.getMapParams();
Card hostCard = sa.getSourceCard();
if (sa instanceof Ability_Sub)
sb.append(" ");
else
sb.append(sa.getSourceCard()).append(" - ");
sb.append("Untap ");
if (params.containsKey("UntapUpTo")) {
sb.append("up to ").append(params.get("Amount")).append(" ");
sb.append(params.get("UntapType")).append("s");
} else {
ArrayList<Card> tgtCards;
Target tgt = af.getAbTgt();
if (tgt != null)
tgtCards = tgt.getTargetCards();
else {
tgtCards = AbilityFactory.getDefinedCards(hostCard, params.get("Defined"), sa);
}
Iterator<Card> it = tgtCards.iterator();
while (it.hasNext()) {
sb.append(it.next());
if (it.hasNext())
sb.append(", ");
}
}
sb.append(".");
Ability_Sub subAb = sa.getSubAbility();
if (subAb != null)
sb.append(subAb.getStackDescription());
return sb.toString();
}
/**
* <p>untapCanPlayAI.</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 untapCanPlayAI(final AbilityFactory af, SpellAbility sa) {
// AI cannot use this properly until he can use SAs during Humans turn
if (!ComputerUtil.canPayCost(sa))
return false;
if (af.getAbCost().getAddCounter())
if (af.getAbCost().getCounterType().equals(Counters.M1M1))
return false;
Target tgt = af.getAbTgt();
Card source = sa.getSourceCard();
Random r = MyRandom.random;
boolean randomReturn = r.nextFloat() <= Math.pow(.6667, source.getAbilityUsed() + 1);
if (tgt == null) {
if (sa.getSourceCard().isUntapped())
return false;
} else {
if (!untapPrefTargeting(tgt, af, sa, false))
return false;
}
Ability_Sub subAb = sa.getSubAbility();
if (subAb != null)
randomReturn &= subAb.chkAI_Drawback();
return randomReturn;
}
/**
* <p>untapTrigger.</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 untapTrigger(AbilityFactory af, SpellAbility sa, boolean mandatory) {
HashMap<String, String> params = af.getMapParams();
if (!ComputerUtil.canPayCost(sa))
return false;
Target tgt = sa.getTarget();
if (tgt == null) {
if (mandatory)
return true;
// TODO: use Defined to determine, if this is an unfavorable result
ArrayList<Card> pDefined = AbilityFactory.getDefinedCards(sa.getSourceCard(), params.get("Defined"), sa);
if (pDefined != null && pDefined.get(0).isUntapped())
return false;
return true;
} else {
if (untapPrefTargeting(tgt, af, sa, mandatory)) {
return true;
} else if (mandatory) {
// not enough preferred targets, but mandatory so keep going:
return untapUnpreferredTargeting(af, sa, mandatory);
}
}
return false;
}
/**
* <p>untapPlayDrawbackAI.</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 untapPlayDrawbackAI(final AbilityFactory af, SpellAbility sa) {
// AI cannot use this properly until he can use SAs during Humans turn
Target tgt = af.getAbTgt();
boolean randomReturn = true;
if (tgt == null) {
// who cares if its already untapped, it's only a subability?
} else {
if (!untapPrefTargeting(tgt, af, sa, false))
return false;
}
Ability_Sub subAb = sa.getSubAbility();
if (subAb != null)
randomReturn &= subAb.chkAI_Drawback();
return randomReturn;
}
/**
* <p>untapPrefTargeting.</p>
*
* @param tgt a {@link forge.card.spellability.Target} object.
* @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 untapPrefTargeting(Target tgt, AbilityFactory af, SpellAbility sa, boolean mandatory) {
Card source = sa.getSourceCard();
Player targetController = AllZone.getComputerPlayer();
if(af.isCurse())
targetController = AllZone.getHumanPlayer();
CardList untapList = AllZoneUtil.getPlayerCardsInPlay(targetController);
untapList = untapList.getTargetableCards(source);
untapList = untapList.getValidCards(tgt.getValidTgts(), source.getController(), source);
untapList = untapList.filter(AllZoneUtil.tapped);
// filter out enchantments and planeswalkers, their tapped state doesn't matter.
String[] tappablePermanents = {"Creature", "Land", "Artifact"};
untapList = untapList.getValidCards(tappablePermanents, source.getController(), source);
if (untapList.size() == 0)
return false;
while (tgt.getNumTargeted() < tgt.getMaxTargets(sa.getSourceCard(), sa)) {
Card choice = null;
if (untapList.size() == 0) {
if (tgt.getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa) || tgt.getNumTargeted() == 0) {
tgt.resetTargets();
return false;
} else {
// TODO is this good enough? for up to amounts?
break;
}
}
if (untapList.getNotType("Creature").size() == 0)
choice = CardFactoryUtil.AI_getBestCreature(untapList); //if only creatures take the best
else
choice = CardFactoryUtil.AI_getMostExpensivePermanent(untapList, af.getHostCard(), false);
if (choice == null) { // can't find anything left
if (tgt.getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa) || tgt.getNumTargeted() == 0) {
tgt.resetTargets();
return false;
} else {
// TODO is this good enough? for up to amounts?
break;
}
}
untapList.remove(choice);
tgt.addTarget(choice);
}
return true;
}
/**
* <p>untapUnpreferredTargeting.</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 untapUnpreferredTargeting(AbilityFactory af, SpellAbility sa, boolean mandatory) {
Card source = sa.getSourceCard();
Target tgt = sa.getTarget();
CardList list = AllZoneUtil.getCardsInPlay();
list = list.getValidCards(tgt.getValidTgts(), source.getController(), source);
list = list.getTargetableCards(source);
// filter by enchantments and planeswalkers, their tapped state doesn't matter.
String[] tappablePermanents = {"Enchantment", "Planeswalker"};
CardList tapList = list.getValidCards(tappablePermanents, source.getController(), source);
if (untapTargetList(source, tgt, af, sa, mandatory, tapList))
return true;
// try to just tap already tapped things
tapList = list.filter(AllZoneUtil.untapped);
if (untapTargetList(source, tgt, af, sa, mandatory, tapList))
return true;
// just tap whatever we can
tapList = list;
if (untapTargetList(source, tgt, af, sa, mandatory, tapList))
return true;
return false;
}
/**
* <p>untapTargetList.</p>
*
* @param source a {@link forge.Card} object.
* @param tgt a {@link forge.card.spellability.Target} object.
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @param mandatory a boolean.
* @param tapList a {@link forge.CardList} object.
* @return a boolean.
*/
private static boolean untapTargetList(Card source, Target tgt, AbilityFactory af, SpellAbility sa, boolean mandatory, CardList tapList) {
for (Card c : tgt.getTargetCards())
tapList.remove(c);
if (tapList.size() == 0)
return false;
while (tgt.getNumTargeted() < tgt.getMaxTargets(source, sa)) {
Card choice = null;
if (tapList.size() == 0) {
if (tgt.getNumTargeted() < tgt.getMinTargets(source, sa) || tgt.getNumTargeted() == 0) {
if (!mandatory)
tgt.resetTargets();
return false;
} else {
// TODO is this good enough? for up to amounts?
break;
}
}
if (tapList.getNotType("Creature").size() == 0)
choice = CardFactoryUtil.AI_getBestCreature(tapList); //if only creatures take the best
else
choice = CardFactoryUtil.AI_getMostExpensivePermanent(tapList, af.getHostCard(), false);
if (choice == null) { // can't find anything left
if (tgt.getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa) || tgt.getNumTargeted() == 0) {
if (!mandatory)
tgt.resetTargets();
return false;
} else {
// TODO is this good enough? for up to amounts?
break;
}
}
tapList.remove(choice);
tgt.addTarget(choice);
}
return true;
}
/**
* <p>untapResolve.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
*/
private static void untapResolve(final AbilityFactory af, final SpellAbility sa) {
HashMap<String, String> params = af.getMapParams();
Card card = sa.getSourceCard();
Target tgt = af.getAbTgt();
ArrayList<Card> tgtCards = null;
if (params.containsKey("UntapUpTo"))
untapChooseUpTo(af, sa, params);
else {
if (tgt != null)
tgtCards = tgt.getTargetCards();
else {
tgtCards = AbilityFactory.getDefinedCards(card, params.get("Defined"), sa);
}
for (Card tgtC : tgtCards) {
if (AllZoneUtil.isCardInPlay(tgtC) && (tgt == null || CardFactoryUtil.canTarget(af.getHostCard(), tgtC)))
tgtC.untap();
}
}
}
/**
* <p>untapChooseUpTo.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @param params a {@link java.util.HashMap} object.
*/
private static void untapChooseUpTo(AbilityFactory af, SpellAbility sa, HashMap<String, String> params) {
int num = Integer.parseInt(params.get("Amount"));
String valid = params.get("UntapType");
Player activatingPlayer = sa.getActivatingPlayer();
// Reuse existing UntapUpTo Input
if (activatingPlayer.isHuman())
AllZone.getInputControl().setInput(CardFactoryUtil.input_UntapUpToNType(num, valid));
else {
CardList list = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer());
list = list.getType(valid);
list = list.filter(AllZoneUtil.tapped);
int count = 0;
while (list.size() != 0)
for (int i = 0; i < num && i < list.size(); i++) {
Card c = CardFactoryUtil.AI_getBestLand(list);
c.untap();
list.remove(c);
count++;
}
}
}
// ****************************************
// ************** Tap *********************
// ****************************************
/**
* <p>createAbilityTap.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createAbilityTap(final AbilityFactory af) {
final SpellAbility abTap = new Ability_Activated(af.getHostCard(), af.getAbCost(), af.getAbTgt()) {
private static final long serialVersionUID = 5445572699000471299L;
@Override
public String getStackDescription() {
return tapStackDescription(af, this);
}
@Override
public boolean canPlayAI() {
return tapCanPlayAI(af, this);
}
@Override
public void resolve() {
tapResolve(af, this);
}
@Override
public boolean doTrigger(boolean mandatory) {
return tapTrigger(af, this, mandatory);
}
};
return abTap;
}
/**
* <p>createSpellTap.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createSpellTap(final AbilityFactory af) {
final SpellAbility spTap = new Spell(af.getHostCard(), af.getAbCost(), af.getAbTgt()) {
private static final long serialVersionUID = -4990932993654533449L;
@Override
public String getStackDescription() {
return tapStackDescription(af, this);
}
@Override
public boolean canPlayAI() {
return tapCanPlayAI(af, this);
}
@Override
public void resolve() {
tapResolve(af, this);
}
};
return spTap;
}
/**
* <p>createDrawbackTap.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createDrawbackTap(final AbilityFactory af) {
final SpellAbility dbTap = new Ability_Sub(af.getHostCard(), af.getAbTgt()) {
private static final long serialVersionUID = -4990932993654533449L;
@Override
public String getStackDescription() {
return tapStackDescription(af, this);
}
@Override
public void resolve() {
tapResolve(af, this);
}
@Override
public boolean chkAI_Drawback() {
return tapPlayDrawbackAI(af, this);
}
@Override
public boolean doTrigger(boolean mandatory) {
return tapTrigger(af, this, mandatory);
}
};
return dbTap;
}
/**
* <p>tapStackDescription.</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 tapStackDescription(AbilityFactory af, SpellAbility sa) {
StringBuilder sb = new StringBuilder();
final HashMap<String, String> params = af.getMapParams();
Card hostCard = sa.getSourceCard();
if (sa instanceof Ability_Sub)
sb.append(" ");
else
sb.append(sa.getSourceCard()).append(" - ");
sb.append("Tap ");
ArrayList<Card> tgtCards;
Target tgt = af.getAbTgt();
if (tgt != null)
tgtCards = tgt.getTargetCards();
else {
tgtCards = AbilityFactory.getDefinedCards(hostCard, params.get("Defined"), sa);
}
Iterator<Card> it = tgtCards.iterator();
while (it.hasNext()) {
sb.append(it.next());
if (it.hasNext()) sb.append(", ");
}
sb.append(".");
Ability_Sub subAb = sa.getSubAbility();
if (subAb != null)
sb.append(subAb.getStackDescription());
return sb.toString();
}
/**
* <p>tapCanPlayAI.</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 tapCanPlayAI(final AbilityFactory af, SpellAbility sa) {
// AI cannot use this properly until he can use SAs during Humans turn
if (!ComputerUtil.canPayCost(sa))
return false;
HashMap<String, String> params = af.getMapParams();
Target tgt = af.getAbTgt();
Card source = sa.getSourceCard();
Random r = MyRandom.random;
boolean randomReturn = r.nextFloat() <= Math.pow(.6667, source.getAbilityUsed());
if (tgt == null) {
ArrayList<Card> defined = AbilityFactory.getDefinedCards(source, params.get("Defined"), sa);
boolean bFlag = false;
for (Card c : defined)
bFlag |= c.isUntapped();
if (!bFlag) // All of the defined stuff is tapped, not very useful
return false;
} else {
tgt.resetTargets();
if (!tapPrefTargeting(source, tgt, af, sa, false))
return false;
}
Ability_Sub subAb = sa.getSubAbility();
if (subAb != null)
randomReturn &= subAb.chkAI_Drawback();
return randomReturn;
}
/**
* <p>tapTrigger.</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 tapTrigger(AbilityFactory af, SpellAbility sa, boolean mandatory) {
if (!ComputerUtil.canPayCost(sa))
return false;
Target tgt = sa.getTarget();
Card source = sa.getSourceCard();
if (tgt == null) {
if (mandatory)
return true;
// TODO: use Defined to determine, if this is an unfavorable result
return true;
} else {
if (tapPrefTargeting(source, tgt, af, sa, mandatory)) {
return true;
} else if (mandatory) {
// not enough preferred targets, but mandatory so keep going:
return tapUnpreferredTargeting(af, sa, mandatory);
}
}
return false;
}
/**
* <p>tapPlayDrawbackAI.</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 tapPlayDrawbackAI(final AbilityFactory af, SpellAbility sa) {
// AI cannot use this properly until he can use SAs during Humans turn
Target tgt = af.getAbTgt();
Card source = sa.getSourceCard();
boolean randomReturn = true;
if (tgt == null) {
// either self or defined, either way should be fine
} else {
// target section, maybe pull this out?
tgt.resetTargets();
if (!tapPrefTargeting(source, tgt, af, sa, false))
return false;
}
Ability_Sub subAb = sa.getSubAbility();
if (subAb != null)
randomReturn &= subAb.chkAI_Drawback();
return randomReturn;
}
/**
* <p>tapPrefTargeting.</p>
*
* @param source a {@link forge.Card} object.
* @param tgt a {@link forge.card.spellability.Target} object.
* @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 tapPrefTargeting(Card source, Target tgt, AbilityFactory af, SpellAbility sa, boolean mandatory) {
CardList tapList = AllZoneUtil.getPlayerCardsInPlay(AllZone.getHumanPlayer());
tapList = tapList.filter(AllZoneUtil.untapped);
tapList = tapList.getValidCards(tgt.getValidTgts(), source.getController(), source);
// filter out enchantments and planeswalkers, their tapped state doesn't matter.
String[] tappablePermanents = {"Creature", "Land", "Artifact"};
tapList = tapList.getValidCards(tappablePermanents, source.getController(), source);
tapList = tapList.getTargetableCards(source);
if (tapList.size() == 0)
return false;
while (tgt.getNumTargeted() < tgt.getMaxTargets(source, sa)) {
Card choice = null;
if (tapList.size() == 0) {
if (tgt.getNumTargeted() < tgt.getMinTargets(source, sa) || tgt.getNumTargeted() == 0) {
if (!mandatory)
tgt.resetTargets();
return false;
} else {
// TODO is this good enough? for up to amounts?
break;
}
}
if (tapList.getNotType("Creature").size() == 0)
choice = CardFactoryUtil.AI_getBestCreature(tapList); //if only creatures take the best
else
choice = CardFactoryUtil.AI_getMostExpensivePermanent(tapList, af.getHostCard(), false);
if (choice == null) { // can't find anything left
if (tgt.getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa) || tgt.getNumTargeted() == 0) {
if (!mandatory)
tgt.resetTargets();
return false;
} else {
// TODO is this good enough? for up to amounts?
break;
}
}
tapList.remove(choice);
tgt.addTarget(choice);
}
return true;
}
/**
* <p>tapUnpreferredTargeting.</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 tapUnpreferredTargeting(AbilityFactory af, SpellAbility sa, boolean mandatory) {
Card source = sa.getSourceCard();
Target tgt = sa.getTarget();
CardList list = AllZoneUtil.getCardsInPlay();
list = list.getValidCards(tgt.getValidTgts(), source.getController(), source);
list = list.getTargetableCards(source);
// filter by enchantments and planeswalkers, their tapped state doesn't matter.
String[] tappablePermanents = {"Enchantment", "Planeswalker"};
CardList tapList = list.getValidCards(tappablePermanents, source.getController(), source);
if (tapTargetList(af, sa, tapList, mandatory))
return true;
// try to just tap already tapped things
tapList = list.filter(AllZoneUtil.tapped);
if (tapTargetList(af, sa, tapList, mandatory))
return true;
// just tap whatever we can
tapList = list;
if (tapTargetList(af, sa, tapList, mandatory))
return true;
return false;
}
/**
* <p>tapTargetList.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @param tapList a {@link forge.CardList} object.
* @param mandatory a boolean.
* @return a boolean.
*/
private static boolean tapTargetList(AbilityFactory af, SpellAbility sa, CardList tapList, boolean mandatory) {
Card source = sa.getSourceCard();
Target tgt = sa.getTarget();
for (Card c : tgt.getTargetCards())
tapList.remove(c);
if (tapList.size() == 0)
return false;
while (tgt.getNumTargeted() < tgt.getMaxTargets(source, sa)) {
Card choice = null;
if (tapList.size() == 0) {
if (tgt.getNumTargeted() < tgt.getMinTargets(source, sa) || tgt.getNumTargeted() == 0) {
if (!mandatory)
tgt.resetTargets();
return false;
} else {
// TODO is this good enough? for up to amounts?
break;
}
}
if (tapList.getNotType("Creature").size() == 0)
choice = CardFactoryUtil.AI_getBestCreature(tapList); //if only creatures take the best
else
choice = CardFactoryUtil.AI_getMostExpensivePermanent(tapList, af.getHostCard(), false);
if (choice == null) { // can't find anything left
if (tgt.getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa) || tgt.getNumTargeted() == 0) {
if (!mandatory)
tgt.resetTargets();
return false;
} else {
// TODO is this good enough? for up to amounts?
break;
}
}
tapList.remove(choice);
tgt.addTarget(choice);
}
return true;
}
/**
* <p>tapResolve.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
*/
private static void tapResolve(final AbilityFactory af, final SpellAbility sa) {
HashMap<String, String> params = af.getMapParams();
Card card = sa.getSourceCard();
ArrayList<Card> tgtCards;
Target tgt = af.getAbTgt();
if (tgt != null)
tgtCards = tgt.getTargetCards();
else {
tgtCards = AbilityFactory.getDefinedCards(card, params.get("Defined"), sa);
}
for (Card tgtC : tgtCards) {
if (AllZoneUtil.isCardInPlay(tgtC) && (tgt == null || CardFactoryUtil.canTarget(af.getHostCard(), tgtC)))
tgtC.tap();
}
}
// ****************************************
// ************** UntapAll *****************
// ****************************************
/**
* <p>createAbilityUntapAll.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createAbilityUntapAll(final AbilityFactory af) {
final SpellAbility abUntap = new Ability_Activated(af.getHostCard(), af.getAbCost(), af.getAbTgt()) {
private static final long serialVersionUID = 8914852730903389831L;
@Override
public String getStackDescription() {
return untapAllStackDescription(af, this);
}
@Override
public boolean canPlayAI() {
return untapAllCanPlayAI(af, this);
}
@Override
public void resolve() {
untapAllResolve(af, this);
}
@Override
public boolean doTrigger(boolean mandatory) {
return untapAllTrigger(af, this, mandatory);
}
};
return abUntap;
}
/**
* <p>createSpellUntapAll.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createSpellUntapAll(final AbilityFactory af) {
final SpellAbility spUntap = new Spell(af.getHostCard(), af.getAbCost(), af.getAbTgt()) {
private static final long serialVersionUID = 5713174052551899363L;
@Override
public String getStackDescription() {
return untapAllStackDescription(af, this);
}
@Override
public boolean canPlayAI() {
return untapAllCanPlayAI(af, this);
}
@Override
public void resolve() {
untapAllResolve(af, this);
}
};
return spUntap;
}
/**
* <p>createDrawbackUntapAll.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createDrawbackUntapAll(final AbilityFactory af) {
final SpellAbility dbUntapAll = new Ability_Sub(af.getHostCard(), af.getAbTgt()) {
private static final long serialVersionUID = -5187900994680626766L;
@Override
public String getStackDescription() {
return untapAllStackDescription(af, this);
}
@Override
public void resolve() {
untapAllResolve(af, this);
}
@Override
public boolean chkAI_Drawback() {
return untapAllPlayDrawbackAI(af, this);
}
@Override
public boolean doTrigger(boolean mandatory) {
return untapAllPlayDrawbackAI(af, this);
}
};
return dbUntapAll;
}
/**
* <p>untapAllPlayDrawbackAI.</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 untapAllPlayDrawbackAI(final AbilityFactory af, SpellAbility sa) {
return true;
}
/**
* <p>untapAllResolve.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
*/
private static void untapAllResolve(final AbilityFactory af, final SpellAbility sa) {
HashMap<String, String> params = af.getMapParams();
Card card = sa.getSourceCard();
String valid = "";
if (params.containsKey("ValidCards"))
valid = params.get("ValidCards");
CardList list = AllZoneUtil.getCardsInPlay();
list = list.getValidCards(valid.split(","), card.getController(), card);
for (int i = 0; i < list.size(); i++) list.get(i).untap();
}
/**
* <p>untapAllCanPlayAI.</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 untapAllCanPlayAI(final AbilityFactory af, final SpellAbility sa) {
/*
* All cards using this currently have SVar:RemAIDeck:True
*/
return false;
}
/**
* <p>untapAllTrigger.</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 untapAllTrigger(AbilityFactory af, SpellAbility sa, boolean mandatory) {
if (!ComputerUtil.canPayCost(sa))
return false;
if (mandatory)
return true;
return false;
}
/**
* <p>untapAllStackDescription.</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 untapAllStackDescription(AbilityFactory af, SpellAbility sa) {
HashMap<String, String> params = af.getMapParams();
StringBuilder sb = new StringBuilder();
if (sa instanceof Ability_Sub) {
sb.append(" ");
sb.append("Untap all valid cards.");
} else {
sb.append(sa.getSourceCard()).append(" - ");
sb.append(params.get("SpellDescription"));
}
Ability_Sub subAb = sa.getSubAbility();
if (subAb != null)
sb.append(subAb.getStackDescription());
return sb.toString();
}
// ****************************************
// ************** TapAll *****************
// ****************************************
/**
* <p>createAbilityTapAll.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createAbilityTapAll(final AbilityFactory af) {
final SpellAbility abUntap = new Ability_Activated(af.getHostCard(), af.getAbCost(), af.getAbTgt()) {
private static final long serialVersionUID = -2095140656782946737L;
@Override
public String getStackDescription() {
return tapAllStackDescription(af, this);
}
@Override
public boolean canPlayAI() {
return tapAllCanPlayAI(af, this);
}
@Override
public void resolve() {
tapAllResolve(af, this);
}
@Override
public boolean doTrigger(boolean mandatory) {
return tapAllTrigger(af, this, mandatory);
}
};
return abUntap;
}
/**
* <p>createSpellTapAll.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createSpellTapAll(final AbilityFactory af) {
final SpellAbility spUntap = new Spell(af.getHostCard(), af.getAbCost(), af.getAbTgt()) {
private static final long serialVersionUID = -62401571838950166L;
@Override
public String getStackDescription() {
return tapAllStackDescription(af, this);
}
@Override
public boolean canPlayAI() {
return tapAllCanPlayAI(af, this);
}
@Override
public void resolve() {
tapAllResolve(af, this);
}
};
return spUntap;
}
/**
* <p>createDrawbackTapAll.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createDrawbackTapAll(final AbilityFactory af) {
final SpellAbility dbTap = new Ability_Sub(af.getHostCard(), af.getAbTgt()) {
private static final long serialVersionUID = -4990932993654533449L;
@Override
public String getStackDescription() {
return tapAllStackDescription(af, this);
}
@Override
public void resolve() {
tapAllResolve(af, this);
}
@Override
public boolean chkAI_Drawback() {
return tapAllPlayDrawbackAI(af, this);
}
@Override
public boolean doTrigger(boolean mandatory) {
return tapAllPlayDrawbackAI(af, this);
}
};
return dbTap;
}
/**
* <p>tapAllResolve.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
*/
private static void tapAllResolve(final AbilityFactory af, final SpellAbility sa) {
HashMap<String, String> params = af.getMapParams();
CardList cards = null;
ArrayList<Player> tgtPlayers = null;
Target tgt = af.getAbTgt();
if (tgt != null)
tgtPlayers = tgt.getTargetPlayers();
else if (params.containsKey("Defined")) // Make sure Defined exists to use it
tgtPlayers = AbilityFactory.getDefinedPlayers(sa.getSourceCard(), params.get("Defined"), sa);
if (tgtPlayers == null || tgtPlayers.isEmpty())
cards = AllZoneUtil.getCardsInPlay();
else
cards = AllZoneUtil.getPlayerCardsInPlay(tgtPlayers.get(0));
cards = AbilityFactory.filterListByType(cards, params.get("ValidCards"), sa);
for (Card c : cards) c.tap();
}
/**
* <p>tapAllCanPlayAI.</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 tapAllCanPlayAI(final AbilityFactory af, final SpellAbility sa) {
// If tapping all creatures do it either during declare attackers of AIs turn
// or during upkeep/begin combat?
if (!ComputerUtil.canPayCost(sa))
return false;
Card source = sa.getSourceCard();
HashMap<String, String> params = af.getMapParams();
if (AllZone.getPhase().isAfter(Constant.Phase.Combat_Begin))
return false;
String valid = "";
if (params.containsKey("ValidCards"))
valid = params.get("ValidCards");
CardList validTappables = AllZoneUtil.getCardsInPlay();
Target tgt = sa.getTarget();
if (sa.getTarget() != null) {
tgt.resetTargets();
sa.getTarget().addTarget(AllZone.getHumanPlayer());
validTappables = AllZoneUtil.getPlayerCardsInPlay(AllZone.getHumanPlayer());
}
validTappables = validTappables.getValidCards(valid, source.getController(), source);
validTappables = validTappables.filter(AllZoneUtil.untapped);
Random r = MyRandom.random;
boolean rr = false;
if (r.nextFloat() <= Math.pow(.6667, source.getAbilityUsed()))
rr = true;
if (validTappables.size() > 0) {
CardList human = validTappables.filter(new CardListFilter() {
public boolean addCard(Card c) {
return c.getController().isHuman();
}
});
CardList compy = validTappables.filter(new CardListFilter() {
public boolean addCard(Card c) {
return c.getController().isComputer();
}
});
if (human.size() > compy.size()) {
return rr;
}
}
return false;
}
/**
* <p>getTapAllTargets.</p>
*
* @param valid a {@link java.lang.String} object.
* @param source a {@link forge.Card} object.
* @return a {@link forge.CardList} object.
*/
private static CardList getTapAllTargets(String valid, Card source) {
CardList tmpList = AllZoneUtil.getCardsInPlay();
tmpList = tmpList.getValidCards(valid, source.getController(), source);
tmpList = tmpList.filter(AllZoneUtil.untapped);
return tmpList;
}
/**
* <p>tapAllStackDescription.</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 tapAllStackDescription(AbilityFactory af, SpellAbility sa) {
HashMap<String, String> params = af.getMapParams();
StringBuilder sb = new StringBuilder();
if (sa instanceof Ability_Sub) {
sb.append(" ");
sb.append("Tap all valid cards.");
} else {
sb.append(sa.getSourceCard()).append(" - ");
sb.append(params.get("SpellDescription"));
}
Ability_Sub subAb = sa.getSubAbility();
if (subAb != null)
sb.append(subAb.getStackDescription());
return sb.toString();
}
/**
* <p>tapAllTrigger.</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 tapAllTrigger(AbilityFactory af, SpellAbility sa, boolean mandatory) {
if (!ComputerUtil.canPayCost(sa))
return false;
if (mandatory)
return true;
Card source = sa.getSourceCard();
HashMap<String, String> params = af.getMapParams();
String valid = "";
if (params.containsKey("ValidCards"))
valid = params.get("ValidCards");
CardList validTappables = getTapAllTargets(valid, source);
Random r = MyRandom.random;
boolean rr = false;
if (r.nextFloat() <= Math.pow(.6667, source.getAbilityUsed()))
rr = true;
if (validTappables.size() > 0) {
CardList human = validTappables.filter(new CardListFilter() {
public boolean addCard(Card c) {
return c.getController().isHuman();
}
});
CardList compy = validTappables.filter(new CardListFilter() {
public boolean addCard(Card c) {
return c.getController().isHuman();
}
});
if (human.size() > compy.size()) {
return rr;
}
}
return false;
}
/**
* <p>tapAllPlayDrawbackAI.</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 tapAllPlayDrawbackAI(final AbilityFactory af, SpellAbility sa) {
return true;
}
// ****************************************
// ************** Tap or Untap ************
// ****************************************
/**
* <p>createAbilityTapOrUntap.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createAbilityTapOrUntap(final AbilityFactory af) {
final SpellAbility abTapOrUntap = new Ability_Activated(af.getHostCard(), af.getAbCost(), af.getAbTgt()) {
private static final long serialVersionUID = -4713183763302932079L;
@Override
public String getStackDescription() {
return tapOrUntapStackDescription(af, this);
}
@Override
public boolean canPlayAI() {
return tapOrUntapCanPlayAI(af, this);
}
@Override
public void resolve() {
tapOrUntapResolve(af, this);
}
@Override
public boolean doTrigger(boolean mandatory) {
return tapOrUntapTrigger(af, this, mandatory);
}
};
return abTapOrUntap;
}
/**
* <p>createSpellTapOrUntap.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createSpellTapOrUntap(final AbilityFactory af) {
final SpellAbility spTapOrUntap = new Spell(af.getHostCard(), af.getAbCost(), af.getAbTgt()) {
private static final long serialVersionUID = -8870476840484788521L;
@Override
public String getStackDescription() {
return tapOrUntapStackDescription(af, this);
}
@Override
public boolean canPlayAI() {
return tapOrUntapCanPlayAI(af, this);
}
@Override
public void resolve() {
tapOrUntapResolve(af, this);
}
};
return spTapOrUntap;
}
/**
* <p>createDrawbackTapOrUntap.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility createDrawbackTapOrUntap(final AbilityFactory af) {
final SpellAbility dbTapOrUntap = new Ability_Sub(af.getHostCard(), af.getAbTgt()) {
private static final long serialVersionUID = -8282868583712773337L;
@Override
public String getStackDescription() {
return tapOrUntapStackDescription(af, this);
}
@Override
public void resolve() {
tapOrUntapResolve(af, this);
}
@Override
public boolean chkAI_Drawback() {
return tapOrUntapPlayDrawbackAI(af, this);
}
@Override
public boolean doTrigger(boolean mandatory) {
return tapOrUntapTrigger(af, this, mandatory);
}
};
return dbTapOrUntap;
}
/**
* <p>tapOrUntapStackDescription.</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 tapOrUntapStackDescription(AbilityFactory af, SpellAbility sa) {
// when getStackDesc is called, just build exactly what is happening
StringBuilder sb = new StringBuilder();
HashMap<String, String> params = af.getMapParams();
if (sa instanceof Ability_Sub)
sb.append(" ");
else
sb.append(sa.getSourceCard()).append(" - ");
sb.append("Tap or untap ");
ArrayList<Card> tgtCards;
Target tgt = af.getAbTgt();
if (tgt != null)
tgtCards = tgt.getTargetCards();
else {
tgtCards = AbilityFactory.getDefinedCards(sa.getSourceCard(), params.get("Defined"), sa);
}
Iterator<Card> it = tgtCards.iterator();
while (it.hasNext()) {
sb.append(it.next());
if (it.hasNext()) sb.append(", ");
}
sb.append(".");
Ability_Sub subAb = sa.getSubAbility();
if (subAb != null)
sb.append(subAb.getStackDescription());
return sb.toString();
}
/**
* <p>tapOrUntapCanPlayAI.</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 tapOrUntapCanPlayAI(final AbilityFactory af, SpellAbility sa) {
// AI cannot use this properly until he can use SAs during Humans turn
if (!ComputerUtil.canPayCost(sa))
return false;
HashMap<String, String> params = af.getMapParams();
Target tgt = af.getAbTgt();
Card source = sa.getSourceCard();
Random r = MyRandom.random;
boolean randomReturn = r.nextFloat() <= Math.pow(.6667, source.getAbilityUsed());
if (tgt == null) {
//assume we are looking to tap human's stuff
//TODO - check for things with untap abilities, and don't tap those.
ArrayList<Card> defined = AbilityFactory.getDefinedCards(source, params.get("Defined"), sa);
boolean bFlag = false;
for (Card c : defined)
bFlag |= c.isUntapped();
if (!bFlag) // All of the defined stuff is tapped, not very useful
return false;
} else {
tgt.resetTargets();
if (!tapPrefTargeting(source, tgt, af, sa, false))
return false;
}
Ability_Sub subAb = sa.getSubAbility();
if (subAb != null)
randomReturn &= subAb.chkAI_Drawback();
return randomReturn;
}
/**
* <p>tapOrUntapTrigger.</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 tapOrUntapTrigger(AbilityFactory af, SpellAbility sa, boolean mandatory) {
if (!ComputerUtil.canPayCost(sa))
return false;
Target tgt = sa.getTarget();
Card source = sa.getSourceCard();
if (tgt == null) {
if (mandatory)
return true;
// TODO: use Defined to determine if this is an unfavorable result
return true;
} else {
if (tapPrefTargeting(source, tgt, af, sa, mandatory)) {
return true;
} else if (mandatory) {
// not enough preferred targets, but mandatory so keep going:
return tapUnpreferredTargeting(af, sa, mandatory);
}
}
return false;
}
/**
* <p>tapOrUntapPlayDrawbackAI.</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 tapOrUntapPlayDrawbackAI(final AbilityFactory af, SpellAbility sa) {
// AI cannot use this properly until he can use SAs during Humans turn
Target tgt = af.getAbTgt();
Card source = sa.getSourceCard();
boolean randomReturn = true;
if (tgt == null) {
// either self or defined, either way should be fine
} else {
// target section, maybe pull this out?
tgt.resetTargets();
if (!tapPrefTargeting(source, tgt, af, sa, false))
return false;
}
Ability_Sub subAb = sa.getSubAbility();
if (subAb != null)
randomReturn &= subAb.chkAI_Drawback();
return randomReturn;
}
/**
* <p>tapOrUntapResolve.</p>
*
* @param af a {@link forge.card.abilityFactory.AbilityFactory} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
*/
private static void tapOrUntapResolve(final AbilityFactory af, final SpellAbility sa) {
HashMap<String, String> params = af.getMapParams();
Card card = sa.getSourceCard();
ArrayList<Card> tgtCards;
Target tgt = af.getAbTgt();
if (tgt != null)
tgtCards = tgt.getTargetCards();
else {
tgtCards = AbilityFactory.getDefinedCards(card, params.get("Defined"), sa);
}
for (Card tgtC : tgtCards) {
if (AllZoneUtil.isCardInPlay(tgtC) && (tgt == null || CardFactoryUtil.canTarget(af.getHostCard(), tgtC))) {
if(sa.getActivatingPlayer().isHuman()) {
String[] tapOrUntap = new String[]{"Tap", "Untap"};
Object z = GuiUtils.getChoiceOptional("Tap or Untap " + tgtC + "?", tapOrUntap);
if (null == z) continue;
boolean tap = (z.equals("Tap")) ? true : false;
if (tap) tgtC.tap();
else tgtC.untap();
}
else {
//computer
tgtC.tap();
}
}
}
}
//Phasing? Something else? Who knows!
}// end of AbilityFactory_PermanentState class