package forge.card.cardFactory;
import com.esotericsoftware.minlog.Log;
import forge.*;
import forge.card.spellability.*;
import forge.gui.GuiUtils;
import forge.gui.input.Input;
import forge.gui.input.Input_PayManaCost;
import java.util.*;
import java.util.Map.Entry;
/**
* <p>CardFactoryUtil class.</p>
*
* @author Forge
* @version $Id: $
*/
public class CardFactoryUtil {
private static Random random = MyRandom.random;
/**
* <p>AI_getMostExpensivePermanent.</p>
*
* @param list a {@link forge.CardList} object.
* @param spell a {@link forge.Card} object.
* @param targeted a boolean.
* @return a {@link forge.Card} object.
*/
public static Card AI_getMostExpensivePermanent(CardList list, final Card spell, boolean targeted) {
CardList all = list;
if (targeted) {
all = all.filter(new CardListFilter() {
public boolean addCard(Card c) {
return CardFactoryUtil.canTarget(spell, c);
}
});
}
return AI_getMostExpensivePermanent(all);
}
public static Card AI_getMostExpensivePermanent(CardList all){
if (all.size() == 0) return null;
Card biggest = null;
biggest = all.get(0);
int bigCMC = 0;
for (int i = 0; i < all.size(); i++) {
Card card = all.get(i);
int curCMC = card.getCMC();
//Add all cost of all auras with the same controller
CardList auras = new CardList(card.getEnchantedBy().toArray());
auras.getController(card.getController());
curCMC += auras.getTotalConvertedManaCost() + auras.size();
if (curCMC >= bigCMC) {
bigCMC = curCMC;
biggest = all.get(i);
}
}
return biggest;
}
//for Sarkhan the Mad
/**
* <p>AI_getCheapestCreature.</p>
*
* @param list a {@link forge.CardList} object.
* @param spell a {@link forge.Card} object.
* @param targeted a boolean.
* @return a {@link forge.Card} object.
*/
public static Card AI_getCheapestCreature(CardList list, final Card spell, boolean targeted) {
list = list.filter(new CardListFilter() {
public boolean addCard(Card c) {
return c.isCreature();
}
});
return AI_getCheapestPermanent(list, spell, targeted);
}
/**
* <p>AI_getCheapestPermanent.</p>
*
* @param list a {@link forge.CardList} object.
* @param spell a {@link forge.Card} object.
* @param targeted a boolean.
* @return a {@link forge.Card} object.
*/
public static Card AI_getCheapestPermanent(CardList list, final Card spell, boolean targeted) {
CardList all = list;
if (targeted) {
all = all.filter(new CardListFilter() {
public boolean addCard(Card c) {
return CardFactoryUtil.canTarget(spell, c);
}
});
}
if (all.size() == 0) return null;
//get cheapest card:
Card cheapest = null;
cheapest = all.get(0);
for (int i = 0; i < all.size(); i++) {
if (CardUtil.getConvertedManaCost(cheapest.getManaCost()) <= CardUtil.getConvertedManaCost(cheapest.getManaCost())) {
cheapest = all.get(i);
}
}
return cheapest;
}
/**
* <p>AI_getBestLand.</p>
*
* @param list a {@link forge.CardList} object.
* @return a {@link forge.Card} object.
*/
public static Card AI_getBestLand(CardList list) {
CardList land = list.getType("Land");
if (!(land.size() > 0)) return null;
CardList nbLand = land.filter(new CardListFilter() // prefer to target non basic lands
{
public boolean addCard(Card c) {
return (!c.isBasicLand());
}
});
if (nbLand.size() > 0) {
//TODO: Rank non basics?
Random r = MyRandom.random;
return nbLand.get(r.nextInt(nbLand.size()));
}
// if no non-basic lands, target the least represented basic land type
String names[] = {"Plains", "Island", "Swamp", "Mountain", "Forest"};
String sminBL = "";
int iminBL = 20000; // hopefully no one will ever have more than 20000 lands of one type....
int n = 0;
for (int i = 0; i < 5; i++) {
n = land.getType(names[i]).size();
if (n < iminBL && n > 0) // if two or more are tied, only the first one checked will be used
{
iminBL = n;
sminBL = names[i];
}
}
if (iminBL == 20000) return null; // no basic land was a minimum
CardList BLand = land.getType(sminBL);
for (int i = 0; i < BLand.size(); i++)
if (!BLand.get(i).isTapped()) // prefer untapped lands
return BLand.get(i);
Random r = MyRandom.random;
return BLand.get(r.nextInt(BLand.size())); // random tapped land of least represented type
}
//The AI doesn't really pick the best enchantment, just the most expensive.
/**
* <p>AI_getBestEnchantment.</p>
*
* @param list a {@link forge.CardList} object.
* @param spell a {@link forge.Card} object.
* @param targeted a boolean.
* @return a {@link forge.Card} object.
*/
public static Card AI_getBestEnchantment(CardList list, final Card spell, boolean targeted) {
CardList all = list;
all = all.getType("Enchantment");
if (targeted) {
all = all.filter(new CardListFilter() {
public boolean addCard(Card c) {
return CardFactoryUtil.canTarget(spell, c);
}
});
}
if (all.size() == 0) {
return null;
}
//get biggest Enchantment
Card biggest = null;
biggest = all.get(0);
int bigCMC = 0;
for (int i = 0; i < all.size(); i++) {
int curCMC = CardUtil.getConvertedManaCost(all.get(i).getManaCost());
if (curCMC > bigCMC) {
bigCMC = curCMC;
biggest = all.get(i);
}
}
return biggest;
}
//The AI doesn't really pick the best artifact, just the most expensive.
/**
* <p>AI_getBestArtifact.</p>
*
* @param list a {@link forge.CardList} object.
* @return a {@link forge.Card} object.
*/
public static Card AI_getBestArtifact(CardList list) {
CardList all = list;
all = all.getType("Artifact");
if (all.size() == 0) {
return null;
}
//get biggest Artifact
Card biggest = null;
biggest = all.get(0);
int bigCMC = 0;
for (int i = 0; i < all.size(); i++) {
int curCMC = CardUtil.getConvertedManaCost(all.get(i).getManaCost());
if (curCMC > bigCMC) {
bigCMC = curCMC;
biggest = all.get(i);
}
}
return biggest;
}
/**
* <p>AI_getHumanArtifact.</p>
*
* @param spell a {@link forge.Card} object.
* @param targeted a boolean.
* @return a {@link forge.CardList} object.
*/
public static CardList AI_getHumanArtifact(final Card spell, boolean targeted) {
CardList artifact = AllZoneUtil.getPlayerCardsInPlay(AllZone.getHumanPlayer());
artifact = artifact.getType("Artifact");
if (targeted) {
artifact = artifact.filter(new CardListFilter() {
public boolean addCard(Card c) {
return canTarget(spell, c);
}
});
}
return artifact;
}
/**
* <p>AI_doesCreatureAttack.</p>
*
* @param card a {@link forge.Card} object.
* @return a boolean.
*/
public static boolean AI_doesCreatureAttack(Card card) {
Combat combat = ComputerUtil.getAttackers();
Card[] att = combat.getAttackers();
for (int i = 0; i < att.length; i++)
if (att[i].equals(card)) return true;
return false;
}
/**
* <p>evaluateCreatureList.</p>
*
* @param list a {@link forge.CardList} object.
* @return a int.
*/
public static int evaluateCreatureList(CardList list) {
int value = 0;
for (int i = 0; i < list.size(); i++) value += evaluateCreature(list.get(i));
return value;
}
/**
* <p>evaluatePermanentList.</p>
*
* @param list a {@link forge.CardList} object.
* @return a int.
*/
public static int evaluatePermanentList(CardList list) {
int value = 0;
for (int i = 0; i < list.size(); i++) value += list.get(i).getCMC() + 1;
return value;
}
/**
* <p>evaluateCreature.</p>
*
* @param c a {@link forge.Card} object.
* @return a int.
*/
public static int evaluateCreature(Card c) {
int value = 100;
if (c.isToken()) value = 80; //tokens should be worth less than actual cards
int power = c.getNetAttack();
int toughness = c.getNetDefense();
//Doran
if (AllZoneUtil.isCardInPlay("Doran, the Siege Tower")) power = toughness;
if (c.hasKeyword("Prevent all combat damage that would be dealt by CARDNAME.")
|| c.hasKeyword("Prevent all damage that would be dealt by CARDNAME.")
|| c.hasKeyword("Prevent all combat damage that would be dealt to and dealt by CARDNAME.")
|| c.hasKeyword("Prevent all damage that would be dealt to and dealt by CARDNAME."))
power = 0;
value += power * 15;
value += toughness * 10;
value += c.getCMC() * 5;
//Evasion keywords
if (c.hasKeyword("Flying")) value += power * 10;
if (c.hasKeyword("Horsemanship")) value += power * 10;
if (c.hasKeyword("Unblockable")) value += power * 10;
if (c.hasKeyword("Fear")) value += power * 6;
if (c.hasKeyword("Intimidate")) value += power * 6;
if (c.hasStartOfKeyword("CARDNAME can't be blocked except by")) value += power * 5;
if (c.hasStartOfKeyword("CARDNAME can't be blocked by")) value += power * 2;
//Battle stats increasing keywords
if (c.hasKeyword("Double Strike")) value += 10 + power * 15;
value += c.getKeywordMagnitude("Bushido") * 16;
value += c.getAmountOfKeyword("Flanking") * 15;
//Other good keywords
if (c.hasKeyword("Deathtouch") && power > 0) value += 25;
value += c.getAmountOfKeyword("Exalted") * 15;
if (c.hasKeyword("First Strike") && !c.hasKeyword("Double Strike") && power > 0) value += 10 + power * 5;
if (c.hasKeyword("Lifelink")) value += power * 10;
if (c.hasKeyword("Trample") && power > 1) value += power * 3;
if (c.hasKeyword("Vigilance")) value += power * 5 + toughness * 5;
if (c.hasKeyword("Wither")) value += power * 10;
value += c.getKeywordMagnitude("Rampage");
value += c.getKeywordMagnitude("Annihilator") * 50;
if (c.hasKeyword("Changeling")) value += 5;
if (c.hasKeyword("Whenever a creature dealt damage by CARDNAME this turn is put into a graveyard, put a +1/+1 counter on CARDNAME.")
&& power > 0) value += 2;
if (c.hasKeyword("Whenever a creature dealt damage by CARDNAME this turn is put into a graveyard, put a +2/+2 counter on CARDNAME.")
&& power > 0) value += 4;
if (c.hasKeyword("Whenever CARDNAME is dealt damage, put a +1/+1 counter on it.")) value += 10;
//Defensive Keywords
if (c.hasKeyword("Reach")) value += 5;
if (c.hasKeyword("CARDNAME can block creatures with shadow as though they didn't have shadow.")) value += 3;
//Protection
if (c.hasKeyword("Indestructible")) value += 70;
if (c.hasKeyword("Prevent all damage that would be dealt to CARDNAME.")) value += 60;
if (c.hasKeyword("Prevent all combat damage that would be dealt to CARDNAME.")) value += 50;
if (c.hasKeyword("Shroud")) value += 30;
if (c.hasKeyword("Hexproof")) value += 35;
if (c.hasStartOfKeyword("Protection")) value += 20;
if (c.hasStartOfKeyword("PreventAllDamageBy")) value += 10;
value += c.getKeywordMagnitude("Absorb") * 11;
//Activated Abilities
if (c.hasStartOfKeyword("ab")) value += 10;
//Bad keywords
if (c.hasKeyword("Defender") || c.hasKeyword("CARDNAME can't attack.")) value -= power * 9 + 40;
if (c.hasKeyword("CARDNAME can't block.")) value -= 10;
if (c.hasKeyword("CARDNAME attacks each turn if able.")) value -= 10;
if (c.hasKeyword("CARDNAME can block only creatures with flying.")) value -= toughness * 5;
if (c.hasStartOfKeyword("When CARDNAME is dealt damage, destroy it.")) value -= (toughness - 1) * 9;
if (c.hasKeyword("CARDNAME can't attack or block.")) value = 50 + c.getCMC() * 5; //reset everything - useless
if (c.hasKeyword("At the beginning of the end step, destroy CARDNAME.")) value -= 50;
if (c.hasKeyword("At the beginning of the end step, exile CARDNAME.")) value -= 50;
if (c.hasKeyword("At the beginning of the end step, sacrifice CARDNAME.")) value -= 50;
if (c.hasStartOfKeyword("At the beginning of your upkeep, CARDNAME deals")) value -= 20;
if (c.hasStartOfKeyword("At the beginning of your upkeep, destroy CARDNAME unless you pay")) value -= 20;
if (c.hasStartOfKeyword("At the beginning of your upkeep, sacrifice CARDNAME unless you pay")) value -= 20;
if (c.hasStartOfKeyword("Upkeep:")) value -= 20;
if (c.hasStartOfKeyword("Cumulative upkeep")) value -= 30;
if (c.hasStartOfKeyword("(Echo unpaid)")) value -= 10;
if (c.hasStartOfKeyword("Fading")) value -= 20; //not used atm
if (c.hasStartOfKeyword("Vanishing")) value -= 20; //not used atm
if (c.isUntapped()) value += 1;
return value;
} //evaluateCreature
//returns null if list.size() == 0
/**
* <p>AI_getBestCreature.</p>
*
* @param list a {@link forge.CardList} object.
* @return a {@link forge.Card} object.
*/
public static Card AI_getBest(CardList list) {
// Get Best will filter by appropriate getBest list if ALL of the list is of that type
if (list.getNotType("Creature").size() == 0)
return AI_getBestCreature(list);
if (list.getNotType("Land").size() == 0)
return AI_getBestLand(list);
// TODO: Once we get an EvaluatePermanent this should call getBestPermanent()
return AI_getMostExpensivePermanent(list);
}
public static Card AI_getBestCreature(CardList list) {
CardList all = list;
all = all.getType("Creature");
Card biggest = null;
if (all.size() != 0) {
biggest = all.get(0);
for (int i = 0; i < all.size(); i++)
if (evaluateCreature(biggest) < evaluateCreature(all.get(i))) biggest = all.get(i);
}
return biggest;
}
//This selection rates tokens higher
/**
* <p>AI_getBestCreatureToBounce.</p>
*
* @param list a {@link forge.CardList} object.
* @return a {@link forge.Card} object.
*/
public static Card AI_getBestCreatureToBounce(CardList list) {
int tokenBonus = 40;
CardList all = list;
all = all.getType("Creature");
Card biggest = null; //returns null if list.size() == 0
int biggestvalue = 0;
int newvalue = 0;
if (all.size() != 0) {
biggest = all.get(0);
for (int i = 0; i < all.size(); i++) {
biggestvalue = evaluateCreature(biggest);
if (biggest.isToken()) biggestvalue += tokenBonus; // raise the value of tokens
newvalue = evaluateCreature(all.get(i));
if (all.get(i).isToken()) newvalue += tokenBonus; // raise the value of tokens
if (biggestvalue < newvalue) biggest = all.get(i);
}
}
return biggest;
}
//returns null if list.size() == 0
/**
* <p>AI_getWorstCreature.</p>
*
* @param list a {@link forge.CardList} object.
* @return a {@link forge.Card} object.
*/
public static Card AI_getWorstCreature(CardList list) {
CardList all = list;
all = all.getType("Creature");
//get smallest creature
Card smallest = null;
if (all.size() != 0) {
smallest = all.get(0);
for (int i = 0; i < all.size(); i++)
if (evaluateCreature(smallest) > evaluateCreature(all.get(i))) smallest = all.get(i);
}
return smallest;
}
/**
* <p>AI_getWorstPermanent.</p>
*
* @param list a {@link forge.CardList} object.
* @param biasEnch a boolean.
* @param biasLand a boolean.
* @param biasArt a boolean.
* @param biasCreature a boolean.
* @return a {@link forge.Card} object.
*/
public static Card AI_getWorstPermanent(final CardList list, boolean biasEnch, boolean biasLand, boolean biasArt, boolean biasCreature) {
if (list.size() == 0) return null;
if (biasEnch && list.getType("Enchantment").size() > 0) {
return AI_getCheapestPermanent(list.getType("Enchantment"), null, false);
}
if (biasArt && list.getType("Artifact").size() > 0) {
return AI_getCheapestPermanent(list.getType("Artifact"), null, false);
}
if (biasLand && list.getType("Land").size() > 0) {
return getWorstLand(list.getType("Land"));
}
if (biasCreature && list.getType("Creature").size() > 0) {
return AI_getWorstCreature(list.getType("Creature"));
}
if (list.getType("Land").size() > 6) {
return getWorstLand(list.getType("Land"));
}
if (list.getType("Artifact").size() > 0 || list.getType("Enchantment").size() > 0) {
return AI_getCheapestPermanent(list.filter(new CardListFilter() {
public boolean addCard(Card c) {
return c.isArtifact() || c.isEnchantment();
}
}), null, false);
}
if (list.getType("Creature").size() > 0) {
return AI_getWorstCreature(list.getType("Creature"));
}
//Planeswalkers fall through to here, lands will fall through if there aren't very many
return AI_getCheapestPermanent(list, null, false);
}
/**
* <p>input_Spell.</p>
*
* @param spell a {@link forge.card.spellability.SpellAbility} object.
* @param choices a {@link forge.CardList} object.
* @param free a boolean.
* @return a {@link forge.gui.input.Input} object.
*/
public static Input input_Spell(final SpellAbility spell, final CardList choices, final boolean free) {
Input target = new Input() {
private static final long serialVersionUID = 2781418414287281005L;
@Override
public void showMessage() {
if (choices.size() == 0) stop();
if (spell.getTargetCard() != null) stop();
AllZone.getDisplay().showMessage("Select target Spell: ");
Card choice = GuiUtils.getChoiceOptional("Choose a Spell", choices.toArray());
if (choice != null) {
spell.setTargetCard(choice);
done();
} else stop();
}
@Override
public void selectButtonCancel() {
stop();
}
void done() {
choices.clear();
if (spell.getManaCost().equals("0") || this.isFree()) {
if (spell.getTargetCard() != null) AllZone.getStack().add(spell);
stop();
} else stopSetNext(new Input_PayManaCost(spell));
}
};
return target;
}//input_targetSpell()
/**
* <p>input_destroyNoRegeneration.</p>
*
* @param choices a {@link forge.CardList} object.
* @param message a {@link java.lang.String} object.
* @return a {@link forge.gui.input.Input} object.
*/
public static Input input_destroyNoRegeneration(final CardList choices, final String message) {
Input target = new Input() {
private static final long serialVersionUID = -6637588517573573232L;
@Override
public void showMessage() {
AllZone.getDisplay().showMessage(message);
ButtonUtil.disableAll();
}
@Override
public void selectCard(Card card, PlayerZone zone) {
if (choices.contains(card)) {
AllZone.getGameAction().destroyNoRegeneration(card);
stop();
}
}
};
return target;
}//input_destroyNoRegeneration()
/**
* <p>ability_Flashback.</p>
*
* @param sourceCard a {@link forge.Card} object.
* @param cost a {@link java.lang.String} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility ability_Flashback(final Card sourceCard, String cost) {
Cost fbCost = new Cost(cost, sourceCard.getName(), true);
final SpellAbility flashback = new Spell(sourceCard) {
private static final long serialVersionUID = -4196027546564209412L;
@Override
public void resolve() {
SpellAbility[] sa = sourceCard.getSpellAbility();
AllZone.getGameAction().moveToStack(sourceCard);
SpellAbility flash = sa[0];
flash.setFlashBackAbility(true);
AllZone.getStack().add(flash);
}
@Override
public boolean canPlayAI() {
return ComputerUtil.canPayCost(this);
}
@Override
public boolean canPlay() {
Card sourceCard = this.getSourceCard();
return AllZoneUtil.isCardInPlayerGraveyard(sourceCard.getController(), sourceCard)
&& (sourceCard.isInstant() || Phase.canCastSorcery(sourceCard.getController()));
}
};
flashback.setPayCosts(fbCost);
String costString = fbCost.toString().replace(":", ".");
StringBuilder sbDesc = new StringBuilder();
sbDesc.append("Flashback: ").append(costString);
flashback.setDescription(sbDesc.toString());
// possibly add Flashback into here?
StringBuilder sbStack = new StringBuilder();
sbStack.append("Flashback: ").append(sourceCard.getName());
flashback.setStackDescription(sbStack.toString());
return flashback;
}//ability_Flashback()
/**
* <p>ability_Unearth.</p>
*
* @param sourceCard a {@link forge.Card} object.
* @param manaCost a {@link java.lang.String} object.
* @return a {@link forge.card.spellability.Ability_Activated} object.
*/
public static Ability_Activated ability_Unearth(final Card sourceCard, String manaCost) {
Cost cost = new Cost(manaCost, sourceCard.getName(), true);
final Ability_Activated unearth = new Ability_Activated(sourceCard, cost, null) {
private static final long serialVersionUID = -5633945565395478009L;
@Override
public void resolve() {
Card card = AllZone.getGameAction().moveToPlay(sourceCard);
card.addIntrinsicKeyword("At the beginning of the end step, exile CARDNAME.");
card.addIntrinsicKeyword("Haste");
card.setUnearthed(true);
}
@Override
public boolean canPlayAI() {
if (AllZone.getPhase().isAfter(Constant.Phase.Main1) || AllZone.getPhase().isPlayerTurn(AllZone.getHumanPlayer()))
return false;
return ComputerUtil.canPayCost(this);
}
};
SpellAbility_Restriction restrict = new SpellAbility_Restriction();
restrict.setZone("Graveyard");
restrict.setSorcerySpeed(true);
unearth.setRestrictions(restrict);
StringBuilder sbStack = new StringBuilder();
sbStack.append("Unearth: ").append(sourceCard.getName());
unearth.setStackDescription(sbStack.toString());
return unearth;
}//ability_Unearth()
/**
* <p>ability_Morph_Down.</p>
*
* @param sourceCard a {@link forge.Card} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility ability_Morph_Down(final Card sourceCard) {
final SpellAbility morph_down = new Spell(sourceCard) {
private static final long serialVersionUID = -1438810964807867610L;
@Override
public void resolve() {
//card.setName("Morph");
sourceCard.setIsFaceDown(true);
sourceCard.setManaCost("");
sourceCard.setColor(new ArrayList<Card_Color>()); //remove all colors
sourceCard.addColor("0");
sourceCard.setBaseAttack(2);
sourceCard.setBaseDefense(2);
sourceCard.comesIntoPlay();
sourceCard.setIntrinsicKeyword(new ArrayList<String>()); //remove all keywords
sourceCard.setType(new ArrayList<String>()); //remove all types
sourceCard.addType("Creature");
AllZone.getGameAction().moveToPlay(sourceCard);
}
@Override
public boolean canPlay() {
return Phase.canCastSorcery(sourceCard.getController())
&& !AllZoneUtil.isCardInPlay(sourceCard);
}
};
morph_down.setManaCost("3");
morph_down.setDescription("(You may cast this face down as a 2/2 creature for 3.)");
morph_down.setStackDescription("Morph - Creature 2/2");
return morph_down;
}
/**
* <p>ability_Morph_Up.</p>
*
* @param sourceCard a {@link forge.Card} object.
* @param cost a {@link forge.card.spellability.Cost} object.
* @param orgManaCost a {@link java.lang.String} object.
* @param a a int.
* @param d a int.
* @return a {@link forge.card.spellability.Ability_Activated} object.
*/
public static Ability_Activated ability_Morph_Up(final Card sourceCard, Cost cost, String orgManaCost, int a, int d) {
//final String player = sourceCard.getController();
//final String manaCost = cost;
final int attack = a;
final int defense = d;
final String origManaCost = orgManaCost;
final Ability_Activated morph_up = new Ability_Activated(sourceCard, cost, null) {
private static final long serialVersionUID = -3663857013937085953L;
@Override
public void resolve() {
//card.setName("Morph");
sourceCard.setIsFaceDown(false);
sourceCard.setManaCost(origManaCost);
sourceCard.addColor(origManaCost);
sourceCard.setBaseAttack(attack);
sourceCard.setBaseDefense(defense);
sourceCard.setIntrinsicKeyword(sourceCard.getPrevIntrinsicKeyword());
sourceCard.setType(sourceCard.getPrevType());
sourceCard.turnFaceUp();
}
@Override
public boolean canPlay() {
// unMorphing a card is a Special Action, and not affected by Linvala
return sourceCard.getController().equals(this.getActivatingPlayer()) && sourceCard.isFaceDown()
&& AllZoneUtil.isCardInPlay(sourceCard);
}
};//morph_up
//morph_up.setManaCost(cost);
String costDesc = cost.toString();
//get rid of the ": " at the end
costDesc = costDesc.substring(0, costDesc.length() - 2);
StringBuilder sb = new StringBuilder();
sb.append("Morph");
if (!cost.isOnlyManaCost()) sb.append(" -");
sb.append(" ").append(costDesc).append(" (Turn this face up any time for its morph cost.)");
morph_up.setDescription(sb.toString());
StringBuilder sbStack = new StringBuilder();
sbStack.append(sourceCard.getName()).append(" - turn this card face up.");
morph_up.setStackDescription(sbStack.toString());
return morph_up;
}
/**
* <p>ability_Rebel_Search.</p>
*
* @param sourceCard a {@link forge.Card} object.
* @param cost a {@link java.lang.String} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility ability_Rebel_Search(final Card sourceCard, String cost) {
final int converted = Integer.parseInt(cost) - 1;
final Cost abCost = new Cost("T", sourceCard.getName(), true);
abCost.setMana(cost);
final SpellAbility ability = new Ability_Activated(sourceCard, abCost, null) {
private static final long serialVersionUID = 7219065355049285681L;
@Override
public boolean canPlay() {
for (int i = 0; i < AllZone.getStack().size(); i++) {
if (AllZone.getStack().peekInstance(i).getSourceCard().equals(sourceCard)) return false;
}
if (AllZoneUtil.isCardInPlay(sourceCard)
&& !sourceCard.hasSickness()
&& !sourceCard.isTapped()
&& super.canPlay()) return true;
else return false;
}
@Override
public boolean canPlayAI() {
CardList rebels = new CardList();
CardList list = AllZoneUtil.getPlayerCardsInLibrary(sourceCard.getController());
list = list.filter(new CardListFilter() {
public boolean addCard(Card c) {
return ((c.isType("Rebel") || c.hasKeyword("Changeling")))
&& c.isPermanent();
}
});
if (list.size() == 0) return false;
for (int i = 0; i < list.size(); i++) {
if (CardUtil.getConvertedManaCost(list.get(i).getManaCost()) <= converted) {
rebels.add(list.get(i));
}
}
if (AllZone.getPhase().getPhase().equals(Constant.Phase.Main2)
&& rebels.size() > 0) return true;
else return false;
}
@Override
public void resolve() {
CardList rebels = new CardList();
CardList list = AllZoneUtil.getPlayerCardsInLibrary(sourceCard.getController());
list = list.getType("Rebel");
list = list.getPermanents();
if (list.size() == 0) return;
for (int i = 0; i < list.size(); i++) {
if (CardUtil.getConvertedManaCost(list.get(i).getManaCost()) <= converted) {
rebels.add(list.get(i));
}
}
if (rebels.size() == 0) return;
if (sourceCard.getController().isComputer()) {
Card rebel = AI_getBestCreature(rebels);
AllZone.getGameAction().moveToPlay(rebel);
} else //human
{
Object o = GuiUtils.getChoiceOptional("Select target Rebel", rebels.toArray());
if (o != null) {
Card rebel = (Card) o;
AllZone.getGameAction().moveToPlay(rebel);
if (rebel.isAura()) {
Object obj = null;
if (rebel.hasKeyword("Enchant creature")) {
CardList creats = AllZoneUtil.getCreaturesInPlay();
obj = GuiUtils.getChoiceOptional("Pick a creature to attach "
+ rebel.getName() + " to", creats.toArray());
}
if (obj != null) {
Card target = (Card) obj;
if (AllZoneUtil.isCardInPlay(target)) {
rebel.enchantCard(target);
}
}
}
}
}
sourceCard.getController().shuffle();
}
};
StringBuilder sbDesc = new StringBuilder();
sbDesc.append(cost).append(", tap: Search your library for a Rebel permanent card with converted mana cost ");
sbDesc.append(converted).append(" or less and put it onto the battlefield. Then shuffle your library.");
ability.setDescription(sbDesc.toString());
StringBuilder sbStack = new StringBuilder();
sbStack.append(sourceCard.getName()).append(" - search for a Rebel and put it onto the battlefield.");
ability.setStackDescription(sbStack.toString());
return ability;
}
/**
* <p>ability_cycle.</p>
*
* @param sourceCard a {@link forge.Card} object.
* @param cycleCost a {@link java.lang.String} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility ability_cycle(final Card sourceCard, String cycleCost) {
cycleCost += " Discard<1/CARDNAME>";
Cost abCost = new Cost(cycleCost, sourceCard.getName(), true);
final SpellAbility cycle = new Ability_Activated(sourceCard, abCost, null) {
private static final long serialVersionUID = -4960704261761785512L;
@Override
public boolean canPlayAI() {
if(AllZone.getPhase().isBefore(Constant.Phase.Main2))
return false;
//The AI should cycle lands if it has 6 already and no cards in hand with higher CMC
CardList hand = AllZoneUtil.getPlayerHand(AllZone.getComputerPlayer());
CardList lands = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer());
lands.addAll(hand);
lands = lands.getType("Land");
if (sourceCard.isLand() && lands.size() >= Math.max(hand.getHighestConvertedManaCost(), 6))
return true;
//TODO: When else should AI Cycle?
return false;
}
@Override
public boolean canPlay() {
if (AllZoneUtil.isCardInPlay("Stabilizer")) return false;
return super.canPlay();
}
@Override
public void resolve() {
sourceCard.getController().drawCard();
sourceCard.cycle();
}
};
cycle.setIsCycling(true);
StringBuilder sbDesc = new StringBuilder();
sbDesc.append("Cycling ").append(cycle.getManaCost()).append(" (").append(abCost.toString()).append(" Draw a card.)");
cycle.setDescription(sbDesc.toString());
StringBuilder sbStack = new StringBuilder();
sbStack.append(sourceCard).append(" Cycling: Draw a card");
cycle.setStackDescription(sbStack.toString());
cycle.getRestrictions().setZone(Constant.Zone.Hand);
return cycle;
}//ability_cycle()
/**
* <p>ability_typecycle.</p>
*
* @param sourceCard a {@link forge.Card} object.
* @param cycleCost a {@link java.lang.String} object.
* @param type a {@link java.lang.String} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility ability_typecycle(final Card sourceCard, String cycleCost, final String type) {
String description;
cycleCost += " Discard<1/CARDNAME>";
Cost abCost = new Cost(cycleCost, sourceCard.getName(), true);
final SpellAbility cycle = new Ability_Activated(sourceCard, abCost, null) {
private static final long serialVersionUID = -4960704261761785512L;
@Override
public boolean canPlayAI() {
return false;
}
// some AI code could be added (certain colored mana needs analyze method maybe)
@Override
public boolean canPlay() {
if (AllZoneUtil.isCardInPlay("Stabilizer")) return false;
return super.canPlay();
}
@Override
public void resolve() {
CardList cards = AllZoneUtil.getPlayerCardsInLibrary(sourceCard.getController());
CardList sameType = new CardList();
for (int i = 0; i < cards.size(); i++) {
if (cards.get(i).isType(type)) {
sameType.add(cards.get(i));
}
}
if (sameType.size() == 0) {
sourceCard.getController().discard(sourceCard, this);
return;
}
Object o = GuiUtils.getChoiceOptional("Select a card", sameType.toArray());
if (o != null) {
//ability.setTargetCard((Card)o);
sourceCard.getController().discard(sourceCard, this);
Card c1 = (Card) o;
AllZone.getGameAction().moveToHand(c1);
}
sourceCard.getController().shuffle();
}
};
if (type.contains("Basic")) description = "Basic land";
else description = type;
cycle.setIsCycling(true);
StringBuilder sbDesc = new StringBuilder();
sbDesc.append(description).append("cycling (").append(abCost.toString()).append(" Search your library for a ");
sbDesc.append(description).append(" card, reveal it, and put it into your hand. Then shuffle your library.)");
cycle.setDescription(sbDesc.toString());
StringBuilder sbStack = new StringBuilder();
sbStack.append(sourceCard).append(" ").append(description);
sbStack.append("cycling: Search your library for a ").append(description).append(" card.)");
cycle.setStackDescription(sbStack.toString());
cycle.getRestrictions().setZone(Constant.Zone.Hand);
return cycle;
}//ability_typecycle()
/**
* <p>ability_transmute.</p>
*
* @param sourceCard a {@link forge.Card} object.
* @param transmuteCost a {@link java.lang.String} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility ability_transmute(final Card sourceCard, String transmuteCost) {
transmuteCost += " Discard<1/CARDNAME>";
Cost abCost = new Cost(transmuteCost, sourceCard.getName(), true);
final SpellAbility transmute = new Ability_Activated(sourceCard, abCost, null) {
private static final long serialVersionUID = -4960704261761785512L;
@Override
public boolean canPlayAI() {
return false;
}
@Override
public boolean canPlay() {
return super.canPlay() && Phase.canCastSorcery(sourceCard.getController());
}
@Override
public void resolve() {
CardList cards = AllZoneUtil.getPlayerCardsInLibrary(sourceCard.getController());
CardList sameCost = new CardList();
for (int i = 0; i < cards.size(); i++) {
if (CardUtil.getConvertedManaCost(cards.get(i).getManaCost()) == CardUtil.getConvertedManaCost(sourceCard.getManaCost())) {
sameCost.add(cards.get(i));
}
}
if (sameCost.size() == 0) return;
Object o = GuiUtils.getChoiceOptional("Select a card", sameCost.toArray());
if (o != null) {
//ability.setTargetCard((Card)o);
sourceCard.getController().discard(sourceCard, this);
Card c1 = (Card) o;
AllZone.getGameAction().moveToHand(c1);
}
sourceCard.getController().shuffle();
}
};
StringBuilder sbDesc = new StringBuilder();
sbDesc.append("Transmute (").append(abCost.toString());
sbDesc.append("Search your library for a card with the same converted mana cost as this card, reveal it, ");
sbDesc.append("and put it into your hand. Then shuffle your library. Transmute only as a sorcery.)");
transmute.setDescription(sbDesc.toString());
StringBuilder sbStack = new StringBuilder();
sbStack.append(sourceCard).append(" Transmute: Search your library for a card with the same converted mana cost.)");
transmute.setStackDescription(sbStack.toString());
transmute.getRestrictions().setZone(Constant.Zone.Hand);
return transmute;
}//ability_transmute()
/**
* <p>ability_suspend.</p>
*
* @param sourceCard a {@link forge.Card} object.
* @param suspendCost a {@link java.lang.String} object.
* @param suspendCounters a int.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility ability_suspend(final Card sourceCard, final String suspendCost, final int suspendCounters) {
// be careful with Suspend ability, it will not hit the stack
final SpellAbility suspend = new Ability_Static(sourceCard, suspendCost) {
private static final long serialVersionUID = 21625903128384507L;
@Override
public boolean canPlay() {
if (!(getRestrictions().canPlay(sourceCard, this)))
return false;
if (sourceCard.isInstant())
return true;
return Phase.canCastSorcery(sourceCard.getOwner());
}
@Override
public boolean canPlayAI() {
return false;
// Suspend currently not functional for the AI,
// seems to be an issue with regaining Priority after Suspension
}
@Override
public void resolve() {
Card c = AllZone.getGameAction().exile(sourceCard);
c.addCounter(Counters.TIME, suspendCounters);
}
};
StringBuilder sbDesc = new StringBuilder();
sbDesc.append("Suspend ").append(suspendCounters).append(": ").append(suspendCost);
suspend.setDescription(sbDesc.toString());
StringBuilder sbStack = new StringBuilder();
sbStack.append(sourceCard.getName()).append(" suspending for ").append(suspendCounters).append(" turns.)");
suspend.setStackDescription(sbStack.toString());
suspend.getRestrictions().setZone(Constant.Zone.Hand);
return suspend;
}//ability_suspend()
/**
* <p>eqPump_Equip.</p>
*
* @param sourceCard a {@link forge.Card} object.
* @param Power a int.
* @param Tough a int.
* @param extrinsicKeywords an array of {@link java.lang.String} objects.
* @param abCost a {@link forge.card.spellability.Cost} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility eqPump_Equip(final Card sourceCard, final int Power, final int Tough, final String[] extrinsicKeywords, final Cost abCost) {
Target target = new Target(sourceCard, "Select target creature you control", "Creature.YouCtrl".split(","));
final SpellAbility equip = new Ability_Activated(sourceCard, abCost, target) {
private static final long serialVersionUID = -4960704261761785512L;
@Override
public void resolve() {
Card targetCard = getTargetCard();
if (AllZoneUtil.isCardInPlay(targetCard)
&& CardFactoryUtil.canTarget(sourceCard, targetCard)) {
if (sourceCard.isEquipping()) {
Card crd = sourceCard.getEquipping().get(0);
if (crd.equals(targetCard)) return;
sourceCard.unEquipCard(crd);
}
sourceCard.equipCard(targetCard);
}
}
// An animated artifact equipmemt can't equip a creature
@Override
public boolean canPlay() {
return AllZone.getZone(sourceCard).is(Constant.Zone.Battlefield)
&& !sourceCard.isCreature()
&& Phase.canCastSorcery(sourceCard.getController());
}
@Override
public boolean canPlayAI() {
return getCreature().size() != 0
&& !sourceCard.isEquipping();
}
@Override
public void chooseTargetAI() {
Card target = CardFactoryUtil.AI_getBestCreature(getCreature());
setTargetCard(target);
}
CardList getCreature() {
CardList list = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer());
list = list.filter(new CardListFilter() {
public boolean addCard(Card c) {
return c.isCreature()
&& (CombatUtil.canAttack(c) || (CombatUtil.canAttackNextTurn(c) && AllZone.getPhase().is(Constant.Phase.Main2)))
&& CardFactoryUtil.canTarget(sourceCard, c)
&& (c.getNetDefense() + Tough > 0 || sourceCard.getName().equals("Skullclamp"));
}
});
// Is there at least 1 Loxodon Punisher and/or Goblin Gaveleer to target
CardList equipMagnetList = list;
equipMagnetList = equipMagnetList.getEquipMagnets();
if (!equipMagnetList.isEmpty() && Tough >= 0) {
return equipMagnetList;
}
// This equipment is keyword only
if (Power == 0 && Tough == 0) {
list = list.filter(new CardListFilter() {
public boolean addCard(Card c) {
ArrayList<String> extKeywords = new ArrayList<String>(Arrays.asList(extrinsicKeywords));
for (String s : extKeywords) {
// We want to give a new keyword
if (!c.hasKeyword(s))
return true;
}
//no new keywords:
return false;
}
});
}
return list;
}//getCreature()
};//equip ability
String costDesc = abCost.toString();
//get rid of the ": " at the end
costDesc = costDesc.substring(0, costDesc.length() - 2);
StringBuilder sbDesc = new StringBuilder();
sbDesc.append("Equip");
if (!abCost.isOnlyManaCost()) sbDesc.append(" -");
sbDesc.append(" ").append(costDesc);
equip.setDescription(sbDesc.toString());
return equip;
}//eqPump_Equip() ( was vanila_equip() )
/**
* <p>eqPump_onEquip.</p>
*
* @param sourceCard a {@link forge.Card} object.
* @param Power a int.
* @param Tough a int.
* @param extrinsicKeywords an array of {@link java.lang.String} objects.
* @param abCost a {@link forge.card.spellability.Cost} object.
* @return a {@link forge.Command} object.
*/
public static Command eqPump_onEquip(final Card sourceCard, final int Power, final int Tough, final String[] extrinsicKeywords, final Cost abCost) {
Command onEquip = new Command() {
private static final long serialVersionUID = 8130682765214560887L;
public void execute() {
if (sourceCard.isEquipping()) {
Card crd = sourceCard.getEquipping().get(0);
for (int i = 0; i < extrinsicKeywords.length; i++) {
if (!(extrinsicKeywords[i].equals("none"))
&& (!crd.hasKeyword(extrinsicKeywords[i]))) // prevent Flying, Flying
crd.addExtrinsicKeyword(extrinsicKeywords[i]);
}
crd.addSemiPermanentAttackBoost(Power);
crd.addSemiPermanentDefenseBoost(Tough);
}
}//execute()
};//Command
return onEquip;
}//eqPump_onEquip ( was vanila_onequip() )
/**
* <p>eqPump_unEquip.</p>
*
* @param sourceCard a {@link forge.Card} object.
* @param Power a int.
* @param Tough a int.
* @param extrinsicKeywords an array of {@link java.lang.String} objects.
* @param abCost a {@link forge.card.spellability.Cost} object.
* @return a {@link forge.Command} object.
*/
public static Command eqPump_unEquip(final Card sourceCard, final int Power, final int Tough, final String[] extrinsicKeywords, final Cost abCost) {
Command onUnEquip = new Command() {
private static final long serialVersionUID = 5783423127748320501L;
public void execute() {
if (sourceCard.isEquipping()) {
Card crd = sourceCard.getEquipping().get(0);
for (int i = 0; i < extrinsicKeywords.length; i++) {
crd.removeExtrinsicKeyword(extrinsicKeywords[i]);
}
crd.addSemiPermanentAttackBoost(-1 * Power);
crd.addSemiPermanentDefenseBoost(-1 * Tough);
}
}//execute()
};//Command
return onUnEquip;
}//eqPump_unEquip ( was vanila_unequip() )
/* Auras which used enPump have been converted to AF_Attach
/**
* <p>enPump_Enchant.</p>
*
* @param sourceCard a {@link forge.Card} object.
* @param Power a int.
* @param Tough a int.
* @param extrinsicKeywords an array of {@link java.lang.String} objects.
* @param spellDescription an array of {@link java.lang.String} objects.
* @param stackDescription an array of {@link java.lang.String} objects.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/ /*
public static SpellAbility enPump_Enchant(final Card sourceCard, final int Power, final int Tough, final String[] extrinsicKeywords,
final String[] spellDescription, final String[] stackDescription) {
Cost cost = new Cost(sourceCard.getManaCost(), sourceCard.getName(), true);
Target tgt = new Target(sourceCard, "C");
final SpellAbility enchant = new Spell_Permanent(sourceCard, cost, tgt) {
private static final long serialVersionUID = -8259560434384053776L;
public boolean canPlayAI() {
CardList list = AllZoneUtil.getCreaturesInPlay(AllZone.getComputerPlayer());
if (list.isEmpty()) return false;
//else (is there a Rabid Wombat or a Uril, the Miststalker to target?)
if (Tough >= -1) { // we want Rabid Wombat or a Uril, the Miststalker to gain at least +1 toughness
CardList auraMagnetList = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer());
auraMagnetList = auraMagnetList.getEnchantMagnets();
if (!auraMagnetList.isEmpty()) { // AI has a special target creature(s) to enchant
auraMagnetList.shuffle();
for (int i = 0; i < auraMagnetList.size(); i++) {
if (CardFactoryUtil.canTarget(sourceCard, auraMagnetList.get(i))) {
setTargetCard(auraMagnetList.get(i)); // Target only Rabid Wombat or Uril, the Miststalker
return true;
}
}
}
}
//
// else (if aura is keyword only)
// Do not duplicate keyword or enchant card with Defender or enchant card already enchanted
//
if (Power == 0 && Tough == 0) {
list = list.filter(new CardListFilter() {
public boolean addCard(Card c) {
ArrayList<String> extKeywords = new ArrayList<String>(Arrays.asList(extrinsicKeywords));
for (String s : extKeywords) {
if (!c.hasKeyword(s)
&& !c.hasKeyword("Defender")
&& !c.isEnchanted())
return true;
}
// no new keywords:
return false;
}
});
}
//
// else aura is power/toughness boost and may have keyword(s)
// Do not reduce power to <= zero or kill by reducing toughness to <= zero
// Do not enchant card with Defender or enchant card already enchanted
//
CardListUtil.sortAttack(list);
CardListUtil.sortFlying(list);
for (int i = 0; i < list.size(); i++) {
if (CardFactoryUtil.canTarget(sourceCard, list.get(i))
&& list.get(i).getNetAttack() + Power > 0
&& list.get(i).getNetDefense() + Tough > 0
&& !list.get(i).hasKeyword("Defender")
&& !list.get(i).isEnchanted()) {
setTargetCard(list.get(i));
return true;
}
}
return false;
}//canPlayAI()
@Override
public void resolve() {
Card aura = AllZone.getGameAction().moveToPlay(sourceCard);
Card c = getTargetCard();
// i think this is checked for already in fizzle?
if (AllZoneUtil.isCardInPlay(c) && CardFactoryUtil.canTarget(sourceCard, c)) {
aura.enchantCard(c);
}
}//resolve()
};//enchant ability
if (!spellDescription[0].replaceAll("[\\+\\-]", "").equals("Enchanted creature gets 0/0.")) {
enchant.setDescription(spellDescription[0]);
}
enchant.setStackDescription(stackDescription[0]);
return enchant;
}//enPump_Enchant() */
/* Auras which used enPump have been converted to AF_Attach
/**
* <p>enPump_onEnchant.</p>
*
* @param sourceCard a {@link forge.Card} object.
* @param Power a int.
* @param Tough a int.
* @param extrinsicKeywords an array of {@link java.lang.String} objects.
* @param spellDescription an array of {@link java.lang.String} objects.
* @param stackDescription an array of {@link java.lang.String} objects.
* @return a {@link forge.Command} object.
*/ /*
public static Command enPump_onEnchant(final Card sourceCard, final int Power, final int Tough, final String[] extrinsicKeywords,
final String[] spellDescription, final String[] stackDescription) {
Command onEnchant = new Command() {
private static final long serialVersionUID = -357890638647936585L;
public void execute() {
if (sourceCard.isEnchanting()) {
Card crd = sourceCard.getEnchanting().get(0);
for (int i = 0; i < extrinsicKeywords.length; i++) {
if (!(extrinsicKeywords[i].equals("none"))
&& (!crd.hasKeyword(extrinsicKeywords[i]))) // prevent Flying, Flying
crd.addExtrinsicKeyword(extrinsicKeywords[i]);
}
crd.addSemiPermanentAttackBoost(Power);
crd.addSemiPermanentDefenseBoost(Tough);
}
}//execute()
};//Command
return onEnchant;
}//enPump_onEnchant */
/* Auras which used enPump have been converted to AF_Attach
/**
* <p>enPump_unEnchant.</p>
*
* @param sourceCard a {@link forge.Card} object.
* @param Power a int.
* @param Tough a int.
* @param extrinsicKeywords an array of {@link java.lang.String} objects.
* @param spellDescription an array of {@link java.lang.String} objects.
* @param stackDescription an array of {@link java.lang.String} objects.
* @return a {@link forge.Command} object.
*/ /*
public static Command enPump_unEnchant(final Card sourceCard, final int Power, final int Tough, final String[] extrinsicKeywords,
final String[] spellDescription, final String[] stackDescription) {
Command onUnEnchant = new Command() {
private static final long serialVersionUID = -7121856650546173401L;
public void execute() {
if (sourceCard.isEnchanting()) {
Card crd = sourceCard.getEnchanting().get(0);
for (int i = 0; i < extrinsicKeywords.length; i++) {
crd.removeExtrinsicKeyword(extrinsicKeywords[i]);
}
crd.addSemiPermanentAttackBoost(-1 * Power);
crd.addSemiPermanentDefenseBoost(-1 * Tough);
}
}//execute()
};//Command
return onUnEnchant;
}//enPump_unEnchant */
/* Auras which used enPump have been converted to AF_Attach
/**
* <p>enPump_LeavesPlay.</p>
*
* @param sourceCard a {@link forge.Card} object.
* @param Power a int.
* @param Tough a int.
* @param extrinsicKeywords an array of {@link java.lang.String} objects.
* @param spellDescription an array of {@link java.lang.String} objects.
* @param stackDescription an array of {@link java.lang.String} objects.
* @return a {@link forge.Command} object.
*/ /*
public static Command enPump_LeavesPlay(final Card sourceCard, final int Power, final int Tough, final String[] extrinsicKeywords,
final String[] spellDescription, final String[] stackDescription) {
Command onLeavesPlay = new Command() {
private static final long serialVersionUID = -924212760053167271L;
public void execute() {
if (sourceCard.isEnchanting()) {
Card crd = sourceCard.getEnchanting().get(0);
sourceCard.unEnchantCard(crd);
}
}//execute()
};//Command
return onLeavesPlay;
}//enPump_LeavesPlay */
/* Auras which used enPumpCurse have been converted to AF_Attach
/**
* <p>enPumpCurse_Enchant.</p>
*
* @param sourceCard a {@link forge.Card} object.
* @param Power a int.
* @param Tough a int.
* @param extrinsicKeywords an array of {@link java.lang.String} objects.
* @param spellDescription an array of {@link java.lang.String} objects.
* @param stackDescription an array of {@link java.lang.String} objects.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/ /*
public static SpellAbility enPumpCurse_Enchant(final Card sourceCard, final int Power, final int Tough, final String[] extrinsicKeywords,
final String[] spellDescription, final String[] stackDescription) {
Cost cost = new Cost(sourceCard.getManaCost(), sourceCard.getName(), true);
Target tgt = new Target(sourceCard, "C");
final SpellAbility enchant = new Spell_Permanent(sourceCard, cost, tgt) {
private static final long serialVersionUID = -4021229901439299033L;
public boolean canPlayAI() {
CardList list = AllZoneUtil.getCreaturesInPlay(AllZone.getHumanPlayer());
if (list.isEmpty()) return false;
//else we may need to filter the list and remove inappropriate targets
final ArrayList<String> extKeywords = new ArrayList<String>(Arrays.asList(extrinsicKeywords));
list = list.filter(new CardListFilter() {
public boolean addCard(Card c) {
for (String s : extKeywords) {
// If extrinsicKeywords contains "CARDNAME attacks each turn if able." then remove creatures
// with Defender and creatures with the keyword "CARDNAME attacks each turn if able."
// and creatures with a keyword that starts with "CARDNAME can't attack"
//
if (s.contains("CARDNAME attacks each turn if able.")) {
if (c.hasKeyword("Defender")
|| c.hasKeyword("CARDNAME attacks each turn if able.")
|| c.hasStartOfKeyword("CARDNAME can't attack"))
return false;
}
// If extrinsicKeywords contains "CARDNAME can't attack." or "CARDNAME can't attack or block."
// then remove creatures with Defender and remove creatures that have one or more of these
// keywords to start with
//
if (s.contains("CARDNAME can't attack.") || s.contains("CARDNAME can't attack or block.")) {
if (c.hasKeyword("Defender")
|| c.hasKeyword("CARDNAME can't attack.")
|| c.hasKeyword("CARDNAME can't attack or block."))
return false;
}
// If extrinsicKeywords contains "CARDNAME doesn't untap during your untap step."
// then remove creatures with Vigilance that are untapped and remove creatures that have the keyword
// "CARDNAME doesn't untap during your untap step." and remove creatures that are untapped
//
if (s.contains("CARDNAME doesn't untap during your untap step.")) {
if ((c.hasKeyword("Vigilance") && c.isUntapped())
|| c.hasKeyword("CARDNAME doesn't untap during your untap step.")
|| c.isUntapped())
return false;
}
}
// Card c is a potential target if we reach this point.
return true;
}
});
//
// If extrinsicKeywords contains "CARDNAME can't attack." or "CARDNAME can't attack or block."
// then remove creatures with Defender from the list and remove creatures that have one
// or more of these keywords to start with
//
final ArrayList<String> extKeywords = new ArrayList<String>(Arrays.asList(extrinsicKeywords));
if (extKeywords.contains("CARDNAME can't attack.") || extKeywords.contains("CARDNAME can't attack or block.")) {
list = list.filter(new CardListFilter() {
public boolean addCard(Card c) {
return c.isCreature()
&& !c.hasKeyword("Defender")
&& !c.hasKeyword("CARDNAME can't attack.")
&& !c.hasKeyword("CARDNAME can't attack or block.");
}
});
}
// If extrinsicKeywords contains "CARDNAME doesn't untap during your untap step."
// then remove creatures with Vigilance from the list
//
if (extKeywords.contains("HIDDEN CARDNAME doesn't untap during your untap step.")) {
list = list.filter(new CardListFilter() {
public boolean addCard(Card c) {
if (c.hasKeyword("CARDNAME doesn't untap during your untap step."))
return false;
if (c.hasKeyword("Vigilance") && c.isUntapped())
return false;
return c.isCreature() && (c.isTapped() || Power < 1);
}
});
}
//else (if aura is keyword only or is Cagemail)
if (Power >= 0 && Tough >= 0) { // This aura is keyword only or is Cagemail
list = list.filter(new CardListFilter() {
public boolean addCard(Card c){
ArrayList<String> extKeywords = new ArrayList<String>(Arrays.asList(extrinsicKeywords));
for (String s:extKeywords) {
if (!c.hasKeyword(s))
return true;
}
//no new keywords:
return false;
}
});
}
//else aura is power/toughness decrease and may have keyword(s)
//
CardListUtil.sortAttack(list);
CardListUtil.sortFlying(list);
for (int i = 0; i < list.size(); i++) {
if (CardFactoryUtil.canTarget(sourceCard, list.get(i))) {
setTargetCard(list.get(i));
return true;
}
}
return false;
}//canPlayAI()
public void resolve() {
Card aura = AllZone.getGameAction().moveToPlay(sourceCard);
Card c = getTargetCard();
if (AllZoneUtil.isCardInPlay(c) && CardFactoryUtil.canTarget(sourceCard, c)) {
aura.enchantCard(c);
}
}//resolve()
};//enchant ability
if (!spellDescription[0].replaceAll("[\\+\\-]", "").equals("Enchanted creature gets 0/0.")) {
enchant.setDescription(spellDescription[0]);
}
enchant.setStackDescription(stackDescription[0]);
return enchant;
}//enPumpCurse_Enchant() */
/**
* <p>getEldraziSpawnAbility.</p>
*
* @param c a {@link forge.Card} object.
* @return a {@link forge.card.spellability.Ability_Mana} object.
*/
public static Ability_Mana getEldraziSpawnAbility(final Card c) {
Cost cost = new Cost("Sac<1/CARDNAME>", c.getName(), true);
Ability_Mana mana = new Ability_Mana(c, cost, "1") {
private static final long serialVersionUID = -2478676548112738019L;
};
mana.setDescription("Sacrifice CARDNAME: Add 1 to your mana pool.");
return mana;
}
/**
* <p>entersBattleFieldWithCounters.</p>
*
* @param c a {@link forge.Card} object.
* @param type a {@link forge.Counters} object.
* @param n a int.
* @return a {@link forge.Command} object.
*/
public static Command entersBattleFieldWithCounters(final Card c, final Counters type, final int n) {
Command addCounters = new Command() {
private static final long serialVersionUID = 4825430555490333062L;
public void execute() {
c.addCounter(type, n);
}
};
return addCounters;
}
/**
* <p>fading.</p>
*
* @param sourceCard a {@link forge.Card} object.
* @param Power a int.
* @return a {@link forge.Command} object.
*/
public static Command fading(final Card sourceCard, final int Power) {
Command fade = new Command() {
private static final long serialVersionUID = 431920157968451817L;
public boolean firstTime = true;
public void execute() {
//testAndSet - only needed when enters the battlefield.
if (firstTime) {
sourceCard.addCounter(Counters.FADE, Power);
}
firstTime = false;
}
};
return fade;
} // fading
/**
* <p>vanishing.</p>
*
* @param sourceCard a {@link forge.Card} object.
* @param Power a int.
* @return a {@link forge.Command} object.
*/
public static Command vanishing(final Card sourceCard, final int Power) {
Command age = new Command() {
private static final long serialVersionUID = 431920157968451817L;
public boolean firstTime = true;
public void execute() {
//testAndSet - only needed when enters the battlefield
if (firstTime) {
sourceCard.addCounter(Counters.TIME, Power);
}
firstTime = false;
}
};
return age;
} // vanishing
/**
* <p>ability_Soulshift.</p>
*
* @param sourceCard a {@link forge.Card} object.
* @param Manacost a {@link java.lang.String} object.
* @return a {@link forge.Command} object.
*/
public static Command ability_Soulshift(final Card sourceCard, final String Manacost) {
final Command Soulshift = new Command() {
private static final long serialVersionUID = -4960704261761785512L;
public void execute() {
AllZone.getStack().add(soulshiftTrigger(sourceCard, Manacost));
}
};
return Soulshift;
}//ability_Soulshift()
/**
* <p>soulshiftTrigger.</p>
*
* @param sourceCard a {@link forge.Card} object.
* @param Manacost a {@link java.lang.String} object.
* @return a {@link forge.card.spellability.SpellAbility} object.
*/
public static SpellAbility soulshiftTrigger(final Card sourceCard, final String Manacost) {
final SpellAbility desc = new Ability(sourceCard, "0") {
private static final long serialVersionUID = -4960704261761785512L;
@Override
public void resolve() {
CardList cards = AllZoneUtil.getPlayerGraveyard(sourceCard.getController());
CardList sameCost = new CardList();
int Cost = CardUtil.getConvertedManaCost(Manacost);
for (int i = 0; i < cards.size(); i++) {
if ((CardUtil.getConvertedManaCost(cards.get(i).getManaCost()) <= Cost)
&& cards.get(i).isType("Spirit")) {
sameCost.add(cards.get(i));
}
}
if (sameCost.size() == 0) return;
if (sourceCard.getController().isHuman()) {
StringBuilder question = new StringBuilder();
question.append("Return target Spirit card with converted mana cost ");
question.append(Manacost).append(" or less from your graveyard to your hand?");
if (GameActionUtil.showYesNoDialog(sourceCard, question.toString())) {
Object o = GuiUtils.getChoiceOptional("Select a card", sameCost.toArray());
if (o != null) {
Card c1 = (Card) o;
AllZone.getGameAction().moveToHand(c1);
}
}
} else {
//Wiser choice should be here
Card choice = null;
sameCost.shuffle();
choice = sameCost.getCard(0);
if (!(choice == null)) {
AllZone.getGameAction().moveToHand(choice);
}
}
}// resolve()
};// SpellAbility desc
// The spell description below fails to appear in the card detail panel
StringBuilder sbDesc = new StringBuilder();
sbDesc.append("Soulshift ").append(Manacost);
sbDesc.append(" - When this permanent is put into a graveyard from play, you may return target Spirit card with converted mana cost ");
sbDesc.append(Manacost).append(" or less from your graveyard to your hand.");
desc.setDescription(sbDesc.toString());
StringBuilder sbStack = new StringBuilder();
sbStack.append(sourceCard.getName()).append(" - Soulshift ").append(Manacost);
desc.setStackDescription(sbStack.toString());
return desc;
}//soul_desc()
//CardList choices are the only cards the user can successful select
/**
* <p>input_targetSpecific.</p>
*
* @param spell a {@link forge.card.spellability.SpellAbility} object.
* @param choices a {@link forge.CardList} object.
* @param message a {@link java.lang.String} object.
* @param targeted a boolean.
* @param free a boolean.
* @return a {@link forge.gui.input.Input} object.
*/
public static Input input_targetSpecific(final SpellAbility spell, final CardList choices, final String message, final boolean targeted, final boolean free) {
return input_targetSpecific(spell, choices, message, Command.Blank, targeted, free);
}
//CardList choices are the only cards the user can successful select
/**
* <p>input_targetSpecific.</p>
*
* @param spell a {@link forge.card.spellability.SpellAbility} object.
* @param choices a {@link forge.CardList} object.
* @param message a {@link java.lang.String} object.
* @param paid a {@link forge.Command} object.
* @param targeted a boolean.
* @param free a boolean.
* @return a {@link forge.gui.input.Input} object.
*/
public static Input input_targetSpecific(final SpellAbility spell, final CardList choices, final String message, final Command paid, final boolean targeted, final boolean free) {
Input target = new Input() {
private static final long serialVersionUID = -1779224307654698954L;
@Override
public void showMessage() {
AllZone.getDisplay().showMessage(message);
ButtonUtil.enableOnlyCancel();
}
@Override
public void selectButtonCancel() {
stop();
}
@Override
public void selectCard(Card card, PlayerZone zone) {
if (targeted && !canTarget(spell, card)) {
AllZone.getDisplay().showMessage("Cannot target this card (Shroud? Protection?).");
} else if (choices.contains(card)) {
spell.setTargetCard(card);
if (spell.getManaCost().equals("0") || free) {
this.setFree(false);
AllZone.getStack().add(spell);
stop();
} else stopSetNext(new Input_PayManaCost(spell));
paid.execute();
}
}//selectCard()
};
return target;
}//input_targetSpecific()
//CardList choices are the only cards the user can successful select
/**
* <p>input_targetChampionSac.</p>
*
* @param crd a {@link forge.Card} object.
* @param spell a {@link forge.card.spellability.SpellAbility} object.
* @param choices a {@link forge.CardList} object.
* @param message a {@link java.lang.String} object.
* @param targeted a boolean.
* @param free a boolean.
* @return a {@link forge.gui.input.Input} object.
*/
public static Input input_targetChampionSac(final Card crd, final SpellAbility spell, final CardList choices, final String message, final boolean targeted, final boolean free) {
Input target = new Input() {
private static final long serialVersionUID = -3320425330743678663L;
@Override
public void showMessage() {
AllZone.getDisplay().showMessage(message);
ButtonUtil.enableOnlyCancel();
}
@Override
public void selectButtonCancel() {
AllZone.getGameAction().sacrifice(crd);
stop();
}
@Override
public void selectCard(Card card, PlayerZone zone) {
if (choices.contains(card)) {
if (card == spell.getSourceCard()) {
AllZone.getGameAction().sacrifice(spell.getSourceCard());
stop();
} else {
spell.getSourceCard().setChampionedCard(card);
AllZone.getGameAction().exile(card);
stop();
//Run triggers
HashMap<String, Object> runParams = new HashMap<String, Object>();
runParams.put("Card", spell.getSourceCard());
runParams.put("Championed", card);
AllZone.getTriggerHandler().runTrigger("Championed", runParams);
}
}
}//selectCard()
};
return target;
}//input_targetSpecific()
/**
* <p>input_equipCreature.</p>
*
* @param equip a {@link forge.card.spellability.SpellAbility} object.
* @return a {@link forge.gui.input.Input} object.
*/
public static Input input_equipCreature(final SpellAbility equip) {
Input runtime = new Input() {
private static final long serialVersionUID = 2029801495067540196L;
@Override
public void showMessage() {
//get all creatures you control
CardList list = AllZoneUtil.getCreaturesInPlay(AllZone.getHumanPlayer());
stopSetNext(input_targetSpecific(equip, list,
"Select target creature to equip", true, false));
}
};//Input
return runtime;
}
/**
* custom input method only for use in Recall
*
* @param numCards a int.
* @param recall a {@link forge.Card} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @return input
*/
public static Input input_discardRecall(final int numCards, final Card recall, final SpellAbility sa) {
Input target = new Input() {
private static final long serialVersionUID = 1942999595292561944L;
int n = 0;
@Override
public void showMessage() {
if (AllZone.getHumanHand().size() == 0) stop();
AllZone.getDisplay().showMessage("Select a card to discard");
ButtonUtil.disableAll();
}
@Override
public void selectCard(Card card, PlayerZone zone) {
if (zone.is(Constant.Zone.Hand)) {
card.getController().discard(card, sa);
n++;
//in case no more cards in hand
if (n == numCards || AllZone.getHumanHand().size() == 0) done();
else
showMessage();
}
}
void done() {
AllZone.getDisplay().showMessage("Returning cards to hand.");
AllZone.getGameAction().exile(recall);
CardList grave = AllZoneUtil.getPlayerGraveyard(AllZone.getHumanPlayer());
for (int i = 1; i <= n; i++) {
String title = "Return card from grave to hand";
Object o = GuiUtils.getChoice(title, grave.toArray());
if (o == null) break;
Card toHand = (Card) o;
grave.remove(toHand);
AllZone.getGameAction().moveToHand(toHand);
}
stop();
}
};
return target;
}//input_discardRecall()
/**
* <p>MasteroftheWildHunt_input_targetCreature.</p>
*
* @param spell a {@link forge.card.spellability.SpellAbility} object.
* @param choices a {@link forge.CardList} object.
* @param paid a {@link forge.Command} object.
* @return a {@link forge.gui.input.Input} object.
*/
public static Input MasteroftheWildHunt_input_targetCreature(final SpellAbility spell, final CardList choices, final Command paid) {
Input target = new Input() {
private static final long serialVersionUID = -1779224307654698954L;
@Override
public void showMessage() {
AllZone.getDisplay().showMessage("Select target wolf to damage for " + spell.getSourceCard());
ButtonUtil.enableOnlyCancel();
}
@Override
public void selectButtonCancel() {
stop();
}
@Override
public void selectCard(Card card, PlayerZone zone) {
if (choices.size() == 0) stop();
if (choices.contains(card)) {
spell.setTargetCard(card);
paid.execute();
stop();
}
}//selectCard()
};
return target;
}//input_MasteroftheWildHunt_input_targetCreature()
/**
* <p>modularInput.</p>
*
* @param ability a {@link forge.card.spellability.SpellAbility} object.
* @param card a {@link forge.Card} object.
* @return a {@link forge.gui.input.Input} object.
*/
public static Input modularInput(final SpellAbility ability, final Card card) {
Input modularInput = new Input() {
private static final long serialVersionUID = 2322926875771867901L;
@Override
public void showMessage() {
AllZone.getDisplay().showMessage("Select target artifact creature");
ButtonUtil.enableOnlyCancel();
}
@Override
public void selectButtonCancel() {
stop();
}
@Override
public void selectCard(Card card2, PlayerZone zone) {
if (card2.isCreature() && card2.isArtifact() && zone.is(Constant.Zone.Battlefield)
&& CardFactoryUtil.canTarget(ability, card)) {
ability.setTargetCard(card2);
ability.setStackDescription("Put " + card.getCounters(Counters.P1P1)
+ " +1/+1 counter/s from " + card + " on " + card2);
AllZone.getStack().add(ability);
stop();
}
}
};
return modularInput;
}
/**
* <p>AI_getHumanCreature.</p>
*
* @param spell a {@link forge.Card} object.
* @param targeted a boolean.
* @return a {@link forge.CardList} object.
*/
public static CardList AI_getHumanCreature(final Card spell, boolean targeted) {
CardList creature = AllZoneUtil.getCreaturesInPlay(AllZone.getHumanPlayer());
if (targeted) {
creature = creature.filter(AllZoneUtil.getCanTargetFilter(spell));
}
return creature;
}
/**
* <p>AI_getHumanCreature.</p>
*
* @param keyword a {@link java.lang.String} object.
* @param spell a {@link forge.Card} object.
* @param targeted a boolean.
* @return a {@link forge.CardList} object.
*/
public static CardList AI_getHumanCreature(final String keyword, final Card spell, final boolean targeted) {
CardList creature = AllZoneUtil.getPlayerCardsInPlay(AllZone.getHumanPlayer());
creature = creature.filter(new CardListFilter() {
public boolean addCard(Card c) {
if (targeted)
return c.isCreature()
&& c.hasKeyword(keyword)
&& canTarget(spell, c);
else
return c.isCreature()
&& c.hasKeyword(keyword);
}
});
return creature;
}//AI_getHumanCreature()
/**
* <p>AI_getHumanCreature.</p>
*
* @param toughness a int.
* @param spell a {@link forge.Card} object.
* @param targeted a boolean.
* @return a {@link forge.CardList} object.
*/
public static CardList AI_getHumanCreature(final int toughness, final Card spell, final boolean targeted) {
CardList creature = AllZoneUtil.getPlayerCardsInPlay(AllZone.getHumanPlayer());
creature = creature.filter(new CardListFilter() {
public boolean addCard(Card c) {
if (targeted) return c.isCreature() && (c.getNetDefense() <= toughness) && canTarget(spell, c);
else return c.isCreature() && (c.getNetDefense() <= toughness);
}
});
return creature;
}//AI_getHumanCreature()
/**
* <p>AI_targetHuman.</p>
*
* @return a {@link forge.CommandArgs} object.
*/
public static CommandArgs AI_targetHuman() {
return new CommandArgs() {
private static final long serialVersionUID = 8406907523134006697L;
public void execute(Object o) {
SpellAbility sa = (SpellAbility) o;
sa.setTargetPlayer(AllZone.getHumanPlayer());
}
};
}//targetHuman()
/**
* <p>getNumberOfPermanentsByColor.</p>
*
* @param color a {@link java.lang.String} object.
* @return a int.
*/
public static int getNumberOfPermanentsByColor(String color) {
CardList cards = AllZoneUtil.getCardsInPlay();
CardList coloredPerms = new CardList();
for (int i = 0; i < cards.size(); i++) {
if (CardUtil.getColors(cards.get(i)).contains(color)) coloredPerms.add(cards.get(i));
}
return coloredPerms.size();
}
/**
* <p>multipleControlled.</p>
*
* @param c a {@link forge.Card} object.
* @return a boolean.
*/
public static boolean multipleControlled(Card c) {
CardList list = AllZoneUtil.getPlayerCardsInPlay(c.getController());
list.remove(c);
return list.containsName(c.getName());
}
/**
* <p>oppHasKismet.</p>
*
* @param player a {@link forge.Player} object.
* @return a boolean.
*/
public static boolean oppHasKismet(Player player) {
Player opp = player.getOpponent();
CardList list = AllZoneUtil.getPlayerCardsInPlay(opp);
list = list.filter(new CardListFilter() {
public boolean addCard(Card c) {
return c.getName().equals("Kismet") || c.getName().equals("Frozen AEther")
|| c.getName().equals("Loxodon Gatekeeper");
}
});
return list.size() > 0;
}
/**
* <p>getNumberOfManaSymbolsControlledByColor.</p>
*
* @param colorAbb a {@link java.lang.String} object.
* @param player a {@link forge.Player} object.
* @return a int.
*/
public static int getNumberOfManaSymbolsControlledByColor(String colorAbb, Player player) {
CardList cards = AllZoneUtil.getPlayerCardsInPlay(player);
return getNumberOfManaSymbolsByColor(colorAbb, cards);
}
/**
* <p>getNumberOfManaSymbolsByColor.</p>
*
* @param colorAbb a {@link java.lang.String} object.
* @param cards a {@link forge.CardList} object.
* @return a int.
*/
public static int getNumberOfManaSymbolsByColor(String colorAbb, CardList cards) {
int count = 0;
for (int i = 0; i < cards.size(); i++) {
Card c = cards.get(i);
if (!c.isToken()) {
String manaCost = c.getManaCost();
manaCost = manaCost.trim();
count += countOccurrences(manaCost, colorAbb);
}
}
return count;
}
/**
* <p>multiplyManaCost.</p>
*
* @param manacost a {@link java.lang.String} object.
* @param multiplier a int.
* @return a {@link java.lang.String} object.
*/
public static String multiplyManaCost(String manacost, int multiplier) {
if (multiplier == 0) return "";
if (multiplier == 1) return manacost;
String tokenized[] = manacost.split("\\s");
StringBuilder sb = new StringBuilder();
if (Character.isDigit(tokenized[0].charAt(0))) //manacost starts with "colorless" number cost
{
int cost = Integer.parseInt(tokenized[0]);
cost = multiplier * cost;
tokenized[0] = "" + cost;
sb.append(tokenized[0]);
} else {
for (int i = 0; i < multiplier; i++) {
//tokenized[0] = tokenized[0] + " " + tokenized[0];
sb.append((" "));
sb.append(tokenized[0]);
}
}
for (int i = 1; i < tokenized.length; i++) {
for (int j = 0; j < multiplier; j++) {
//tokenized[i] = tokenized[i] + " " + tokenized[i];
sb.append((" "));
sb.append(tokenized[i]);
}
}
String result = sb.toString();
System.out.println("result: " + result);
result = result.trim();
return result;
}
/**
* <p>isTargetStillValid.</p>
*
* @param ability a {@link forge.card.spellability.SpellAbility} object.
* @param target a {@link forge.Card} object.
* @return a boolean.
*/
public static boolean isTargetStillValid(SpellAbility ability, Card target) {
if (AllZone.getZone(target) == null) return false; // for tokens that disappeared
Card source = ability.getSourceCard();
Target tgt = ability.getTarget();
if (tgt != null) {
// Reconfirm the Validity of a TgtValid, or if the Creature is still a Creature
if (tgt.doesTarget() && !target.isValidCard(tgt.getValidTgts(), ability.getActivatingPlayer(), ability.getSourceCard()))
return false;
// Check if the target is in the zone it needs to be in to be targeted
if (!AllZone.getZone(target).is(tgt.getZone()))
return false;
} else {
// If an Aura's target is removed before it resolves, the Aura fizzles
if (source.isAura() && !AllZone.getZone(target).is(Constant.Zone.Battlefield))
return false;
}
// Make sure it's still targetable as well
return canTarget(source, target);
}
/**
* <p>canTarget.</p>
*
* @param ability a {@link forge.card.spellability.SpellAbility} object.
* @param target a {@link forge.Card} object.
* @return a boolean.
*/
public static boolean canTarget(SpellAbility ability, Card target) {
return canTarget(ability.getSourceCard(), target);
}
/**
* <p>isColored.</p>
*
* @param c a {@link forge.Card} object.
* @return a boolean.
*/
public static boolean isColored(Card c) {
return c.isWhite() || c.isBlue() || c.isBlack() || c.isRed() || c.isGreen();
}
/**
* <p>canTarget.</p>
*
* @param spell a {@link forge.Card} object.
* @param target a {@link forge.Card} object.
* @return a boolean.
*/
public static boolean canTarget(Card spell, Card target) {
if (target == null) return true;
//System.out.println("Target:" + target);
if (target.isImmutable())
return false;
PlayerZone zone = AllZone.getZone(target);
// if zone is null, it means its on the stack
if (zone == null || !zone.is(Constant.Zone.Battlefield)) {
// targets not in play, can normally be targeted
return true;
}
if (AllZoneUtil.isCardInPlay("Spellbane Centaur", target.getController()) && target.isCreature()
&& spell.isBlue()) return false;
if (target.getName().equals("Gaea's Revenge") && !spell.isGreen()) return false;
if (hasProtectionFrom(spell, target)) return false;
if (target.getKeyword() != null) {
ArrayList<String> list = target.getKeyword();
String kw = "";
for (int i = 0; i < list.size(); i++) {
kw = list.get(i);
if (kw.equals("Shroud")) return false;
if (kw.equals("Hexproof")) {
if (!spell.getController().equals(target.getController())) return false;
}
if (kw.equals("CARDNAME can't be the target of Aura spells.")) {
if (spell.isAura() && spell.isSpell()) return false;
}
if (kw.equals("CARDNAME can't be the target of red spells or abilities from red sources.")) {
if (spell.isRed()) return false;
}
if (kw.equals("CARDNAME can't be the target of black spells.")) {
if (spell.isBlack() && spell.isSpell()) return false;
}
if (kw.equals("CARDNAME can't be the target of blue spells.")) {
if (spell.isBlue() && spell.isSpell()) return false;
}
if (kw.equals("CARDNAME can't be the target of spells.")) {
if (spell.isSpell()) return false;
}
}
}
return true;
}
//does "target" have protection from "card"?
/**
* <p>hasProtectionFrom.</p>
*
* @param card a {@link forge.Card} object.
* @param target a {@link forge.Card} object.
* @return a boolean.
*/
public static boolean hasProtectionFrom(Card card, Card target) {
if (target == null) return false;
if (target.isImmutable())
return true;
if (target.getKeyword() != null) {
ArrayList<String> list = target.getKeyword();
String kw = "";
for (int i = 0; i < list.size(); i++) {
kw = list.get(i);
if (kw.equals("Protection from white")
&& card.isWhite()
&& !card.getName().contains("White Ward")) return true;
if (kw.equals("Protection from blue")
&& card.isBlue()
&& !card.getName().contains("Blue Ward")) return true;
if (kw.equals("Protection from black")
&& card.isBlack()
&& !card.getName().contains("Black Ward")) return true;
if (kw.equals("Protection from red")
&& card.isRed()
&& !card.getName().contains("Red Ward")) return true;
if (kw.equals("Protection from green")
&& card.isGreen()
&& !card.getName().contains("Green Ward")) return true;
if (kw.equals("Protection from creatures")
&& card.isCreature()) return true;
if (kw.equals("Protection from artifacts")
&& card.isArtifact()) return true;
if (kw.equals("Protection from enchantments")
&& card.isEnchantment()
&& !card.getName().contains("Tattoo Ward")) return true;
if (kw.equals("Protection from everything")) return true;
if (kw.equals("Protection from colored spells")
&& (card.isInstant() || card.isSorcery() || card.isAura())
&& isColored(card)) return true;
if (kw.equals("Protection from Dragons")
&& card.isType("Dragon")) return true;
if (kw.equals("Protection from Demons")
&& card.isType("Demon")) return true;
if (kw.equals("Protection from Goblins")
&& card.isType("Goblin")) return true;
if (kw.equals("Protection from Clerics")
&& card.isType("Cleric")) return true;
if (kw.equals("Protection from Gorgons")
&& card.isType("Gorgon")) return true;
if (kw.startsWith("Protection:")) { //uses isValidCard
String characteristic = kw.split(":")[1];
String characteristics[] = characteristic.split(",");
if (card.isValidCard(characteristics, card.getController(), card)) return true;
}
}
}
return false;
}
/**
* <p>isCounterable.</p>
*
* @param c a {@link forge.Card} object.
* @return a boolean.
*/
public static boolean isCounterable(Card c) {
if (!c.hasKeyword("CARDNAME can't be countered.")) return true;
else return false;
}
//returns the number of equipments named "e" card c is equipped by
/**
* <p>hasNumberEquipments.</p>
*
* @param c a {@link forge.Card} object.
* @param e a {@link java.lang.String} object.
* @return a int.
*/
public static int hasNumberEquipments(Card c, String e) {
if (!c.isEquipped()) return 0;
final String equipmentName = e;
CardList list = new CardList(c.getEquippedBy().toArray());
list = list.filter(new CardListFilter() {
public boolean addCard(Card c) {
return c.getName().equals(equipmentName);
}
});
return list.size();
}
/**
* <p>getGraveyardActivationCards.</p>
*
* @param player a {@link forge.Player} object.
* @return a {@link forge.CardList} object.
*/
public static CardList getExternalZoneActivationCards(final Player player) {
StringBuilder sb = new StringBuilder();
sb.append(Constant.Zone.Graveyard).append(",");
sb.append(Constant.Zone.Exile).append(",");
sb.append(Constant.Zone.Command).append(",");
sb.append(Constant.Zone.Stack).append(",");
CardList cl = AllZoneUtil.getCardsInZone(sb.toString(), player);
cl = cl.filter(new CardListFilter() {
public boolean addCard(Card c) {
return activateFromExternalZones(c, player);
}
});
return cl;
}
/**
* <p>activateFromGrave.</p>
*
* @param c a {@link forge.Card} object.
* @param player a {@link forge.Player} object.
* @return a boolean.
*/
public static boolean activateFromExternalZones(Card c, Player player) {
if (AllZone.getZone(c).is(Constant.Zone.Graveyard)){
if (c.hasFlashback() || c.hasUnearth())
return true;
final CardList crucible = AllZoneUtil.getPlayerCardsInPlay(player, "Crucible of Worlds");
if (c.isLand() && crucible.size() > 0)
return true;
}
for (SpellAbility sa : c.getSpellAbility()) {
// TODO: Add check for Yawgmoth's Will keyword when it happens
if (AllZone.getZone(c).is(sa.getRestrictions().getZone()))
return true;
}
return false;
}
/**
* <p>countOccurrences.</p>
*
* @param arg1 a {@link java.lang.String} object.
* @param arg2 a {@link java.lang.String} object.
* @return a int.
*/
public static int countOccurrences(String arg1, String arg2) {
int count = 0;
int index = 0;
while ((index = arg1.indexOf(arg2, index)) != -1) {
++index;
++count;
}
return count;
}
/**
* <p>parseMath.</p>
*
* @param l an array of {@link java.lang.String} objects.
* @return an array of {@link java.lang.String} objects.
*/
public static String[] parseMath(String[] l) {
String[] m = {"none"};
if (l.length > 1) m[0] = l[1];
return m;
}
//parser for player targeted X variables
/**
* <p>playerXCount.</p>
*
* @param players a {@link java.util.ArrayList} object.
* @param s a {@link java.lang.String} object.
* @param source a {@link forge.Card} object.
* @return a int.
*/
public static int playerXCount(ArrayList<Player> players, String s, Card source) {
if (players.size() == 0) return 0;
final String[] l = s.split("/");
final String[] m = parseMath(l);
int n = 0;
// count valid cards on the battlefield
if (l[0].contains("Valid")) {
String restrictions = l[0].replace("Valid ", "");
final String rest[] = restrictions.split(",");
CardList cardsonbattlefield = AllZoneUtil.getCardsInPlay();
cardsonbattlefield = cardsonbattlefield.getValidCards(rest, players.get(0), source);
n = cardsonbattlefield.size();
return doXMath(n, m, source);
}
final String[] sq;
sq = l[0].split("\\.");
if (sq[0].contains("CardsInHand")) {
if (players.size() > 0) {
return doXMath(AllZoneUtil.getPlayerHand(players.get(0)).size(), m, source);
}
}
if (sq[0].contains("CardsInLibrary")) {
if (players.size() > 0) {
return doXMath(AllZoneUtil.getPlayerCardsInLibrary(players.get(0)).size(), m, source);
}
}
if (sq[0].contains("CardsInGraveyard")) {
if (players.size() > 0) {
return doXMath(AllZoneUtil.getPlayerGraveyard(players.get(0)).size(), m, source);
}
}
if (sq[0].contains("LandsInGraveyard"))
if (players.size() > 0) {
return doXMath(AllZoneUtil.getPlayerTypeInGraveyard(players.get(0), "Land").size(), m, source);
}
if (sq[0].contains("CreaturesInPlay")) {
if (players.size() > 0) {
return doXMath(AllZoneUtil.getCreaturesInPlay(players.get(0)).size(), m, source);
}
}
if (sq[0].contains("CardsInPlay")) {
if (players.size() > 0) {
return doXMath(AllZoneUtil.getPlayerCardsInPlay(players.get(0)).size(), m, source);
}
}
if (sq[0].contains("LifeTotal")) {
if (players.size() > 0) {
return doXMath(players.get(0).getLife(), m, source);
}
}
if (sq[0].contains("TopOfLibraryCMC")) {
if (players.size() > 0) {
return doXMath(AllZoneUtil.getPlayerCardsInLibrary(players.get(0), 1).getTotalConvertedManaCost(), m, source);
}
}
return doXMath(n, m, source);
}
public static int parseSVar(Card hostCard, String amount){
int num = 0;
if (amount == null)
return num;
try{
num = Integer.valueOf(amount);
}
catch(NumberFormatException e){
num = xCount(hostCard, hostCard.getSVar(amount).split("\\$")[1]);
}
return num;
}
//parser for non-mana X variables
/**
* <p>xCount.</p>
*
* @param c a {@link forge.Card} object.
* @param s a {@link java.lang.String} object.
* @return a int.
*/
public static int xCount(Card c, String s) {
int n = 0;
Player cardController = c.getController();
Player oppController = cardController.getOpponent();
final String[] l = s.split("/");
final String[] m = parseMath(l);
//accept straight numbers
if (l[0].contains("Number$")) {
String number = l[0].replace("Number$", "");
return doXMath(Integer.parseInt(number), m, c);
}
//Manapool
if (l[0].contains("ManaPool")) {
String color = l[0].split(":")[1];
return AllZone.getManaPool().getAmountOfColor(color);
}
// count valid cards on the battlefield
if (l[0].contains("Valid")) {
String restrictions = l[0].replace("Valid ", "");
restrictions = restrictions.replace("Count$", "");
final String rest[] = restrictions.split(",");
CardList cardsonbattlefield = AllZoneUtil.getCardsInPlay();
cardsonbattlefield = cardsonbattlefield.getValidCards(rest, cardController, c);
n = cardsonbattlefield.size();
return doXMath(n, m, c);
}
if (l[0].contains("ImprintedCardPower")) {
if (c.getImprinted().size() > 0) {
return c.getImprinted().get(0).getNetAttack();
}
}
if (l[0].contains("ImprintedCardToughness")) {
if (c.getImprinted().size() > 0) {
return c.getImprinted().get(0).getNetDefense();
}
}
if(l[0].contains("GreatestPowerYouControl")) {
CardList list = AllZoneUtil.getCreaturesInPlay(c.getController());
int highest = 0;
for (Card crd : list) {
if (crd.getNetAttack() > highest) highest = crd.getNetAttack();
}
return highest;
}
final String[] sq;
sq = l[0].split("\\.");
if (sq[0].contains("xPaid")) {
return c.getXManaCostPaid();
}
if (sq[0].contains("xLifePaid")) {
if (c.getController().isHuman()) {
return c.getXLifePaid();
} else {
//copied for xPaid
//not implemented for Compy
//int dam = ComputerUtil.getAvailableMana().size()- CardUtil.getConvertedManaCost(c);
//if (dam < 0) dam = 0;
//return dam;
return 0;
}
}
CardList someCards = new CardList();
//Complex counting methods
//TriggeringObjects
if (sq[0].startsWith("Triggered")) {
return doXMath((Integer) c.getTriggeringObject(sq[0].substring(9)), m, c);
}
// Count$Domain
if (sq[0].contains("Domain")) {
someCards.addAll(AllZoneUtil.getPlayerCardsInPlay(cardController));
String basic[] = {"Forest", "Plains", "Mountain", "Island", "Swamp"};
for (int i = 0; i < basic.length; i++)
if (!someCards.getType(basic[i]).isEmpty()) n++;
return doXMath(n, m, c);
}
// Count$YourLifeTotal
if (sq[0].contains("YourLifeTotal")) {
if (cardController.isComputer()) return doXMath(AllZone.getComputerPlayer().getLife(), m, c);
else if (cardController.isHuman()) return doXMath(AllZone.getHumanPlayer().getLife(), m, c);
return 0;
}
// Count$OppLifeTotal
if (sq[0].contains("OppLifeTotal")) {
if (oppController.isComputer()) return doXMath(AllZone.getComputerPlayer().getLife(), m, c);
else if (oppController.isHuman()) return doXMath(AllZone.getHumanPlayer().getLife(), m, c);
return 0;
}
// Count$YourPoisonCounters
if (sq[0].contains("YourPoisonCounters")) {
if (cardController.isComputer()) return doXMath(AllZone.getComputerPlayer().getPoisonCounters(), m, c);
else if (cardController.isHuman()) return doXMath(AllZone.getHumanPlayer().getPoisonCounters(), m, c);
return 0;
}
// Count$OppPoisonCounters
if (sq[0].contains("OppPoisonCounters")) {
if (oppController.isComputer()) return doXMath(AllZone.getComputerPlayer().getPoisonCounters(), m, c);
else if (oppController.isHuman()) return doXMath(AllZone.getHumanPlayer().getPoisonCounters(), m, c);
return 0;
}
// Count$HighestLifeTotal
if (sq[0].contains("HighestLifeTotal")) {
return Math.max(AllZone.getHumanPlayer().getLife(), AllZone.getComputerPlayer().getLife());
}
// Count$LowestLifeTotal
if (sq[0].contains("LowestLifeTotal")) {
return Math.min(AllZone.getHumanPlayer().getLife(), AllZone.getComputerPlayer().getLife());
}
// Count$TopOfLibraryCMC
if (sq[0].contains("TopOfLibraryCMC")) {
CardList topcard = AllZoneUtil.getPlayerCardsInLibrary(cardController, 1);
return doXMath(topcard.getTotalConvertedManaCost(), m, c);
}
// Count$EnchantedControllerCreatures
if (sq[0].contains("EnchantedControllerCreatures")) {
CardList EnchantedControllerInPlay = AllZoneUtil.getPlayerCardsInPlay(c.getEnchantingCard().getController());
EnchantedControllerInPlay = EnchantedControllerInPlay.getType("Creature");
return EnchantedControllerInPlay.size();
}
// Count$LowestLibrary
if (sq[0].contains("LowestLibrary")) {
return Math.min(AllZone.getHumanLibrary().size(),AllZone.getComputerLibrary().size());
}
// Count$Chroma.<mana letter>
if (sq[0].contains("Chroma")) return doXMath(
getNumberOfManaSymbolsControlledByColor(sq[1], cardController), m, c);
// Count$Hellbent.<numHB>.<numNotHB>
if (sq[0].contains("Hellbent")) {
if (cardController.hasHellbent())
return doXMath(Integer.parseInt(sq[1]), m, c); // Hellbent
else
return doXMath(Integer.parseInt(sq[2]), m, c); // not Hellbent
}
//Count$Metalcraft.<numMC>.<numNotMC>
if (sq[0].contains("Metalcraft")) {
if (cardController.hasMetalcraft())
return doXMath(Integer.parseInt(sq[1]), m, c);
else
return doXMath(Integer.parseInt(sq[2]), m, c);
}
if (sq[0].contains("Threshold")) {
if (cardController.hasThreshold())
return doXMath(Integer.parseInt(sq[1]), m, c); // Have Threshold
else
return doXMath(Integer.parseInt(sq[2]), m, c); // not Threshold
}
if (sq[0].contains("Landfall")) {
if (cardController.hasLandfall())
return doXMath(Integer.parseInt(sq[1]), m, c); // Have Landfall
else
return doXMath(Integer.parseInt(sq[2]), m, c); // not Landfall
}
if (sq[0].startsWith("Devoured")) {
final String validDevoured = l[0].split(" ")[1];
final Card csource = c;
CardList cl = c.getDevoured();
cl = cl.filter(new CardListFilter() {
public boolean addCard(Card cdev)
{
return cdev.isValidCard(validDevoured.split(","),csource.getController(),csource);
}
});
return doXMath(cl.size(),m,c);
}
// Count$CardPower
if (sq[0].contains("CardPower")) return doXMath(c.getNetAttack(), m, c);
// Count$CardToughness
if (sq[0].contains("CardToughness")) return doXMath(c.getNetDefense(), m, c);
// Count$CardPowerPlusToughness
if (sq[0].contains("CardSumPT")) return doXMath((c.getNetAttack() + c.getNetDefense()), m, c);
// Count$CardManaCost
if (sq[0].contains("CardManaCost")) return doXMath(CardUtil.getConvertedManaCost(c), m, c);
// Count$CardCounters.<counterType>
if (sq[0].contains("CardCounters"))
return doXMath(c.getCounters(Counters.getType(sq[1])), m, c);
// Count$TimesKicked
if (sq[0].contains("TimesKicked"))
return doXMath(c.getMultiKickerMagnitude(), m, c);
if (sq[0].contains("NumCounters")) {
int num = c.getCounters(Counters.getType(sq[1]));
return doXMath(num, m, c);
}
if (sq[0].contains("NumBlockingMe"))
return doXMath(AllZone.getCombat().getBlockers(c).size(), m, c);
//Count$IfMainPhase.<numMain>.<numNotMain> // 7/10
if (sq[0].contains("IfMainPhase")) {
String cPhase = AllZone.getPhase().getPhase();
if ((cPhase.equals(Constant.Phase.Main1) ||
cPhase.equals(Constant.Phase.Main2)) &&
AllZone.getPhase().getPlayerTurn().equals(cardController))
return doXMath(Integer.parseInt(sq[1]), m, c);
else
return doXMath(Integer.parseInt(sq[2]), m, c); // not Main Phase
}
//Count$ThisTurnEntered <ZoneDestination> <ZoneOrigin> <Valid>
//or
//Count$ThisTurnEntered <ZoneDestination <Valid>
if (sq[0].startsWith("ThisTurnEntered")) {
String[] workingCopy = l[0].split(" ");
String destination, origin, validFilter;
destination = workingCopy[1];
if (workingCopy[2].equals("from")) {
origin = workingCopy[3];
validFilter = workingCopy[4];
} else {
origin = "Any";
validFilter = workingCopy[2];
}
final String[] valid = validFilter.split(",");
final Card csource = c;
CardList res = ((DefaultPlayerZone) AllZone.getZone(destination, AllZone.getHumanPlayer())).getCardsAddedThisTurn(origin);
res.addAll(((DefaultPlayerZone) AllZone.getZone(destination, AllZone.getComputerPlayer())).getCardsAddedThisTurn(origin));
res = res.filter(new CardListFilter() {
public boolean addCard(Card csubject) {
return csubject.isValidCard(valid, csource.getController(), csource);
}
});
return doXMath(res.size(), m, c);
}
//Generic Zone-based counting
// Count$QualityAndZones.Subquality
// build a list of cards in each possible specified zone
// if a card was ever written to count two different zones,
// make sure they don't get added twice.
boolean MF = false, MY = false, MH = false;
boolean OF = false, OY = false, OH = false;
if (sq[0].contains("YouCtrl")) if (MF == false) {
someCards.addAll(AllZoneUtil.getPlayerCardsInPlay(cardController));
MF = true;
}
if (sq[0].contains("InYourYard")) if (MY == false) {
someCards.addAll(AllZoneUtil.getPlayerGraveyard(cardController));
MY = true;
}
if (sq[0].contains("InYourLibrary")) if (MY == false) {
someCards.addAll(AllZoneUtil.getPlayerCardsInLibrary(cardController));
MY = true;
}
if (sq[0].contains("InYourHand")) if (MH == false) {
someCards.addAll(AllZoneUtil.getPlayerHand(cardController));
MH = true;
}
if (sq[0].contains("OppCtrl")) if (OF == false) {
someCards.addAll(AllZoneUtil.getPlayerCardsInPlay(oppController));
OF = true;
}
if (sq[0].contains("InOppYard")) if (OY == false) {
someCards.addAll(AllZoneUtil.getPlayerGraveyard(oppController));
OY = true;
}
if (sq[0].contains("InOppHand")) if (OH == false) {
someCards.addAll(AllZoneUtil.getPlayerHand(oppController));
OH = true;
}
if (sq[0].contains("OnBattlefield")) {
if (MF == false) someCards.addAll(AllZoneUtil.getPlayerCardsInPlay(cardController));
if (OF == false) someCards.addAll(AllZoneUtil.getPlayerCardsInPlay(oppController));
}
if (sq[0].contains("InAllYards")) {
if (MY == false) someCards.addAll(AllZoneUtil.getPlayerGraveyard(cardController));
if (OY == false) someCards.addAll(AllZoneUtil.getPlayerGraveyard(oppController));
}
if (sq[0].contains("InAllHands")) {
if (MH == false) someCards.addAll(AllZoneUtil.getPlayerHand(cardController));
if (OH == false) someCards.addAll(AllZoneUtil.getPlayerHand(oppController));
}
// filter lists based on the specified quality
// "Clerics you control" - Count$TypeYouCtrl.Cleric
if (sq[0].contains("Type")) {
someCards = someCards.filter(new CardListFilter() {
public boolean addCard(Card c) {
if (c.isType(sq[1])) return true;
return false;
}
});
}
// "Named <CARDNAME> in all graveyards" - Count$NamedAllYards.<CARDNAME>
if (sq[0].contains("Named")) {
if (sq[1].equals("CARDNAME"))
sq[1] = c.getName();
someCards = someCards.filter(new CardListFilter() {
public boolean addCard(Card c) {
if (c.getName().equals(sq[1])) return true;
return false;
}
});
}
// Refined qualities
// "Untapped Lands" - Count$UntappedTypeYouCtrl.Land
if (sq[0].contains("Untapped")) {
someCards = someCards.filter(new CardListFilter() {
public boolean addCard(Card c) {
return !c.isTapped();
}
});
}
if (sq[0].contains("Tapped")) {
someCards = someCards.filter(new CardListFilter() {
public boolean addCard(Card c) {
return c.isTapped();
}
});
}
// "White Creatures" - Count$WhiteTypeYouCtrl.Creature
if (sq[0].contains("White")) {
someCards = someCards.filter(new CardListFilter() {
public boolean addCard(Card c) {
return CardUtil.isColor(c, Constant.Color.White);
}
});
}
if (sq[0].contains("Blue")) {
someCards = someCards.filter(new CardListFilter() {
public boolean addCard(Card c) {
return CardUtil.isColor(c, Constant.Color.Blue);
}
});
}
if (sq[0].contains("Black")) {
someCards = someCards.filter(new CardListFilter() {
public boolean addCard(Card c) {
return CardUtil.isColor(c, Constant.Color.Black);
}
});
}
if (sq[0].contains("Red")) {
someCards = someCards.filter(new CardListFilter() {
public boolean addCard(Card c) {
return CardUtil.isColor(c, Constant.Color.Red);
}
});
}
if (sq[0].contains("Green")) {
someCards = someCards.filter(new CardListFilter() {
public boolean addCard(Card c) {
return CardUtil.isColor(c, Constant.Color.Green);
}
});
}
if (sq[0].contains("Multicolor")) someCards = someCards.filter(new CardListFilter() {
public boolean addCard(Card c) {
return (CardUtil.getColors(c).size() > 1);
}
});
if (sq[0].contains("Monocolor")) someCards = someCards.filter(new CardListFilter() {
public boolean addCard(Card c) {
return (CardUtil.getColors(c).size() == 1);
}
});
//Count$CardMulticolor.<numMC>.<numNotMC>
if (sq[0].contains("CardMulticolor")) {
if (CardUtil.getColors(c).size() > 1)
return doXMath(Integer.parseInt(sq[1]), m, c);
else
return doXMath(Integer.parseInt(sq[2]), m, c);
}
// 1/10 - Count$MaxCMCYouCtrl
if (sq[0].contains("MaxCMC")) {
int mmc = 0;
int cmc = 0;
for (int i = 0; i < someCards.size(); i++) {
cmc = CardUtil.getConvertedManaCost(someCards.getCard(i).getManaCost());
if (cmc > mmc) mmc = cmc;
}
return doXMath(mmc, m, c);
}
n = someCards.size();
return doXMath(n, m, c);
}
private static int doXMath(int num, String m, Card c) {
if (m.equals("none")) return num;
String[] s = m.split("\\.");
int secondaryNum = 0;
try {
if (s.length == 2) {
secondaryNum = Integer.parseInt(s[1]);
}
} catch (Exception e) {
secondaryNum = xCount(c, c.getSVar(s[1]));
}
if (s[0].contains("Plus")) return num + secondaryNum;
else if (s[0].contains("NMinus")) return secondaryNum - num;
else if (s[0].contains("Minus")) return num - secondaryNum;
else if (s[0].contains("Twice")) return num * 2;
else if (s[0].contains("HalfUp")) return (int) (Math.ceil(num / 2.0));
else if (s[0].contains("HalfDown")) return (int) (Math.floor(num / 2.0));
else if (s[0].contains("ThirdUp")) return (int) (Math.ceil(num / 3.0));
else if (s[0].contains("ThirdDown")) return (int) (Math.floor(num / 3.0));
else if (s[0].contains("Negative")) return num * -1;
else if (s[0].contains("Times")) return num * secondaryNum;
else return num;
}
/**
* <p>doXMath.</p>
*
* @param num a int.
* @param m an array of {@link java.lang.String} objects.
* @param c a {@link forge.Card} object.
* @return a int.
*/
public static int doXMath(int num, String[] m, Card c) {
if (m.length == 0)
return num;
return doXMath(num, m[0], c);
}
/**
* <p>handlePaid.</p>
*
* @param paidList a {@link forge.CardList} object.
* @param string a {@link java.lang.String} object.
* @param source a {@link forge.Card} object.
* @return a int.
*/
public static int handlePaid(CardList paidList, String string, Card source) {
if (paidList == null || paidList.size() == 0)
return 0;
if (string.startsWith("Amount")) {
if (string.contains(".")) {
String[] splitString = string.split("\\.", 2);
return doXMath(paidList.size(), splitString[1], source);
} else
return paidList.size();
}
if (string.contains("Valid")) {
final String m[] = {"none"};
String valid = string.replace("Valid ", "");
final String[] l;
l = valid.split("/"); // separate the specification from any math
valid = l[0];
if (l.length > 1) m[0] = l[1];
CardList list = paidList.getValidCards(valid, source.getController(), source);
return doXMath(list.size(), m, source);
}
int tot = 0;
for (Card c : paidList)
tot += xCount(c, string);
return tot;
}
/**
* <p>getNumberOfMostProminentCreatureType.</p>
*
* @param list a {@link forge.CardList} object.
* @param type a {@link java.lang.String} object.
* @return a int.
*/
public static int getNumberOfMostProminentCreatureType(CardList list, String type) {
list = list.getType(type);
return list.size();
}
/**
* <p>input_UntapUpToNType.</p>
*
* @param n a int.
* @param type a {@link java.lang.String} object.
* @return a {@link forge.gui.input.Input} object.
*/
public static Input input_UntapUpToNType(final int n, final String type) {
Input untap = new Input() {
private static final long serialVersionUID = -2167059918040912025L;
int stop = n;
int count = 0;
@Override
public void showMessage() {
AllZone.getDisplay().showMessage("Select a " + type + " to untap");
ButtonUtil.enableOnlyCancel();
}
@Override
public void selectButtonCancel() {
stop();
}
@Override
public void selectCard(Card card, PlayerZone zone) {
if (card.isType(type) && zone.is(Constant.Zone.Battlefield)) {
card.untap();
count++;
if (count == stop) stop();
}
}//selectCard()
};
return untap;
}
/**
* <p>getMostProminentCreatureType.</p>
*
* @param list a {@link forge.CardList} object.
* @return a {@link java.lang.String} object.
*/
public static String getMostProminentCreatureType(CardList list) {
if (list.size() == 0) return "";
Map<String, Integer> map = new HashMap<String, Integer>();
for (Card c : list) {
ArrayList<String> typeList = c.getType();
for (String var : typeList) {
if (CardUtil.isACreatureType(var)) {
if (!map.containsKey(var)) map.put(var, 1);
else map.put(var, map.get(var) + 1);
}
}
}//for
int max = 0;
String maxType = "";
for (Entry<String, Integer> entry : map.entrySet()) {
String type = entry.getKey();
//Log.debug(type + " - " + entry.getValue());
if (max < entry.getValue()) {
max = entry.getValue();
maxType = type;
}
}
return maxType;
}
/**
* <p>getMostProminentColor.</p>
*
* @param list a {@link forge.CardList} object.
* @return a {@link java.lang.String} object.
*/
public static String getMostProminentColor(CardList list) {
Map<String, Integer> map = new HashMap<String, Integer>();
for (Card c : list) {
ArrayList<String> colorList = CardUtil.getColors(c);
for (String color : colorList) {
if (color.equals("colorless")) ;
else if (!map.containsKey(color)) map.put(color, 1);
else {
map.put(color, map.get(color) + 1);
}
}
}//for
int max = 0;
String maxColor = "";
for (Entry<String, Integer> entry : map.entrySet()) {
String color = entry.getKey();
Log.debug(color + " - " + entry.getValue());
if (max < entry.getValue()) {
max = entry.getValue();
maxColor = color;
}
}
return maxColor;
}
/**
* <p>chooseCreatureTypeAI.</p>
*
* @param c a {@link forge.Card} object.
* @return a {@link java.lang.String} object.
*/
public static String chooseCreatureTypeAI(Card c) {
String s = "";
//TODO, take into account what human has
CardList humanPlay = AllZoneUtil.getPlayerCardsInPlay(AllZone.getHumanPlayer());
CardList humanLib = AllZoneUtil.getPlayerCardsInLibrary(AllZone.getHumanPlayer());
CardList compPlay = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer());
CardList compHand = AllZoneUtil.getPlayerHand(AllZone.getComputerPlayer());
CardList compLib = AllZoneUtil.getPlayerCardsInLibrary(AllZone.getComputerPlayer());
CardList compAll = new CardList();
compAll.addAll(compLib);
compAll.addAll(compHand);
compAll.addAll(compPlay);
humanPlay = humanPlay.getType("Creature");
humanLib = humanLib.getType("Creature");
compPlay = compPlay.getType("Creature");
compHand = compHand.getType("Creature");
compAll = compAll.getType("Creature");
//Buffs
if (c.getName().equals("Conspiracy") || c.getName().equals("Cover of Darkness")
|| c.getName().equals("Belbe's Portal") || c.getName().equals("Steely Resolve")
|| c.getName().equals("Shared Triumph")) {
String type = "";
int number = 0;
type = getMostProminentCreatureType(compAll);
number = getNumberOfMostProminentCreatureType(compAll, type);
if (number >= 5) s = type;
if ((c.getName().equals("Shared Triumph") || c.getName().equals("Cover of Darkness")
|| c.getName().equals("Steely Resolve")) && compPlay.size() > 7) {
type = getMostProminentCreatureType(compPlay);
number = getNumberOfMostProminentCreatureType(compPlay, type);
if (number >= 3) s = type;
} else if ((c.getName().equals("Belbe's Portal")) && compHand.size() > 1) {
type = getMostProminentCreatureType(compHand);
number = getNumberOfMostProminentCreatureType(compHand, type);
if (number >= 2) s = type;
} else if ((c.getName().equals("Conspiracy")) && compAll.size() > 1) {
CardList turnTimber = compAll;
turnTimber = turnTimber.getName("Turntimber Ranger");
if (c.getName().equals("Conspiracy") && turnTimber.size() > 0) s = "Ally";
}
}
//Debuffs
else if (c.getName().equals("Engineered Plague")) {
String type = "";
int number = 0;
if (c.getName().equals("Engineered Plague") && humanPlay.size() > 6) {
type = getMostProminentCreatureType(humanPlay);
number = getNumberOfMostProminentCreatureType(humanPlay, type);
if (number >= 3) s = type;
else if (humanLib.size() > 0) {
type = getMostProminentCreatureType(humanLib);
number = getNumberOfMostProminentCreatureType(humanLib, type);
if (number >= 5) s = type;
}
}
}
return s;
}
/**
* <p>countBasicLandTypes.</p>
*
* @param player a {@link forge.Player} object.
* @return a int.
*/
public static int countBasicLandTypes(Player player) {
String basic[] = {"Forest", "Plains", "Mountain", "Island", "Swamp"};
CardList list = AllZoneUtil.getPlayerCardsInPlay(player);
int count = 0;
for (int i = 0; i < basic.length; i++)
if (!list.getType(basic[i]).isEmpty()) count++;
return count;
}
//total cost to pay for an attacker c, cards like Propaganda, Ghostly Prison, Collective Restraint, ...
/**
* <p>getPropagandaCost.</p>
*
* @param c a {@link forge.Card} object.
* @return a {@link java.lang.String} object.
*/
public static String getPropagandaCost(Card c) {
int cost = 0;
CardList list = AllZoneUtil.getCardsInPlay();
for (Card card : list) {
if (card.hasStartOfKeyword("Creatures can't attack unless their controller pays")) {
int KeywordPosition = card.getKeywordPosition("Creatures can't attack unless their controller pays");
String parse = card.getKeyword().get(KeywordPosition).toString();
String k[] = parse.split(":");
String restrictions[] = k[1].split(",");
if (!c.isValidCard(restrictions, card.getController(), card))
continue;
String costString = k[2];
if (costString.equals("X"))
cost += CardFactoryUtil.xCount(card, card.getSVar("X"));
else if (costString.equals("Y"))
cost += CardFactoryUtil.xCount(card, card.getSVar("Y"));
else
cost += Integer.parseInt(k[2]);
}
}
String s = Integer.toString(cost);
return s;
}
/**
* <p>getUsableManaSources.</p>
*
* @param player a {@link forge.Player} object.
* @return a int.
*/
public static int getUsableManaSources(Player player) {
CardList list = AllZoneUtil.getPlayerCardsInPlay(player);
list = list.filter(new CardListFilter() {
public boolean addCard(Card c) {
for (Ability_Mana am : c.getAIPlayableMana())
if (am.canPlay()) return true;
return false;
}
});
return list.size();
}
/**
* <p>getTopCard.</p>
*
* @param c a {@link forge.Card} object.
* @return a {@link forge.Card} object.
*/
public static Card getTopCard(Card c) {
PlayerZone lib = AllZone.getZone(Constant.Zone.Library, c.getController());
if (lib.size() > 0)
return lib.get(0);
else
return null;
}
/**
* <p>makeTokenSaproling.</p>
*
* @param controller a {@link forge.Player} object.
* @return a {@link forge.CardList} object.
*/
public static CardList makeTokenSaproling(Player controller) {
return makeToken("Saproling", "G 1 1 Saproling", controller, "G", new String[]{"Creature", "Saproling"}, 1, 1, new String[]{""});
}
/**
* <p>makeToken.</p>
*
* @param name a {@link java.lang.String} object.
* @param imageName a {@link java.lang.String} object.
* @param controller a {@link forge.Player} object.
* @param manaCost a {@link java.lang.String} object.
* @param types an array of {@link java.lang.String} objects.
* @param baseAttack a int.
* @param baseDefense a int.
* @param intrinsicKeywords an array of {@link java.lang.String} objects.
* @return a {@link forge.CardList} object.
*/
public static CardList makeToken(String name, String imageName, Player controller, String manaCost, String[] types, int baseAttack, int baseDefense, String[] intrinsicKeywords) {
CardList list = new CardList();
Card c = new Card();
c.setName(name);
c.setImageName(imageName);
//c.setController(controller);
//c.setOwner(controller);
// TODO: most tokens mana cost is 0, this needs to be fixed
//c.setManaCost(manaCost);
c.addColor(manaCost);
c.setToken(true);
for (String t : types)
c.addType(t);
c.setBaseAttack(baseAttack);
c.setBaseDefense(baseDefense);
for (String kw : intrinsicKeywords)
if (kw.startsWith("HIDDEN"))
c.addExtrinsicKeyword(kw);
else c.addIntrinsicKeyword(kw);
int multiplier = AllZoneUtil.getDoublingSeasonMagnitude(controller);
// TODO: does this need to set PlayerZone_ComesIntoPlay.SimultaneousEntry like Rite of Replication does?
for (int i = 0; i < multiplier; i++) {
Card temp = CardFactory.copyStats(c);
temp.setController(controller);
temp.setOwner(controller);
temp.setToken(true);
CardFactory.parseKeywords(temp, temp.getName());
temp = CardFactory.postFactoryKeywords(temp);
AllZone.getGameAction().moveToPlay(temp);
list.add(temp);
}
return list;
}
/**
* <p>copyTokens.</p>
*
* @param tokenList a {@link forge.CardList} object.
* @return a {@link forge.CardList} object.
*/
public static CardList copyTokens(CardList tokenList) {
CardList list = new CardList();
for (int tokenAdd = 0; tokenAdd < tokenList.size(); tokenAdd++) {
Card thisToken = tokenList.getCard(tokenAdd);
ArrayList<String> tal = thisToken.getType();
String tokenTypes[] = new String[tal.size()];
tal.toArray(tokenTypes);
ArrayList<String> kal = thisToken.getIntrinsicKeyword();
String tokenKeywords[] = new String[kal.size()];
kal.toArray(tokenKeywords);
CardList tokens = makeToken(thisToken.getName(), thisToken.getImageName(), thisToken.getController(), thisToken.getManaCost(), tokenTypes, thisToken.getBaseAttack(), thisToken.getBaseDefense(), tokenKeywords);
for (Card token : tokens)
token.setColor(thisToken.getColor());
list.addAll(tokens);
}
return list;
}
/**
* <p>getBushidoEffects.</p>
*
* @param c a {@link forge.Card} object.
* @return a {@link java.util.ArrayList} object.
*/
public static ArrayList<Ability> getBushidoEffects(Card c) {
ArrayList<String> keywords = c.getKeyword();
ArrayList<Ability> list = new ArrayList<Ability>();
final Card crd = c;
for (String kw : keywords) {
if (kw.contains("Bushido")) {
String[] parse = kw.split(" ");
String s = parse[1];
final int magnitude = Integer.parseInt(s);
Ability ability = new Ability(c, "0") {
@Override
public void resolve() {
final Command untilEOT = new Command() {
private static final long serialVersionUID = 3014846051064254493L;
public void execute() {
if (AllZoneUtil.isCardInPlay(crd)) {
crd.addTempAttackBoost(-1 * magnitude);
crd.addTempDefenseBoost(-1 * magnitude);
}
}
};
AllZone.getEndOfTurn().addUntil(untilEOT);
crd.addTempAttackBoost(magnitude);
crd.addTempDefenseBoost(magnitude);
}
};
StringBuilder sb = new StringBuilder();
sb.append(c);
sb.append(" - (Bushido) gets +");
sb.append(magnitude);
sb.append("/+");
sb.append(magnitude);
sb.append(" until end of turn.");
ability.setStackDescription(sb.toString());
list.add(ability);
}
}
return list;
}
/**
* <p>getNeededXDamage.</p>
*
* @param ability a {@link forge.card.spellability.SpellAbility} object.
* @return a int.
*/
static public int getNeededXDamage(SpellAbility ability) {
//when targeting a creature, make sure the AI won't overkill on X damage
Card target = ability.getTargetCard();
int neededDamage = -1;
Card c = ability.getSourceCard();
if (target != null && c.getText().contains("deals X damage to target") && !c.getName().equals("Death Grasp"))
neededDamage = target.getNetDefense() - target.getDamage();
return neededDamage;
}
/**
* getWorstLand(String)
* <p/>
* This function finds the worst land a player has in play based on:
* worst
* 1. tapped, basic land
* 2. tapped, non-basic land
* 3. untapped, basic land
* 4. untapped, non-basic land
* best
* <p/>
* This is useful when the AI needs to find one of its lands to sacrifice
*
* @param player - AllZone.getHumanPlayer() or AllZone.getComputerPlayer()
* @return the worst land found based on the description above
*/
public static Card getWorstLand(Player player) {
CardList lands = AllZoneUtil.getPlayerLandsInPlay(player);
return getWorstLand(lands);
}//end getWorstLand
/**
* <p>getWorstLand.</p>
*
* @param lands a {@link forge.CardList} object.
* @return a {@link forge.Card} object.
*/
public static Card getWorstLand(CardList lands) {
Card worstLand = null;
//first, check for tapped, basic lands
for (int i = 0; i < lands.size(); i++) {
Card tmp = lands.get(i);
if (tmp.isTapped() && tmp.isBasicLand()) {
worstLand = tmp;
}
}
//next, check for tapped, non-basic lands
if (worstLand == null) {
for (int i = 0; i < lands.size(); i++) {
Card tmp = lands.get(i);
if (tmp.isTapped()) {
worstLand = tmp;
}
}
}
//next, untapped, basic lands
if (worstLand == null) {
for (int i = 0; i < lands.size(); i++) {
Card tmp = lands.get(i);
if (tmp.isUntapped() && tmp.isBasicLand()) {
worstLand = tmp;
}
}
}
//next, untapped, non-basic lands
if (worstLand == null) {
for (int i = 0; i < lands.size(); i++) {
Card tmp = lands.get(i);
if (tmp.isUntapped()) {
worstLand = tmp;
}
}
}
return worstLand;
}//end getWorstLand
//may return null
/**
* <p>getRandomCard.</p>
*
* @param list a {@link forge.CardList} object.
* @return a {@link forge.Card} object.
*/
static public Card getRandomCard(CardList list) {
if (list.size() == 0) return null;
int index = random.nextInt(list.size());
return list.get(index);
}
/**
* <p>revertManland.</p>
*
* @param c a {@link forge.Card} object.
* @param cost a {@link java.lang.String} object.
* @param timeStamp a long.
* @param removeTypes an array of {@link java.lang.String} objects.
* @param removeKeywords an array of {@link java.lang.String} objects.
*/
public static void revertManland(Card c, String[] removeTypes, String[] removeKeywords, String cost, long timeStamp) {
c.setBaseAttack(0);
c.setBaseDefense(0);
for (String r : removeTypes)
c.removeType(r);
for (String k : removeKeywords)
c.removeIntrinsicKeyword(k);
//c.setManaCost(cost);
c.removeColor(cost, c, false, timeStamp);
c.unEquipAllCards();
}
/**
* <p>activateManland.</p>
*
* @param c a {@link forge.Card} object.
* @param cost a {@link java.lang.String} object.
* @return a long.
* @param attack a int.
* @param defense a int.
* @param addTypes an array of {@link java.lang.String} objects.
* @param addKeywords an array of {@link java.lang.String} objects.
*/
public static long activateManland(Card c, int attack, int defense, String[] addTypes, String[] addKeywords, String cost) {
c.setBaseAttack(attack);
c.setBaseDefense(defense);
for (String r : addTypes) {
// if the card doesn't have that type, add it
if (!c.isType(r))
c.addType(r);
}
for (String k : addKeywords) {
// if the card doesn't have that keyword, add it (careful about stackable keywords)
if (!c.getIntrinsicKeyword().contains(k))
c.addIntrinsicKeyword(k);
}
//c.setManaCost(cost);
if (cost.equals(""))
cost = "0";
long timestamp = c.addColor(cost, c, false, true);
return timestamp;
}
/**
* <p>playLandEffects.</p>
*
* @param c a {@link forge.Card} object.
*/
public static void playLandEffects(Card c) {
final Player player = c.getController();
// > 0 because land amount isn't incremented until after playLandEffects
boolean extraLand = player.getNumLandsPlayed() > 0;
if (extraLand) {
CardList fastbonds = AllZoneUtil.getPlayerCardsInPlay(player, "Fastbond");
for (final Card f : fastbonds) {
SpellAbility ability = new Ability(f, "0") {
@Override
public void resolve() {
f.getController().addDamage(1, f);
}
};
ability.setStackDescription("Fastbond - Deals 1 damage to you.");
AllZone.getStack().addSimultaneousStackEntry(ability);
}
}
}
/**
* <p>isNegativeCounter.</p>
*
* @param c a {@link forge.Counters} object.
* @return a boolean.
*/
public static boolean isNegativeCounter(Counters c) {
return c == Counters.AGE || c == Counters.BLAZE || c == Counters.BRIBERY || c == Counters.DOOM || c == Counters.ICE ||
c == Counters.M1M1 || c == Counters.M0M2 || c == Counters.M0M1 || c == Counters.TIME;
}
/**
* <p>checkEmblemKeyword.</p>
*
* @param c a {@link forge.Card} object.
* @return a {@link java.lang.String} object.
*/
public static String checkEmblemKeyword(Card c) {
if (c.hasKeyword("Artifacts, creatures, enchantments, and lands you control are indestructible."))
return "Elspeth_Emblem";
if (c.hasKeyword("Mountains you control have 'tap: This land deals 1 damage to target creature or player.'"))
return "Koth_Emblem";
return "";
}
/*
//whenever CARDNAME becomes the target of a spell or ability, ... :
public static void checkTargetingEffects(SpellAbility sa, final Card c)
{
//if (AllZoneUtil.isCardInPlay(c))
//{
if (c.hasKeyword("When CARDNAME becomes the target of a spell or ability, return CARDNAME to its owner's hand.") ) { // || (c.isCreature() && AllZoneUtil.isCardInPlay("Cowardice"))
SpellAbility ability = new Ability(c, "0")
{
public void resolve()
{
AllZone.getGameAction().moveToHand(c);
}
};
StringBuilder sb = new StringBuilder();
sb.append(c).append(" - return CARDNAME to its owner's hand.");
ability.setStackDescription(sb.toString());
AllZone.getStack().add(ability);
}
if (c.hasKeyword("When CARDNAME becomes the target of a spell or ability, destroy CARDNAME.")
|| AllZoneUtil.isCardInPlay("Horobi, Death's Wail")) {
SpellAbility ability = new Ability(c, "0")
{
public void resolve()
{
AllZone.getGameAction().destroy(c);
}
};
StringBuilder sb = new StringBuilder();
sb.append(c).append(" - destroy CARDNAME.");
ability.setStackDescription(sb.toString());
AllZone.getStack().add(ability);
}
if (c.hasKeyword("When CARDNAME becomes the target of a spell or ability, sacrifice it.")) {
SpellAbility ability = new Ability(c, "0")
{
public void resolve()
{
AllZone.getGameAction().sacrifice(c);
}
};
StringBuilder sb = new StringBuilder();
sb.append(c).append(" - sacrifice CARDNAME.");
ability.setStackDescription(sb.toString());
AllZone.getStack().add(ability);
}
//When enchanted creature becomes the target of a spell or ability, <destroy/exile/sacrifice> <that creature/CARDNAME>. (It can't be regenerated.)
ArrayList<Card> auras = c.getEnchantedBy();
for(int a=0;a<auras.size();a++)
{
final Card aura = auras.get(a);
ArrayList<String> keywords = aura.getKeyword();
for(int i=0;i<keywords.size();i++)
{
final String keyword = keywords.get(i);
if(keyword.startsWith("When enchanted creature becomes the target of a spell or ability, "))
{
final String action[] = new String[1];
action[0] = keyword.substring(66);
String stackDesc = action[0];
stackDesc = stackDesc.replace("that", "enchanted");
stackDesc = stackDesc.substring(0,1).toUpperCase().concat(stackDesc.substring(1));
stackDesc = aura.getName().concat(" (").concat(Integer.toString(aura.getUniqueNumber())).concat(") - ").concat(stackDesc);
Ability saTrigger = new Ability(aura,"0") {
public void resolve()
{
Card target = null;
boolean noRegen = false;
if(action[0].endsWith(" It can't be regenerated."))
{
noRegen = true;
action[0] = action[0].substring(0,action[0].length()-25);
}
if(action[0].endsWith("CARDNAME."))
{
target = aura;
}
else if(action[0].endsWith("that creature."))
{
target = c;
}
else
{
throw new IllegalArgumentException("There is a problem in the keyword " + keyword + "for card \"" + c.getName() + "\"");
}
if(action[0].startsWith("exile"))
{
AllZone.getGameAction().exile(target);
}
else if(action[0].startsWith("destroy"))
{
if(noRegen)
{
AllZone.getGameAction().destroyNoRegeneration(target);
}
else
{
AllZone.getGameAction().destroy(target);
}
}
else if(action[0].startsWith("sacrifice"))
{
AllZone.getGameAction().sacrifice(target);
}
else
{
throw new IllegalArgumentException("There is a problem in the keyword " + keyword + "for card \"" + c.getName() + "\"");
}
}
};
saTrigger.setStackDescription(stackDesc);
AllZone.getStack().add(saTrigger);
}
}
}
//}
}*/
}