package forge;
import forge.card.cardFactory.CardFactoryUtil;
import forge.card.spellability.Ability;
import forge.card.spellability.SpellAbility;
import javax.swing.*;
import java.util.*;
/**
* <p>Abstract Player class.</p>
*
* @author Forge
* @version $Id: $
*/
public abstract class Player extends MyObservable {
protected String name;
protected int poisonCounters;
protected int life;
protected int assignedDamage;
protected int preventNextDamage;
protected int numPowerSurgeLands;
protected boolean altWin = false;
protected String winCondition = "";
protected boolean altLose = false;
protected String loseCondition = "";
protected int nTurns = 0;
protected boolean skipNextUntap = false;
protected int maxLandsToPlay = 1;
protected int numLandsPlayed = 0;
protected Card lastDrawnCard;
protected int numDrawnThisTurn = 0;
protected CardList slowtripList = new CardList();
/**
* <p>Constructor for Player.</p>
*
* @param myName a {@link java.lang.String} object.
*/
public Player(String myName) {
this(myName, 20, 0);
}
/**
* <p>Constructor for Player.</p>
*
* @param myName a {@link java.lang.String} object.
* @param myLife a int.
* @param myPoisonCounters a int.
*/
public Player(String myName, int myLife, int myPoisonCounters) {
name = myName;
life = myLife;
poisonCounters = myPoisonCounters;
assignedDamage = 0;
preventNextDamage = 0;
lastDrawnCard = null;
numDrawnThisTurn = 0;
nTurns = 0;
altWin = false;
altLose = false;
winCondition = "";
loseCondition = "";
maxLandsToPlay = 1;
numLandsPlayed = 0;
handSizeOperations = new ArrayList<HandSizeOp>();
}
/**
* <p>reset.</p>
*/
public void reset() {
life = 20;
poisonCounters = 0;
assignedDamage = 0;
preventNextDamage = 0;
lastDrawnCard = null;
numDrawnThisTurn = 0;
slowtripList = new CardList();
nTurns = 0;
altWin = false;
altLose = false;
winCondition = "";
loseCondition = "";
maxLandsToPlay = 1;
numLandsPlayed = 0;
this.updateObservers();
}
/**
* <p>Getter for the field <code>name</code>.</p>
*
* @return a {@link java.lang.String} object.
*/
public String getName() {
return name;
}
/**
* <p>isHuman.</p>
*
* @return a boolean.
*/
public abstract boolean isHuman();
/**
* <p>isComputer.</p>
*
* @return a boolean.
*/
public abstract boolean isComputer();
/**
* <p>isPlayer.</p>
*
* @param p1 a {@link forge.Player} object.
* @return a boolean.
*/
public abstract boolean isPlayer(Player p1);
/**
* <p>getOpponent.</p>
*
* @return a {@link forge.Player} object.
*/
public abstract Player getOpponent();
//////////////////////////
//
// methods for manipulating life
//
//////////////////////////
/**
* <p>Setter for the field <code>life</code>.</p>
*
* @param newLife a int.
* @param source a {@link forge.Card} object.
* @return a boolean.
*/
public boolean setLife(final int newLife, final Card source) {
boolean change = false;
//rule 118.5
if (life > newLife) {
change = loseLife(life - newLife, source);
} else if (newLife > life) {
change = gainLife(newLife - life, source);
} else {
//life == newLife
change = false;
}
this.updateObservers();
return change;
}
/**
* <p>Getter for the field <code>life</code>.</p>
*
* @return a int.
*/
public int getLife() {
return life;
}
/**
* <p>addLife.</p>
*
* @param toAdd a int.
*/
private void addLife(final int toAdd) {
life += toAdd;
this.updateObservers();
}
/**
* <p>gainLife.</p>
*
* @param toGain a int.
* @param source a {@link forge.Card} object.
* @return a boolean.
*/
public boolean gainLife(final int toGain, final Card source) {
boolean newLifeSet = false;
if (!canGainLife()) return false;
int lifeGain = toGain;
if (AllZoneUtil.isCardInPlay("Boon Reflection", this)) {
int amount = AllZoneUtil.getCardsInPlay("Boon Reflection").size();
for (int i = 0; i < amount; i++)
lifeGain += lifeGain;
}
if (lifeGain > 0) {
if (AllZoneUtil.isCardInPlay("Lich", this)) {
//draw cards instead of gain life
drawCards(lifeGain);
newLifeSet = false;
} else {
addLife(lifeGain);
newLifeSet = true;
this.updateObservers();
//Run triggers
HashMap<String, Object> runParams = new HashMap<String, Object>();
runParams.put("Player", this);
runParams.put("LifeAmount", lifeGain);
AllZone.getTriggerHandler().runTrigger("LifeGained", runParams);
}
} else System.out.println("Player - trying to gain negative or 0 life");
return newLifeSet;
}
/**
* <p>canGainLife.</p>
*
* @return a boolean.
*/
public boolean canGainLife() {
if (AllZoneUtil.isCardInPlay("Sulfuric Vortex") || AllZoneUtil.isCardInPlay("Leyline of Punishment") ||
AllZoneUtil.isCardInPlay("Platinum Emperion", this) || AllZoneUtil.isCardInPlay("Forsaken Wastes"))
return false;
return true;
}
/**
* <p>loseLife.</p>
*
* @param toLose a int.
* @param c a {@link forge.Card} object.
* @return a boolean.
*/
public boolean loseLife(final int toLose, final Card c) {
boolean newLifeSet = false;
if (!canLoseLife()) return false;
if (toLose > 0) {
subtractLife(toLose);
newLifeSet = true;
this.updateObservers();
} else if (toLose == 0) {
//Rule 118.4
//this is for players being able to pay 0 life
//nothing to do
} else System.out.println("Player - trying to lose positive life");
//Run triggers
HashMap<String, Object> runParams = new HashMap<String, Object>();
runParams.put("Player", this);
runParams.put("LifeAmount", toLose);
AllZone.getTriggerHandler().runTrigger("LifeLost", runParams);
return newLifeSet;
}
/**
* <p>canLoseLife.</p>
*
* @return a boolean.
*/
public boolean canLoseLife() {
if (AllZoneUtil.isCardInPlay("Platinum Emperion", this)) return false;
return true;
}
/**
* <p>subtractLife.</p>
*
* @param toSub a int.
*/
private void subtractLife(final int toSub) {
life -= toSub;
this.updateObservers();
}
/**
* <p>canPayLife.</p>
*
* @param lifePayment a int.
* @return a boolean.
*/
public boolean canPayLife(int lifePayment) {
if (life < lifePayment) return false;
if (lifePayment > 0 && AllZoneUtil.isCardInPlay("Platinum Emperion", this)) return false;
return true;
}
/**
* <p>payLife.</p>
*
* @param lifePayment a int.
* @param source a {@link forge.Card} object.
* @return a boolean.
*/
public boolean payLife(int lifePayment, Card source) {
if (!canPayLife(lifePayment)) return false;
//rule 118.8
if (life >= lifePayment) {
return loseLife(lifePayment, source);
}
return false;
}
//////////////////////////
//
// methods for handling damage
//
//////////////////////////
/**
* <p>addDamage.</p>
*
* @param damage a int.
* @param source a {@link forge.Card} object.
*/
public void addDamage(final int damage, final Card source) {
int damageToDo = damage;
damageToDo = replaceDamage(damageToDo, source, false);
damageToDo = preventDamage(damageToDo, source, false);
addDamageAfterPrevention(damageToDo, source, false);
}
/**
* <p>addDamageWithoutPrevention.</p>
*
* @param damage a int.
* @param source a {@link forge.Card} object.
*/
public void addDamageWithoutPrevention(final int damage, final Card source) {
int damageToDo = damage;
damageToDo = replaceDamage(damageToDo, source, false);
addDamageAfterPrevention(damageToDo, source, false);
}
//This function handles damage after replacement and prevention effects are applied
/**
* <p>addDamageAfterPrevention.</p>
*
* @param damage a int.
* @param source a {@link forge.Card} object.
* @param isCombat a boolean.
*/
public void addDamageAfterPrevention(final int damage, final Card source, final boolean isCombat) {
int damageToDo = damage;
if (source.hasKeyword("Infect")) {
addPoisonCounters(damageToDo);
} else {
//Worship does not reduce the damage dealt but changes the effect of the damage
if (PlayerUtil.worshipFlag(this) && life <= damageToDo) {
loseLife(Math.min(damageToDo, life - 1), source);
} else
//rule 118.2. Damage dealt to a player normally causes that player to lose that much life.
loseLife(damageToDo, source);
}
if (damageToDo > 0) {
addAssignedDamage(damageToDo);
GameActionUtil.executeDamageDealingEffects(source, damageToDo);
GameActionUtil.executeDamageToPlayerEffects(this, source, damageToDo);
//Run triggers
HashMap<String, Object> runParams = new HashMap<String, Object>();
runParams.put("DamageSource", source);
runParams.put("DamageTarget", this);
runParams.put("DamageAmount", damageToDo);
runParams.put("IsCombatDamage", isCombat);
AllZone.getTriggerHandler().runTrigger("DamageDone", runParams);
}
}
/**
* <p>predictDamage.</p>
*
* @param damage a int.
* @param source a {@link forge.Card} object.
* @param isCombat a boolean.
* @return a int.
*/
public int predictDamage(final int damage, final Card source, final boolean isCombat) {
int restDamage = damage;
restDamage = staticReplaceDamage(restDamage, source, isCombat);
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 damage a int.
* @param source a {@link forge.Card} object.
* @param isCombat a boolean.
* @return a int.
*/
public int staticDamagePrevention(final int damage, final Card source, final boolean isCombat) {
if (AllZoneUtil.isCardInPlay("Leyline of Punishment")) return damage;
int restDamage = damage;
if (isCombat) {
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 (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 (AllZoneUtil.isCardInPlay("Purity", this) && !isCombat) return 0;
//stPreventDamage
CardList allp = AllZoneUtil.getCardsInPlay();
for (Card ca : allp) {
if (ca.hasStartOfKeyword("stPreventDamage")) {
//syntax stPreventDamage:[Who is protected(You/Player/ValidCards)]:[ValidSource]:[Amount/All]
int KeywordPosition = ca.getKeywordPosition("stPreventDamage");
String parse = ca.getKeyword().get(KeywordPosition).toString();
String k[] = parse.split(":");
final Card card = ca;
if (k[1].equals("Player") || (k[1].equals("You") && card.getController().isPlayer(this))) {
final String restrictions[] = k[2].split(",");
if (source.isValidCard(restrictions, card.getController(), card)) {
if (k[3].equals("All")) return 0;
restDamage = restDamage - Integer.valueOf(k[3]);
}
}
}
} //stPreventDamage
//specific cards
if (AllZoneUtil.isCardInPlay("Spirit of Resistance", this)) {
if (AllZoneUtil.getPlayerColorInPlay(this, Constant.Color.Black).size() > 0
&& AllZoneUtil.getPlayerColorInPlay(this, Constant.Color.Blue).size() > 0
&& AllZoneUtil.getPlayerColorInPlay(this, Constant.Color.Green).size() > 0
&& AllZoneUtil.getPlayerColorInPlay(this, Constant.Color.Red).size() > 0
&& AllZoneUtil.getPlayerColorInPlay(this, Constant.Color.White).size() > 0) {
return 0;
}
}
if (restDamage > 0)
return restDamage;
else return 0;
}
//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.
*/
public int staticReplaceDamage(final int damage, Card source, boolean isCombat) {
int restDamage = damage;
if (AllZoneUtil.isCardInPlay("Sulfuric Vapors") && source.isSpell() && source.isRed()) {
int amount = AllZoneUtil.getCardsInPlay("Sulfuric Vapors").size();
for (int i = 0; i < amount; i++)
restDamage += 1;
}
if (AllZoneUtil.isCardInPlay("Furnace of Rath")) {
int amount = AllZoneUtil.getCardsInPlay("Furnace of Rath").size();
for (int i = 0; i < amount; i++)
restDamage += restDamage;
}
if (AllZoneUtil.isCardInPlay("Gratuitous Violence", source.getController())) {
int amount = AllZoneUtil.getPlayerCardsInPlay(source.getController(), "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 = AllZoneUtil.getPlayerCardsInPlay(source.getController(), "Fire Servant").size();
for (int i = 0; i < amount; i++)
restDamage += restDamage;
}
if (AllZoneUtil.isCardInPlay("Benevolent Unicorn") && source.isSpell()) {
int amount = AllZoneUtil.getCardsInPlay("Benevolent Unicorn").size();
for (int i = 0; i < amount; i++)
if (restDamage > 0)
restDamage -= 1;
}
if (AllZoneUtil.isCardInPlay("Divine Presence") && restDamage > 3) {
restDamage = 3;
}
if (AllZoneUtil.isCardInPlay("Forethought Amulet", this) && (source.isInstant() || source.isSorcery()) && restDamage > 2) {
restDamage = 2;
}
return restDamage;
}
/**
* <p>replaceDamage.</p>
*
* @param damage a int.
* @param source a {@link forge.Card} object.
* @param isCombat a boolean.
* @return a int.
*/
public int replaceDamage(final int damage, Card source, boolean isCombat) {
int restDamage = staticReplaceDamage(damage, source, isCombat);
if (source.getName().equals("Szadek, Lord of Secrets") && isCombat) {
source.addCounter(Counters.P1P1, restDamage);
for (int i = 0; i < restDamage; i++) {
CardList lib = AllZoneUtil.getPlayerCardsInLibrary(this);
if (lib.size() > 0) {
AllZone.getGameAction().moveToGraveyard(lib.get(0));
}
}
return 0;
}
if (AllZoneUtil.isCardInPlay("Crumbling Sanctuary")) {
for (int i = 0; i < restDamage; i++) {
CardList lib = AllZoneUtil.getPlayerCardsInLibrary(this);
if (lib.size() > 0) {
AllZone.getGameAction().exile(lib.get(0));
}
}
//return so things like Lifelink, etc do not trigger. This is a replacement effect I think.
return 0;
}
return restDamage;
}
/**
* <p>preventDamage.</p>
*
* @param damage a int.
* @param source a {@link forge.Card} object.
* @param isCombat a boolean.
* @return a int.
*/
public int preventDamage(final int damage, Card source, boolean isCombat) {
if (AllZoneUtil.isCardInPlay("Leyline of Punishment")) return damage;
int restDamage = damage;
// Purity has to stay here because it changes the game state
if (AllZoneUtil.isCardInPlay("Purity", this) && !isCombat) {
gainLife(restDamage, null);
return 0;
}
restDamage = staticDamagePrevention(restDamage, source, isCombat);
if (restDamage >= preventNextDamage) {
restDamage = restDamage - preventNextDamage;
preventNextDamage = 0;
} else {
restDamage = 0;
preventNextDamage = preventNextDamage - restDamage;
}
return restDamage;
}
/**
* <p>Setter for the field <code>assignedDamage</code>.</p>
*
* @param n a int.
*/
public void setAssignedDamage(int n) {
assignedDamage = n;
}
/**
* <p>addAssignedDamage.</p>
*
* @param n a int.
*/
public void addAssignedDamage(int n) {
assignedDamage += n;
}
/**
* <p>Getter for the field <code>assignedDamage</code>.</p>
*
* @return a int.
*/
public int getAssignedDamage() {
return assignedDamage;
}
/**
* <p>addCombatDamage.</p>
*
* @param damage a int.
* @param source a {@link forge.Card} object.
*/
public void addCombatDamage(final int damage, final Card source) {
int damageToDo = damage;
damageToDo = replaceDamage(damageToDo, source, true);
damageToDo = preventDamage(damageToDo, source, true);
addDamageAfterPrevention(damageToDo, source, true); //damage prevention is already checked
if (damageToDo > 0) {
GameActionUtil.executeCombatDamageToPlayerEffects(this, source, damageToDo);
}
}
//////////////////////////
//
// methods for handling Damage Prevention
//
//////////////////////////
//PreventNextDamage
/**
* <p>Setter for the field <code>preventNextDamage</code>.</p>
*
* @param n a int.
*/
public void setpreventNextDamage(int n) {
preventNextDamage = n;
}
/**
* <p>Getter for the field <code>preventNextDamage</code>.</p>
*
* @return a int.
*/
public int getPreventNextDamage() {
return preventNextDamage;
}
/**
* <p>addPreventNextDamage.</p>
*
* @param n a int.
*/
public void addPreventNextDamage(int n) {
preventNextDamage += n;
}
/**
* <p>subtractPreventNextDamage.</p>
*
* @param n a int.
*/
public void subtractPreventNextDamage(int n) {
preventNextDamage -= n;
}
/**
* <p>resetPreventNextDamage.</p>
*/
public void resetPreventNextDamage() {
preventNextDamage = 0;
}
//////////////////////////
//
// methods for handling Poison counters
//
//////////////////////////
/**
* <p>addPoisonCounters.</p>
*
* @param num a int.
*/
public void addPoisonCounters(int num) {
poisonCounters += num;
this.updateObservers();
}
/**
* <p>Setter for the field <code>poisonCounters</code>.</p>
*
* @param num a int.
*/
public void setPoisonCounters(int num) {
poisonCounters = num;
this.updateObservers();
}
/**
* <p>Getter for the field <code>poisonCounters</code>.</p>
*
* @return a int.
*/
public int getPoisonCounters() {
return poisonCounters;
}
/**
* <p>subtractPoisonCounters.</p>
*
* @param num a int.
*/
public void subtractPoisonCounters(int num) {
poisonCounters -= num;
this.updateObservers();
}
/**
* <p>hasShroud.</p>
*
* @return a boolean.
*/
public boolean hasShroud() {
return false;
}
/**
* <p>canTarget.</p>
*
* @param card a {@link forge.Card} object.
* @return a boolean.
*/
public boolean canTarget(Card card) {
return !hasShroud();
}
/**
* <p>canPlaySpells.</p>
*
* @return a boolean.
*/
public boolean canPlaySpells() {
return true;
}
/**
* <p>canPlayAbilities.</p>
*
* @return a boolean.
*/
public boolean canPlayAbilities() {
return true;
}
/**
* <p>getCards.</p>
*
* @param zone a {@link forge.PlayerZone} object.
* @return a {@link forge.CardList} object.
*/
public CardList getCards(PlayerZone zone) {
//TODO
return new CardList();
}
////////////////////////////////
///
/// replaces AllZone.getGameAction().draw* methods
///
////////////////////////////////
/**
* <p>mayDrawCard.</p>
*
* @return a CardList of cards actually drawn
*/
public abstract CardList mayDrawCard();
/**
* <p>mayDrawCards.</p>
*
* @param numCards a int.
* @return a CardList of cards actually drawn
*/
public abstract CardList mayDrawCards(int numCards);
/**
* <p>drawCard.</p>
*
* @return a CardList of cards actually drawn
*/
public CardList drawCard() {
return drawCards(1);
}
/**
* <p>drawCards.</p>
*
* @return a CardList of cards actually drawn
*/
public CardList drawCards() {
return drawCards(1);
}
/**
* <p>dredge.</p>
*
* @return a boolean.
*/
public abstract boolean dredge();
/**
* <p>drawCards.</p>
*
* @param n a int.
* @return a CardList of cards actually drawn
*/
public CardList drawCards(int n) {
return drawCards(n, false);
}
/**
* <p>drawCards.</p>
*
* @param n a int.
* @param firstFromDraw true if this is the card drawn from that player's draw step each turn
* @return a CardList of cards actually drawn
*/
public CardList drawCards(int n, boolean firstFromDraw) {
CardList drawn = new CardList();
for (int i = 0; i < n; i++) {
// TODO: multiple replacements need to be selected by the controller
if (getDredge().size() != 0)
if(dredge())
continue;
if(!firstFromDraw && AllZoneUtil.isCardInPlay("Chains of Mephistopheles")) {
if(AllZoneUtil.getPlayerHand(this).size() > 0) {
if(isHuman()) discard_Chains_of_Mephistopheles();
else { //Computer
discard(1, null, false);
//true causes this code not to be run again
drawn.addAll(drawCards(1, true));
}
}
else {
mill(1);
}
}
else {
drawn.addAll(doDraw());
}
}
return drawn;
}
/**
* <p>doDraw.</p>
*
* @return a CardList of cards actually drawn
*/
private CardList doDraw() {
CardList drawn = new CardList();
PlayerZone library = AllZone.getZone(Constant.Zone.Library, this);
if (library.size() != 0) {
Card c = library.get(0);
c = AllZone.getGameAction().moveToHand(c);
setLastDrawnCard(c);
c.setDrawnThisTurn(true);
numDrawnThisTurn++;
drawn.add(c);
//Run triggers
HashMap<String, Object> runParams = new HashMap<String, Object>();
runParams.put("Card", c);
AllZone.getTriggerHandler().runTrigger("Drawn", runParams);
}
//lose:
else if (!Constant.Runtime.DevMode[0] || AllZone.getDisplay().canLoseByDecking()) {
// if devMode is off, or canLoseByDecking is Enabled, run Lose Condition
if (altLoseConditionMet("Milled")) {
AllZone.getGameAction().checkStateEffects();
}
}
return drawn;
}
/**
* <p>getDredge.</p>
*
* @return a {@link forge.CardList} object.
*/
protected CardList getDredge() {
CardList dredge = new CardList();
CardList cl = AllZoneUtil.getPlayerGraveyard(this);
for (Card c : cl) {
ArrayList<String> kw = c.getKeyword();
for (int i = 0; i < kw.size(); i++) {
if (kw.get(i).toString().startsWith("Dredge")) {
if (AllZoneUtil.getPlayerCardsInLibrary(this).size() >= getDredgeNumber(c)) dredge.add(c);
}
}
}
return dredge;
}//hasDredge()
/**
* <p>getDredgeNumber.</p>
*
* @param c a {@link forge.Card} object.
* @return a int.
*/
protected int getDredgeNumber(Card c) {
ArrayList<String> a = c.getKeyword();
for (int i = 0; i < a.size(); i++)
if (a.get(i).toString().startsWith("Dredge")) {
String s = a.get(i).toString();
return Integer.parseInt("" + s.charAt(s.length() - 1));
}
throw new RuntimeException("Input_Draw : getDredgeNumber() card doesn't have dredge - " + c.getName());
}//getDredgeNumber()
/**
* <p>resetNumDrawnThisTurn.</p>
*/
public void resetNumDrawnThisTurn() {
numDrawnThisTurn = 0;
}
/**
* <p>Getter for the field <code>numDrawnThisTurn</code>.</p>
*
* @return a int.
*/
public int getNumDrawnThisTurn() {
return numDrawnThisTurn;
}
////////////////////////////////
///
/// replaces AllZone.getGameAction().discard* methods
///
////////////////////////////////
protected abstract void discard_Chains_of_Mephistopheles();
/**
* <p>discard.</p>
*
* @param num a int.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @param duringResolution a boolean.
* @return a {@link forge.CardList} object.
*/
public abstract CardList discard(final int num, final SpellAbility sa, boolean duringResolution);
/**
* <p>discard.</p>
*
* @param sa a {@link forge.card.spellability.SpellAbility} object.
* @return a {@link forge.CardList} object.
*/
public CardList discard(final SpellAbility sa) {
return discard(1, sa, false);
}
/**
* <p>discard.</p>
*
* @param c a {@link forge.Card} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
*/
public void discard(Card c, SpellAbility sa) {
doDiscard(c, sa);
}
/**
* <p>doDiscard.</p>
*
* @param c a {@link forge.Card} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
*/
protected void doDiscard(final Card c, final SpellAbility sa) {
// TODO: This line should be moved inside CostPayment somehow
if (sa != null) {
sa.addCostToHashList(c, "Discarded");
}
/*
* When a spell or ability an opponent controls causes you
* to discard Psychic Purge, that player loses 5 life.
*/
if (c.getName().equals("Psychic Purge")) {
if (null != sa && !sa.getSourceCard().getController().equals(this)) {
SpellAbility ability = new Ability(c, "") {
public void resolve() {
sa.getSourceCard().getController().loseLife(5, c);
}
};
ability.setStackDescription(c.getName() + " - " +
sa.getSourceCard().getController() + " loses 5 life.");
AllZone.getStack().add(ability);
}
}
// necro disrupts madness
if (AllZoneUtil.getPlayerCardsInPlay(c.getOwner(), "Necropotence").size() > 0) {
AllZone.getGameAction().exile(c);
return;
}
AllZone.getGameAction().discard_madness(c);
if ((c.hasKeyword("If a spell or ability an opponent controls causes you to discard CARDNAME, put it onto the battlefield instead of putting it into your graveyard.")
|| c.hasKeyword("If a spell or ability an opponent controls causes you to discard CARDNAME, put it onto the battlefield with two +1/+1 counters on it instead of putting it into your graveyard."))
&& !c.getController().equals(sa.getSourceCard().getController())) {
AllZone.getGameAction().discard_PutIntoPlayInstead(c);
} else if (c.hasKeyword("If a spell or ability an opponent controls causes you to discard CARDNAME, return it to your hand.")) {
;
} else {
AllZone.getGameAction().moveToGraveyard(c);
}
//Run triggers
Card cause = null;
if (sa != null) {
cause = sa.getSourceCard();
}
HashMap<String, Object> runParams = new HashMap<String, Object>();
runParams.put("Player", this);
runParams.put("Card", c);
runParams.put("Cause", cause);
AllZone.getTriggerHandler().runTrigger("Discarded", runParams);
}//end doDiscard
/**
* <p>discardHand.</p>
*
* @param sa a {@link forge.card.spellability.SpellAbility} object.
*/
public void discardHand(SpellAbility sa) {
CardList list = AllZoneUtil.getPlayerHand(this);
discardRandom(list.size(), sa);
}
/**
* <p>discardRandom.</p>
*
* @param sa a {@link forge.card.spellability.SpellAbility} object.
*/
public void discardRandom(SpellAbility sa) {
discardRandom(1, sa);
}
/**
* <p>discardRandom.</p>
*
* @param num a int.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
*/
public void discardRandom(final int num, final SpellAbility sa) {
for (int i = 0; i < num; i++) {
CardList list = AllZoneUtil.getPlayerHand(this);
if (list.size() != 0)
doDiscard(CardUtil.getRandom(list.toArray()), sa);
}
}
/**
* <p>discardUnless.</p>
*
* @param num a int.
* @param uType a {@link java.lang.String} object.
* @param sa a {@link forge.card.spellability.SpellAbility} object.
*/
public abstract void discardUnless(int num, String uType, SpellAbility sa);
/**
* <p>mill.</p>
*
* @param n a int.
*/
public void mill(int n) {
mill(n, Constant.Zone.Graveyard);
}
/**
* <p>mill.</p>
*
* @param n a int.
* @param zone a {@link java.lang.String} object.
*/
public void mill(int n, String zone) {
CardList lib = AllZoneUtil.getPlayerCardsInLibrary(this);
int max = Math.min(n, lib.size());
PlayerZone destination = AllZone.getZone(zone, this);
for (int i = 0; i < max; i++) {
AllZone.getGameAction().moveTo(destination, lib.get(i));
}
}
/**
* <p>handToLibrary.</p>
*
* @param numToLibrary a int.
* @param libPos a {@link java.lang.String} object.
*/
public abstract void handToLibrary(final int numToLibrary, String libPos);
////////////////////////////////
/**
* <p>shuffle.</p>
*/
public void shuffle() {
PlayerZone library = AllZone.getZone(Constant.Zone.Library, this);
Card c[] = AllZoneUtil.getPlayerCardsInLibrary(this).toArray();
if (c.length <= 1) return;
ArrayList<Object> list = new ArrayList<Object>(Arrays.asList(c));
//overdone but wanted to make sure it was really random
Random random = MyRandom.random;
Collections.shuffle(list, random);
Collections.shuffle(list, random);
Collections.shuffle(list, random);
Collections.shuffle(list, random);
Collections.shuffle(list, random);
Collections.shuffle(list, random);
Object o;
for (int i = 0; i < list.size(); i++) {
o = list.remove(random.nextInt(list.size()));
list.add(random.nextInt(list.size()), o);
}
Collections.shuffle(list, random);
Collections.shuffle(list, random);
Collections.shuffle(list, random);
Collections.shuffle(list, random);
Collections.shuffle(list, random);
Collections.shuffle(list, random);
list.toArray(c);
library.setCards(c);
//Run triggers
HashMap<String, Object> runParams = new HashMap<String, Object>();
runParams.put("Player", this);
AllZone.getTriggerHandler().runTrigger("Shuffled", runParams);
}//shuffle
////////////////////////////////
////////////////////////////////
/**
* <p>doScry.</p>
*
* @param topN a {@link forge.CardList} object.
* @param N a int.
*/
protected abstract void doScry(CardList topN, int N);
/**
* <p>scry.</p>
*
* @param numScry a int.
*/
public void scry(int numScry) {
CardList topN = new CardList();
PlayerZone library = AllZone.getZone(Constant.Zone.Library, this);
numScry = Math.min(numScry, library.size());
for (int i = 0; i < numScry; i++) {
topN.add(library.get(i));
}
doScry(topN, topN.size());
}
///////////////////////////////
/**
* <p>playLand.</p>
*
* @param land a {@link forge.Card} object.
*/
public void playLand(Card land) {
if (canPlayLand()) {
AllZone.getGameAction().moveToPlay(land);
CardFactoryUtil.playLandEffects(land);
numLandsPlayed++;
//check state effects for static animate (Living Lands, Conversion, etc...)
AllZone.getGameAction().checkStateEffects();
//Run triggers
HashMap<String, Object> runParams = new HashMap<String, Object>();
runParams.put("Card", land);
AllZone.getTriggerHandler().runTrigger("LandPlayed", runParams);
}
AllZone.getStack().unfreezeStack();
}
/**
* <p>canPlayLand.</p>
*
* @return a boolean.
*/
public boolean canPlayLand() {
return Phase.canCastSorcery(this) && (numLandsPlayed < maxLandsToPlay ||
AllZoneUtil.getPlayerCardsInPlay(this, "Fastbond").size() > 0);
}
///////////////////////////////
////
//// properties about the player and his/her cards/game status
////
///////////////////////////////
/**
* <p>hasPlaneswalker.</p>
*
* @return a boolean.
*/
public boolean hasPlaneswalker() {
return null != getPlaneswalker();
}
/**
* <p>getPlaneswalker.</p>
*
* @return a {@link forge.Card} object.
*/
public Card getPlaneswalker() {
CardList c = AllZoneUtil.getPlayerTypeInPlay(this, "Planeswalker");
if (null != c && c.size() > 0) return c.get(0);
else return null;
}
/**
* <p>Getter for the field <code>numPowerSurgeLands</code>.</p>
*
* @return a int.
*/
public int getNumPowerSurgeLands() {
return numPowerSurgeLands;
}
/**
* <p>Setter for the field <code>numPowerSurgeLands</code>.</p>
*
* @param n a int.
* @return a int.
*/
public int setNumPowerSurgeLands(int n) {
numPowerSurgeLands = n;
return numPowerSurgeLands;
}
/**
* <p>Getter for the field <code>lastDrawnCard</code>.</p>
*
* @return a {@link forge.Card} object.
*/
public Card getLastDrawnCard() {
return lastDrawnCard;
}
/**
* <p>Setter for the field <code>lastDrawnCard</code>.</p>
*
* @param c a {@link forge.Card} object.
* @return a {@link forge.Card} object.
*/
public Card setLastDrawnCard(Card c) {
lastDrawnCard = c;
return lastDrawnCard;
}
/**
* <p>resetLastDrawnCard.</p>
*
* @return a {@link forge.Card} object.
*/
public Card resetLastDrawnCard() {
Card old = lastDrawnCard;
lastDrawnCard = null;
return old;
}
/**
* <p>skipNextUntap.</p>
*
* @return a boolean.
*/
public boolean skipNextUntap() {
return skipNextUntap;
}
/**
* <p>Setter for the field <code>skipNextUntap</code>.</p>
*
* @param b a boolean.
*/
public void setSkipNextUntap(boolean b) {
skipNextUntap = b;
}
/**
* <p>Getter for the field <code>slowtripList</code>.</p>
*
* @return a {@link forge.CardList} object.
*/
public CardList getSlowtripList() {
return slowtripList;
}
/**
* <p>clearSlowtripList.</p>
*/
public void clearSlowtripList() {
slowtripList.clear();
}
/**
* <p>addSlowtripList.</p>
*
* @param card a {@link forge.Card} object.
*/
public void addSlowtripList(Card card) {
slowtripList.add(card);
}
/**
* <p>getTurn.</p>
*
* @return a int.
*/
public int getTurn() {
return nTurns;
}
/**
* <p>incrementTurn.</p>
*/
public void incrementTurn() {
nTurns++;
}
////////////////////////////////
/**
* <p>sacrificePermanent.</p>
*
* @param prompt a {@link java.lang.String} object.
* @param choices a {@link forge.CardList} object.
*/
public abstract void sacrificePermanent(String prompt, CardList choices);
/**
* <p>sacrificeCreature.</p>
*/
public void sacrificeCreature() {
CardList choices = AllZoneUtil.getCreaturesInPlay(this);
sacrificePermanent("Select a creature to sacrifice.", choices);
}
/**
* <p>sacrificeCreature.</p>
*
* @param choices a {@link forge.CardList} object.
*/
public void sacrificeCreature(CardList choices) {
sacrificePermanent("Select a creature to sacrifice.", choices);
}
// Game win/loss
/**
* <p>Getter for the field <code>altWin</code>.</p>
*
* @return a boolean.
*/
public boolean getAltWin() {
return altWin;
}
/**
* <p>Getter for the field <code>altLose</code>.</p>
*
* @return a boolean.
*/
public boolean getAltLose() {
return altLose;
}
/**
* <p>Getter for the field <code>winCondition</code>.</p>
*
* @return a {@link java.lang.String} object.
*/
public String getWinCondition() {
return winCondition;
}
/**
* <p>Getter for the field <code>loseCondition</code>.</p>
*
* @return a {@link java.lang.String} object.
*/
public String getLoseCondition() {
return loseCondition;
}
/**
* <p>altWinConditionMet.</p>
*
* @param s a {@link java.lang.String} object.
*/
public void altWinConditionMet(String s) {
if (cantWin()) {
System.out.println("Tried to win, but currently can't.");
return;
}
altWin = true;
winCondition = s;
}
/**
* <p>altLoseConditionMet.</p>
*
* @param s a {@link java.lang.String} object.
* @return a boolean.
*/
public boolean altLoseConditionMet(String s) {
if (cantLose()) {
System.out.println("Tried to lose, but currently can't.");
return false;
}
altLose = true;
loseCondition = s;
return true;
}
/**
* <p>cantLose.</p>
*
* @return a boolean.
*/
public boolean cantLose() {
CardList list = AllZoneUtil.getPlayerCardsInPlay(this);
list = list.getKeyword("You can't lose the game.");
if (list.size() > 0)
return true;
CardList oppList = AllZoneUtil.getPlayerCardsInPlay(getOpponent());
oppList = oppList.getKeyword("Your opponents can't lose the game.");
return oppList.size() > 0;
}
/**
* <p>cantLoseForZeroOrLessLife.</p>
*
* @return a boolean.
*/
public boolean cantLoseForZeroOrLessLife() {
CardList list = AllZoneUtil.getPlayerCardsInPlay(this);
list = list.getKeyword("You don't lose the game for having 0 or less life.");
return list.size() > 0;
}
/**
* <p>cantWin.</p>
*
* @return a boolean.
*/
public boolean cantWin() {
CardList list = AllZoneUtil.getPlayerCardsInPlay(getOpponent());
list = list.getKeyword("You can't win the game.");
if (list.size() > 0)
return true;
CardList oppList = AllZoneUtil.getPlayerCardsInPlay(this);
oppList = oppList.getKeyword("Your opponents can't win the game.");
return oppList.size() > 0;
}
/**
* <p>hasLost.</p>
*
* @return a boolean.
*/
public boolean hasLost() {
if (cantLose())
return false;
if (altLose) {
return true;
}
if (poisonCounters >= 10) {
altLoseConditionMet("Poison Counters");
return true;
}
if (cantLoseForZeroOrLessLife()) {
return false;
}
return getLife() <= 0;
}
/**
* <p>hasWon.</p>
*
* @return a boolean.
*/
public boolean hasWon() {
if (cantWin())
return false;
return altWin;
}
/**
* <p>hasMetalcraft.</p>
*
* @return a boolean.
*/
public boolean hasMetalcraft() {
CardList list = AllZoneUtil.getPlayerTypeInPlay(this, "Artifact");
return list.size() >= 3;
}
/**
* <p>hasThreshold.</p>
*
* @return a boolean.
*/
public boolean hasThreshold() {
CardList grave = AllZoneUtil.getPlayerGraveyard(this);
return grave.size() >= 7;
}
/**
* <p>hasHellbent.</p>
*
* @return a boolean.
*/
public boolean hasHellbent() {
CardList hand = AllZoneUtil.getPlayerHand(this);
return hand.size() == 0;
}
/**
* <p>hasLandfall.</p>
*
* @return a boolean.
*/
public boolean hasLandfall() {
CardList list = ((DefaultPlayerZone) AllZone.getZone("Battlefield", this)).getCardsAddedThisTurn("Any").getType("Land");
return !list.isEmpty();
}
private ArrayList<HandSizeOp> handSizeOperations;
/**
* <p>getMaxHandSize.</p>
*
* @return a int.
*/
public int getMaxHandSize() {
int ret = 7;
for (int i = 0; i < handSizeOperations.size(); i++) {
if (handSizeOperations.get(i).Mode.equals("=")) {
ret = handSizeOperations.get(i).Amount;
} else if (handSizeOperations.get(i).Mode.equals("+") && ret >= 0) {
ret = ret + handSizeOperations.get(i).Amount;
} else if (handSizeOperations.get(i).Mode.equals("-") && ret >= 0) {
ret = ret - handSizeOperations.get(i).Amount;
if (ret < 0) {
ret = 0;
}
}
}
return ret;
}
/**
* <p>sortHandSizeOperations.</p>
*/
public void sortHandSizeOperations() {
if (handSizeOperations.size() < 2) {
return;
}
int changes = 1;
while (changes > 0) {
changes = 0;
for (int i = 1; i < handSizeOperations.size(); i++) {
if (handSizeOperations.get(i).hsTimeStamp < handSizeOperations.get(i - 1).hsTimeStamp) {
HandSizeOp tmp = handSizeOperations.get(i);
handSizeOperations.set(i, handSizeOperations.get(i - 1));
handSizeOperations.set(i - 1, tmp);
changes++;
}
}
}
}
/**
* <p>addHandSizeOperation.</p>
*
* @param theNew a {@link forge.HandSizeOp} object.
*/
public void addHandSizeOperation(HandSizeOp theNew) {
handSizeOperations.add(theNew);
}
/**
* <p>removeHandSizeOperation.</p>
*
* @param timestamp a int.
*/
public void removeHandSizeOperation(int timestamp) {
for (int i = 0; i < handSizeOperations.size(); i++) {
if (handSizeOperations.get(i).hsTimeStamp == timestamp) {
handSizeOperations.remove(i);
break;
}
}
}
/**
* <p>clearHandSizeOperations.</p>
*/
public void clearHandSizeOperations() {
handSizeOperations.clear();
}
/** Constant <code>NextHandSizeStamp=0</code> */
private static int NextHandSizeStamp = 0;
/**
* <p>getHandSizeStamp.</p>
*
* @return a int.
*/
public static int getHandSizeStamp() {
return NextHandSizeStamp++;
}
/**
* <p>Getter for the field <code>maxLandsToPlay</code>.</p>
*
* @return a int.
*/
public int getMaxLandsToPlay() {
return maxLandsToPlay;
}
/**
* <p>Setter for the field <code>maxLandsToPlay</code>.</p>
*
* @param n a int.
*/
public void setMaxLandsToPlay(int n) {
maxLandsToPlay = n;
}
/**
* <p>addMaxLandsToPlay.</p>
*
* @param n a int.
*/
public void addMaxLandsToPlay(int n) {
maxLandsToPlay += n;
}
/**
* <p>Getter for the field <code>numLandsPlayed</code>.</p>
*
* @return a int.
*/
public int getNumLandsPlayed() {
return numLandsPlayed;
}
/**
* <p>Setter for the field <code>numLandsPlayed</code>.</p>
*
* @param n a int.
*/
public void setNumLandsPlayed(int n) {
numLandsPlayed = n;
}
////////////////////////////////
//
// Clash
//
/////////////////////////////////
/**
* <p>clashWithOpponent.</p>
*
* @param source a {@link forge.Card} object.
* @return a boolean.
*/
public boolean clashWithOpponent(Card source) {
/*
* Each clashing player reveals the top card of his or
* her library, then puts that card on the top or bottom.
* A player wins if his or her card had a higher mana cost.
*
* Clash you win or win you don't. There is no tie.
*/
Player player = source.getController();
Player opponent = player.getOpponent();
String lib = Constant.Zone.Library;
PlayerZone pLib = AllZone.getZone(lib, player);
PlayerZone oLib = AllZone.getZone(lib, opponent);
StringBuilder reveal = new StringBuilder();
Card pCard = null;
Card oCard = null;
if (pLib.size() > 0) pCard = pLib.get(0);
if (oLib.size() > 0) oCard = oLib.get(0);
if (pLib.size() == 0 && oLib.size() == 0) return false;
else if (pLib.size() == 0) {
opponent.clashMoveToTopOrBottom(oCard);
return false;
} else if (oLib.size() == 0) {
player.clashMoveToTopOrBottom(pCard);
return true;
} else {
int pCMC = CardUtil.getConvertedManaCost(pCard);
int oCMC = CardUtil.getConvertedManaCost(oCard);
reveal.append(player).append(" reveals: ").append(pCard.getName()).append(". CMC = ").append(pCMC);
reveal.append("\r\n");
reveal.append(opponent).append(" reveals: ").append(oCard.getName()).append(". CMC = ").append(oCMC);
reveal.append("\r\n\r\n");
if (pCMC > oCMC) reveal.append(player).append(" wins clash.");
else reveal.append(player).append(" loses clash.");
JOptionPane.showMessageDialog(null, reveal.toString(), source.getName(), JOptionPane.PLAIN_MESSAGE);
player.clashMoveToTopOrBottom(pCard);
opponent.clashMoveToTopOrBottom(oCard);
//JOptionPane.showMessageDialog(null, reveal.toString(), source.getName(), JOptionPane.PLAIN_MESSAGE);
return pCMC > oCMC;
}
}
/**
* <p>clashMoveToTopOrBottom.</p>
*
* @param c a {@link forge.Card} object.
*/
protected abstract void clashMoveToTopOrBottom(Card c);
////////////////////////////////
//
// generic Object overrides
//
/////////////////////////////////
/** {@inheritDoc} */
@Override
public boolean equals(Object o) {
if (o instanceof Player) {
Player p1 = (Player) o;
return p1.getName().equals(name);
} else return false;
}
/** {@inheritDoc} */
@Override
public String toString() {
return name;
}
}