Files
forge/src/main/java/forge/Card.java
Hellfish d9b15e6e67 *The big Multistate commit.
-Alternate states can be viewed in all deckeditors.
-Alternate states are downloaded by Download Set Card LQ Pictures automatically.
-Multistate cards act correctly outside the battlefield.
-SHOULD interact correctly with Clone / AF_CopyPermanent.
-Innistrad booster pack composition is correct (9 Commons,3 Uncommons, 1 Rare/Mythic, 1 Doublefaced of any rarity)
-oracleScript.py and setInfoScript.py are not working entirely correct for multistate cards, though the output is easily tweaked.
*Added
	Bushi Tenderfoot / Kenzo the Hardhearted
	Cloistered Youth / Unholy Fiend
	Daybreak Ranger / Nightfall Predator
	Garruk Relentless / Garruk, the Veil-Cursed
	Gatstaf Shepherd / Gatstaf Howler
	Moonmist
	Screeching Bat / Stalking Vampire
	Thraben Sentry / Thraben Militia
2011-10-25 15:17:34 +00:00

6358 lines
180 KiB
Java

package forge;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.TreeMap;
import com.esotericsoftware.minlog.Log;
import forge.Constant.Zone;
import forge.card.CardCharacteristics;
import forge.card.cardFactory.CardFactoryUtil;
import forge.card.cost.Cost;
import forge.card.mana.ManaCost;
import forge.card.spellability.Ability_Mana;
import forge.card.spellability.Ability_Triggered;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.Spell_Permanent;
import forge.card.staticAbility.StaticAbility;
import forge.card.trigger.Trigger;
import forge.item.CardDb;
/**
* <p>Card class.</p>
*
* Can now be used as keys in Tree data structures. The comparison is based
* entirely on getUniqueNumber().
*
* @author Forge
* @version $Id$
*/
public class Card extends GameEntity implements Comparable<Card> {
private static int nextUniqueNumber = 1;
private int uniqueNumber = nextUniqueNumber++;
private long value;
private CardCharacteristics[] characteristics = new CardCharacteristics[] { new CardCharacteristics(), null };
private int currentCharacteristic = 0;
private boolean isDoubleFaced = false;
private boolean isFlip = false;
private CardCharacteristics getCharacteristics() {
return characteristics[currentCharacteristic];
}
public void addAlternateState() {
characteristics[1] = new CardCharacteristics();
}
public void clearAlternateState() {
if(currentCharacteristic == 1) {
changeState();
}
characteristics[1] = null;
}
public void clearOtherState() {
characteristics[1-currentCharacteristic] = null;
}
@Override
public String getName() {
return getCharacteristics().getName();
}
@Override
public void setName(String name0) {
getCharacteristics().setName(name0);
}
public boolean isInAlternateState() {
return currentCharacteristic == 1;
}
public boolean hasAlternateState() {
return characteristics[1] != null;
}
public boolean changeState() {
if(characteristics[1-currentCharacteristic] != null) {
currentCharacteristic = 1 - currentCharacteristic;
return true;
}
return false;
}
/**
* @return the isDoubleFaced
*/
public boolean isDoubleFaced() {
return isDoubleFaced;
}
/**
* @param isDoubleFaced0 the isDoubleFaced to set
*/
public void setDoubleFaced(boolean isDoubleFaced0) {
this.isDoubleFaced = isDoubleFaced0; // TODO: Add 0 to parameter's name.
}
/**
* @return the isFlip
*/
public boolean isFlip() {
return isFlip;
}
/**
* @param isFlip0 the isFlip to set
*/
public void setFlip(boolean isFlip0) {
this.isFlip = isFlip0; // TODO: Add 0 to parameter's name.
}
private Map<Counters, Integer> counters = new TreeMap<Counters, Integer>();
private Map<String, Object> triggeringObjects = new TreeMap<String, Object>();
private ArrayList<String> staticAbilityStrings = new ArrayList<String>();
private ArrayList<String> extrinsicKeyword = new ArrayList<String>();
//Hidden keywords won't be displayed on the card
private ArrayList<String> hiddenExtrinsicKeyword = new ArrayList<String>();
private ArrayList<String> prevIntrinsicKeyword = new ArrayList<String>();
private ArrayList<Card> attachedByMindsDesire = new ArrayList<Card>();
//which equipment cards are equipping this card?
private ArrayList<Card> equippedBy = new ArrayList<Card>();
//equipping size will always be 0 or 1
//if this card is of the type equipment, what card is it currently equipping?
private ArrayList<Card> equipping = new ArrayList<Card>();
//which auras enchanted this card?
//if this card is an Aura, what Entity is it enchanting?
private GameEntity enchanting = null;
private ArrayList<String> prevType = new ArrayList<String>();
private ArrayList<String> choicesMade = new ArrayList<String>();
private ArrayList<String> targetsForChoices = new ArrayList<String>();
//changes by AF animate and continuous static effects
private ArrayList<Card_Type> changedCardTypes = new ArrayList<Card_Type>();
private ArrayList<Card_Keywords> changedCardKeywords = new ArrayList<Card_Keywords>();
private ArrayList<Object> rememberedObjects = new ArrayList<Object>();
private ArrayList<Card> imprintedCards = new ArrayList<Card>();
private Card championedCard = null;
private CardList devouredCards = new CardList();
private Map<Card, Integer> receivedDamageFromThisTurn = new TreeMap<Card, Integer>();
private Map<Card, Integer> dealtDamageToThisTurn = new TreeMap<Card, Integer>();
private Map<Card, Integer> assignedDamageMap = new TreeMap<Card, Integer>();
private boolean unCastable;
private boolean drawnThisTurn = false;
private boolean tapped;
private boolean sickness = true; //summoning sickness
private boolean token = false;
private boolean copiedToken = false;
private boolean copiedSpell = false;
private boolean spellWithChoices = false;
private boolean spellCopyingCard = false;
private boolean creatureAttackedThisTurn = false;
private boolean creatureAttackedThisCombat = false;
private boolean creatureBlockedThisCombat = false;
private boolean creatureGotBlockedThisCombat = false;
private boolean dealtDmgToHumanThisTurn = false;
private boolean dealtDmgToComputerThisTurn = false;
private boolean sirenAttackOrDestroy = false;
private ArrayList<Card> mustBlockCards = new ArrayList<Card>();
private boolean canMorph = false;
private boolean faceDown = false;
private boolean kicked = false;
private boolean evoked = false;
private boolean levelUp = false;
private boolean bounceAtUntap = false;
private boolean finishedEnteringBF = false;
private boolean flashback = false;
private boolean unearth = false;
private boolean unearthed;
private boolean madness = false;
private boolean suspendCast = false;
private boolean suspend = false;
private boolean phasedOut = false;
private boolean directlyPhasedOut = true;
//for Vanguard / Manapool / Emblems etc.
private boolean isImmutable = false;
private long timestamp = -1; // permanents on the battlefield
private ArrayList<CardPowerToughness> newPT = new ArrayList<CardPowerToughness>(); // stack of set power/toughness
private int baseLoyalty = 0;
private String baseAttackString = null;
private String baseDefenseString = null;
private int damage;
// regeneration
private int nShield;
private int turnInZone;
private int tempAttackBoost = 0;
private int tempDefenseBoost = 0;
private int semiPermanentAttackBoost = 0;
private int semiPermanentDefenseBoost = 0;
private int randomPicture = 0;
private int xManaCostPaid = 0;
private int xLifePaid = 0;
private int multiKickerMagnitude = 0;
private int replicateMagnitude = 0;
private int sunburstValue = 0;
private String colorsPaid = "";
private Player owner = null;
private ArrayList<Object> controllerObjects = new ArrayList<Object>();
private String imageName = "";
//private String rarity = "";
private String text = "";
private String echoCost = "";
private String madnessCost = "";
private String chosenType = "";
//private String chosenColor = "";
private ArrayList<String> chosenColor = new ArrayList<String>();
private String namedCard = "";
private int chosenNumber;
private Player chosenPlayer;
private Card cloneOrigin = null;
private ArrayList<Card> clones = new ArrayList<Card>();
private Card currentlyCloningCard = null;
private Command cloneLeavesPlayCommand = null;
private ArrayList<Card> gainControlTargets = new ArrayList<Card>();
private ArrayList<Command> gainControlReleaseCommands = new ArrayList<Command>();
private ArrayList<Ability_Triggered> zcTriggers = new ArrayList<Ability_Triggered>();
private ArrayList<Command> equipCommandList = new ArrayList<Command>();
private ArrayList<Command> unEquipCommandList = new ArrayList<Command>();
private ArrayList<Command> enchantCommandList = new ArrayList<Command>();
private ArrayList<Command> unEnchantCommandList = new ArrayList<Command>();
private ArrayList<Command> untapCommandList = new ArrayList<Command>();
private ArrayList<Command> changeControllerCommandList = new ArrayList<Command>();
private static String[] storableSVars = {"ChosenX"};
private ArrayList<Card> hauntedBy = new ArrayList<Card>();
private Card haunting = null;
/**
*
* TODO Write javadoc for this method.
* @return a String array
*/
public static String[] getStorableSVars() { return storableSVars; }
//hacky code below, used to limit the number of times an ability
//can be used per turn like Vampire Bats
//should be put in SpellAbility, but it is put here for convienance
//this is make public just to make things easy
//this code presumes that each card only has one ability that can be
//used a limited number of times per turn
//CardFactory.SSP_canPlay(Card) uses these variables
//Only used with Replicate
private int abilityUsed;
/**
*
* TODO Write javadoc for this method.
*/
public static void resetUniqueNumber() {
nextUniqueNumber = 1;
}
/**
*
* TODO Write javadoc for this method.
* @param c a Card object
*/
public final void addDevoured(final Card c) {
devouredCards.add(c);
}
/**
*
* TODO Write javadoc for this method.
*/
public final void clearDevoured() {
devouredCards.clear();
}
/**
*
* TODO Write javadoc for this method.
* @return a CardList object
*/
public final CardList getDevoured() {
return devouredCards;
}
/**
* <p>addRemembered.</p>
*
* @param o a {@link java.lang.Object} object.
*/
public final void addRemembered(final Object o) {
rememberedObjects.add(o);
}
/**
* <p>getRemembered.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public final ArrayList<Object> getRemembered() {
return rememberedObjects;
}
/**
* <p>clearRemembered.</p>
*/
public final void clearRemembered() {
rememberedObjects.clear();
}
/**
* <p>addImprinted.</p>
*
* @param c a {@link forge.Card} object.
*/
public final void addImprinted(final Card c) {
imprintedCards.add(c);
}
/**
* <p>addImprinted.</p>
*
* @param list a {@link java.util.ArrayList} object.
*/
public final void addImprinted(final ArrayList<Card> list) {
imprintedCards.addAll(list);
}
/**
* <p>getImprinted.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public final ArrayList<Card> getImprinted() {
return imprintedCards;
}
/**
* <p>clearImprinted.</p>
*/
public final void clearImprinted() {
imprintedCards.clear();
}
/**
* <p>Setter for the field <code>championedCard</code>.</p>
*
* @param c a {@link forge.Card} object.
* @since 1.0.15
*/
public final void setChampionedCard(final Card c) {
championedCard = c;
}
/**
* <p>Getter for the field <code>championedCard</code>.</p>
*
* @return a {@link forge.Card} object.
* @since 1.0.15
*/
public final Card getChampionedCard() {
return championedCard;
}
/**
* <p>addTrigger.</p>
*
* @param t a {@link forge.card.trigger.Trigger} object.
* @return a {@link forge.card.trigger.Trigger} object.
*/
public final Trigger addTrigger(final Trigger t) {
Trigger newtrig = t.getCopy();
newtrig.setHostCard(this);
getCharacteristics().getTriggers().add(newtrig);
return newtrig;
}
public final void moveTrigger(final Trigger t) {
t.setHostCard(this);
if(!getCharacteristics().getTriggers().contains(t))
getCharacteristics().getTriggers().add(t);
}
/**
* <p>removeTrigger.</p>
*
* @param t a {@link forge.card.trigger.Trigger} object.
*/
public final void removeTrigger(final Trigger t) {
getCharacteristics().getTriggers().remove(t);
}
/**
* <p>Getter for the field <code>triggers</code>.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public final ArrayList<Trigger> getTriggers() {
return getCharacteristics().getTriggers();
}
/**
* <p>getNamedTrigger.</p>
*
* @param name a {@link java.lang.String} object.
* @return a {@link forge.card.trigger.Trigger} object.
*/
public final Trigger getNamedTrigger(final String name) {
for (Trigger t : getCharacteristics().getTriggers()) {
if (t.getName() != null && t.getName().equals(name)) {
return t;
}
}
return null;
}
/**
* <p>Setter for the field <code>triggers</code>.</p>
*
* @param trigs a {@link java.util.ArrayList} object.
*/
public final void setTriggers(final ArrayList<Trigger> trigs) {
ArrayList<Trigger> copyList = new ArrayList<Trigger>();
for (Trigger t : trigs) {
Trigger newtrig = t.getCopy();
newtrig.setHostCard(this);
copyList.add(newtrig);
}
getCharacteristics().setTriggers(copyList);
}
/**
* <p>clearTriggersNew.</p>
*/
public final void clearTriggersNew() {
getCharacteristics().getTriggers().clear();
}
/**
* <p>getTriggeringObject.</p>
*
* @param typeIn a {@link java.lang.String} object.
* @return a {@link java.lang.Object} object.
*/
public final Object getTriggeringObject(final String typeIn) {
return triggeringObjects.get(typeIn);
}
/**
field <code>abilityUsed</code>.
*
* @param i a int.
*/
public final void setAbilityUsed(final int i) {
abilityUsed = i;
}
/**
* <p>Getter for the field <code>abilityUsed</code>.</p>
*
* @return a int.
*/
public final int getAbilityUsed() {
return abilityUsed;
}
/**
* <p>Getter for the field <code>sunburstValue</code>.</p>
*
* @return a int.
*/
public final int getSunburstValue() {
return sunburstValue;
}
/**
* <p>Setter for the field <code>colorsPaid</code>.</p>
*
* @param s a String
*/
public final void setColorsPaid(final String s) {
colorsPaid = s;
}
/**
* <p>Getter for the field <code>colorsPaid</code>.</p>
*
* @return a String.
*/
public final String getColorsPaid() {
return colorsPaid;
}
/**
* <p>Setter for the field <code>sunburstValue</code>.</p>
*
* @param valueIn a int.
*/
public final void setSunburstValue(final int valueIn) {
sunburstValue = valueIn;
}
/**
* <p>addXManaCostPaid.</p>
*
* @param n a int.
*/
public final void addXManaCostPaid(final int n) {
xManaCostPaid += n;
}
/**
* <p>Setter for the field <code>xManaCostPaid</code>.</p>
*
* @param n a int.
*/
public final void setXManaCostPaid(final int n) {
xManaCostPaid = n;
}
/**
* <p>Getter for the field <code>xManaCostPaid</code>.</p>
*
* @return a int.
*/
public final int getXManaCostPaid() {
return xManaCostPaid;
}
/**
* <p>Setter for the field <code>xLifePaid</code>.</p>
*
* @param n a int.
*/
public final void setXLifePaid(final int n) {
xLifePaid = n;
}
/**
* <p>Getter for the field <code>xLifePaid</code>.</p>
*
* @return a int.
*/
public final int getXLifePaid() {
return xLifePaid;
}
//used to see if an attacking creature with a triggering attack ability triggered this phase:
/**
* <p>Setter for the field <code>creatureAttackedThisCombat</code>.</p>
*
* @param b a boolean.
*/
public final void setCreatureAttackedThisCombat(final boolean b) {
creatureAttackedThisCombat = b;
if (true == b) {
setCreatureAttackedThisTurn(true);
}
}
/**
* <p>Getter for the field <code>creatureAttackedThisCombat</code>.</p>
*
* @return a boolean.
*/
public final boolean getCreatureAttackedThisCombat() {
return creatureAttackedThisCombat;
}
/**
* <p>Setter for the field <code>creatureAttackedThisTurn</code>.</p>
*
* @param b a boolean.
*/
public final void setCreatureAttackedThisTurn(final boolean b) {
creatureAttackedThisTurn = b;
}
/**
* <p>Getter for the field <code>creatureAttackedThisTurn</code>.</p>
*
* @return a boolean.
*/
public final boolean getCreatureAttackedThisTurn() {
return creatureAttackedThisTurn;
}
/**
* <p>Setter for the field <code>creatureBlockedThisCombat</code>.</p>
*
* @param b a boolean.
*/
public final void setCreatureBlockedThisCombat(final boolean b) {
creatureBlockedThisCombat = b;
}
/**
* <p>Getter for the field <code>creatureBlockedThisCombat</code>.</p>
*
* @return a boolean.
*/
public final boolean getCreatureBlockedThisCombat() {
return creatureBlockedThisCombat;
}
/**
* <p>Setter for the field <code>creatureGotBlockedThisCombat</code>.</p>
*
* @param b a boolean.
*/
public final void setCreatureGotBlockedThisCombat(final boolean b) {
creatureGotBlockedThisCombat = b;
}
/**
* <p>Getter for the field <code>creatureGotBlockedThisCombat</code>.</p>
*
* @return a boolean.
*/
public final boolean getCreatureGotBlockedThisCombat() {
return creatureGotBlockedThisCombat;
}
/**
* <p>canAnyPlayerActivate.</p>
*
* @return a boolean.
*/
public final boolean canAnyPlayerActivate() {
for (SpellAbility s : getCharacteristics().getSpellAbility()) {
if (s.getRestrictions().getAnyPlayer()) {
return true;
}
}
return false;
}
/**
* <p>Setter for the field <code>dealtDmgToHumanThisTurn</code>.</p>
*
* @param b a boolean.
*/
public final void setDealtDmgToHumanThisTurn(final boolean b) {
dealtDmgToHumanThisTurn = b;
}
/**
* <p>Getter for the field <code>dealtDmgToHumanThisTurn</code>.</p>
*
* @return a boolean.
*/
public final boolean getDealtDmgToHumanThisTurn() {
return dealtDmgToHumanThisTurn;
}
/**
* <p>Setter for the field <code>dealtDmgToComputerThisTurn</code>.</p>
*
* @param b a boolean.
*/
public final void setDealtDmgToComputerThisTurn(final boolean b) {
dealtDmgToComputerThisTurn = b;
}
/**
* <p>Getter for the field <code>dealtDmgToComputerThisTurn</code>.</p>
*
* @return a boolean.
*/
public final boolean getDealtDmgToComputerThisTurn() {
return dealtDmgToComputerThisTurn;
}
/**
* <p>Setter for the field <code>sirenAttackOrDestroy</code>.</p>
*
* @param b a boolean.
*/
public final void setSirenAttackOrDestroy(final boolean b) {
sirenAttackOrDestroy = b;
}
/**
* <p>Getter for the field <code>sirenAttackOrDestroy</code>.</p>
*
* @return a boolean.
*/
public final boolean getSirenAttackOrDestroy() {
return sirenAttackOrDestroy;
}
/**
* a Card that this Card must block if able in an upcoming combat.
* This is cleared at the end of each turn.
*
* @param o Card to block
*
* @since 1.1.6
*/
public void addMustBlockCard(Card c) {
mustBlockCards.add(c);
}
/**
* get the Card that this Card must block this combat
*
* @return the Cards to block (if able)
*
* @since 1.1.6
*/
public ArrayList<Card> getMustBlockCards() {
return mustBlockCards;
}
/**
* clear the list of Cards that this Card must block this combat
*
* @since 1.1.6
*/
public void clearMustBlockCards() {
mustBlockCards.clear();
}
/**
* <p>Getter for the field <code>clones</code>.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public final ArrayList<Card> getClones() {
return clones;
}
/**
* <p>Setter for the field <code>clones</code>.</p>
*
* @param c a {@link java.util.ArrayList} object.
*/
public final void setClones(final ArrayList<Card> c) {
clones.clear();
clones.addAll(c);
}
/**
* <p>addClone.</p>
*
* @param c a {@link forge.Card} object.
*/
public final void addClone(final Card c) {
clones.add(c);
}
/**
* <p>addClones.</p>
*
* @param c a {@link java.util.ArrayList} object.
*/
public final void addClones(final ArrayList<Card> c) {
clones.addAll(c);
}
/**
* <p>clearClones.</p>
*/
public final void clearClones() {
clones.clear();
}
/**
* <p>Getter for the field <code>cloneOrigin</code>.</p>
*
* @return a {@link forge.Card} object.
*/
public final Card getCloneOrigin() {
return cloneOrigin;
}
/**
* <p>Setter for the field <code>cloneOrigin</code>.</p>
*
* @param name a {@link forge.Card} object.
*/
public final void setCloneOrigin(final Card name) {
cloneOrigin = name;
}
/**
* <p>Getter for the field <code>cloneLeavesPlayCommand</code>.</p>
*
* @return a {@link forge.Command} object.
*/
public final Command getCloneLeavesPlayCommand() {
return cloneLeavesPlayCommand;
}
/**
* <p>Setter for the field <code>cloneLeavesPlayCommand</code>.</p>
*
* @param com a {@link forge.Command} object.
*/
public final void setCloneLeavesPlayCommand(final Command com) {
cloneLeavesPlayCommand = com;
}
/**
* <p>Getter for the field <code>currentlyCloningCard</code>.</p>
*
* @return a {@link forge.Card} object.
*/
public final Card getCurrentlyCloningCard() {
return currentlyCloningCard;
}
/**
* <p>Setter for the field <code>currentlyCloningCard</code>.</p>
*
* @param c a {@link forge.Card} object.
*/
public final void setCurrentlyCloningCard(final Card c) {
currentlyCloningCard = c;
}
/**
* <p>Getter for the field <code>sacrificeAtEOT</code>.</p>
*
* @return a boolean.
*/
public final boolean getSacrificeAtEOT() {
return hasKeyword("At the beginning of the end step, sacrifice CARDNAME.");
}
/**
* <p>Getter for the field <code>bounceAtUntap</code>.</p>
*
* @return a boolean.
*/
public final boolean getBounceAtUntap() {
return bounceAtUntap;
}
/**
* <p>Setter for the field <code>bounceAtUntap</code>.</p>
*
* @param bounce a boolean.
*/
public final void setBounceAtUntap(final boolean bounce) {
this.bounceAtUntap = bounce;
}
/**
* <p>Getter for the field <code>finishedEnteringBF</code>.</p>
*
* @return a boolean.
*/
public final boolean getFinishedEnteringBF() {
return finishedEnteringBF;
}
/**
* <p>Setter for the field <code>finishedEnteringBF</code>.</p>
*
* @param b a boolean.
*/
public final void setFinishedEnteringBF(final boolean b) {
this.finishedEnteringBF = b;
}
/**
* <p>hasFirstStrike.</p>
*
* @return a boolean.
*/
public final boolean hasFirstStrike() {
return hasKeyword("First Strike");
}
/**
* <p>hasDoubleStrike.</p>
*
* @return a boolean.
*/
public final boolean hasDoubleStrike() {
return hasKeyword("Double Strike");
}
/**
* <p>hasSecondStrike.</p>
*
* @return a boolean.
*/
public final boolean hasSecondStrike() {
return hasDoubleStrike() || !hasFirstStrike();
}
//for costs (like Planeswalker abilities) Doubling Season gets ignored.
/**
* <p>addCounterFromNonEffect.</p>
*
* @param counterName a {@link forge.Counters} object.
* @param n a int.
*/
public final void addCounterFromNonEffect(final Counters counterName, final int n) {
if (this.hasKeyword("CARDNAME can't have counters placed on it.")) {
return;
}
if (this.hasKeyword("CARDNAME can't have -1/-1 counters placed on it.") && counterName.equals(Counters.M1M1)) {
return;
}
if (counters.containsKey(counterName)) {
Integer aux = counters.get(counterName) + n;
counters.put(counterName, aux);
} else {
counters.put(counterName, Integer.valueOf(n));
}
if (counterName.equals(Counters.P1P1) || counterName.equals(Counters.M1M1)) {
// +1/+1 counters should erase -1/-1 counters
int plusOneCounters = 0;
int minusOneCounters = 0;
Counters p1Counter = Counters.P1P1;
Counters m1Counter = Counters.M1M1;
if (counters.containsKey(p1Counter)) {
plusOneCounters = counters.get(p1Counter);
}
if (counters.containsKey(m1Counter)) {
minusOneCounters = counters.get(m1Counter);
}
if (plusOneCounters == minusOneCounters) {
counters.remove(m1Counter);
counters.remove(p1Counter);
}
if (plusOneCounters > minusOneCounters) {
counters.remove(m1Counter);
counters.put(p1Counter, (Integer) (plusOneCounters - minusOneCounters));
} else {
counters.put(m1Counter, (Integer) (minusOneCounters - plusOneCounters));
counters.remove(p1Counter);
}
}
/////////////////
//
// Not sure if we want to fire triggers on addCounterFromNonEffect
// I don't think so since reverting cost payments uses this.
/*
//Run triggers
HashMap<String,Object> runParams = new HashMap<String,Object>();
runParams.put("Card", this);
runParams.put("CounterType", counterName);
AllZone.getTriggerHandler().runTrigger("CounterAdded", runParams);
*/
this.updateObservers();
}
/**
* <p>addCounter.</p>
*
* @param counterName a {@link forge.Counters} object.
* @param n a int.
*/
public final void addCounter(final Counters counterName, final int n) {
if (this.hasKeyword("CARDNAME can't have counters placed on it.")) {
return;
}
if (this.hasKeyword("CARDNAME can't have -1/-1 counters placed on it.") && counterName.equals(Counters.M1M1)) {
return;
}
int multiplier = AllZoneUtil.getDoublingSeasonMagnitude(this.getController());
if (counters.containsKey(counterName)) {
Integer aux = counters.get(counterName) + (multiplier * n);
counters.put(counterName, aux);
} else {
counters.put(counterName, Integer.valueOf(multiplier * n));
}
//Run triggers
Map<String, Object> runParams = new TreeMap<String, Object>();
runParams.put("Card", this);
runParams.put("CounterType", counterName);
for (int i = 0; i < (multiplier * n); i++) {
AllZone.getTriggerHandler().runTrigger("CounterAdded", runParams);
}
if (counterName.equals(Counters.P1P1) || counterName.equals(Counters.M1M1)) {
// +1/+1 counters should erase -1/-1 counters
int plusOneCounters = 0;
int minusOneCounters = 0;
Counters p1Counter = Counters.P1P1;
Counters m1Counter = Counters.M1M1;
if (counters.containsKey(p1Counter)) {
plusOneCounters = counters.get(p1Counter);
}
if (counters.containsKey(m1Counter)) {
minusOneCounters = counters.get(m1Counter);
}
if (plusOneCounters == minusOneCounters) {
counters.remove(m1Counter);
counters.remove(p1Counter);
}
if (plusOneCounters > minusOneCounters) {
counters.remove(m1Counter);
counters.put(p1Counter, (Integer) (plusOneCounters - minusOneCounters));
} else {
counters.put(m1Counter, (Integer) (minusOneCounters - plusOneCounters));
counters.remove(p1Counter);
}
}
AllZone.getGameAction().checkStateEffects();
this.updateObservers();
}
/**
* <p>subtractCounter.</p>
*
* @param counterName a {@link forge.Counters} object.
* @param n a int.
*/
public final void subtractCounter(final Counters counterName, final int n) {
if (counters.containsKey(counterName)) {
Integer aux = counters.get(counterName) - n;
if (aux < 0) {
aux = 0;
}
counters.put(counterName, aux);
if (counterName.equals(Counters.TIME) && aux == 0) {
boolean hasVanish = CardFactoryUtil.hasKeyword(this, "Vanishing") != -1;
if (hasVanish && AllZoneUtil.isCardInPlay(this)) {
AllZone.getGameAction().sacrifice(this);
}
if (hasSuspend() && AllZoneUtil.isCardExiled(this)) {
final Card c = this;
c.setSuspendCast(true);
// set activating player for base spell ability
c.getSpellAbility()[0].setActivatingPlayer(c.getOwner());
// Any trigger should cause the phase not to skip
AllZone.getPhase().setSkipPhase(false);
AllZone.getGameAction().playCardNoCost(c);
}
}
AllZone.getGameAction().checkStateEffects();
this.updateObservers();
}
}
/**
* <p>Getter for the field <code>counters</code>.</p>
*
* @param counterName a {@link forge.Counters} object.
* @return a int.
*/
public final int getCounters(final Counters counterName) {
if (counters.containsKey(counterName)) {
return counters.get(counterName);
} else {
return 0;
}
}
//get all counters from a card
/**
* <p>Getter for the field <code>counters</code>.</p>
*
* @return a Map object.
* @since 1.0.15
*/
public final Map<Counters, Integer> getCounters() {
return counters;
}
/**
* <p>hasCounters.</p>
*
* @return a boolean.
*/
public final boolean hasCounters() {
return counters.size() > 0;
}
public final int getNumberOfCounters() {
int number = 0;
for(Integer i : counters.values()) {
number += i.intValue();
}
return number;
}
/**
* <p>setCounter.</p>
*
* @param counterName a {@link forge.Counters} object.
* @param n a int.
* @param bSetValue a boolean.
*/
public final void setCounter(final Counters counterName, final int n, final boolean bSetValue) {
if (this.hasKeyword("CARDNAME can't have counters placed on it.")) {
return;
}
if (this.hasKeyword("CARDNAME can't have -1/-1 counters placed on it.") && counterName.equals(Counters.M1M1)) {
return;
}
// sometimes you just need to set the value without being affected by DoublingSeason
if (bSetValue) {
counters.put(counterName, Integer.valueOf(n));
}
else {
int num = getCounters(counterName);
// if counters on card is less than the setting value, addCounters
if (num < n) {
addCounter(counterName, n - num);
} else {
subtractCounter(counterName, num - n);
}
}
this.updateObservers();
}
//get all counters from a card
/**
* <p>Setter for the field <code>counters</code>.</p>
*
* @param allCounters a Map object.
* @since 1.0.15
*/
public final void setCounters(final Map<Counters, Integer> allCounters) {
counters = allCounters;
}
//get all counters from a card
/**
* <p>clearCounters.</p>
*
* @since 1.0.15
*/
public final void clearCounters() {
counters = new TreeMap<Counters, Integer>();
}
/**
* hasLevelUp() - checks to see if a creature has the "Level up" ability introduced in Rise of the Eldrazi.
*
* @return true if this creature can "Level up", false otherwise
*/
public final boolean hasLevelUp() {
return levelUp;
}
/**
* <p>Setter for the field <code>levelUp</code>.</p>
*
* @param b a boolean.
*/
public final void setLevelUp(final boolean b) {
levelUp = b;
}
/**
* <p>getSVar.</p>
*
* @param var a {@link java.lang.String} object.
* @return a {@link java.lang.String} object.
*/
public final String getSVar(final String var) {
if (getCharacteristics().getsVars().containsKey(var)) {
return getCharacteristics().getsVars().get(var);
} else {
return "";
}
}
/**
* <p>setSVar.</p>
*
* @param var a {@link java.lang.String} object.
* @param str a {@link java.lang.String} object.
*/
public final void setSVar(final String var, final String str) {
if (getCharacteristics().getsVars().containsKey(var)) {
getCharacteristics().getsVars().remove(var);
}
getCharacteristics().getsVars().put(var, str);
}
/**
* <p>getSVars.</p>
*
* @return a Map object.
*/
public final Map<String, String> getSVars() {
return getCharacteristics().getsVars();
}
/**
* <p>setSVars.</p>
*
* @param newSVars a Map object.
*/
public final void setSVars(final Map<String, String> newSVars) {
getCharacteristics().setsVars(newSVars);
}
/**
* <p>sumAllCounters.</p>
*
* @return a int.
*/
public final int sumAllCounters() {
Object[] values = counters.values().toArray();
int count = 0;
int num = 0;
for (int i = 0; i < values.length; i++) {
num = (Integer) values[i];
count += num;
}
return count;
}
/**
* <p>getNetPTCounters.</p>
*
* @return a int.
*/
public final int getNetPTCounters() {
return getCounters(Counters.P1P1) - getCounters(Counters.M1M1);
}
/**
* <p>Getter for the field <code>turnInZone</code>.</p>
*
* @return a int.
*/
public final int getTurnInZone() {
return turnInZone;
}
/**
* <p>Setter for the field <code>turnInZone</code>.</p>
*
* @param turn a int.
*/
public final void setTurnInZone(final int turn) {
turnInZone = turn;
}
/**
* <p>Setter for the field <code>echoCost</code>.</p>
*
* @param s a {@link java.lang.String} object.
*/
public final void setEchoCost(final String s) {
echoCost = s;
}
/**
* <p>Getter for the field <code>echoCost</code>.</p>
*
* @return a {@link java.lang.String} object.
*/
public final String getEchoCost() {
return echoCost;
}
/**
* <p>Setter for the field <code>manaCost</code>.</p>
*
* @param s a {@link java.lang.String} object.
*/
public final void setManaCost(final String s) {
getCharacteristics().setManaCost(s);
}
/**
* <p>Getter for the field <code>manaCost</code>.</p>
*
* @return a {@link java.lang.String} object.
*/
public final String getManaCost() {
return getCharacteristics().getManaCost();
}
/**
* <p>addColor.</p>
*
* @param s a {@link java.lang.String} object.
*/
public final void addColor(String s) {
if (s.equals("")) {
s = "0";
}
getCharacteristics().getCardColor().add(new Card_Color(new ManaCost(s), this, false, true));
}
/**
* <p>addColor.</p>
*
* @param s a {@link java.lang.String} object.
* @param c a {@link forge.Card} object.
* @param addToColors a boolean.
* @param bIncrease a boolean.
* @return a long.
*/
public final long addColor(final String s, final Card c, final boolean addToColors, final boolean bIncrease) {
if (bIncrease) {
Card_Color.increaseTimestamp();
}
getCharacteristics().getCardColor().add(new Card_Color(new ManaCost(s), c, addToColors, false));
return Card_Color.getTimestamp();
}
/**
* <p>removeColor.</p>
*
* @param s a {@link java.lang.String} object.
* @param c a {@link forge.Card} object.
* @param addTo a boolean.
* @param timestampIn a long.
*/
public final void removeColor(final String s, final Card c, final boolean addTo, final long timestampIn) {
Card_Color removeCol = null;
for (Card_Color cc : getCharacteristics().getCardColor()) {
if (cc.equals(s, c, addTo, timestampIn)) {
removeCol = cc;
}
}
if (removeCol != null) {
getCharacteristics().getCardColor().remove(removeCol);
}
}
/**
* <p>determineColor.</p>
*
* @return a {@link forge.Card_Color} object.
*/
public final Card_Color determineColor() {
if (this.isImmutable()) {
return new Card_Color(this);
}
Card_Color colors = null;
ArrayList<Card_Color> globalChanges = AllZone.getColorChanger().getColorChanges();
colors = determineColor(globalChanges);
colors.fixColorless();
return colors;
}
/**
* <p>setColor.</p>
*
* @param colors a {@link java.util.ArrayList} object.
*/
public final void setColor(final ArrayList<Card_Color> colors) {
getCharacteristics().setCardColor(colors);
}
/**
* <p>getColor.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public final ArrayList<Card_Color> getColor() {
return getCharacteristics().getCardColor();
}
/**
*
* TODO Write javadoc for this method.
* @param globalChanges an ArrayList<Card_Color>
* @return a Card_Color
*/
final Card_Color determineColor(final ArrayList<Card_Color> globalChanges) {
Card_Color colors = new Card_Color(this);
int i = getCharacteristics().getCardColor().size() - 1;
int j = -1;
if (globalChanges != null) { j = globalChanges.size() - 1; }
// if both have changes, see which one is most recent
while (i >= 0 && j >= 0) {
Card_Color cc = null;
if (getCharacteristics().getCardColor().get(i).getStamp() > globalChanges.get(j).getStamp()) {
// Card has a more recent color stamp
cc = getCharacteristics().getCardColor().get(i);
i--;
} else {
// Global effect has a more recent color stamp
cc = globalChanges.get(j);
j--;
}
for (String s : cc.toStringArray()) {
colors.addToCardColor(s);
}
if (!cc.getAdditional()) {
return colors;
}
}
while (i >= 0) {
Card_Color cc = getCharacteristics().getCardColor().get(i);
i--;
for (String s : cc.toStringArray()) {
colors.addToCardColor(s);
}
if (!cc.getAdditional()) {
return colors;
}
}
while (j >= 0) {
Card_Color cc = globalChanges.get(j);
j--;
for (String s : cc.toStringArray()) {
colors.addToCardColor(s);
}
if (!cc.getAdditional()) {
return colors;
}
}
return colors;
}
/**
* <p>getCMC.</p>
*
* @return a int.
*/
public final int getCMC() {
return CardUtil.getConvertedManaCost(getCharacteristics().getManaCost());
}
/**
* <p>Getter for the field <code>chosenPlayer</code>.</p>
*
* @return a Player
* @since 1.1.6
*/
public final Player getChosenPlayer() {
return chosenPlayer;
}
/**
* <p>Setter for the field <code>chosenNumber</code>.</p>
*
* @param s an int
* @since 1.1.6
*/
public final void setChosenPlayer(final Player p) {
chosenPlayer = p;
}
/**
* <p>Getter for the field <code>chosenNumber</code>.</p>
*
* @return an int
*/
public final int getChosenNumber() {
return chosenNumber;
}
/**
* <p>Setter for the field <code>chosenNumber</code>.</p>
*
* @param s an int
*/
public final void setChosenNumber(final int i) {
chosenNumber = i;
}
//used for cards like Belbe's Portal, Conspiracy, Cover of Darkness, etc.
/**
* <p>Getter for the field <code>chosenType</code>.</p>
*
* @return a {@link java.lang.String} object.
*/
public final String getChosenType() {
return chosenType;
}
/**
* <p>Setter for the field <code>chosenType</code>.</p>
*
* @param s a {@link java.lang.String} object.
*/
public final void setChosenType(final String s) {
chosenType = s;
}
/**
* <p>Getter for the field <code>chosenColor</code>.</p>
*
* @return an ArrayList<String> object.
*/
public final ArrayList<String> getChosenColor() {
return chosenColor;
}
/**
* <p>Setter for the field <code>chosenColor</code>.</p>
*
* @param s an ArrayList<String> object.
*/
public final void setChosenColor(final ArrayList<String> s) {
chosenColor = s;
}
//used for cards like Meddling Mage...
/**
* <p>Getter for the field <code>namedCard</code>.</p>
*
* @return a {@link java.lang.String} object.
*/
public final String getNamedCard() {
return namedCard;
}
/**
* <p>Setter for the field <code>namedCard</code>.</p>
*
* @param s a {@link java.lang.String} object.
*/
public final void setNamedCard(final String s) {
namedCard = s;
}
/**
* <p>Setter for the field <code>drawnThisTurn</code>.</p>
*
* @param b a boolean.
*/
public final void setDrawnThisTurn(final boolean b) {
drawnThisTurn = b;
}
/**
* <p>Getter for the field <code>drawnThisTurn</code>.</p>
*
* @return a boolean.
*/
public final boolean getDrawnThisTurn() {
return drawnThisTurn;
}
/**
* get a list of Cards this card has gained control of.
* <p/>
* used primarily with AbilityFactory_GainControl
*
* @return a list of cards this card has gained control of
*/
public final ArrayList<Card> getGainControlTargets() {
return gainControlTargets;
}
/**
* add a Card to the list of Cards this card has gained control of.
* <p/>
* used primarily with AbilityFactory_GainControl
*
* @param c a {@link forge.Card} object.
*/
public final void addGainControlTarget(Card c) {
gainControlTargets.add(c);
}
/**
* clear the list of Cards this card has gained control of.
* <p/>
* used primarily with AbilityFactory_GainControl
*/
public final void clearGainControlTargets() {
gainControlTargets.clear();
}
/**
* get the commands to be executed to lose control of Cards this
* card has gained control of.
* <p/>
* used primarily with AbilityFactory_GainControl (Old Man of the Sea specifically)
*
* @return a {@link java.util.ArrayList} object.
*/
public final ArrayList<Command> getGainControlReleaseCommands() {
return gainControlReleaseCommands;
}
/**
* set a command to be executed to lose control of Cards this
* card has gained control of.
* <p/>
* used primarily with AbilityFactory_GainControl (Old Man of the Sea specifically)
*
* @param c the Command to be executed
*/
public final void addGainControlReleaseCommand(final Command c) {
gainControlReleaseCommands.add(c);
}
/**
* <p>clearGainControlReleaseCommands.</p>
*/
public final void clearGainControlReleaseCommands() {
gainControlReleaseCommands.clear();
}
/**
* <p>getSpellText.</p>
*
* @return a {@link java.lang.String} object.
*/
public final String getSpellText() {
return text;
}
/**
* <p>Setter for the field <code>text</code>.</p>
*
* @param t a {@link java.lang.String} object.
*/
public final void setText(final String t) {
text = t;
}
// get the text that should be displayed
/**
* <p>Getter for the field <code>text</code>.</p>
*
* @return a {@link java.lang.String} object.
*/
public String getText() {
StringBuilder sb = new StringBuilder();
sb.append(this.getAbilityText());
String nonAbilityText = getNonAbilityText();
if (nonAbilityText.length() > 0) {
sb.append("\r\n \r\nNon ability features: \r\n");
sb.append(nonAbilityText.replaceAll("CARDNAME", getName()));
}
return sb.toString();
}
// get the text that does not belong to a cards abilities (and is not really there rules-wise)
/**
* <p>getNonAbilityText.</p>
*
* @return a {@link java.lang.String} object.
*/
public final String getNonAbilityText() {
StringBuilder sb = new StringBuilder();
ArrayList<String> keyword = getHiddenExtrinsicKeyword();
sb.append(keywordsToText(keyword));
return sb.toString();
}
// convert a keyword list to the String that should be displayed ingame
/**
* <p>keywordsToText.</p>
*
* @param keyword a {@link java.util.ArrayList} object.
* @return a {@link java.lang.String} object.
*/
public final String keywordsToText(final ArrayList<String> keyword) {
StringBuilder sb = new StringBuilder();
StringBuilder sbLong = new StringBuilder();
StringBuilder sbMana = new StringBuilder();
for (int i = 0; i < keyword.size(); i++) {
if (!keyword.get(i).toString().contains("CostChange")
&&
!keyword.get(i).toString().contains("Permanents don't untap during their controllers' untap steps")
&&
!keyword.get(i).toString().contains("PreventAllDamageBy")
&&
!keyword.get(i).toString().contains("CantBlock")
&&
!keyword.get(i).toString().contains("CantBeBlockedBy"))
{
if (keyword.get(i).toString().contains("StaticEffect")) {
String[] k = keyword.get(i).split(":");
sbLong.append(k[5]).append("\r\n");
} else if (keyword.get(i).toString().contains("Protection:")) {
String[] k = keyword.get(i).split(":");
sbLong.append(k[2]).append("\r\n");
} else if (keyword.get(i).toString().contains("Creatures can't attack unless their controller pays")) {
String[] k = keyword.get(i).split(":");
if (!k[3].equals("no text")) {
sbLong.append(k[3]).append("\r\n");
}
} else if (keyword.get(i).startsWith("Enchant")) {
String k = keyword.get(i);
k = k.replace("Curse", "");
sbLong.append(k).append("\r\n");
} else if (keyword.get(i).startsWith("Soulshift") || keyword.get(i).startsWith("Cumulative upkeep")
|| keyword.get(i).startsWith("Echo") || keyword.get(i).startsWith("Fading")
|| keyword.get(i).startsWith("Ripple") || keyword.get(i).startsWith("Unearth")
|| keyword.get(i).startsWith("Vanishing") || keyword.get(i).startsWith("Madness")
|| keyword.get(i).startsWith("Devour")) {
String k = keyword.get(i);
k = k.replace(":", " ");
sbLong.append(k).append("\r\n");
} else if (keyword.get(i).startsWith("Champion")) {
String k = getKeyword().get(i);
String[] kk = k.split(":");
String types = kk[1];
if (kk.length > 2) {
types = kk[2];
}
if (kk[1].equals("Creature")) {
kk[1] = kk[1].toLowerCase();
}
sbLong.append("Champion a");
if (kk[1].toLowerCase().startsWith("a")
|| kk[1].toLowerCase().startsWith("e")
|| kk[1].toLowerCase().startsWith("i")
|| kk[1].toLowerCase().startsWith("o")
|| kk[1].toLowerCase().startsWith("u")) {
sbLong.append("n");
}
sbLong.append(" ").append(types);
sbLong.append(" (When this enters the battlefield, sacrifice it unless you exile another ");
sbLong.append(types);
sbLong.append(" you control. When this leaves the battlefield, that card returns to the battlefield.)\r\n");
} else if (keyword.get(i).endsWith(".") && !keyword.get(i).startsWith("Haunt")) {
sbLong.append(keyword.get(i).toString()).append("\r\n");
} else if (keyword.get(i).contains("At the beginning of your upkeep, ")
&& keyword.get(i).contains(" unless you pay"))
{
sbLong.append(keyword.get(i).toString()).append("\r\n");
} else if (keyword.get(i).toString().contains("tap: add ")) {
sbMana.append(keyword.get(i).toString()).append("\r\n");
} else if (keyword.get(i).contains("Bloodthirst")) {
String k = keyword.get(i);
String[] kk = k.split(" ");
sbLong.append(keyword.get(i)).append(" (If an opponent was dealt damage this turn, this creature enters the battlefield with ");
sbLong.append(kk[1]).append(" +1/+1 counter");
if (kk[1].equals("X")) {
sbLong.append("s on it, where X is the damage dealt to your opponents this turn.)");
sbLong.append("\r\n");
} else {
if (Integer.parseInt(kk[1]) > 1) {
sbLong.append("s");
}
sbLong.append(" on it.)").append("\r\n");
}
} else if (keyword.get(i).startsWith("Modular")) {
String numCounters = keyword.get(i).split(" ")[1];
sbLong.append(keyword.get(i));
sbLong.append(" (This enters the battlefield with ");
sbLong.append(numCounters);
sbLong.append(" +1/+1 counters on it. When it's put into a graveyard, you may put its +1/+1 counters on target artifact creature.)");
} else if (keyword.get(i).startsWith("Provoke")) {
sbLong.append(keyword.get(i));
sbLong.append(" (When this attacks, you may have target creature defending player controls untap and block it if able.)");
} else if (keyword.get(i).startsWith("MayEffectFromOpeningHand")) {
continue;
} else if (keyword.get(i).contains("Haunt")) {
sb.append("\r\nHaunt (");
if(isCreature()) {
sb.append("When this creature dies, exile it haunting target creature.");
}
else
{
sb.append("When this spell card is put into a graveyard after resolving, exile it haunting target creature.");
}
sb.append(")");
continue;
} else {
if (i != 0 && sb.length() != 0) {
sb.append(", ");
}
sb.append(keyword.get(i).toString());
}
}
}
if (sb.length() > 0) {
sb.append("\r\n\r\n");
}
if (sbLong.length() > 0) {
sbLong.append("\r\n");
}
sb.append(sbLong);
sb.append(sbMana);
return sb.toString();
}
//get the text of the abilities of a card
/**
* <p>getAbilityText.</p>
*
* @return a {@link java.lang.String} object.
*/
public String getAbilityText() {
if (isInstant() || isSorcery()) {
String s = getSpellText();
StringBuilder sb = new StringBuilder();
// Give spellText line breaks for easier reading
sb.append(s.replaceAll("\\\\r\\\\n", "\r\n"));
// NOTE:
if (sb.toString().contains(" (NOTE: ")) {
sb.insert(sb.indexOf("(NOTE: "), "\r\n");
}
if (sb.toString().contains("(NOTE: ") && sb.toString().endsWith(".)") && !sb.toString().endsWith("\r\n")) {
sb.append("\r\n");
}
// Add SpellAbilities
SpellAbility[] sa = getSpellAbility();
for (int i = 0; i < sa.length; i++) {
sb.append(sa[i].toString() + "\r\n");
}
// Add Keywords
ArrayList<String> kw = getKeyword();
// Triggered abilities
for (Trigger trig : getCharacteristics().getTriggers()) {
if (!trig.isSecondary()) {
sb.append(trig.toString() + "\r\n");
}
}
// static abilities
for (StaticAbility stAb : getCharacteristics().getStaticAbilities()) {
String stAbD = stAb.toString();
if (!stAbD.equals("")) {
sb.append(stAbD + "\r\n");
}
}
// Ripple + Dredge + Madness + CARDNAME is {color} + Recover.
for (int i = 0; i < kw.size(); i++) {
if ((kw.get(i).startsWith("Ripple") && !sb.toString().contains("Ripple"))
|| (kw.get(i).startsWith("Dredge") && !sb.toString().contains("Dredge"))
|| (kw.get(i).startsWith("Madness") && !sb.toString().contains("Madness"))
|| (kw.get(i).startsWith("CARDNAME is ") && !sb.toString().contains("CARDNAME is "))
|| (kw.get(i).startsWith("Recover") && !sb.toString().contains("Recover")))
{
sb.append(kw.get(i).replace(":", " ")).append("\r\n");
}
}
// Changeling + CARDNAME can't be countered. + Cascade + Multikicker
for (int i = 0; i < kw.size(); i++) {
if ((kw.get(i).contains("CARDNAME can't be countered.")
&& !sb.toString().contains("CARDNAME can't be countered."))
|| (kw.get(i).contains("Cascade") && !sb.toString().contains("Cascade"))
|| (kw.get(i).contains("Multikicker") && !sb.toString().contains("Multikicker")))
{
sb.append(kw.get(i)).append("\r\n");
}
}
// Storm
if (hasKeyword("Storm") && !sb.toString().contains("Storm (When you ")) {
if (sb.toString().endsWith("\r\n\r\n")) {
sb.delete(sb.lastIndexOf("\r\n"), sb.lastIndexOf("\r\n") + 3);
}
sb.append("Storm (When you cast this spell, copy it for each spell cast before it this turn.");
if (sb.toString().contains("Target") || sb.toString().contains("target")) {
sb.append(" You may choose new targets for the copies.");
}
sb.append(")\r\n");
}
//Replicate
for (String keyw : kw) {
if (keyw.contains("Replicate") && !sb.toString().contains("you paid its replicate cost.")) {
if (sb.toString().endsWith("\r\n\r\n")) {
sb.delete(sb.lastIndexOf("\r\n"), sb.lastIndexOf("\r\n") + 3);
}
sb.append(keyw);
sb.append(" (When you cast this spell, copy it for each time you paid its replicate cost.");
if (sb.toString().contains("Target") || sb.toString().contains("target")) {
sb.append(" You may choose new targets for the copies.");
}
sb.append(")\r\n");
}
}
for(String keyw : kw) {
if(keyw.startsWith("Haunt")) {
if (sb.toString().endsWith("\r\n\r\n")) {
sb.delete(sb.lastIndexOf("\r\n"), sb.lastIndexOf("\r\n") + 3);
}
sb.append("Haunt (");
if(isCreature()) {
sb.append("When this creature dies, exile it haunting target creature.");
}
else {
sb.append("When this spell card is put into a graveyard after resolving, exile it haunting target creature.");
}
sb.append(")\r\n");
}
}
if(haunting != null) {
sb.append("Haunting: ").append(haunting);
sb.append("\r\n");
}
while (sb.toString().endsWith("\r\n")) {
sb.delete(sb.lastIndexOf("\r\n"), sb.lastIndexOf("\r\n") + 3);
}
return sb.toString().replaceAll("CARDNAME", getName());
}
StringBuilder sb = new StringBuilder();
ArrayList<String> keyword = getUnhiddenKeyword();
sb.append(keywordsToText(keyword));
// Give spellText line breaks for easier reading
sb.append("\r\n");
sb.append(text.replaceAll("\\\\r\\\\n", "\r\n"));
sb.append("\r\n");
/*
* if(isAura()) {
// Give spellText line breaks for easier reading
sb.append(getSpellText().replaceAll("\\\\r\\\\n", "\r\n")).append("\r\n");
}
*/
// Triggered abilities
for (Trigger trig : getCharacteristics().getTriggers()) {
if (!trig.isSecondary()) {
sb.append(trig.toString() + "\r\n");
}
}
// static abilities
for (StaticAbility stAb : getCharacteristics().getStaticAbilities()) {
sb.append(stAb.toString() + "\r\n");
}
ArrayList<String> addedManaStrings = new ArrayList<String>();
SpellAbility[] abilities = getSpellAbility();
boolean primaryCost = true;
for (SpellAbility sa : abilities) {
// only add abilities not Spell portions of cards
if (!isPermanent()) {
continue;
}
if (sa instanceof Spell_Permanent && primaryCost && !isAura()) {
// For Alt costs, make sure to display the cost!
primaryCost = false;
continue;
}
String sAbility = sa.toString();
if (sa instanceof Ability_Mana) {
if (addedManaStrings.contains(sAbility)) {
continue;
}
addedManaStrings.add(sAbility);
}
if (sa instanceof Spell_Permanent && !isAura()) {
sb.insert(0, "\r\n");
sb.insert(0, sAbility);
} else if (!sAbility.endsWith(getName())) {
sb.append(sAbility);
sb.append("\r\n");
// The test above appears to prevent the card name from showing and therefore
//it no longer needs to be deleted from the stringbuilder
//if (sb.toString().endsWith("CARDNAME"))
// sb.replace(sb.toString().lastIndexOf("CARDNAME"),
// sb.toString().lastIndexOf("CARDNAME") + name.length() - 1, "");
}
}
// NOTE:
if (sb.toString().contains(" (NOTE: ")) {
sb.insert(sb.indexOf("(NOTE: "), "\r\n");
}
if (sb.toString().contains("(NOTE: ") && sb.toString().contains(".) ")) {
sb.insert(sb.indexOf(".) ") + 3, "\r\n");
}
// replace tripple line feeds with double line feeds
int start;
String s = "\r\n\r\n\r\n";
while (sb.toString().contains(s)) {
start = sb.lastIndexOf(s);
if (start < 0 || start >= sb.length()) {
break;
}
sb.replace(start, start + 4, "\r\n");
}
//Remembered cards
if (rememberedObjects.size() > 0) {
sb.append("\r\nRemembered: \r\n");
for (Object o : rememberedObjects) {
if (o instanceof Card) {
Card c = (Card) o;
sb.append(c.getName());
sb.append("(");
sb.append(c.getUniqueNumber());
sb.append(")");
} else {
sb.append(o.toString());
}
sb.append("\r\n");
}
}
if(hauntedBy.size() != 0) {
sb.append("Haunted by: ");
for(Card c : hauntedBy) {
sb.append(c).append(",");
}
sb.deleteCharAt(sb.length()-1);
sb.append("\r\n");
}
if(haunting != null) {
sb.append("Haunting: ").append(haunting);
sb.append("\r\n");
}
/*
sb.append("\r\nOwner: ").append(owner).append("\r\n");
sb.append("Controller(s):");
for(Object o : controllerObjects)
{
sb.append(o);
}
sb.append("\r\n");
*/
return sb.toString().replaceAll("CARDNAME", getName()).trim();
} //getText()
/**
* <p>Getter for the field <code>manaAbility</code>.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public final ArrayList<Ability_Mana> getManaAbility() {
return new ArrayList<Ability_Mana>(getCharacteristics().getManaAbility());
}
// Returns basic mana abilities plus "reflected mana" abilities
/**
* <p>getAIPlayableMana.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public final ArrayList<Ability_Mana> getAIPlayableMana() {
ArrayList<Ability_Mana> res = new ArrayList<Ability_Mana>();
for (Ability_Mana am : getManaAbility()) {
//if a mana ability has a mana cost the AI will miscalculate
Cost cost = am.getPayCosts();
if (!cost.hasNoManaCost()) {
continue;
}
if (am.isBasic() && !res.contains(am)) {
res.add(am);
} else if (am.isReflectedMana() && !res.contains(am)) {
res.add(am);
}
}
return res;
}
/**
* <p>getBasicMana.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public final ArrayList<Ability_Mana> getBasicMana() {
ArrayList<Ability_Mana> res = new ArrayList<Ability_Mana>();
for (Ability_Mana am : getManaAbility())
if (am.isBasic() && !res.contains(am)) {
res.add(am);
}
return res;
}
/**
* <p>clearFirstSpellAbility.</p>
*/
public final void clearFirstSpell() {
for(int i = 0; i < getCharacteristics().getSpellAbility().size(); i++) {
if (getCharacteristics().getSpellAbility().get(i).isSpell()) {
getCharacteristics().getSpellAbility().remove(i);
return;
}
}
}
/**
* <p>clearAllButFirstSpellAbility.</p>
*/
public final void clearAllButFirstSpellAbility() {
if (!getCharacteristics().getSpellAbility().isEmpty()) {
SpellAbility first = getCharacteristics().getSpellAbility().get(0);
getCharacteristics().getSpellAbility().clear();
getCharacteristics().getSpellAbility().add(first);
}
getCharacteristics().getManaAbility().clear();
}
/**
* <p>getAllButFirstSpellAbility.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public final ArrayList<SpellAbility> getAllButFirstSpellAbility() {
ArrayList<SpellAbility> sas = new ArrayList<SpellAbility>();
sas.addAll(getCharacteristics().getSpellAbility());
if (!sas.isEmpty()) {
SpellAbility first = getCharacteristics().getSpellAbility().get(0);
sas.remove(first);
}
sas.addAll(getCharacteristics().getManaAbility());
return sas;
}
/**
* <p>clearSpellAbility.</p>
*/
public final void clearSpellAbility() {
getCharacteristics().getSpellAbility().clear();
getCharacteristics().getManaAbility().clear();
}
/**
* <p>getSpellPermanent.</p>
*
* @return a {@link forge.card.spellability.Spell_Permanent} object.
*/
public final Spell_Permanent getSpellPermanent() {
for (SpellAbility sa : getCharacteristics().getSpellAbility()) {
if (sa instanceof Spell_Permanent) {
return (Spell_Permanent) sa;
}
}
return null;
}
/**
* <p>clearSpellKeepManaAbility.</p>
*/
public final void clearSpellKeepManaAbility() {
getCharacteristics().getSpellAbility().clear();
}
/**
* <p>clearManaAbility.</p>
*/
public final void clearManaAbility() {
getCharacteristics().getManaAbility().clear();
}
/**
* <p>addFirstSpellAbility.</p>
*
* @param a a {@link forge.card.spellability.SpellAbility} object.
*/
public final void addFirstSpellAbility(final SpellAbility a) {
a.setSourceCard(this);
if (a instanceof Ability_Mana) {
getCharacteristics().getManaAbility().add(0, (Ability_Mana) a);
} else {
getCharacteristics().getSpellAbility().add(0, a);
}
}
/**
* <p>addSpellAbility.</p>
*
* @param a a {@link forge.card.spellability.SpellAbility} object.
*/
public final void addSpellAbility(final SpellAbility a) {
a.setSourceCard(this);
if (a instanceof Ability_Mana) {
getCharacteristics().getManaAbility().add((Ability_Mana) a);
} else {
getCharacteristics().getSpellAbility().add(a);
}
}
/**
* <p>removeSpellAbility.</p>
*
* @param a a {@link forge.card.spellability.SpellAbility} object.
*/
public final void removeSpellAbility(final SpellAbility a) {
if (a instanceof Ability_Mana) {
//if (a.isExtrinsic()) //never remove intrinsic mana abilities, is this the way to go??
getCharacteristics().getManaAbility().remove(a);
}
else {
getCharacteristics().getSpellAbility().remove(a);
}
}
/**
* <p>removeAllExtrinsicManaAbilities.</p>
*/
public final void removeAllExtrinsicManaAbilities() {
//temp ArrayList, otherwise ConcurrentModificationExceptions occur:
ArrayList<SpellAbility> saList = new ArrayList<SpellAbility>();
for (SpellAbility var : getCharacteristics().getManaAbility()) {
if (var.isExtrinsic()) {
saList.add(var);
}
}
for (SpellAbility sa : saList) {
removeSpellAbility(sa);
}
}
/**
* <p>getIntrinsicManaAbilitiesDescriptions.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public ArrayList<String> getIntrinsicManaAbilitiesDescriptions() {
ArrayList<String> list = new ArrayList<String>();
for (SpellAbility var : getCharacteristics().getManaAbility()) {
if (var.isIntrinsic()) list.add(var.toString());
}
return list;
}
/**
* <p>Getter for the field <code>spellAbility</code>.</p>
*
* @return an array of {@link forge.card.spellability.SpellAbility} objects.
*/
public SpellAbility[] getSpellAbility() {
ArrayList<SpellAbility> res = new ArrayList<SpellAbility>(getCharacteristics().getSpellAbility());
res.addAll(getManaAbility());
SpellAbility[] s = new SpellAbility[res.size()];
res.toArray(s);
return s;
}
/**
* <p>getSpellAbilities.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public ArrayList<SpellAbility> getSpellAbilities() {
ArrayList<SpellAbility> res = new ArrayList<SpellAbility>(getCharacteristics().getSpellAbility());
res.addAll(getManaAbility());
return res;
}
public ArrayList<SpellAbility> getAllSpellAbilities() {
ArrayList<SpellAbility> res = new ArrayList<SpellAbility>(getSpellAbilities());
if(hasAlternateState()) {
changeState();
res.addAll(getSpellAbilities());
changeState();
}
return res;
}
/**
* <p>getSpells.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public ArrayList<SpellAbility> getSpells() {
ArrayList<SpellAbility> s = new ArrayList<SpellAbility>(getCharacteristics().getSpellAbility());
ArrayList<SpellAbility> res = new ArrayList<SpellAbility>();
for (SpellAbility sa : s) {
if (sa.isSpell()) res.add(sa);
}
return res;
}
/**
* <p>getBasicSpells.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public ArrayList<SpellAbility> getBasicSpells() {
ArrayList<SpellAbility> s = new ArrayList<SpellAbility>(getCharacteristics().getSpellAbility());
ArrayList<SpellAbility> res = new ArrayList<SpellAbility>();
for (SpellAbility sa : s) {
if (sa.isSpell() && !sa.isFlashBackAbility() && !sa.isBuyBackAbility()) res.add(sa);
}
return res;
}
/**
* <p>getAdditionalCostSpells.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public ArrayList<SpellAbility> getAdditionalCostSpells() {
ArrayList<SpellAbility> s = new ArrayList<SpellAbility>(getCharacteristics().getSpellAbility());
ArrayList<SpellAbility> res = new ArrayList<SpellAbility>();
for (SpellAbility sa : s) {
if (sa.isSpell() && !sa.getAdditionalManaCost().equals("")) res.add(sa);
}
return res;
}
//shield = regeneration
/**
* <p>setShield.</p>
*
* @param n a int.
*/
public void setShield(int n) {
nShield = n;
}
/**
* <p>getShield.</p>
*
* @return a int.
*/
public int getShield() {
return nShield;
}
/**
* <p>addShield.</p>
*/
public void addShield() {
nShield++;
}
/**
* <p>subtractShield.</p>
*/
public void subtractShield() {
nShield--;
}
/**
* <p>resetShield.</p>
*/
public void resetShield() {
nShield = 0;
}
/**
* <p>canBeShielded.</p>
*
* @return a boolean.
*/
public boolean canBeShielded() {
return !hasKeyword("CARDNAME can't be regenerated.");
}
//is this "Card" supposed to be a token?
/**
* <p>Setter for the field <code>token</code>.</p>
*
* @param b a boolean.
*/
public void setToken(boolean b) {
token = b;
}
/**
* <p>isToken.</p>
*
* @return a boolean.
*/
public boolean isToken() {
return token;
}
/**
* <p>Setter for the field <code>copiedToken</code>.</p>
*
* @param b a boolean.
*/
public void setCopiedToken(boolean b) {
copiedToken = b;
}
/**
* <p>isCopiedToken.</p>
*
* @return a boolean.
*/
public boolean isCopiedToken() {
return copiedToken;
}
/**
* <p>Setter for the field <code>copiedSpell</code>.</p>
*
* @param b a boolean.
*/
public void setCopiedSpell(boolean b) {
copiedSpell = b;
}
/**
* <p>isCopiedSpell.</p>
*
* @return a boolean.
*/
public boolean isCopiedSpell() {
return copiedSpell;
}
/**
* <p>addSpellChoice.</p>
*
* @param string a {@link java.lang.String} object.
*/
public void addSpellChoice(String string) {
choicesMade.add(string);
}
/**
* <p>getChoices.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public ArrayList<String> getChoices() {
return choicesMade;
}
/**
* <p>getChoice.</p>
*
* @param i a int.
* @return a {@link java.lang.String} object.
*/
public final String getChoice(final int i) {
return choicesMade.get(i);
}
/**
* <p>setSpellChoiceTarget.</p>
*
* @param string a {@link java.lang.String} object.
*/
public final void setSpellChoiceTarget(final String string) {
targetsForChoices.add(string);
}
/**
* <p>getChoiceTargets.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public final ArrayList<String> getChoiceTargets() {
return targetsForChoices;
}
/**
* <p>getChoiceTarget.</p>
*
* @param i a int.
* @return a {@link java.lang.String} object.
*/
public final String getChoiceTarget(final int i) {
return targetsForChoices.get(i);
}
/**
* <p>setSpellWithChoices.</p>
*
* @param b a boolean.
*/
public final void setSpellWithChoices(final boolean b) {
spellWithChoices = b;
}
/**
* <p>hasChoices.</p>
*
* @return a boolean.
*/
public final boolean hasChoices() {
return spellWithChoices;
}
/**
* <p>setCopiesSpells.</p>
*
* @param b a boolean.
*/
public final void setCopiesSpells(final boolean b) {
spellCopyingCard = b;
}
/**
* <p>copiesSpells.</p>
*
* @return a boolean.
*/
public final boolean copiesSpells() {
return spellCopyingCard;
}
/**
* <p>setIsFaceDown.</p>
*
* @param b a boolean.
*/
public final void setIsFaceDown(final boolean b) {
faceDown = b;
}
/**
* <p>isFaceDown.</p>
*
* @return a boolean.
*/
public final boolean isFaceDown() {
return faceDown;
}
/**
* <p>setCanMorph.</p>
* @param b a boolean.
*/
public final void setCanMorph(final boolean b) {
canMorph = b;
}
/**
* <p>getCanMorph.</p>
* @return a boolean.
*/
public final boolean getCanMorph() {
return canMorph;
}
/**
* <p>addTrigger.</p>
*
* @param c a {@link forge.Command} object.
* @param typeIn a {@link forge.ZCTrigger} object.
*/
public final void addTrigger(final Command c, final ZCTrigger typeIn) {
zcTriggers.add(new Ability_Triggered(this, c, typeIn));
}
/**
* <p>removeTrigger.</p>
*
* @param c a {@link forge.Command} object.
* @param typeIn a {@link forge.ZCTrigger} object.
*/
public final void removeTrigger(final Command c, final ZCTrigger typeIn) {
zcTriggers.remove(new Ability_Triggered(this, c, typeIn));
}
/**
* <p>executeTrigger.</p>
*
* @param type a {@link forge.ZCTrigger} object.
*/
public final void executeTrigger(final ZCTrigger type) {
for (Ability_Triggered t : zcTriggers) {
if (t.trigger.equals(type) && t.isBasic()) {
t.execute();
}
}
}
/**
* <p>clearTriggers.</p>
*/
public final void clearTriggers() {
zcTriggers.clear();
}
/**
* <p>addComesIntoPlayCommand.</p>
*
* @param c a {@link forge.Command} object.
*/
public final void addComesIntoPlayCommand(final Command c) {
addTrigger(c, ZCTrigger.ENTERFIELD);
}
/**
* <p>removeComesIntoPlayCommand.</p>
*
* @param c a {@link forge.Command} object.
*/
public final void removeComesIntoPlayCommand(final Command c) {
removeTrigger(c, ZCTrigger.ENTERFIELD);
}
/**
* <p>comesIntoPlay.</p>
*/
public final void comesIntoPlay() {
executeTrigger(ZCTrigger.ENTERFIELD);
}
/**
* <p>addDestroyCommand.</p>
*
* @param c a {@link forge.Command} object.
*/
public final void addDestroyCommand(final Command c) {
addTrigger(c, ZCTrigger.DESTROY);
}
/**
* <p>removeDestroyCommand.</p>
*
* @param c a {@link forge.Command} object.
*/
public final void removeDestroyCommand(final Command c) {
removeTrigger(c, ZCTrigger.DESTROY);
}
/**
* <p>destroy.</p>
*/
public final void destroy() {
executeTrigger(ZCTrigger.DESTROY);
}
/**
* <p>addLeavesPlayCommand.</p>
*
* @param c a {@link forge.Command} object.
*/
public final void addLeavesPlayCommand(final Command c) {
addTrigger(c, ZCTrigger.LEAVEFIELD);
}
/**
* <p>removeLeavesPlayCommand.</p>
*
* @param c a {@link forge.Command} object.
*/
public final void removeLeavesPlayCommand(final Command c) {
removeTrigger(c, ZCTrigger.LEAVEFIELD);
}
/**
* <p>leavesPlay.</p>
*/
public final void leavesPlay() {
executeTrigger(ZCTrigger.LEAVEFIELD);
}
/**
* <p>addEquipCommand.</p>
*
* @param c a {@link forge.Command} object.
*/
public final void addEquipCommand(final Command c) {
equipCommandList.add(c);
}
/**
* <p>removeEquipCommand.</p>
*
* @param c a {@link forge.Command} object.
*/
public final void removeEquipCommand(final Command c) {
equipCommandList.remove(c);
}
/**
* <p>equip.</p>
*/
public final void equip() {
for (Command var : equipCommandList) {
var.execute();
}
}
/**
* <p>addUnEquipCommand.</p>
*
* @param c a {@link forge.Command} object.
*/
public final void addUnEquipCommand(final Command c) {
unEquipCommandList.add(c);
}
/**
* <p>removeUnEquipCommand.</p>
*
* @param c a {@link forge.Command} object.
*/
public final void removeUnEquipCommand(final Command c) {
unEquipCommandList.remove(c);
}
/**
* <p>unEquip.</p>
*/
public final void unEquip() {
for (Command var : unEquipCommandList) {
var.execute();
}
}
/**
* <p>addEnchantCommand.</p>
*
* @param c a {@link forge.Command} object.
*/
public final void addEnchantCommand(final Command c) {
enchantCommandList.add(c);
}
/**
* <p>clearEnchantCommand.</p>
*/
public final void clearEnchantCommand() {
enchantCommandList.clear();
}
/**
* <p>enchant.</p>
*/
public final void enchant() {
for (Command var : enchantCommandList) {
var.execute();
}
}
/**
* <p>addUnEnchantCommand.</p>
*
* @param c a {@link forge.Command} object.
*/
public final void addUnEnchantCommand(final Command c) {
unEnchantCommandList.add(c);
}
/**
* <p>clearUnEnchantCommand.</p>
*/
public final void clearUnEnchantCommand() {
unEnchantCommandList.clear();
}
/**
* <p>unEnchant.</p>
*/
public final void unEnchant() {
for (Command var : unEnchantCommandList)
var.execute();
}
/**
* <p>addUntapCommand.</p>
*
* @param c a {@link forge.Command} object.
*/
public final void addUntapCommand(Command c) {
untapCommandList.add(c);
}
/**
* <p>addChangeControllerCommand.</p>
*
* @param c a {@link forge.Command} object.
*/
public final void addChangeControllerCommand(Command c) {
changeControllerCommandList.add(c);
}
/**
* <p>Setter for the field <code>sickness</code>.</p>
*
* @param b a boolean.
*/
public final void setSickness(final boolean b) {
sickness = b;
}
/**
* <p>hasSickness.</p>
*
* @return a boolean.
*/
public final boolean hasSickness() { return !hasKeyword("Haste") && sickness; }
public final boolean isSick() { return !hasKeyword("Haste") && sickness && isCreature(); }
/**
* <p>Setter for the field <code>imageName</code>.</p>
*
* @param s a {@link java.lang.String} object.
*/
public final void setImageName(final String s) {
imageName = s;
}
/**
* <p>Getter for the field <code>imageName</code>.</p>
*
* @return a {@link java.lang.String} object.
*/
public final String getImageName() {
if (!imageName.equals("")) {
return imageName;
}
return getName();
}
/**
* <p>Getter for the field <code>owner</code>.</p>
*
* @return a {@link forge.Player} object.
*/
public final Player getOwner() {
return owner;
}
/**
* TODO write a javadoc for this method.
*
* @return a {@link forge.Player} object.
*/
public final Player getController() {
if (controllerObjects.size() == 0) {
return owner;
}
Object topController = controllerObjects.get(controllerObjects.size() - 1);
if (topController instanceof Player) {
return (Player) topController;
}
else {
return ((Card) topController).getController();
}
}
/**
*
* TODO Write javadoc for this method.
* @param controllerObject an Object
*/
public final void addController(final Object controllerObject) {
Object prevController = controllerObjects.size() == 0 ? owner
: controllerObjects.get(controllerObjects.size() - 1);
if (!controllerObject.equals(prevController)) {
if (controllerObject instanceof Player) {
for (int i = 0; i < controllerObjects.size(); i++) {
if (controllerObjects.get(i) instanceof Player) {
controllerObjects.remove(i);
}
}
}
controllerObjects.add(controllerObject);
if (AllZone.getGameAction() != null && prevController != null) {
AllZone.getGameAction().controllerChangeZoneCorrection(this);
}
if (prevController != null) {
for (Command c : changeControllerCommandList) {
c.execute();
}
}
updateObservers();
}
}
/**
*
* TODO Write javadoc for this method.
* @param controllerObject a Object
*/
public final void removeController(final Object controllerObject) {
Object currentController = getController();
controllerObjects.remove(controllerObject);
if (!currentController.equals(getController())) {
AllZone.getGameAction().controllerChangeZoneCorrection(this);
for (Command c : changeControllerCommandList) {
c.execute();
}
updateObservers();
}
}
/**
*
* TODO Write javadoc for this method.
*/
public final void clearControllers() {
controllerObjects.clear();
}
/**
*
* TODO Write javadoc for this method.
* @return an ArrayList<Object>
*/
public final ArrayList<Object> getControllerObjects() {
return controllerObjects;
}
/**
*
* TODO Write javadoc for this method.
* @param in an Object
*/
public final void setControllerObjects(final ArrayList<Object> in) {
controllerObjects = in;
}
/**
* <p>Setter for the field <code>owner</code>.</p>
*
* @param player a {@link forge.Player} object.
*/
public final void setOwner(final Player player) {
owner = player;
this.updateObservers();
}
/**
* <p>Setter for the field <code>controller</code>.</p>
*
* @param player a {@link forge.Player} object.
*/ /*
public void setController(Player player) {
boolean sameController = controller == null ? false : controller.isPlayer(player);
controller = player;
if (null != controller && !sameController) {
for (Command var : changeControllerCommandList)
var.execute();
}
this.updateObservers();
}*/
/**
* <p>Getter for the field <code>equippedBy</code>.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public final ArrayList<Card> getEquippedBy() {
return equippedBy;
}
/**
* <p>Setter for the field <code>equippedBy</code>.</p>
*
* @param list a {@link java.util.ArrayList} object.
*/
public final void setEquippedBy(final ArrayList<Card> list) {
equippedBy = list;
}
/**
* <p>Getter for the field <code>equipping</code>.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public final ArrayList<Card> getEquipping() {
return equipping;
}
/**
* <p>getEquippingCard.</p>
*
* @return a {@link forge.Card} object.
*/
public final Card getEquippingCard() {
if (equipping.size() == 0) {
return null;
}
return equipping.get(0);
}
/**
* <p>Setter for the field <code>equipping</code>.</p>
*
* @param list a {@link java.util.ArrayList} object.
*/
public final void setEquipping(final ArrayList<Card> list) {
equipping = list;
}
/**
* <p>isEquipped.</p>
*
* @return a boolean.
*/
public final boolean isEquipped() {
return !equippedBy.isEmpty();
}
/**
* <p>isEquipping.</p>
*
* @return a boolean.
*/
public final boolean isEquipping() {
return equipping.size() != 0;
}
/**
* <p>addEquippedBy.</p>
*
* @param c a {@link forge.Card} object.
*/
public final void addEquippedBy(final Card c) {
equippedBy.add(c);
this.updateObservers();
}
/**
* <p>removeEquippedBy.</p>
*
* @param c a {@link forge.Card} object.
*/
public final void removeEquippedBy(final Card c) {
equippedBy.remove(c);
this.updateObservers();
}
/**
* <p>addEquipping.</p>
*
* @param c a {@link forge.Card} object.
*/
public final void addEquipping(final Card c) {
equipping.add(c);
setTimestamp(AllZone.getNextTimestamp());
this.updateObservers();
}
/**
* <p>removeEquipping.</p>
*
* @param c a {@link forge.Card} object.
*/
public final void removeEquipping(final Card c) {
equipping.remove(c);
this.updateObservers();
}
/**
* <p>equipCard.</p>
* equipment.equipCard(cardToBeEquipped)
*
* @param c a {@link forge.Card} object.
*/
public void equipCard(Card c) {
addEquipping(c);
c.addEquippedBy(this);
this.equip();
}
/**
* <p>unEquipCard.</p>
*
* @param c a {@link forge.Card} object.
*/
public void unEquipCard(Card c) //equipment.unEquipCard(equippedCard);
{
this.unEquip();
equipping.remove(c);
c.removeEquippedBy(this);
//Run triggers
Map<String, Object> runParams = new TreeMap<String, Object>();
runParams.put("Equipment", this);
runParams.put("Card", c);
AllZone.getTriggerHandler().runTrigger("Unequip", runParams);
}
/**
* <p>unEquipAllCards.</p>
*/
public final void unEquipAllCards() {
while (equippedBy.size() > 0) { // while there exists equipment, unequip the first one
equippedBy.get(0).unEquipCard(this);
}
}
/**
* <p>Getter for the field <code>enchanting</code>.</p>
*
* @return a {@link forge.enchanting} object.
*/
public final GameEntity getEnchanting() {
return enchanting;
}
/**
* <p>getEnchantingCard.</p>
*
* @return a {@link forge.Card} object.
*/
public final Card getEnchantingCard() {
if (enchanting != null && enchanting instanceof Card){
return (Card)enchanting;
}
return null;
}
/**
* <p>getEnchantingPlayer.</p>
*
* @return a {@link forge.Player} object.
*/
public final Player getEnchantingPlayer() {
if (enchanting != null && enchanting instanceof Player){
return (Player)enchanting;
}
return null;
}
/**
* <p>Setter for the field <code>enchanting</code>.</p>
*
* @param list a {@link java.util.ArrayList} object.
*/
public final void setEnchanting(GameEntity e) {
enchanting = e;
}
/**
* <p>isEnchanting.</p>
*
* @return a boolean.
*/
public final boolean isEnchanting() {
return enchanting != null;
}
/**
* <p>isEnchanting.</p>
*
* @return a boolean.
*/
public final boolean isEnchantingCard() {
return getEnchantingCard() != null;
}
/**
* <p>isEnchanting.</p>
*
* @return a boolean.
*/
public final boolean isEnchantingPlayer() {
return getEnchantingPlayer() != null;
}
/**
* checks to see if this card is enchanted by an aura with a given name.
*
* @param cardName the name of the aura
* @return true if this card is enchanted by an aura with the given name, false otherwise
*/
public final boolean isEnchantedBy(final String cardName) {
ArrayList<Card> allAuras = this.getEnchantedBy();
for (Card aura : allAuras) {
if (aura.getName().equals(cardName)) {
return true;
}
}
return false;
}
/**
* <p>addEnchanting.</p>
*
* @param e a {@link forge.GameEntity} object.
*/
public final void addEnchanting(final GameEntity e) {
enchanting = e;
setTimestamp(AllZone.getNextTimestamp());
this.updateObservers();
}
/**
* <p>removeEnchanting.</p>
*
* @param e a {@link forge.GameEntity} object.
*/
public final void removeEnchanting(final GameEntity e) {
if (enchanting.equals(e)){
enchanting = null;
this.updateObservers();
}
}
/**
* <p>enchant</p>
*
* @param entity a {@link forge.GameEntity} object.
*/
public final void enchantEntity(final GameEntity entity) {
addEnchanting(entity);
entity.addEnchantedBy(this);
this.enchant();
}
/**
* <p>unEnchant.</p>
*
* @param gameEntity a {@link forge.GameEntity} object.
*/
public final void unEnchantEntity(final GameEntity gameEntity) {
if (enchanting != null && enchanting.equals(gameEntity)){
this.unEnchant();
enchanting = null;
gameEntity.removeEnchantedBy(this);
}
}
//array size might equal 0, will NEVER be null
/**
* <p>getAttachedCards.</p>
*
* @return an array of {@link forge.Card} objects.
*/
public final Card[] getAttachedCardsByMindsDesire() {
Card[] c = new Card[attachedByMindsDesire.size()];
attachedByMindsDesire.toArray(c);
return c;
}
/**
* <p>hasAttachedCards.</p>
*
* @return a boolean.
*/
public final boolean hasAttachedCardsByMindsDesire() {
return getAttachedCardsByMindsDesire().length != 0;
}
/**
* <p>attachCard.</p>
*
* @param c a {@link forge.Card} object.
*/
public final void attachCardByMindsDesire(final Card c) {
attachedByMindsDesire.add(c);
this.updateObservers();
}
/**
* <p>unattachCard.</p>
*
* @param c a {@link forge.Card} object.
*/
public final void unattachCardByMindDesire(final Card c) {
attachedByMindsDesire.remove(c);
this.updateObservers();
}
/**
* <p>Setter for the field <code>type</code>.</p>
*
* @param a a {@link java.util.ArrayList} object.
*/
public final void setType(final ArrayList<String> a) {
getCharacteristics().setType(new ArrayList<String>(a));
}
/**
* <p>addType.</p>
*
* @param a a {@link java.lang.String} object.
*/
public final void addType(final String a) {
getCharacteristics().getType().add(a);
}
/**
* <p>removeType.</p>
*
* @param a a {@link java.lang.String} object.
*/
public final void removeType(final String a) {
getCharacteristics().getType().remove(a);
}
/**
* <p>Getter for the field <code>type</code>.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public final ArrayList<String> getType() {
// see if type changes are in effect
if (!changedCardTypes.isEmpty()) {
ArrayList<String> newType = new ArrayList<String>(getCharacteristics().getType());
ArrayList<Card_Type> types = changedCardTypes;
Collections.sort(types); // sorts types by timeStamp
for (Card_Type ct : types) {
ArrayList<String> removeTypes = new ArrayList<String>();
if (ct.getRemoveType() != null) {
removeTypes.addAll(ct.getRemoveType());
}
//remove old types
for (int i = 0; i < newType.size(); i++) {
String t = newType.get(i);
if (ct.isRemoveSuperTypes() && CardUtil.isASuperType(t)) {
removeTypes.add(t);
}
if (ct.isRemoveCardTypes() && CardUtil.isACardType(t)) {
removeTypes.add(t);
}
if (ct.isRemoveSubTypes() && CardUtil.isASubType(t)) {
removeTypes.add(t);
}
if (ct.isRemoveCreatureTypes() && (CardUtil.isACreatureType(t)
|| t.equals("AllCreatureTypes")))
{
removeTypes.add(t);
}
}
newType.removeAll(removeTypes);
//add new types
if (ct.getType() != null) {
newType.addAll(ct.getType());
}
}
return newType;
}
//nothing changed
return new ArrayList<String>(getCharacteristics().getType());
}
public void setChangedCardTypes(ArrayList<Card_Type> types) {
changedCardTypes = types;
}
public ArrayList<Card_Type> getChangedCardTypes() {
return changedCardTypes;
}
public void addChangedCardTypes(ArrayList<String> types, ArrayList<String> removeTypes, boolean removeSuperTypes,
boolean removeCardTypes, boolean removeSubTypes, boolean removeCreatureTypes, long timestamp) {
changedCardTypes.add(new Card_Type(types, removeTypes, removeSuperTypes, removeCardTypes, removeSubTypes, removeCreatureTypes,
timestamp));
}
public void addChangedCardTypes(String[] types, final String[] removeTypes, boolean removeSuperTypes, boolean removeCardTypes,
boolean removeSubTypes, boolean removeCreatureTypes, long timestamp) {
ArrayList<String> typeList = null;
ArrayList<String> removeTypeList = null;
if(types != null) {
typeList = new ArrayList<String>(Arrays.asList(types));
}
if(removeTypes != null) {
removeTypeList = new ArrayList<String>(Arrays.asList(removeTypes));
}
addChangedCardTypes(typeList, removeTypeList, removeSuperTypes, removeCardTypes, removeSubTypes, removeCreatureTypes, timestamp);
}
public void removeChangedCardTypes(long timestamp) {
for (int i = 0; i < changedCardTypes.size(); i++) {
Card_Type cardT = changedCardTypes.get(i);
if (cardT.getTimestamp() == timestamp) {
changedCardTypes.remove(cardT);
}
}
}
/**
* <p>clearAllTypes.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public final ArrayList<String> clearAllTypes() {
ArrayList<String> originalTypes = new ArrayList<String>();
originalTypes.addAll(getCharacteristics().getType());
getCharacteristics().getType().clear();
return originalTypes;
}
/**
* <p>Setter for the field <code>prevType</code>.</p>
*
* @param a a {@link java.util.ArrayList} object.
*/
public final void setPrevType(final ArrayList<String> a) {
prevType = new ArrayList<String>(a);
}
/**
* <p>addPrevType.</p>
*
* @param a a {@link java.lang.String} object.
*/
public final void addPrevType(final String a) {
prevType.add(a);
}
/**
* <p>removePrevType.</p>
*
* @param a a {@link java.lang.String} object.
*/
public final void removePrevType(final String a) {
prevType.remove(a);
}
/**
* <p>Getter for the field <code>prevType</code>.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public final ArrayList<String> getPrevType() {
return new ArrayList<String>(prevType);
}
//values that are printed on card
/**
* <p>Getter for the field <code>baseLoyalty</code>.</p>
*
* @return a int.
*/
public final int getBaseLoyalty() {
return baseLoyalty;
}
//values that are printed on card
/**
* <p>Setter for the field <code>baseLoyalty</code>.</p>
*
* @param n a int.
*/
public final void setBaseLoyalty(final int n) {
baseLoyalty = n;
}
//values that are printed on card
/**
* <p>Getter for the field <code>baseAttack</code>.</p>
*
* @return a int.
*/
public final int getBaseAttack() {
return getCharacteristics().getBaseAttack();
}
/**
* <p>Getter for the field <code>baseDefense</code>.</p>
*
* @return a int.
*/
public final int getBaseDefense() {
return getCharacteristics().getBaseDefense();
}
//values that are printed on card
/**
* <p>Setter for the field <code>baseAttack</code>.</p>
*
* @param n a int.
*/
public final void setBaseAttack(final int n) {
getCharacteristics().setBaseAttack(n);
}
/**
* <p>Setter for the field <code>baseDefense</code>.</p>
*
* @param n a int.
*/
public final void setBaseDefense(final int n) {
getCharacteristics().setBaseDefense(n);
}
//values that are printed on card
/**
* <p>Getter for the field <code>baseAttackString</code>.</p>
*
* @return a {@link java.lang.String} object.
*/
public final String getBaseAttackString() {
return (null == baseAttackString) ? "" + getBaseAttack() : baseAttackString;
}
/**
* <p>Getter for the field <code>baseDefenseString</code>.</p>
*
* @return a {@link java.lang.String} object.
*/
public final String getBaseDefenseString() {
return (null == baseDefenseString) ? "" + getBaseDefense() : baseDefenseString;
}
//values that are printed on card
/**
* <p>Setter for the field <code>baseAttackString</code>.</p>
*
* @param s a {@link java.lang.String} object.
*/
public final void setBaseAttackString(final String s) {
baseAttackString = s;
}
/**
* <p>Setter for the field <code>baseDefenseString</code>.</p>
*
* @param s a {@link java.lang.String} object.
*/
public final void setBaseDefenseString(String s) {
baseDefenseString = s;
}
public void setNewPT(final ArrayList<CardPowerToughness> pt) {
newPT = pt;
}
public ArrayList<CardPowerToughness> getNewPT() {
return newPT;
}
public int getSetPower() {
if (newPT.isEmpty())
return -1;
CardPowerToughness latestPT = getLatestPT();
return latestPT.getPower();
}
public int getSetToughness() {
if (newPT.isEmpty())
return -1;
CardPowerToughness latestPT = getLatestPT();
return latestPT.getToughness();
}
public CardPowerToughness getLatestPT() {
CardPowerToughness latestPT = new CardPowerToughness(-1,-1,0);
long max = 0;
for (CardPowerToughness pt : newPT) {
if (pt.getTimestamp() >= max) {
max = pt.getTimestamp();
latestPT = pt;
}
}
return latestPT;
}
public void addNewPT(int power, int toughness, long timestamp) {
newPT.add(new CardPowerToughness(power, toughness, timestamp));
}
public void removeNewPT(long timestamp) {
for (int i = 0; i < newPT.size(); i++) {
CardPowerToughness cardPT = newPT.get(i);
if (cardPT.getTimestamp() == timestamp)
newPT.remove(cardPT);
}
}
public int getCurrentPower() {
int total = getBaseAttack();
int setPower = getSetPower();
if(setPower != -1)
total = setPower;
return total;
}
/**
* <p>getUnswitchedAttack.</p>
*
* @return a int.
*/
public final int getUnswitchedAttack() {
int total = getCurrentPower();
total += getTempAttackBoost() + getSemiPermanentAttackBoost()
+ getCounters(Counters.P1P1) + getCounters(Counters.P1P2)
+ getCounters(Counters.P1P0) - getCounters(Counters.M1M1)
+ (2 * getCounters(Counters.P2P2) - (2 * getCounters(Counters.M2M1))
- (2 * getCounters(Counters.M2M2)) - getCounters(Counters.M1M0));
return total;
}
/**
* <p>getNetAttack.</p>
*
* @return a int.
*/
public final int getNetAttack() {
if (this.getAmountOfKeyword("CARDNAME's power and toughness are switched") % 2 != 0) {
return getUnswitchedDefense();
} else {
return getUnswitchedAttack();
}
}
/**
*
* TODO Write javadoc for this method.
* @return an int
*/
public final int getCurrentToughness() {
int total = getBaseDefense();
int setToughness = getSetToughness();
if (setToughness != -1) {
total = setToughness;
}
return total;
}
/**
* <p>getUnswitchedDefense.</p>
*
* @return a int.
*/
public final int getUnswitchedDefense() {
int total = getCurrentToughness();
total += getTempDefenseBoost() + getSemiPermanentDefenseBoost()
+ getCounters(Counters.P1P1) + (2 * getCounters(Counters.P1P2))
- getCounters(Counters.M1M1) + getCounters(Counters.P0P1)
- (2 * getCounters(Counters.M0M2))
+ (2 * getCounters(Counters.P2P2)) - getCounters(Counters.M0M1)
- getCounters(Counters.M2M1) - (2 * getCounters(Counters.M2M2));
return total;
}
/**
* <p>getNetDefense.</p>
*
* @return a int.
*/
public final int getNetDefense() {
if (this.getAmountOfKeyword("CARDNAME's power and toughness are switched") % 2 != 0) {
return getUnswitchedAttack();
} else {
return getUnswitchedDefense();
}
}
//How much combat damage does the card deal
/**
* <p>getNetCombatDamage.</p>
*
* @return a int.
*/
public final int getNetCombatDamage() {
if (hasKeyword("CARDNAME assigns no combat damage")) {
return 0;
}
if (AllZoneUtil.isCardInPlay("Doran, the Siege Tower")) {
return getNetDefense();
}
return getNetAttack();
}
/**
* <p>Setter for the field <code>randomPicture</code>.</p>
*
* @param n a int.
*/
public final void setRandomPicture(final int n) {
randomPicture = n;
}
/**
* <p>Getter for the field <code>randomPicture</code>.</p>
*
* @return a int.
*/
public final int getRandomPicture() {
return randomPicture;
}
/**
* <p>addMultiKickerMagnitude.</p>
*
* @param n a int.
*/
public final void addMultiKickerMagnitude(final int n) {
multiKickerMagnitude += n;
}
/**
* <p>Setter for the field <code>multiKickerMagnitude</code>.</p>
*
* @param n a int.
*/
public final void setMultiKickerMagnitude(final int n) {
multiKickerMagnitude = n;
}
/**
* <p>Getter for the field <code>multiKickerMagnitude</code>.</p>
*
* @return a int.
*/
public final int getMultiKickerMagnitude() {
return multiKickerMagnitude;
}
/**
* <p>addReplicateMagnitude.</p>
*
* @param n a int.
*/
public final void addReplicateMagnitude(final int n) {
replicateMagnitude += n;
}
/**
* <p>Setter for the field <code>replicateMagnitude</code>.</p>
*
* @param n a int.
*/
public final void setReplicateMagnitude(final int n) {
replicateMagnitude = n;
}
/**
* <p>Getter for the field <code>replicateMagnitude</code>.</p>
*
* @return a int.
*/
public final int getReplicateMagnitude() {
return replicateMagnitude;
}
//for cards like Giant Growth, etc.
/**
* <p>Getter for the field <code>tempAttackBoost</code>.</p>
*
* @return a int.
*/
public final int getTempAttackBoost() {
return tempAttackBoost;
}
/**
* <p>Getter for the field <code>tempDefenseBoost</code>.</p>
*
* @return a int.
*/
public final int getTempDefenseBoost() {
return tempDefenseBoost;
}
/**
* <p>addTempAttackBoost.</p>
*
* @param n a int.
*/
public final void addTempAttackBoost(final int n) {
tempAttackBoost += n;
}
/**
* <p>addTempDefenseBoost.</p>
*
* @param n a int.
*/
public final void addTempDefenseBoost(final int n) {
tempDefenseBoost += n;
}
/**
* <p>Setter for the field <code>tempAttackBoost</code>.</p>
*
* @param n a int.
*/
public final void setTempAttackBoost(final int n) {
tempAttackBoost = n;
}
/**
* <p>Setter for the field <code>tempDefenseBoost</code>.</p>
*
* @param n a int.
*/
public final void setTempDefenseBoost(final int n) {
tempDefenseBoost = n;
}
//for cards like Glorious Anthem, etc.
/**
* <p>Getter for the field <code>semiPermanentAttackBoost</code>.</p>
*
* @return a int.
*/
public final int getSemiPermanentAttackBoost() {
return semiPermanentAttackBoost;
}
/**
* <p>Getter for the field <code>semiPermanentDefenseBoost</code>.</p>
*
* @return a int.
*/
public final int getSemiPermanentDefenseBoost() {
return semiPermanentDefenseBoost;
}
/**
* <p>addSemiPermanentAttackBoost.</p>
*
* @param n a int.
*/
public final void addSemiPermanentAttackBoost(final int n) {
semiPermanentAttackBoost += n;
}
/**
* <p>addSemiPermanentDefenseBoost.</p>
*
* @param n a int.
*/
public final void addSemiPermanentDefenseBoost(final int n) {
semiPermanentDefenseBoost += n;
}
/**
* <p>Setter for the field <code>semiPermanentAttackBoost</code>.</p>
*
* @param n a int.
*/
public final void setSemiPermanentAttackBoost(final int n) {
semiPermanentAttackBoost = n;
}
/**
* <p>Setter for the field <code>semiPermanentDefenseBoost</code>.</p>
*
* @param n a int.
*/
public final void setSemiPermanentDefenseBoost(final int n) {
semiPermanentDefenseBoost = n;
}
/**
* <p>isUntapped.</p>
*
* @return a boolean.
*/
public final boolean isUntapped() {
return !tapped;
}
/**
* <p>isTapped.</p>
*
* @return a boolean.
*/
public final boolean isTapped() {
return tapped;
}
/**
* <p>Setter for the field <code>tapped</code>.</p>
*
* @param b a boolean.
*/
public final void setTapped(final boolean b) {
tapped = b;
updateObservers();
}
/**
* <p>tap.</p>
*/
public final void tap() {
if (isUntapped()) {
//Run triggers
Map<String, Object> runParams = new TreeMap<String, Object>();
runParams.put("Card", this);
AllZone.getTriggerHandler().runTrigger("Taps", runParams);
}
setTapped(true);
}
/**
* <p>untap.</p>
*/
public final void untap() {
if (isTapped()) {
//Run triggers
Map<String, Object> runParams = new TreeMap<String, Object>();
runParams.put("Card", this);
AllZone.getTriggerHandler().runTrigger("Untaps", runParams);
}
for (Command var : untapCommandList) {
var.execute();
}
setTapped(false);
}
/**
* <p>isUnCastable.</p>
*
* @return a boolean.
*/
public final boolean isUnCastable() {
return unCastable;
}
/**
* <p>Setter for the field <code>unCastable</code>.</p>
*
* @param b a boolean.
*/
public final void setUnCastable(final boolean b) {
unCastable = b;
updateObservers();
}
//keywords are like flying, fear, first strike, etc...
/**
* <p>getKeyword.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public final ArrayList<String> getKeyword() {
ArrayList<String> keywords = getUnhiddenKeyword();
ArrayList<String> a4 = new ArrayList<String>(getHiddenExtrinsicKeyword());
keywords.addAll(a4);
return keywords;
}
public int getKeywordAmount(final String keyword) {
int res = 0;
for (String k : getKeyword()) {
if (k.equals(keyword)) {
res++;
}
}
return res;
}
public void setChangedCardKeywords(ArrayList<Card_Keywords> kw) {
changedCardKeywords = kw;
}
public ArrayList<Card_Keywords> getChangedCardKeywords() {
return changedCardKeywords;
}
public void addChangedCardKeywords(ArrayList<String> keywords, ArrayList<String> removeKeywords, boolean removeAllKeywords,
long timestamp) {
changedCardKeywords.add(new Card_Keywords(keywords, removeKeywords, removeAllKeywords, timestamp));
}
public void addChangedCardKeywords(String[] keywords, String[] removeKeywords, boolean removeAllKeywords, long timestamp) {
ArrayList<String> keywordsList = null;
ArrayList<String> removeKeywordsList = null;
if(keywords != null) {
keywordsList = new ArrayList<String>(Arrays.asList(keywords));
}
if(removeKeywords != null) {
removeKeywordsList = new ArrayList<String>(Arrays.asList(removeKeywords));
}
addChangedCardKeywords(keywordsList, removeKeywordsList, removeAllKeywords, timestamp);
}
public void removeChangedCardKeywords(long timestamp) {
for (int i = 0; i < changedCardKeywords.size(); i++) {
Card_Keywords cardK = changedCardKeywords.get(i);
if (cardK.getTimestamp() == timestamp) {
changedCardKeywords.remove(cardK);
}
}
}
// Hidden keywords will be left out
/**
* <p>getUnhiddenKeyword.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public final ArrayList<String> getUnhiddenKeyword() {
ArrayList<String> keywords = new ArrayList<String>(getIntrinsicKeyword());
ArrayList<String> a2 = new ArrayList<String>(getExtrinsicKeyword());
keywords.addAll(a2);
// see if keyword changes are in effect
if (!changedCardKeywords.isEmpty()) {
ArrayList<Card_Keywords> newKeywords = changedCardKeywords;
Collections.sort(newKeywords); // sorts newKeywords by timeStamp
for (Card_Keywords ck : newKeywords) {
if(ck.isRemoveAllKeywords()) {
keywords.clear();
} else if (ck.getRemoveKeywords() != null) {
keywords.removeAll(ck.getRemoveKeywords());
}
if (ck.getKeywords() != null) {
keywords.addAll(ck.getKeywords());
}
}
}
return keywords;
}
/**
* <p>getIntrinsicAbilities.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public final ArrayList<String> getIntrinsicAbilities() {
return getCharacteristics().getIntrinsicAbility();
}
/**
* <p>Getter for the field <code>intrinsicKeyword</code>.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public final ArrayList<String> getIntrinsicKeyword() {
return new ArrayList<String>(getCharacteristics().getIntrinsicKeyword());
}
/**
* <p>clearIntrinsicKeyword.</p>
*/
public final void clearIntrinsicKeyword() {
getCharacteristics().getIntrinsicKeyword().clear();
}
/**
* <p>Setter for the field <code>intrinsicKeyword</code>.</p>
*
* @param a a {@link java.util.ArrayList} object.
*/
public final void setIntrinsicKeyword(final ArrayList<String> a) {
getCharacteristics().setIntrinsicKeyword(new ArrayList<String>(a));
}
/**
* <p>clearAllKeywords.</p>
*/
public final void clearAllKeywords() {
getCharacteristics().getIntrinsicKeyword().clear();
extrinsicKeyword.clear();
hiddenExtrinsicKeyword.clear(); //Hidden keywords won't be displayed on the card
}
/**
* <p>setIntrinsicAbilities.</p>
*
* @param a a {@link java.util.ArrayList} object.
*/
public final void setIntrinsicAbilities(final ArrayList<String> a) {
getCharacteristics().setIntrinsicAbility(new ArrayList<String>(a));
}
/**
* <p>addIntrinsicKeyword.</p>
*
* @param s a {@link java.lang.String} object.
*/
public final void addIntrinsicKeyword(final String s) {
if (s.trim().length() != 0) {
getCharacteristics().getIntrinsicKeyword().add(s);
//intrinsicKeyword.add((getName().trim().length()== 0 ? s :s.replaceAll(getName(), "CARDNAME")));
}
}
/**
* <p>addIntrinsicAbility.</p>
*
* @param s a {@link java.lang.String} object.
*/
public void addIntrinsicAbility(String s) {
if (s.trim().length() != 0)
getCharacteristics().getIntrinsicAbility().add(s);
}
/**
* <p>addNonStackingIntrinsicKeyword.</p>
*
* @param s a {@link java.lang.String} object.
*/
public final void addNonStackingIntrinsicKeyword(String s) {
if (!getIntrinsicKeyword().contains(s) && s.trim().length() != 0) {
getCharacteristics().getIntrinsicKeyword().add((getName().trim().length() == 0 ? s : s.replaceAll(getName(), "CARDNAME")));
}
}
/**
* <p>removeIntrinsicKeyword.</p>
*
* @param s a {@link java.lang.String} object.
*/
public final void removeIntrinsicKeyword(String s) {
getCharacteristics().getIntrinsicKeyword().remove(s);
}
/**
* <p>getIntrinsicKeywordSize.</p>
*
* @return a int.
*/
public final int getIntrinsicKeywordSize() {
return getCharacteristics().getIntrinsicKeyword().size();
}
/**
* <p>Getter for the field <code>extrinsicKeyword</code>.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public ArrayList<String> getExtrinsicKeyword() {
return new ArrayList<String>(extrinsicKeyword);
}
/**
* <p>Setter for the field <code>extrinsicKeyword</code>.</p>
*
* @param a a {@link java.util.ArrayList} object.
*/
public void setExtrinsicKeyword(final ArrayList<String> a) {
extrinsicKeyword = new ArrayList<String>(a);
}
/**
* <p>addExtrinsicKeyword.</p>
*
* @param s a {@link java.lang.String} object.
*/
public void addExtrinsicKeyword(final String s) {
//if(!hasKeyword(s)){
if (s.startsWith("HIDDEN")) {
addHiddenExtrinsicKeyword(s);
}
else {
extrinsicKeyword.add(s);
//extrinsicKeyword.add((getName().trim().length()==0 ? s :s.replaceAll(getName(), "CARDNAME")));
//}
}
}
/**
* <p>addStackingExtrinsicKeyword.</p>
*
* @param s a {@link java.lang.String} object.
*/
public void addStackingExtrinsicKeyword(final String s) {
if (s.startsWith("HIDDEN")) {
addHiddenExtrinsicKeyword(s);
} else {
extrinsicKeyword.add(s);
}
}
/**
* <p>removeExtrinsicKeyword.</p>
*
* @param s a {@link java.lang.String} object.
*/
public void removeExtrinsicKeyword(final String s) {
if (s.startsWith("HIDDEN")) {
removeHiddenExtrinsicKeyword(s);
} else {
extrinsicKeyword.remove(s);
}
}
/**
* <p>getExtrinsicKeywordSize.</p>
*
* @return a int.
*/
public int getExtrinsicKeywordSize() {
return extrinsicKeyword.size();
}
/**
* <p>Getter for the field <code>prevIntrinsicKeyword</code>.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public ArrayList<String> getPrevIntrinsicKeyword() {
return new ArrayList<String>(prevIntrinsicKeyword);
}
/**
* <p>Setter for the field <code>prevIntrinsicKeyword</code>.</p>
*
* @param a a {@link java.util.ArrayList} object.
*/
public void setPrevIntrinsicKeyword(ArrayList<String> a) {
prevIntrinsicKeyword = new ArrayList<String>(a);
this.updateObservers();
}
/**
* <p>addPrevIntrinsicKeyword.</p>
*
* @param s a {@link java.lang.String} object.
*/
public void addPrevIntrinsicKeyword(String s) {
prevIntrinsicKeyword.add(s);
}
/**
* <p>removePrevIntrinsicKeyword.</p>
*
* @param s a {@link java.lang.String} object.
*/
public void removePrevIntrinsicKeyword(String s) {
prevIntrinsicKeyword.remove(s);
this.updateObservers();
}
/**
* <p>getPrevIntrinsicKeywordSize.</p>
*
* @return a int.
*/
public int getPrevIntrinsicKeywordSize() {
return prevIntrinsicKeyword.size();
}
// Hidden Keywords will be returned without the indicator HIDDEN
/**
* <p>getHiddenExtrinsicKeyword.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public ArrayList<String> getHiddenExtrinsicKeyword() {
ArrayList<String> keywords = new ArrayList<String>();
for (int i = 0; i < hiddenExtrinsicKeyword.size(); i++) {
String keyword = hiddenExtrinsicKeyword.get(i);
keywords.add(keyword.substring(7));
}
return keywords;
}
/**
* <p>addHiddenExtrinsicKeyword.</p>
*
* @param s a {@link java.lang.String} object.
*/
public void addHiddenExtrinsicKeyword(String s) {
hiddenExtrinsicKeyword.add(s);
}
/**
* <p>removeHiddenExtrinsicKeyword.</p>
*
* @param s a {@link java.lang.String} object.
*/
public void removeHiddenExtrinsicKeyword(String s) {
hiddenExtrinsicKeyword.remove(s);
//this.updateObservers();
}
/**
* <p>setStaticAbilityStrings.</p>
*
* @param a a {@link java.util.ArrayList} object.
*/
public void setStaticAbilityStrings(ArrayList<String> a) {
staticAbilityStrings = new ArrayList<String>(a);
}
public ArrayList<String> getStaticAbilityStrings() {
return staticAbilityStrings;
}
/**
* <p>addStaticAbilityStrings.</p>
*
* @param s a {@link java.lang.String} object.
*/
public void addStaticAbilityString(String s) {
if (s.trim().length() != 0)
staticAbilityStrings.add(s);
}
public void setStaticAbilities(ArrayList<StaticAbility> a) {
getCharacteristics().setStaticAbilities(new ArrayList<StaticAbility>(a));
}
public ArrayList<StaticAbility> getStaticAbilities() {
return new ArrayList<StaticAbility>(getCharacteristics().getStaticAbilities());
}
public void addStaticAbility(String s) {
if (s.trim().length() != 0) {
StaticAbility stAb = new StaticAbility(s,this);
getCharacteristics().getStaticAbilities().add(stAb);
}
}
/**
* <p>isPermanent.</p>
*
* @return a boolean.
*/
public boolean isPermanent() {
return !(isInstant() || isSorcery() || isImmutable());
}
/**
* <p>isSpell.</p>
*
* @return a boolean.
*/
public boolean isSpell() {
return (isInstant() || isSorcery() || (isAura() && !AllZoneUtil.getCardsIn(Zone.Battlefield).contains(this)));
}
/**
* <p>isCreature.</p>
*
* @return a boolean.
*/
public boolean isCreature() {
return typeContains("Creature");
}
/**
* <p>isWall.</p>
*
* @return a boolean.
*/
public boolean isWall() {
return typeContains("Wall");
}
/**
* <p>isBasicLand.</p>
*
* @return a boolean.
*/
public boolean isBasicLand() {
return typeContains("Basic");
}
/**
* <p>isLand.</p>
*
* @return a boolean.
*/
public boolean isLand() {
return typeContains("Land");
}
/**
* <p>isSorcery.</p>
*
* @return a boolean.
*/
public boolean isSorcery() {
return typeContains("Sorcery");
}
/**
* <p>isInstant.</p>
*
* @return a boolean.
*/
public boolean isInstant() {
return typeContains("Instant");
}
/**
* <p>isArtifact.</p>
*
* @return a boolean.
*/
public boolean isArtifact() {
return typeContains("Artifact");
}
/**
* <p>isEquipment.</p>
*
* @return a boolean.
*/
public boolean isEquipment() {
return typeContains("Equipment");
}
/**
* <p>isPlaneswalker.</p>
*
* @return a boolean.
*/
public boolean isPlaneswalker() {
return typeContains("Planeswalker");
}
/**
* <p>isEmblem.</p>
*
* @return a boolean.
*/
public boolean isEmblem() {
return typeContains("Emblem");
}
/**
* <p>isTribal.</p>
*
* @return a boolean.
*/
public boolean isTribal() {
return typeContains("Tribal");
}
/**
* <p>isSnow.</p>
*
* @return a boolean.
*/
public boolean isSnow() {
return typeContains("Snow");
}
//global and local enchantments
/**
* <p>isEnchantment.</p>
*
* @return a boolean.
*/
public boolean isEnchantment() {
return typeContains("Enchantment");
}
/**
* <p>isAura.</p>
*
* @return a boolean.
*/
public boolean isAura() {
return typeContains("Aura");
}
/**
* <p>isGlobalEnchantment.</p>
*
* @return a boolean.
*/
public boolean isGlobalEnchantment() {
return typeContains("Enchantment") && (!isAura());
}
private boolean typeContains(String s) {
Iterator<?> it = this.getType().iterator();
while (it.hasNext())
if (it.next().toString().startsWith(s)) return true;
return false;
}
/**
* <p>Setter for the field <code>uniqueNumber</code>.</p>
*
* @param n a int.
*/
public void setUniqueNumber(int n) {
uniqueNumber = n;
this.updateObservers();
}
/**
* <p>Getter for the field <code>uniqueNumber</code>.</p>
*
* @return a int.
*/
public int getUniqueNumber() {
return uniqueNumber;
}
/**
* <p>Setter for the field <code>value</code>.</p>
*
* @param n a long.
*/
public void setValue(long n) {
value = n;
}
/**
* <p>Getter for the field <code>value</code>.</p>
*
* @return a long.
*/
public long getValue() {
return value;
}
/** {@inheritDoc} */
@Override
public int compareTo(Card that) {
/*
* Return a negative integer of this < that,
* a positive integer if this > that,
* and zero otherwise.
*/
if (that == null) {
/*
* "Here we can arbitrarily decide that all non-null Cards are
* `greater than' null Cards. It doesn't really matter what we
* return in this case, as long as it is consistent. I rather think
* of null as being lowly." --Braids
*/
return +1;
}
else if (getUniqueNumber() > that.getUniqueNumber()) {
return +1;
}
else if (getUniqueNumber() < that.getUniqueNumber()) {
return -1;
}
else {
return 0;
}
}
/** {@inheritDoc} */
@Override
public boolean equals(Object o) {
if (o instanceof Card) {
Card c = (Card) o;
int a = getUniqueNumber();
int b = c.getUniqueNumber();
return (a == b);
}
return false;
}
/** {@inheritDoc} */
@Override
public int hashCode() {
return getUniqueNumber();
}
/** {@inheritDoc} */
@Override
public String toString() {
return this.getName() + " (" + this.getUniqueNumber() + ")";
}
/**
* <p>hasFlashback.</p>
*
* @return a boolean.
*/
public boolean hasFlashback() {
return flashback;
}
/**
* <p>Setter for the field <code>flashback</code>.</p>
*
* @param b a boolean.
*/
public void setFlashback(boolean b) {
flashback = b;
}
/**
* <p>hasUnearth.</p>
*
* @return a boolean.
*/
public boolean hasUnearth() {
return unearth;
}
/**
* <p>Setter for the field <code>unearth</code>.</p>
*
* @param b a boolean.
*/
public void setUnearth(boolean b) {
unearth = b;
}
/**
* <p>isUnearthed.</p>
*
* @return a boolean.
*/
public boolean isUnearthed() {
return unearthed;
}
/**
* <p>Setter for the field <code>unearthed</code>.</p>
*
* @param b a boolean.
*/
public void setUnearthed(boolean b) {
unearthed = b;
}
/**
* <p>hasMadness.</p>
*
* @return a boolean.
*/
public boolean hasMadness() {
return madness;
}
/**
* <p>Setter for the field <code>madness</code>.</p>
*
* @param b a boolean.
*/
public void setMadness(boolean b) {
madness = b;
}
/**
* <p>Getter for the field <code>madnessCost</code>.</p>
*
* @return a {@link java.lang.String} object.
*/
public String getMadnessCost() {
return madnessCost;
}
/**
* <p>Setter for the field <code>madnessCost</code>.</p>
*
* @param cost a {@link java.lang.String} object.
*/
public void setMadnessCost(String cost) {
madnessCost = cost;
}
/**
* <p>hasSuspend.</p>
*
* @return a boolean.
*/
public boolean hasSuspend() {
return suspend;
}
/**
* <p>Setter for the field <code>suspend</code>.</p>
*
* @param b a boolean.
*/
public void setSuspend(boolean b) {
suspend = b;
}
/**
* <p>wasSuspendCast.</p>
*
* @return a boolean.
*/
public boolean wasSuspendCast() {
return suspendCast;
}
/**
* <p>Setter for the field <code>suspendCast</code>.</p>
*
* @param b a boolean.
*/
public void setSuspendCast(boolean b) {
suspendCast = b;
}
/**
* <p>Setter for the field <code>kicked</code>.</p>
*
* @param b a boolean.
*/
public void setKicked(boolean b) {
kicked = b;
}
/**
* <p>isKicked.</p>
*
* @return a boolean.
*/
public boolean isKicked() {
return kicked;
}
public boolean isPhasedOut() {
return phasedOut;
}
public void setPhasedOut(boolean phasedOut) {
this.phasedOut = phasedOut;
}
public void phase(){
this.phase(true);
}
public void phase(boolean direct){
boolean phasingIn = this.isPhasedOut();
if (!this.switchPhaseState()){
// Switch Phase State returns False if the Permanent can't Phase Out
return;
}
if (!phasingIn){
this.setDirectlyPhasedOut(direct);
}
for (Card eq : this.getEquippedBy()){
if (eq.isPhasedOut() == phasingIn){
eq.phase(false);
}
}
for (Card aura : this.getEnchantedBy()){
if (aura.isPhasedOut() == phasingIn){
aura.phase(false);
}
}
}
private boolean switchPhaseState(){
if (!this.phasedOut && this.hasKeyword("CARDNAME can't phase out.")){
return false;
}
this.phasedOut = !this.phasedOut;
if (this.phasedOut && isToken()){
// 702.23k Phased-out tokens cease to exist as a state-based action. See rule 704.5d.
// 702.23d The phasing event doesn't actually cause a permanent to change zones or control,
// even though it's treated as though it's not on the battlefield and not under its controller's control while it's phased out.
// Zone-change triggers don't trigger when a permanent phases in or out.
// Suppressed Exiling is as close as we can get to "ceasing to exist"
AllZone.getTriggerHandler().suppressMode("ChangesZone");
AllZone.getGameAction().exile(this);
AllZone.getTriggerHandler().clearSuppression("ChangesZone");
}
return true;
}
public boolean isDirectlyPhasedOut(){
return this.directlyPhasedOut;
}
public void setDirectlyPhasedOut(boolean direct){
this.directlyPhasedOut = direct;
}
/**
* <p>isReflectedLand.</p>
*
* @return a boolean.
*/
public boolean isReflectedLand() {
for(Ability_Mana am : getCharacteristics().getManaAbility())
if (am.isReflectedMana())
return true;
return false;
}
/**
* <p>hasKeyword.</p>
*
* @param keyword a {@link java.lang.String} object.
* @return a boolean.
*/
public boolean hasKeyword(String keyword) {
return getKeyword().contains(keyword);
}
/**
* <p>hasStartOfKeyword.</p>
*
* @param keyword a {@link java.lang.String} object.
* @return a boolean.
*/
public boolean hasStartOfKeyword(String keyword) {
ArrayList<String> a = getKeyword();
for (int i = 0; i < a.size(); i++)
if (a.get(i).toString().startsWith(keyword)) return true;
return false;
}
/**
* <p>hasStartOfKeyword.</p>
*
* @param keyword a {@link java.lang.String} object.
* @return a boolean.
*/
public boolean hasStartOfUnHiddenKeyword(String keyword) {
ArrayList<String> a = this.getUnhiddenKeyword();
for (int i = 0; i < a.size(); i++)
if (a.get(i).toString().startsWith(keyword)) return true;
return false;
}
/**
* <p>getKeywordPosition.</p>
*
* @param k a {@link java.lang.String} object.
* @return a int.
*/
public int getKeywordPosition(String k) {
ArrayList<String> a = getKeyword();
for (int i = 0; i < a.size(); i++)
if (a.get(i).toString().startsWith(k)) return i;
return -1;
}
/**
* <p>keywordsContain.</p>
*
* @param keyword a {@link java.lang.String} object.
* @return a boolean.
*/
public boolean keywordsContain(String keyword) {
ArrayList<String> a = getKeyword();
for (int i = 0; i < a.size(); i++)
if (a.get(i).toString().contains(keyword)) return true;
return false;
}
/**
* <p>hasAnyKeyword.</p>
*
* @param keywords an array of {@link java.lang.String} objects.
* @return a boolean.
*/
public boolean hasAnyKeyword(String keywords[]) {
for (int i = 0; i < keywords.length; i++)
if (hasKeyword(keywords[i]))
return true;
return false;
}
/**
* <p>hasAnyKeyword.</p>
*
* @param keywords a {@link java.util.ArrayList} object.
* @return a boolean.
*/
public boolean hasAnyKeyword(ArrayList<String> keywords) {
for (int i = 0; i < keywords.size(); i++)
if (hasKeyword(keywords.get(i)))
return true;
return false;
}
//This counts the number of instances of a keyword a card has
/**
* <p>getAmountOfKeyword.</p>
*
* @param k a {@link java.lang.String} object.
* @return a int.
*/
public int getAmountOfKeyword(String k) {
int count = 0;
ArrayList<String> keywords = getKeyword();
for (int j = 0; j < keywords.size(); j++) {
if (keywords.get(j).equals(k)) count++;
}
return count;
}
// This is for keywords with a number like Bushido, Annihilator and Rampage. It returns the total.
/**
* <p>getKeywordMagnitude.</p>
*
* @param k a {@link java.lang.String} object.
* @return a int.
*/
public int getKeywordMagnitude(String k) {
int count = 0;
ArrayList<String> keywords = getKeyword();
for (String kw : keywords) {
if (kw.startsWith(k)) {
String[] parse = kw.split(" ");
String s = parse[1];
count += Integer.parseInt(s);
}
}
return count;
}
private String toMixedCase(String s) {
if (s.equals("")) return s;
StringBuilder sb = new StringBuilder();
// to handle hyphenated Types
String[] types = s.split("-");
for (int i = 0; i < types.length; i++) {
if (i != 0)
sb.append("-");
sb.append(types[i].substring(0, 1).toUpperCase());
sb.append(types[i].substring(1).toLowerCase());
}
return sb.toString();
}
//usable to check for changelings
/**
* <p>isType.</p>
*
* @param cardType a {@link java.lang.String} object.
* @return a boolean.
*/
public boolean isType(String cardType) {
cardType = toMixedCase(cardType);
if (typeContains(cardType)
|| ((isCreature() || isTribal())
&& CardUtil.isACreatureType(cardType) && typeContains("AllCreatureTypes"))) return true;
return false;
}//isType
// Takes one argument like Permanent.Blue+withFlying
/**
* <p>isValid.</p>
*
* @param Restriction a {@link java.lang.String} object.
* @param sourceController a {@link forge.Player} object.
* @param source a {@link forge.Card} object.
* @return a boolean.
*/
@Override
public boolean isValid(final String Restriction, final Player sourceController, final Card source) {
if (getName().equals("Mana Pool") || isImmutable()) return false;
String incR[] = Restriction.split("\\."); // Inclusive restrictions are Card types
if (incR[0].equals("Spell") && !isSpell())
return false;
if (incR[0].equals("Permanent") && (isInstant() || isSorcery()))
return false;
if (!incR[0].equals("card")
&& !incR[0].equals("Card")
&& !incR[0].equals("Spell")
&& !incR[0].equals("Permanent")
&& !(isType(incR[0])))
return false; //Check for wrong type
if (incR.length > 1) {
final String excR = incR[1];
String exR[] = excR.split("\\+"); // Exclusive Restrictions are ...
for (int j = 0; j < exR.length; j++)
if (hasProperty(exR[j], sourceController, source) == false) return false;
}
return true;
}//isValid(String Restriction)
// Takes arguments like Blue or withFlying
/**
* <p>hasProperty.</p>
*
* @param Property a {@link java.lang.String} object.
* @param sourceController a {@link forge.Player} object.
* @param source a {@link forge.Card} object.
* @return a boolean.
*/
@Override
public boolean hasProperty(String Property, final Player sourceController, final Card source) {
//by name can also have color names, so needs to happen before colors.
if (Property.startsWith("named")) {
if (!getName().equals(Property.substring(5))) return false;
} else if (Property.startsWith("notnamed")) {
if (getName().equals(Property.substring(8))) return false;
} else if (Property.startsWith("sameName")) {
if (!getName().equals(source.getName())) return false;
} else if (Property.equals("NamedCard")) {
if (!getName().equals(source.getNamedCard())) return false;
}
// ... Card colors
else if (Property.contains("White")
|| Property.contains("Blue")
|| Property.contains("Black")
|| Property.contains("Red")
|| Property.contains("Green")
|| Property.contains("Colorless")) {
if (Property.startsWith("non")) {
if (CardUtil.getColors(this).contains(Property.substring(3).toLowerCase())) return false;
} else if (!CardUtil.getColors(this).contains(Property.toLowerCase())) return false;
} else if (Property.contains("MultiColor")) // ... Card is multicolored
{
if (Property.startsWith("non") && (CardUtil.getColors(this).size() > 1)) return false;
if (!Property.startsWith("non") && (CardUtil.getColors(this).size() <= 1)) return false;
} else if (Property.contains("MonoColor")) // ... Card is monocolored
{
if (Property.startsWith("non") && (CardUtil.getColors(this).size() == 1 && !isColorless())) return false;
if (!Property.startsWith("non") && (CardUtil.getColors(this).size() > 1 || isColorless())) return false;
} else if (Property.equals("ChosenColor")) {
//Should this match All chosen colors, or any? Default to first chosen for now until it matters.
if (source.getChosenColor().size() == 0) return false;
if (!CardUtil.getColors(this).contains(source.getChosenColor().get(0))) return false;
} else if (Property.equals("DoubleFaced")) {
if (!isDoubleFaced) return false;
} else if (Property.equals("Flip")) {
if (!isFlip) return false;
} else if (Property.startsWith("YouCtrl")) {
if (!getController().isPlayer(sourceController)) return false;
} else if (Property.startsWith("YouDontCtrl")) {
if (getController().isPlayer(sourceController)) return false;
} else if (Property.startsWith("EnchantedPlayerCtrl")) {
Object o = source.getEnchanting();
if (o instanceof Player) {
if (!getController().isPlayer((Player) o)) return false;
}
else { // source not enchanting a player
return false;
}
} else if (Property.startsWith("YouOwn")) {
if (!getOwner().isPlayer(sourceController)) return false;
} else if (Property.startsWith("YouDontOwn")) {
if (getOwner().isPlayer(sourceController)) return false;
} else if (Property.startsWith("OwnerDoesntControl")) {
if(getOwner().isPlayer(getController())) return false;
} else if (Property.startsWith("ControllerControls")) {
String type = Property.substring(18);
CardList list = getController().getCardsIn(Zone.Battlefield);
if (list.getType(type).isEmpty()) return false;
} else if (Property.startsWith("Other")) {
if (this.equals(source)) return false;
} else if (Property.startsWith("Self")) {
if (!this.equals(source)) return false;
} else if (Property.startsWith("AttachedBy")) {
if (!equippedBy.contains(source) && !enchantedBy.contains(source)) return false;
} else if (Property.startsWith("Attached")) {
if (!equipping.contains(source) && !source.equals(enchanting)) return false;
} else if (Property.startsWith("EnchantedBy")) {
if (!enchantedBy.contains(source)) return false;
} else if (Property.startsWith("NotEnchantedBy")) {
if (enchantedBy.contains(source)) return false;
} else if (Property.startsWith("Enchanted")) {
if (!source.equals(enchanting)) return false;
} else if (Property.startsWith("EquippedBy")) {
if (!equippedBy.contains(source)) return false;
} else if (Property.startsWith("Equipped")) {
if (!equipping.contains(source)) return false;
} else if (Property.startsWith("HauntedBy")) {
if (!hauntedBy.contains(source)) return false;
} else if (Property.startsWith("Above")){ // "Are Above" Source
CardList list = this.getOwner().getCardsIn(Zone.Graveyard);
if (!list.getAbove(source, this))
return false;
} else if (Property.startsWith("DirectlyAbove")){ // "Are Directly Above" Source
CardList list = this.getOwner().getCardsIn(Zone.Graveyard);
if (!list.getDirectlyAbove(source, this))
return false;
} else if (Property.startsWith("TopGraveyardCreature")){
CardList list = this.getOwner().getCardsIn(Zone.Graveyard);
list = list.getType("Creature");
list.reverse();
if (list.isEmpty() || !this.equals(list.get(0)))
return false;
} else if (Property.startsWith("TopGraveyard")){
CardList list = this.getOwner().getCardsIn(Zone.Graveyard);
list.reverse();
if (list.isEmpty() || !this.equals(list.get(0)))
return false;
} else if (Property.startsWith("TopLibrary")){
CardList list = this.getOwner().getCardsIn(Zone.Library);
if (list.isEmpty() || !this.equals(list.get(0)))
return false;
} else if (Property.startsWith("Cloned")) {
if (cloneOrigin == null || !cloneOrigin.equals(source)) return false;
} else if (Property.startsWith("DamagedBy")) {
if (!receivedDamageFromThisTurn.containsKey(source)) return false;
} else if (Property.startsWith("Damaged")) {
if (!dealtDamageToThisTurn.containsKey(source)) return false;
} else if (Property.startsWith("SharesColorWith")) {
if (!sharesColorWith(source)) return false;
} else if (Property.startsWith("withFlashback")) {
boolean fb = false;
if (hasStartOfUnHiddenKeyword("Flashback")) {
fb = true;
}
for (SpellAbility sa : this.getSpellAbilities()) {
if (sa.isFlashBackAbility()) {
fb = true;
}
}
if (!fb) {
return false;
}
} else if (Property.startsWith("with")) // ... Card keywords
{
if (Property.startsWith("without") && hasStartOfUnHiddenKeyword(Property.substring(7))) return false;
if (!Property.startsWith("without") && !hasStartOfUnHiddenKeyword(Property.substring(4))) return false;
} else if (Property.startsWith("tapped")) {
if (!isTapped()) return false;
} else if (Property.startsWith("untapped")) {
if (!isUntapped()) return false;
} else if (Property.startsWith("faceDown")) {
if (!isFaceDown()) return false;
} else if (Property.startsWith("faceUp")) {
if (isFaceDown()) return false;
} else if (Property.startsWith("hasLevelUp")) {
if (!hasLevelUp()) return false;
} else if (Property.startsWith("enteredBattlefieldThisTurn")) {
if (!(getTurnInZone() == AllZone.getPhase().getTurn())) return false;
} else if (Property.startsWith("dealtDamageToYouThisTurn")) {
if (!(dealtDmgToHumanThisTurn && getController().isPlayer(AllZone.getComputerPlayer()))
&& !(dealtDmgToComputerThisTurn && getController().isPlayer(AllZone.getHumanPlayer())))
return false;
} else if (Property.startsWith("wasDealtDamageThisTurn")) {
if ((getReceivedDamageFromThisTurn().keySet()).isEmpty()) return false;
} else if (Property.startsWith("greatestPower")) {
CardList list = AllZoneUtil.getCreaturesInPlay();
for (Card crd : list) {
if (crd.getNetAttack() > getNetAttack()) {
return false;
}
}
} else if (Property.startsWith("leastPower")) {
CardList list = AllZoneUtil.getCreaturesInPlay();
for (Card crd : list) {
if (crd.getNetAttack() < getNetAttack()) {
return false;
}
}
} else if (Property.startsWith("greatestCMC")) {
CardList list = AllZoneUtil.getCreaturesInPlay();
for (Card crd : list) {
if (crd.getCMC() > getCMC()) {
return false;
}
}
} else if (Property.startsWith("enchanted")) {
if (!isEnchanted()) return false;
} else if (Property.startsWith("unenchanted")) {
if (isEnchanted()) return false;
} else if (Property.startsWith("enchanting")) {
if (!isEnchanting()) return false;
} else if (Property.startsWith("equipped")) {
if (!isEquipped()) return false;
} else if (Property.startsWith("unequipped")) {
if (isEquipped()) return false;
} else if (Property.startsWith("equipping")) {
if (!isEquipping()) return false;
} else if (Property.startsWith("token")) {
if (!isToken()) return false;
} else if (Property.startsWith("nonToken")) {
if (isToken()) return false;
} else if (Property.startsWith("hasXCost")) {
if (getSpellAbility().length > 0)
if (!getSpellAbility()[0].isXCost())
return false;
} else if (Property.startsWith("power") || // 8/10
Property.startsWith("toughness") ||
Property.startsWith("cmc")) {
int x = 0;
int y = 0;
int z = 0;
if (Property.startsWith("power")) {
z = 7;
y = getNetAttack();
} else if (Property.startsWith("toughness")) {
z = 11;
y = getNetDefense();
} else if (Property.startsWith("cmc")) {
z = 5;
y = getCMC();
}
if (Property.substring(z).equals("X")) {
x = CardFactoryUtil.xCount(source, source.getSVar("X"));
} else if (Property.substring(z).equals("Y")) {
x = CardFactoryUtil.xCount(source, source.getSVar("Y"));
} else
x = Integer.parseInt(Property.substring(z));
if (!AllZoneUtil.compare(y, Property, x))
return false;
}
// syntax example: countersGE9 P1P1 or countersLT12TIME (greater number than 99 not supported)
/*
* slapshot5 - fair warning, you cannot use numbers with 2 digits (greater number than 9 not supported
* you can use X and the SVar:X:Number$12 to get two digits. This will need a better fix, and I have the
* beginnings of a regex below
*/
else if (Property.startsWith("counters")) {
/*
Pattern p = Pattern.compile("[a-z]*[A-Z][A-Z][X0-9]+.*$");
String[] parse = ???
System.out.println("Parsing completed of: "+Property);
for(int i = 0; i < parse.length; i++) {
System.out.println("parse["+i+"]: "+parse[i]);
}*/
// TODO: get a working regex out of this pattern so the amount of digits doesn't matter
int number = 0;
String[] splitProperty = Property.split("_");
String strNum = splitProperty[1].substring(2);
String comparator = splitProperty[1].substring(0,2);
String counterType = "";
try {
number = Integer.parseInt(strNum);
}
catch(NumberFormatException e) {
number = CardFactoryUtil.xCount(source, source.getSVar(strNum));
}
counterType = splitProperty[2];
int actualnumber = getCounters(Counters.getType(counterType));
if (!AllZoneUtil.compare(actualnumber, comparator, number))
return false;
} else if (Property.startsWith("attacking")) {
if (!isAttacking()) return false;
} else if (Property.startsWith("notattacking")) {
if (isAttacking()) return false;
} else if (Property.equals("blocking")) {
if (!isBlocking()) return false;
} else if (Property.startsWith("blockingSource")) {
if (!isBlocking(source)) return false;
} else if (Property.startsWith("notblocking")) {
if (isBlocking()) return false;
} else if (Property.equals("blocked")) {
if (!AllZone.getCombat().isBlocked(this)) return false;
} else if (Property.startsWith("blockedBySource")) {
if (!isBlockedBy(source)) return false;
} else if (Property.startsWith("unblocked")) {
if (!AllZone.getCombat().isUnblocked(this)) return false;
} else if (Property.startsWith("kicked")) {
if (!isKicked()) return false;
} else if (Property.startsWith("notkicked")) {
if (isKicked()) return false;
} else if (Property.startsWith("evoked")) {
if (!isEvoked()) return false;
} else if (Property.equals("HasDevoured")) {
if(devouredCards.size() == 0) return false;
} else if (Property.equals("HasNotDevoured")) {
if(devouredCards.size() != 0) return false;
} else if (Property.startsWith("non")) // ... Other Card types
{
if (isType(Property.substring(3))) return false;
} else if (Property.equals("CostsPhyrexianMana")) {
if (!getCharacteristics().getManaCost().contains("P")) return false;
} else if (Property.equals("IsRemembered")) {
if(!source.getRemembered().contains(this)) return false;
} else {
if (Property.equals("ChosenType")) {
if (!isType(source.getChosenType())) return false;
} else {
if (!isType(Property)) return false;
}
}
return true;
}//hasProperty
/**
* <p>setImmutable.</p>
*
* @param isImmutable a boolean.
*/
public void setImmutable(boolean isImmutable) {
this.isImmutable = isImmutable;
}
/**
* <p>isImmutable.</p>
*
* @return a boolean.
*/
public boolean isImmutable() {
return isImmutable;
}
/*
* there are easy checkers for Color. The CardUtil functions should
* be made part of the Card class, so calling out is not necessary
*/
/**
* <p>isColor.</p>
*
* @param col a {@link java.lang.String} object.
* @return a boolean.
*/
public boolean isColor(String col) {
return CardUtil.getColors(this).contains(col);
}
/**
* <p>isBlack.</p>
*
* @return a boolean.
*/
public boolean isBlack() {
return CardUtil.getColors(this).contains(Constant.Color.Black);
}
/**
* <p>isBlue.</p>
*
* @return a boolean.
*/
public boolean isBlue() {
return CardUtil.getColors(this).contains(Constant.Color.Blue);
}
/**
* <p>isRed.</p>
*
* @return a boolean.
*/
public boolean isRed() {
return CardUtil.getColors(this).contains(Constant.Color.Red);
}
/**
* <p>isGreen.</p>
*
* @return a boolean.
*/
public boolean isGreen() {
return CardUtil.getColors(this).contains(Constant.Color.Green);
}
/**
* <p>isWhite.</p>
*
* @return a boolean.
*/
public boolean isWhite() {
return CardUtil.getColors(this).contains(Constant.Color.White);
}
/**
* <p>isColorless.</p>
*
* @return a boolean.
*/
public boolean isColorless() {
return CardUtil.getColors(this).contains(Constant.Color.Colorless);
}
/**
* <p>sharesColorWith.</p>
*
* @param c1 a {@link forge.Card} object.
* @return a boolean.
*/
public boolean sharesColorWith(final Card c1) {
boolean shares = false;
shares |= (isBlack() && c1.isBlack());
shares |= (isBlue() && c1.isBlue());
shares |= (isGreen() && c1.isGreen());
shares |= (isRed() && c1.isRed());
shares |= (isWhite() && c1.isWhite());
return shares;
}
/**
* <p>isAttacking.</p>
*
* @return a boolean.
*/
public boolean isAttacking() {
return AllZone.getCombat().isAttacking(this);
}
/**
* <p>isBlocking.</p>
*
* @return a boolean.
*/
public boolean isBlocking() {
CardList blockers = AllZone.getCombat().getAllBlockers();
return blockers.contains(this);
}
/**
* <p>isBlocking.</p>
*
* @param attacker a {@link forge.Card} object.
* @return a boolean.
*/
public boolean isBlocking(Card attacker) {
return attacker.equals(AllZone.getCombat().getAttackerBlockedBy(this));
}
/**
* <p>isBlockedBy.</p>
*
* @param blocker a {@link forge.Card} object.
* @return a boolean.
*/
public boolean isBlockedBy(Card blocker) {
return this.equals(AllZone.getCombat().getAttackerBlockedBy(blocker));
}
///////////////////////////
//
// Damage code
//
//////////////////////////
//all damage to cards is now handled in Card.java, no longer AllZone.getGameAction()...
/**
* <p>addReceivedDamageFromThisTurn.</p>
*
* @param c a {@link forge.Card} object.
* @param damage a int.
*/
public void addReceivedDamageFromThisTurn(Card c, int damage) {
receivedDamageFromThisTurn.put(c, damage);
}
/**
* <p>Setter for the field <code>receivedDamageFromThisTurn</code>.</p>
*
* @param receivedDamageList a Map object.
*/
public void setReceivedDamageFromThisTurn(Map<Card, Integer> receivedDamageList) {
receivedDamageFromThisTurn = receivedDamageList;
}
/**
* <p>Getter for the field <code>receivedDamageFromThisTurn</code>.</p>
*
* @return a Map object.
*/
public Map<Card, Integer> getReceivedDamageFromThisTurn() {
return receivedDamageFromThisTurn;
}
/**
* <p>resetReceivedDamageFromThisTurn.</p>
*/
public void resetReceivedDamageFromThisTurn() {
receivedDamageFromThisTurn.clear();
}
/**
* <p>addDealtDamageToThisTurn.</p>
*
* @param c a {@link forge.Card} object.
* @param damage a int.
*/
public void addDealtDamageToThisTurn(Card c, int damage) {
dealtDamageToThisTurn.put(c, damage);
}
/**
* <p>Setter for the field <code>dealtDamageToThisTurn</code>.</p>
*
* @param dealtDamageList a {@link java.util.Map} object.
*/
public void setDealtDamageToThisTurn(Map<Card, Integer> dealtDamageList) {
dealtDamageToThisTurn = dealtDamageList;
}
/**
* <p>Getter for the field <code>dealtDamageToThisTurn</code>.</p>
*
* @return a {@link java.util.Map} object.
*/
public Map<Card, Integer> getDealtDamageToThisTurn() {
return dealtDamageToThisTurn;
}
/**
* <p>resetDealtDamageToThisTurn.</p>
*/
public void resetDealtDamageToThisTurn() {
dealtDamageToThisTurn.clear();
}
//how much damage is enough to kill the creature (for AI)
/**
* <p>getEnoughDamageToKill.</p>
*
* @param maxDamage a int.
* @param source a {@link forge.Card} object.
* @param isCombat a boolean.
* @return a int.
*/
public int getEnoughDamageToKill(int maxDamage, Card source, boolean isCombat) {
return getEnoughDamageToKill(maxDamage, source, isCombat, false);
}
/**
* <p>getEnoughDamageToKill.</p>
*
* @param maxDamage a int.
* @param source a {@link forge.Card} object.
* @param isCombat a boolean.
* @param noPrevention a boolean.
* @return a int.
*/
public int getEnoughDamageToKill(int maxDamage, Card source, boolean isCombat, boolean noPrevention) {
int killDamage = getKillDamage();
if (hasKeyword("Indestructible") || getShield() > 0) {
if (!(source.hasKeyword("Wither") || source.hasKeyword("Infect")))
return maxDamage + 1;
} else if (source.hasKeyword("Deathtouch")) {
for (int i = 1; i <= maxDamage; i++) {
if (noPrevention) {
if (staticReplaceDamage(i, source, isCombat) > 0)
return i;
} else if (predictDamage(i, source, isCombat) > 0)
return i;
}
}
for (int i = 1; i <= maxDamage; i++) {
if (noPrevention) {
if (staticReplaceDamage(i, source, isCombat) >= killDamage)
return i;
} else {
if (predictDamage(i, source, isCombat) >= killDamage)
return i;
}
}
return maxDamage + 1;
}
//the amount of damage needed to kill the creature (for AI)
/**
* <p>getKillDamage.</p>
*
* @return a int.
*/
public int getKillDamage() {
int killDamage = getLethalDamage() + getPreventNextDamage();
if (killDamage > getPreventNextDamage() && hasStartOfKeyword("When CARDNAME is dealt damage, destroy it.")) {
killDamage = 1 + getPreventNextDamage();
}
return killDamage;
}
//this is the minimal damage a trampling creature has to assign to a blocker
/**
* <p>getLethalDamage.</p>
*
* @return a int.
*/
public int getLethalDamage() {
int lethalDamage = getNetDefense() - getDamage() - getTotalAssignedDamage();
return lethalDamage;
}
/**
* <p>Setter for the field <code>damage</code>.</p>
*
* @param n a int.
*/
public void setDamage(int n) {
//if (this.hasKeyword("Prevent all damage that would be dealt to CARDNAME.")) n = 0;
damage = n;
}
/**
* <p>Getter for the field <code>damage</code>.</p>
*
* @return a int.
*/
public int getDamage() {
return damage;
}
/**
* <p>addAssignedDamage.</p>
*
* @param damage a int.
* @param sourceCard a {@link forge.Card} object.
*/
public void addAssignedDamage(int damage, Card sourceCard) {
if (damage < 0) {
damage = 0;
}
int assignedDamage = damage;
Log.debug(this + " - was assigned " + assignedDamage + " damage, by " + sourceCard);
if (!assignedDamageMap.containsKey(sourceCard)) {
assignedDamageMap.put(sourceCard, assignedDamage);
} else {
assignedDamageMap.put(sourceCard, assignedDamageMap.get(sourceCard) + assignedDamage);
}
Log.debug("***");
/*
if(sourceCards.size() > 1)
System.out.println("(MULTIPLE blockers):");
System.out.println("Assigned " + damage + " damage to " + card);
for (int i=0;i<sourceCards.size();i++){
System.out.println(sourceCards.get(i).getName() + " assigned damage to " + card.getName());
}
System.out.println("***");
*/
}
/**
* <p>clearAssignedDamage.</p>
*/
public void clearAssignedDamage() {
assignedDamageMap.clear();
}
/**
* <p>getTotalAssignedDamage.</p>
*
* @return a int.
*/
public int getTotalAssignedDamage() {
int total = 0;
Collection<Integer> c = assignedDamageMap.values();
Iterator<Integer> itr = c.iterator();
while (itr.hasNext()) {
total += itr.next();
}
return total;
}
/**
* <p>Getter for the field <code>assignedDamageMap</code>.</p>
*
* @return a {@link java.util.Map} object.
*/
public Map<Card, Integer> getAssignedDamageMap() {
return assignedDamageMap;
}
/**
* <p>addCombatDamage.</p>
*
* @param map a {@link java.util.Map} object.
*/
public void addCombatDamage(Map<Card, Integer> map) {
CardList list = new CardList();
for (Entry<Card, Integer> entry : map.entrySet()) {
Card source = entry.getKey();
list.add(source);
int damageToAdd = entry.getValue();
damageToAdd = replaceDamage(damageToAdd, source, true);
damageToAdd = preventDamage(damageToAdd, source, true);
if (damageToAdd > 0 && isCreature()) {
GameActionUtil.executeCombatDamageToCreatureEffects(source, this, damageToAdd);
}
map.put(source, damageToAdd);
}
if (AllZoneUtil.isCardInPlay(this)) {
addDamage(map);
}
}
//This function helps the AI calculate the actual amount of damage an effect would deal
/**
* <p>predictDamage.</p>
*
* @param damage a int.
* @param possiblePrevention a int.
* @param source a {@link forge.Card} object.
* @param isCombat a boolean.
* @return a int.
*/
public int predictDamage(final int damage, final int possiblePrevention, final Card source, final boolean isCombat) {
int restDamage = damage;
restDamage = staticReplaceDamage(restDamage, source, isCombat);
restDamage = staticDamagePrevention(restDamage, possiblePrevention, source, isCombat);
return restDamage;
}
//This should be also usable by the AI to forecast an effect (so it must not change the game state)
/**
* <p>staticDamagePrevention.</p>
*
* @param damage a int.
* @param possiblePrvenetion a int.
* @param source a {@link forge.Card} object.
* @param isCombat a boolean.
* @return a int.
*/
public int staticDamagePrevention(final int damage, final int possiblePrvenetion, final Card source, final boolean isCombat) {
if (AllZoneUtil.isCardInPlay("Leyline of Punishment")) {
return damage;
}
int restDamage = damage - possiblePrvenetion;
restDamage = staticDamagePrevention(restDamage, source, isCombat);
return restDamage;
}
//This should be also usable by the AI to forecast an effect (so it must not change the game state)
/**
* <p>staticDamagePrevention.</p>
*
* @param damageIn a int.
* @param source a {@link forge.Card} object.
* @param isCombat a boolean.
* @return a int.
*/
@Override
public final int staticDamagePrevention(final int damageIn, final Card source, final boolean isCombat) {
if (AllZoneUtil.isCardInPlay("Leyline of Punishment")) {
return damageIn;
}
int restDamage = damageIn;
if (CardFactoryUtil.hasProtectionFrom(source, this)) {
return 0;
}
if (isCombat) {
if (hasKeyword("Prevent all combat damage that would be dealt to and dealt by CARDNAME.")) return 0;
if (hasKeyword("Prevent all combat damage that would be dealt to CARDNAME.")) return 0;
if (source.hasKeyword("Prevent all combat damage that would be dealt to and dealt by CARDNAME.")) return 0;
if (source.hasKeyword("Prevent all combat damage that would be dealt by CARDNAME.")) return 0;
}
if (hasKeyword("Prevent all damage that would be dealt to CARDNAME.")) return 0;
if (hasKeyword("Prevent all damage that would be dealt to and dealt by CARDNAME.")) return 0;
if (source.hasKeyword("Prevent all damage that would be dealt to and dealt by CARDNAME.")) return 0;
if (source.hasKeyword("Prevent all damage that would be dealt by CARDNAME.")) return 0;
if (hasStartOfKeyword("Absorb")) {
int absorbed = this.getKeywordMagnitude("Absorb");
if (restDamage > absorbed) {
restDamage = restDamage - absorbed;
} else {
return 0;
}
}
if (hasStartOfKeyword("PreventAllDamageBy")) {
String valid = getKeyword().get(getKeywordPosition("PreventAllDamageBy"));
valid = valid.split(" ", 2)[1];
if (source.isValid(valid, this.getController(), this)) {
return 0;
}
}
//Prevent Damage static abilities
CardList allp = AllZoneUtil.getCardsIn(Zone.Battlefield);
for (Card ca : allp) {
ArrayList<StaticAbility> staticAbilities = ca.getStaticAbilities();
for (StaticAbility stAb : staticAbilities) {
restDamage = stAb.applyAbility("PreventDamage", source, this, restDamage, isCombat);
}
}
// specific Cards
if (isCreature()) { //and not a planeswalker
if (getName().equals("Swans of Bryn Argoll")) {
return 0;
}
if ((source.isCreature() && AllZoneUtil.isCardInPlay("Well-Laid Plans") && source.sharesColorWith(this))) {
return 0;
}
} //Creature end
if (restDamage > 0) {
return restDamage;
} else {
return 0;
}
}
/**
* <p>preventDamage.</p>
*
* @param damage a int.
* @param source a {@link forge.Card} object.
* @param isCombat a boolean.
* @return a int.
*/
@Override
public int preventDamage(final int damage, Card source, boolean isCombat) {
if (AllZoneUtil.isCardInPlay("Leyline of Punishment")) {
return damage;
}
int restDamage = damage;
if (getName().equals("Swans of Bryn Argoll")) {
source.getController().drawCards(restDamage);
return 0;
}
restDamage = staticDamagePrevention(restDamage, source, isCombat);
if (restDamage == 0) {
return 0;
}
if (this.hasKeyword("If damage would be dealt to CARDNAME, prevent that damage. Remove a +1/+1 counter from CARDNAME.")) {
restDamage = 0;
this.subtractCounter(Counters.P1P1, 1);
}
if (restDamage >= getPreventNextDamage()) {
restDamage = restDamage - getPreventNextDamage();
setPreventNextDamage(0);
} else {
setPreventNextDamage(getPreventNextDamage() - restDamage);
restDamage = 0;
}
if (getName().equals("Phyrexian Hydra")) {
addCounter(Counters.M1M1, restDamage);
return 0;
}
return restDamage;
}
//This should be also usable by the AI to forecast an effect (so it must not change the game state)
/**
* <p>staticReplaceDamage.</p>
*
* @param damage a int.
* @param source a {@link forge.Card} object.
* @param isCombat a boolean.
* @return a int.
*/
@Override
public final int staticReplaceDamage(final int damage, Card source, boolean isCombat) {
int restDamage = damage;
if (AllZoneUtil.isCardInPlay("Sulfuric Vapors") && source.isSpell() && source.isRed()) {
int amount = AllZoneUtil.getCardsIn(Zone.Battlefield, "Sulfuric Vapors").size();
for (int i = 0; i < amount; i++) {
restDamage += 1;
}
}
if (AllZoneUtil.isCardInPlay("Pyromancer's Swath", source.getController()) && (source.isInstant() || source.isSorcery())
&& isCreature())
{
int amount = source.getController().getCardsIn(Zone.Battlefield, "Pyromancer's Swath").size();
for (int i = 0; i < amount; i++) {
restDamage += 2;
}
}
if (AllZoneUtil.isCardInPlay("Furnace of Rath") && isCreature()) {
int amount = AllZoneUtil.getCardsIn(Zone.Battlefield, "Furnace of Rath").size();
for (int i = 0; i < amount; i++) {
restDamage += restDamage;
}
}
if (AllZoneUtil.isCardInPlay("Gratuitous Violence", source.getController()) && source.isCreature() && isCreature()) {
int amount = source.getController().getCardsIn(Zone.Battlefield, "Gratuitous Violence").size();
for (int i = 0; i < amount; i++) {
restDamage += restDamage;
}
}
if (AllZoneUtil.isCardInPlay("Fire Servant", source.getController()) && source.isRed()
&& (source.isInstant() || source.isSorcery()))
{
int amount = source.getController().getCardsIn(Zone.Battlefield, "Fire Servant").size();
for (int i = 0; i < amount; i++) {
restDamage += restDamage;
}
}
if (AllZoneUtil.isCardInPlay("Benevolent Unicorn") && source.isSpell() && isCreature()) {
int amount = AllZoneUtil.getCardsIn(Zone.Battlefield, "Benevolent Unicorn").size();
for (int i = 0; i < amount; i++) {
if (restDamage > 0) {
restDamage -= 1;
}
}
}
if (AllZoneUtil.isCardInPlay("Lashknife Barrier", getController()) && isCreature()) {
int amount = getController().getCardsIn(Zone.Battlefield, "Lashknife Barrier").size();
for (int i = 0; i < amount; i++) {
if (restDamage > 0) {
restDamage -= 1;
}
}
}
if (AllZoneUtil.isCardInPlay("Divine Presence") && isCreature() && restDamage > 3) {
restDamage = 3;
}
if (getName().equals("Phytohydra")) {
return 0;
}
return restDamage;
}
/**
* <p>replaceDamage.</p>
*
* @param damageIn a int.
* @param source a {@link forge.Card} object.
* @param isCombat a boolean.
* @return a int.
*/
@Override
public final int replaceDamage(final int damageIn, final Card source, final boolean isCombat) {
int restDamage = damageIn;
CardList auras = new CardList(getEnchantedBy().toArray());
if (getName().equals("Phytohydra")) {
addCounter(Counters.P1P1, restDamage);
return 0;
}
if (auras.containsName("Treacherous Link")) {
getController().addDamage(restDamage, source);
return 0;
}
restDamage = staticReplaceDamage(restDamage, source, isCombat);
if (getName().equals("Lichenthrope")) {
addCounter(Counters.M1M1, restDamage);
return 0;
}
return restDamage;
}
/**
* <p>addDamage.</p>
*
* @param sourcesMap a {@link java.util.Map} object.
*/
public final void addDamage(final Map<Card, Integer> sourcesMap) {
for (Entry<Card, Integer> entry : sourcesMap.entrySet()) {
addDamageAfterPrevention(entry.getValue(), entry.getKey(), true); // damage prevention is already checked!
}
}
/**
* <p>addDamageAfterPrevention.</p>
* This function handles damage after replacement and prevention effects are applied.
*
* @param damageIn a int.
* @param source a {@link forge.Card} object.
* @param isCombat a boolean.
*/
@Override
public final void addDamageAfterPrevention(final int damageIn, final Card source, final boolean isCombat) {
int damageToAdd = damageIn;
boolean wither = false;
if (damageToAdd == 0) {
return; //Rule 119.8
}
System.out.println("Adding " + damageToAdd + " damage to " + getName());
Log.debug("Adding " + damageToAdd + " damage to " + getName());
addReceivedDamageFromThisTurn(source, damageToAdd);
source.addDealtDamageToThisTurn(this, damageToAdd);
GameActionUtil.executeDamageDealingEffects(source, damageToAdd);
//Run triggers
Map<String, Object> runParams = new TreeMap<String, Object>();
runParams.put("DamageSource", source);
runParams.put("DamageTarget", this);
runParams.put("DamageAmount", damageToAdd);
runParams.put("IsCombatDamage", isCombat);
AllZone.getTriggerHandler().runTrigger("DamageDone", runParams);
if (this.isPlaneswalker()) {
this.subtractCounter(Counters.LOYALTY, damageToAdd);
return;
}
if ((source.hasKeyword("Wither") || source.hasKeyword("Infect"))) {
wither = true;
}
GameActionUtil.executeDamageToCreatureEffects(source, this, damageToAdd);
if (AllZoneUtil.isCardInPlay(this) && wither) {
addCounter(Counters.M1M1, damageToAdd);
}
if (AllZoneUtil.isCardInPlay(this) && !wither) {
damage += damageToAdd;
}
}
private String curSetCode = "";
/**
* <p>addSet.</p>
*
* @param sInfo a {@link forge.SetInfo} object.
*/
public final void addSet(final SetInfo sInfo) {
getCharacteristics().getSets().add(sInfo);
}
/**
* <p>getSets.</p>
*
* @return a {@link java.util.ArrayList} object.
*/
public final ArrayList<SetInfo> getSets() {
return getCharacteristics().getSets();
}
/**
* <p>setSets.</p>
*
* @param siList a {@link java.util.ArrayList} object.
*/
public final void setSets(final ArrayList<SetInfo> siList) {
getCharacteristics().setSets(siList);
}
/**
* <p>Setter for the field <code>curSetCode</code>.</p>
*
* @param setCode a {@link java.lang.String} object.
*/
public final void setCurSetCode(final String setCode) {
curSetCode = setCode;
}
/**
* <p>Getter for the field <code>curSetCode</code>.</p>
*
* @return a {@link java.lang.String} object.
*/
public final String getCurSetCode() {
return curSetCode;
}
/**
* <p>setRandomSetCode.</p>
*/
public final void setRandomSetCode() {
if (getCharacteristics().getSets().size() < 1) {
return;
}
Random r = MyRandom.random;
SetInfo si = getCharacteristics().getSets().get(r.nextInt(getCharacteristics().getSets().size()));
curSetCode = si.Code;
}
/**
* <p>getSetImageName.</p>
*
* @param setCode a {@link java.lang.String} object.
* @return a {@link java.lang.String} object.
*/
public final String getSetImageName(final String setCode) {
return "/" + setCode + "/" + getImageName();
}
/**
* <p>getCurSetImage.</p>
*
* @return a {@link java.lang.String} object.
*/
public final String getCurSetImage() {
return getSetImageName(curSetCode);
}
/**
* <p>getCurSetRarity.</p>
*
* @return a {@link java.lang.String} object.
*/
public final String getCurSetRarity() {
for (int i = 0; i < getCharacteristics().getSets().size(); i++) {
if (getCharacteristics().getSets().get(i).Code.equals(curSetCode)) {
return getCharacteristics().getSets().get(i).Rarity;
}
}
return "";
}
/**
* <p>getCurSetURL.</p>
*
* @return a {@link java.lang.String} object.
*/
public final String getCurSetURL() {
for (int i = 0; i < getCharacteristics().getSets().size(); i++) {
if (getCharacteristics().getSets().get(i).Code.equals(curSetCode)) {
return getCharacteristics().getSets().get(i).URL;
}
}
return "";
}
/**
* <p>getMostRecentSet.</p>
*
* @return a {@link java.lang.String} object.
*/
public final String getMostRecentSet() {
return CardDb.instance().getCard(this.getName()).getSet();
}
/**
* <p>setImageFilename.</p>
*
* @param iFN a {@link java.lang.String} object.
*/
public void setImageFilename(final String iFN) {
getCharacteristics().setImageFilename(iFN);
}
/**
* <p>getImageFilename.</p>
*
* @return a {@link java.lang.String} object.
*/
public final String getImageFilename() {
return getCharacteristics().getImageFilename();
}
/**
* <p>Setter for the field <code>evoked</code>.</p>
*
* @param evokedIn a boolean.
*/
public final void setEvoked(final boolean evokedIn) {
evoked = evokedIn;
}
/**
* <p>isEvoked.</p>
*
* @return a boolean.
*/
public final boolean isEvoked() {
return evoked;
}
/**
*
* TODO Write javadoc for this method.
* @param t a long
*/
public final void setTimestamp(final long t) {
timestamp = t;
}
/**
*
* TODO Write javadoc for this method.
* @return a long
*/
public final long getTimestamp() {
return timestamp;
}
/**
*
* TODO Write javadoc for this method.
* @return an int
*/
public final int getFoil() {
if (getCharacteristics().getsVars().containsKey("Foil")) {
return Integer.parseInt(getCharacteristics().getsVars().get("Foil"));
}
return 0;
}
/**
*
* TODO Write javadoc for this method.
* @param f an int
*/
public final void setFoil(final int f) {
getCharacteristics().getsVars().put("Foil", Integer.toString(f));
}
public final void addHauntedBy(final Card c) {
hauntedBy.add(c);
if(c != null) {
c.setHaunting(this);
}
}
public final ArrayList<Card> getHauntedBy() {
return hauntedBy;
}
public final void removeHauntedBy(final Card c) {
hauntedBy.remove(c);
}
public final Card getHaunting() {
return haunting;
}
public final void setHaunting(final Card c) {
haunting = c;
}
public final int getDamageDoneThisTurn() {
int sum = 0;
for(Card c : dealtDamageToThisTurn.keySet()) {
sum += dealtDamageToThisTurn.get(c);
}
return sum;
}
/**
* @return the cardColorsOverridden
*/
public boolean isCardColorsOverridden() {
return getCharacteristics().isCardColorsOverridden();
}
/**
* @param cardColorsOverridden0 the cardColorsOverridden to set
*/
public void setCardColorsOverridden(boolean cardColorsOverridden0) {
getCharacteristics().setCardColorsOverridden(cardColorsOverridden0);
}
} //end Card class