package forge;
import forge.card.cardFactory.CardFactoryUtil;
import forge.gui.input.Input;
import java.util.ArrayList;
/**
* <p>PhaseUtil class.</p>
*
* @author Forge
* @version $Id: $
*/
public class PhaseUtil {
// ******* UNTAP PHASE *****
/**
* <p>skipUntap.</p>
*
* @param p a {@link forge.Player} object.
* @return a boolean.
*/
private static boolean skipUntap(Player p) {
if (AllZoneUtil.isCardInPlay("Sands of Time") || AllZoneUtil.isCardInPlay("Stasis"))
return true;
if (p.skipNextUntap()) {
p.setSkipNextUntap(false);
return true;
}
return false;
}
/**
* <p>handleUntap.</p>
*/
public static void handleUntap() {
Player turn = AllZone.getPhase().getPlayerTurn();
AllZone.getPhase().turnReset();
AllZone.getCombat().reset();
AllZone.getCombat().setAttackingPlayer(turn);
AllZone.getCombat().setDefendingPlayer(turn.getOpponent());
// For tokens a player starts the game with they don't recover from Sum. Sickness on first turn
if (turn.getTurn() > 0) {
CardList list = AllZoneUtil.getPlayerCardsInPlay(turn);
for (Card c : list)
c.setSickness(false);
}
turn.incrementTurn();
AllZone.getGameAction().resetActivationsPerTurn();
CardList lands = AllZoneUtil.getPlayerLandsInPlay(turn);
lands = lands.filter(AllZoneUtil.untapped);
turn.setNumPowerSurgeLands(lands.size());
// anything before this point happens regardless of whether the Untap phase is skipped
if (skipUntap(turn)) {
AllZone.getPhase().setNeedToNextPhase(true);
return;
}
// Phasing would happen here
doUntap();
//otherwise land seems to stay tapped when it is really untapped
AllZone.getHumanBattlefield().updateObservers();
AllZone.getPhase().setNeedToNextPhase(true);
}
/**
* <p>doUntap.</p>
*/
private static void doUntap() {
Player player = AllZone.getPhase().getPlayerTurn();
CardList list = AllZoneUtil.getPlayerCardsInPlay(player);
for (Card c : list) {
if (c.getBounceAtUntap() && c.getName().contains("Undiscovered Paradise")) {
AllZone.getGameAction().moveToHand(c);
}
}
list = list.filter(new CardListFilter() {
public boolean addCard(Card c) {
if (!canUntap(c)) return false;
if (canOnlyUntapOneLand() && c.isLand()) return false;
if ((AllZoneUtil.isCardInPlay("Damping Field") || AllZoneUtil.isCardInPlay("Imi Statue"))
&& c.isArtifact()) return false;
if ((AllZoneUtil.isCardInPlay("Smoke") || AllZoneUtil.isCardInPlay("Stoic Angel")
|| AllZoneUtil.isCardInPlay("Intruder Alarm")) && c.isCreature()) return false;
return true;
}
});
for (Card c : list) {
if (c.hasKeyword("You may choose not to untap CARDNAME during your untap step.")) {
if (c.isTapped()) {
if (c.getController().isHuman()) {
String prompt = "Untap " + c.getName() + "?";
boolean defaultNo = false;
if (c.getGainControlTargets().size() > 0) {
ArrayList<Card> targets = c.getGainControlTargets();
prompt += "\r\n" + c + " is controlling: ";
for (Card target : targets) {
prompt += target;
if(AllZoneUtil.isCardInPlay(target)) defaultNo |= true;
}
}
if (GameActionUtil.showYesNoDialog(c, prompt, defaultNo)) {
c.untap();
}
} else { //computer
//if it is controlling something by staying tapped, leave it tapped
//if not, untap it
if (c.getGainControlTargets().size() > 0) {
ArrayList<Card> targets = c.getGainControlTargets();
boolean untap = true;
for (Card target : targets) {
if(AllZoneUtil.isCardInPlay(target)) untap |= true;
}
if(untap) c.untap();
}
}
}
} else if ((c.getCounters(Counters.WIND) > 0) && AllZoneUtil.isCardInPlay("Freyalise's Winds")) {
//remove a WIND counter instead of untapping
c.subtractCounter(Counters.WIND, 1);
} else c.untap();
}
//Remove temporary keywords
list = AllZoneUtil.getPlayerCardsInPlay(player);
for (Card c : list) {
c.removeExtrinsicKeyword("This card doesn't untap during your next untap step.");
c.removeExtrinsicKeyword("HIDDEN This card doesn't untap during your next untap step.");
}
//opponent untapping during your untap phase
CardList opp = AllZoneUtil.getPlayerCardsInPlay(player.getOpponent());
for (Card oppCard : opp)
if (oppCard.hasKeyword("CARDNAME untaps during each other player's untap step."))
oppCard.untap();
//end opponent untapping during your untap phase
if (canOnlyUntapOneLand()) {
if (AllZone.getPhase().getPlayerTurn().isComputer()) {
//search for lands the computer has and only untap 1
CardList landList = AllZoneUtil.getPlayerLandsInPlay(AllZone.getComputerPlayer());
landList = landList.filter(AllZoneUtil.tapped);
if (landList.size() > 0) {
landList.get(0).untap();
}
} else {
Input target = new Input() {
private static final long serialVersionUID = 6653677835629939465L;
public void showMessage() {
AllZone.getDisplay().showMessage("Select one tapped land to untap");
ButtonUtil.enableOnlyCancel();
}
public void selectButtonCancel() {
stop();
}
public void selectCard(Card c, PlayerZone zone) {
if (c.isLand() && zone.is(Constant.Zone.Battlefield) && c.isTapped()) {
c.untap();
stop();
}
}//selectCard()
};//Input
CardList landList = AllZoneUtil.getPlayerLandsInPlay(AllZone.getHumanPlayer());
landList = landList.filter(AllZoneUtil.tapped);
if (landList.size() > 0) {
AllZone.getInputControl().setInput(target);
}
}
}
if (AllZoneUtil.isCardInPlay("Damping Field") || AllZoneUtil.isCardInPlay("Imi Statue")) {
if (AllZone.getPhase().getPlayerTurn().isComputer()) {
CardList artList = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer());
artList = artList.filter(AllZoneUtil.artifacts);
artList = artList.filter(AllZoneUtil.tapped);
if (artList.size() > 0) {
CardFactoryUtil.AI_getBestArtifact(artList).untap();
}
} else {
Input target = new Input() {
private static final long serialVersionUID = 5555427219659889707L;
public void showMessage() {
AllZone.getDisplay().showMessage("Select one tapped artifact to untap");
ButtonUtil.enableOnlyCancel();
}
public void selectButtonCancel() {
stop();
}
public void selectCard(Card c, PlayerZone zone) {
if (c.isArtifact() && zone.is(Constant.Zone.Battlefield)
&& c.getController().isHuman()) {
c.untap();
stop();
}
}//selectCard()
};//Input
CardList artList = AllZoneUtil.getPlayerCardsInPlay(AllZone.getHumanPlayer());
artList = artList.filter(AllZoneUtil.artifacts);
artList = artList.filter(AllZoneUtil.tapped);
if (artList.size() > 0) {
AllZone.getInputControl().setInput(target);
}
}
}
if ((AllZoneUtil.isCardInPlay("Smoke") || AllZoneUtil.isCardInPlay("Stoic Angel"))) {
if (AllZone.getPhase().getPlayerTurn().isComputer()) {
CardList creatures = AllZoneUtil.getCreaturesInPlay(AllZone.getComputerPlayer());
creatures = creatures.filter(AllZoneUtil.tapped);
if (creatures.size() > 0) {
creatures.get(0).untap();
}
} else {
Input target = new Input() {
private static final long serialVersionUID = 5555427219659889707L;
public void showMessage() {
AllZone.getDisplay().showMessage("Select one creature to untap");
ButtonUtil.enableOnlyCancel();
}
public void selectButtonCancel() {
stop();
}
public void selectCard(Card c, PlayerZone zone) {
if (c.isCreature() && zone.is(Constant.Zone.Battlefield)
&& c.getController().isHuman()) {
c.untap();
stop();
}
}//selectCard()
};//Input
CardList creatures = AllZoneUtil.getCreaturesInPlay(AllZone.getHumanPlayer());
creatures = creatures.filter(AllZoneUtil.tapped);
if (creatures.size() > 0) {
AllZone.getInputControl().setInput(target);
}
}
}
}//end doUntap
/**
* <p>canUntap.</p>
*
* @param c a {@link forge.Card} object.
* @return a boolean.
*/
public static boolean canUntap(Card c) {
if (c.hasKeyword("CARDNAME doesn't untap during your untap step.")
|| c.hasKeyword("This card doesn't untap during your next untap step.")) return false;
CardList allp = AllZoneUtil.getCardsInPlay();
for (Card ca : allp) {
if (ca.hasStartOfKeyword("Permanents don't untap during their controllers' untap steps")) {
int KeywordPosition = ca.getKeywordPosition("Permanents don't untap during their controllers' untap steps");
String parse = ca.getKeyword().get(KeywordPosition).toString();
String k[] = parse.split(":");
final String restrictions[] = k[1].split(",");
final Card card = ca;
if (c.isValidCard(restrictions, card.getController(), card)) return false;
}
} // end of Permanents don't untap during their controllers' untap steps
return true;
}
/**
* <p>canOnlyUntapOneLand.</p>
*
* @return a boolean.
*/
private static boolean canOnlyUntapOneLand() {
//Winter Orb was given errata so it no longer matters if it's tapped or not
if (AllZoneUtil.getCardsInPlay("Winter Orb").size() > 0)
return true;
if (AllZoneUtil.getCardsInPlay("Hokori, Dust Drinker").size() > 0)
return true;
if (AllZoneUtil.getPlayerCardsInPlay(AllZone.getPhase().getPlayerTurn(), "Mungha Wurm").size() > 0)
return true;
return false;
}
// ******* UPKEEP PHASE *****
/**
* <p>handleUpkeep.</p>
*/
public static void handleUpkeep() {
Player turn = AllZone.getPhase().getPlayerTurn();
if (skipUpkeep()) {
// Slowtrips all say "on the next turn's upkeep" if there is no upkeep next turn, the trigger will never occur.
turn.clearSlowtripList();
turn.getOpponent().clearSlowtripList();
AllZone.getPhase().setNeedToNextPhase(true);
return;
}
AllZone.getUpkeep().executeUntil(turn);
GameActionUtil.executeUpkeepEffects();
}
/**
* <p>skipUpkeep.</p>
*
* @return a boolean.
*/
public static boolean skipUpkeep() {
if (AllZoneUtil.isCardInPlay("Eon Hub"))
return true;
Player turn = AllZone.getPhase().getPlayerTurn();
if (AllZoneUtil.getPlayerHand(turn).size() == 0 && AllZoneUtil.isCardInPlay("Gibbering Descent", turn))
return true;
return false;
}
// ******* DRAW PHASE *****
/**
* <p>handleDraw.</p>
*/
public static void handleDraw() {
Player playerTurn = AllZone.getPhase().getPlayerTurn();
if (skipDraw(playerTurn)) {
AllZone.getPhase().setNeedToNextPhase(true);
return;
}
playerTurn.drawCards(1, true);
GameActionUtil.executeDrawStepEffects();
}
/**
* <p>skipDraw.</p>
*
* @param player a {@link forge.Player} object.
* @return a boolean.
*/
private static boolean skipDraw(Player player) {
// starting player skips his draw
if (AllZone.getPhase().getTurn() == 1) {
return true;
}
CardList list = AllZoneUtil.getPlayerCardsInPlay(player);
if (list.containsName("Necropotence") || list.containsName("Yawgmoth's Bargain") || list.containsName("Recycle") ||
list.containsName("Dragon Appeasement") || list.containsName("Null Profusion") || list.containsName("Colfenor's Plans") ||
list.containsName("Psychic Possession") || list.containsName("Solitary Confinement") ||
list.containsName("Symbiotic Deployment"))
return true;
return false;
}
// ********* Declare Attackers ***********
/**
* <p>verifyCombat.</p>
*/
public static void verifyCombat() {
AllZone.getCombat().verifyCreaturesInPlay();
CombatUtil.showCombat();
}
/**
* <p>handleDeclareAttackers.</p>
*/
public static void handleDeclareAttackers() {
verifyCombat();
CardList list = new CardList();
list.addAll(AllZone.getCombat().getAttackers());
// TODO move propaganda to happen as the Attacker is Declared
// Remove illegal Propaganda attacks first only for attacking the Player
int size = list.size();
for (int i = 0; i < size; i++) {
Card c = list.get(i);
boolean last = (i == size - 1);
CombatUtil.checkPropagandaEffects(c, last);
}
}
/**
* <p>handleAttackingTriggers.</p>
*/
public static void handleAttackingTriggers() {
CardList list = new CardList();
list.addAll(AllZone.getCombat().getAttackers());
AllZone.getStack().freezeStack();
// Then run other Attacker bonuses
//check for exalted:
if (list.size() == 1) {
Player attackingPlayer = AllZone.getCombat().getAttackingPlayer();
CardList exalted = AllZoneUtil.getPlayerCardsInPlay(attackingPlayer);
exalted = exalted.getKeyword("Exalted");
if (exalted.size() > 0) CombatUtil.executeExaltedAbility(list.get(0), exalted.size());
// Make sure exalted effects get applied only once per combat
}
for (Card c : list)
CombatUtil.checkDeclareAttackers(c);
AllZone.getStack().unfreezeStack();
}
/**
* <p>handleDeclareBlockers.</p>
*/
public static void handleDeclareBlockers() {
verifyCombat();
AllZone.getStack().freezeStack();
AllZone.getCombat().setUnblocked();
CardList list = new CardList();
list.addAll(AllZone.getCombat().getAllBlockers());
list = list.filter(new CardListFilter() {
public boolean addCard(Card c) {
return !c.getCreatureBlockedThisCombat();
}
});
CardList attList = new CardList();
attList.addAll(AllZone.getCombat().getAttackers());
CombatUtil.checkDeclareBlockers(list);
for (Card a : attList) {
CardList blockList = AllZone.getCombat().getBlockers(a);
for (Card b : blockList)
CombatUtil.checkBlockedAttackers(a, b);
}
AllZone.getStack().unfreezeStack();
CombatUtil.showCombat();
}
// ***** Combat Utility **********
// TODO: the below functions should be removed and the code blocks that use them should instead use SA_Restriction
/**
* <p>isBeforeAttackersAreDeclared.</p>
*
* @return a boolean.
*/
public static boolean isBeforeAttackersAreDeclared() {
String phase = AllZone.getPhase().getPhase();
return phase.equals(Constant.Phase.Untap) || phase.equals(Constant.Phase.Upkeep)
|| phase.equals(Constant.Phase.Draw) || phase.equals(Constant.Phase.Main1)
|| phase.equals(Constant.Phase.Combat_Begin);
}
}