diff --git a/.gitattributes b/.gitattributes index 8b053dcbc72..a7f204ab3d2 100644 --- a/.gitattributes +++ b/.gitattributes @@ -9584,6 +9584,23 @@ src/main/java/forge/card/cardFactory/CardFactory_Sorceries.java svneol=native#te src/main/java/forge/card/cardFactory/LazyCardFactory.java svneol=native#text/plain src/main/java/forge/card/cardFactory/PreloadingCardFactory.java svneol=native#text/plain src/main/java/forge/card/cardFactory/package-info.java svneol=native#text/plain +src/main/java/forge/card/cost/Cost.java svneol=native#text/plain +src/main/java/forge/card/cost/CostDiscard.java -text +src/main/java/forge/card/cost/CostExile.java -text +src/main/java/forge/card/cost/CostMana.java -text +src/main/java/forge/card/cost/CostPart.java -text +src/main/java/forge/card/cost/CostPartWithList.java -text +src/main/java/forge/card/cost/CostPayLife.java -text +src/main/java/forge/card/cost/CostPutCounter.java -text +src/main/java/forge/card/cost/CostRemoveCounter.java -text +src/main/java/forge/card/cost/CostReturn.java -text +src/main/java/forge/card/cost/CostSacrifice.java -text +src/main/java/forge/card/cost/CostTap.java -text +src/main/java/forge/card/cost/CostTapType.java -text +src/main/java/forge/card/cost/CostUntap.java -text +src/main/java/forge/card/cost/CostUtil.java -text +src/main/java/forge/card/cost/Cost_Input.java -text +src/main/java/forge/card/cost/Cost_Payment.java svneol=native#text/plain src/main/java/forge/card/mana/Mana.java svneol=native#text/plain src/main/java/forge/card/mana/ManaCost.java svneol=native#text/plain src/main/java/forge/card/mana/ManaPool.java svneol=native#text/plain @@ -9601,8 +9618,6 @@ src/main/java/forge/card/spellability/Ability_Mana.java svneol=native#text/plain src/main/java/forge/card/spellability/Ability_Static.java svneol=native#text/plain src/main/java/forge/card/spellability/Ability_Sub.java svneol=native#text/plain src/main/java/forge/card/spellability/Ability_Triggered.java svneol=native#text/plain -src/main/java/forge/card/spellability/Cost.java svneol=native#text/plain -src/main/java/forge/card/spellability/Cost_Payment.java svneol=native#text/plain src/main/java/forge/card/spellability/Spell.java svneol=native#text/plain src/main/java/forge/card/spellability/SpellAbility.java svneol=native#text/plain src/main/java/forge/card/spellability/SpellAbilityList.java svneol=native#text/plain diff --git a/res/cardsfolder/c/copper_leaf_angel.txt b/res/cardsfolder/c/copper_leaf_angel.txt index 9daa00b2bee..40f90f0c388 100644 --- a/res/cardsfolder/c/copper_leaf_angel.txt +++ b/res/cardsfolder/c/copper_leaf_angel.txt @@ -4,8 +4,9 @@ Types:Artifact Creature Angel Text:no text PT:2/2 K:Flying -A:AB$PutCounter | Cost$ T Sac | Defined$ Self | CounterType$ P1P1 | CounterNum$ X | SpellDescription$ Put X +1/+1 counters on CARDNAME. -SVar:X:Sacrificed$Amount +A:AB$PutCounter | Cost$ T Sac | Defined$ Self | CounterType$ P1P1 | CounterNum$ ChosenX | SpellDescription$ Put X +1/+1 counters on CARDNAME. +SVar:X:XChoice +#ChosenX SVar created by Cost payment SVar:RemAIDeck:True SVar:Rarity:Rare SVar:Picture:http://www.wizards.com/global/images/magic/general/copper_leaf_angel.jpg diff --git a/res/cardsfolder/d/devastating_summons.txt b/res/cardsfolder/d/devastating_summons.txt index 573b56a6af2..cc63031cc70 100644 --- a/res/cardsfolder/d/devastating_summons.txt +++ b/res/cardsfolder/d/devastating_summons.txt @@ -2,8 +2,9 @@ Name:Devastating Summons ManaCost:R Types:Sorcery Text:no text -A:SP$ Token | Cost$ R Sac | TokenAmount$ 2 | TokenName$ Elemental | TokenTypes$ Creature,Elemental | TokenOwner$ You | TokenColors$ Red | TokenPower$ X | TokenToughness$ X | SpellDescription$ Put two X/X red Elemental creature tokens onto the battlefield. -SVar:X:Sacrificed$Amount +A:SP$ Token | Cost$ R Sac | TokenAmount$ 2 | TokenName$ Elemental | TokenTypes$ Creature,Elemental | TokenOwner$ You | TokenColors$ Red | TokenPower$ ChosenX | TokenToughness$ ChosenX | SpellDescription$ Put two X/X red Elemental creature tokens onto the battlefield. +SVar:X:XChoice +#ChosenX SVar created by Cost payment SVar:RemAIDeck:True SVar:Rarity:Rare SVar:Picture:http://www.wizards.com/global/images/magic/general/devastating_summons.jpg diff --git a/res/cardsfolder/f/firecat_blitz.txt b/res/cardsfolder/f/firecat_blitz.txt index cd4a029dadf..461da4af905 100644 --- a/res/cardsfolder/f/firecat_blitz.txt +++ b/res/cardsfolder/f/firecat_blitz.txt @@ -3,9 +3,11 @@ ManaCost:X R R Types:Sorcery Text:no text A:SP$ Token | Cost$ X R R | TokenAmount$ X | TokenName$ Elemental Cat | TokenTypes$ Creature,Elemental,Cat | TokenOwner$ You | TokenColors$ Red | TokenPower$ 1 | TokenToughness$ 1 | TokenKeywords$ Haste<>At the beginning of the end step, exile CARDNAME. | SpellDescription$ Put X 1/1 red Elemental Cat creature tokens with haste onto the battlefield. Exile them at the beginning of the next end step. -A:SP$ Token | Cost$ R R Sac | TokenAmount$ Y | TokenName$ Elemental Cat | TokenTypes$ Creature,Elemental,Cat | TokenOwner$ You | TokenColors$ Red | TokenPower$ 1 | TokenToughness$ 1 | TokenKeywords$ Haste<>At the beginning of the end step, exile CARDNAME. | Flashback$ True | CostDesc$ Flashback - R R, Sacrifice X Mountains. | SpellDescription$ (You may cast this card from your graveyard for its flashback cost. Then exile it.) +A:SP$ Token | Cost$ R R Sac | TokenAmount$ ChosenX | TokenName$ Elemental Cat | TokenTypes$ Creature,Elemental,Cat | TokenOwner$ You | TokenColors$ Red | TokenPower$ 1 | TokenToughness$ 1 | TokenKeywords$ Haste<>At the beginning of the end step, exile CARDNAME. | Flashback$ True | CostDesc$ Flashback - R R, Sacrifice X Mountains. | SpellDescription$ (You may cast this card from your graveyard for its flashback cost. Then exile it.) SVar:X:Count$xPaid -SVar:Y:Sacrificed$Amount +SVar:Y:XChoice +#Flashback uses Y because SVars can't overlap +#ChosenX SVar created by Cost payment SVar:RemAIDeck:True SVar:Rarity:Uncommon SVar:Picture:http://www.wizards.com/global/images/magic/general/firecat_blitz.jpg diff --git a/src/main/java/forge/Card.java b/src/main/java/forge/Card.java index b99495e59eb..8ea41c9425d 100644 --- a/src/main/java/forge/Card.java +++ b/src/main/java/forge/Card.java @@ -175,6 +175,8 @@ public class Card extends MyObservable implements Comparable { private Map counters = new TreeMap(); private Map SVars = new TreeMap(); + private static String[] storableSVars = { "ChosenX" }; + 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 diff --git a/src/main/java/forge/CardListUtil.java b/src/main/java/forge/CardListUtil.java index a32facd016a..186c70bdfc5 100644 --- a/src/main/java/forge/CardListUtil.java +++ b/src/main/java/forge/CardListUtil.java @@ -451,4 +451,17 @@ public class CardListUtil { return cmc; }//sumCMC + + public static CardList getRandomSubList(CardList c, int amount){ + if (c.size() < amount) + return null; + + CardList subList = new CardList(); + while(subList.size() < amount){ + c.shuffle(); + subList.add(c.get(0)); + c.remove(0); + } + return subList; + } } diff --git a/src/main/java/forge/ComputerUtil.java b/src/main/java/forge/ComputerUtil.java index bad5cac58dc..317b2cb27e2 100644 --- a/src/main/java/forge/ComputerUtil.java +++ b/src/main/java/forge/ComputerUtil.java @@ -2,6 +2,9 @@ package forge; import forge.card.abilityFactory.AbilityFactory; import forge.card.cardFactory.CardFactoryUtil; +import forge.card.cost.Cost; +import forge.card.cost.CostUtil; +import forge.card.cost.Cost_Payment; import forge.card.mana.ManaCost; import forge.card.mana.ManaPool; import forge.card.spellability.*; @@ -128,7 +131,7 @@ public class ComputerUtil { //String totalMana = source.getSVar("PayX"); // + cost.getCMC() // Consider the costs here for relative "scoring" - if (cost.getDiscardType().equals("Hand")) { + if (CostUtil.hasDiscardHandCost(cost)) { // Null Brooch aid restrict -= (AllZoneUtil.getPlayerHand(AllZone.getComputerPlayer()).size() * 20); } @@ -418,160 +421,11 @@ public class ComputerUtil { * @return a boolean. */ static public boolean canPayAdditionalCosts(SpellAbility sa, Player player) { - // Add additional cost checks here before attempting to activate abilities - Cost cost = sa.getPayCosts(); - if (cost == null) - return true; - Card card = sa.getSourceCard(); - - if (cost.getTap() && (card.isTapped() || card.isSick())) - return false; - - if (cost.getUntap() && (card.isUntapped() || card.isSick())) - return false; - - if (cost.getTapXTypeCost()) { - CardList typeList = AllZoneUtil.getPlayerCardsInPlay(player); - typeList = typeList.getValidCards(cost.getTapXType().split(","), sa.getActivatingPlayer(), sa.getSourceCard()); - - if (cost.getTap()) - typeList.remove(sa.getSourceCard()); - typeList = typeList.filter(AllZoneUtil.untapped); - - if (cost.getTapXTypeAmount() > typeList.size()) - return false; + if (sa.getActivatingPlayer() == null){ + System.out.println(sa.getSourceCard() + " in ComputerUtil.canPayAdditionalCosts() without an activating player"); + sa.setActivatingPlayer(player); } - - if (cost.getSubCounter()) { - Counters c = cost.getCounterType(); - if (card.getCounters(c) - cost.getCounterNum() < 0 || !AllZoneUtil.isCardInPlay(card)) { - return false; - } - } - - if (cost.getAddCounter()) { - // this should always be true - } - - if (cost.getLifeCost()) { - if (player.getLife() <= cost.getLifeAmount()) - return false; - } - - if (cost.getDiscardCost()) { - CardList handList = AllZoneUtil.getPlayerHand(player); - String discType = cost.getDiscardType(); - int discAmount = cost.getDiscardAmount(); - - if (cost.getDiscardThis()) { - if (!AllZone.getZone(card).getZoneName().equals(Constant.Zone.Hand)) - return false; - } else if (discType.equals("LastDrawn")) { - //compy can't yet use this effectively - return false; - } else if (discType.equals("Hand")) { - // this will always work - } else { - if (!discType.equals("Any") && !discType.equals("Random")) { - String validType[] = discType.split(","); - handList = handList.getValidCards(validType, sa.getActivatingPlayer(), sa.getSourceCard()); - } - if (discAmount > handList.size()) { - // not enough cards in hand to pay - return false; - } - } - } - - if (cost.getSacCost()) { - // if there's a sacrifice in the cost, just because we can Pay it doesn't mean we want to. - if (!cost.getSacThis()) { - CardList typeList = AllZoneUtil.getPlayerCardsInPlay(player); - typeList = typeList.getValidCards(cost.getSacType().split(","), sa.getActivatingPlayer(), sa.getSourceCard()); - Card target = sa.getTargetCard(); - if (target != null && target.getController().isPlayer(player)) // don't sacrifice the card we're pumping - typeList.remove(target); - - if (cost.getSacAmount() > typeList.size()) - return false; - } else if (cost.getSacThis() && !AllZoneUtil.isCardInPlay(card)) - return false; - } - - if (cost.getExileCost()) { - // if there's an exile in the cost, just because we can Pay it doesn't mean we want to. - if (!cost.getExileThis()) { - CardList typeList = AllZoneUtil.getPlayerCardsInPlay(player); - typeList = typeList.getValidCards(cost.getExileType().split(","), sa.getActivatingPlayer(), sa.getSourceCard()); - Card target = sa.getTargetCard(); - if (target != null && target.getController().isPlayer(player)) // don't exile the card we're pumping - typeList.remove(target); - - if (cost.getExileAmount() > typeList.size()) - return false; - } else if (cost.getExileThis() && !AllZoneUtil.isCardInPlay(card)) - return false; - } - - if (cost.getExileFromHandCost()) { - // if there's an exile in the cost, just because we can Pay it doesn't mean we want to. - if (!cost.getExileFromHandThis()) { - CardList typeList = AllZoneUtil.getPlayerHand(player); - typeList = typeList.getValidCards(cost.getExileFromHandType().split(","), sa.getActivatingPlayer(), sa.getSourceCard()); - Card target = sa.getTargetCard(); - if (target != null && target.getController().isPlayer(player)) // don't exile the card we're pumping - typeList.remove(target); - - if (cost.getExileFromHandAmount() > typeList.size()) - return false; - } else if (cost.getExileFromHandThis() && !AllZoneUtil.isCardInPlayerHand(player, card)) - return false; - } - - if (cost.getExileFromGraveCost()) { - if (!cost.getExileFromGraveThis()) { - CardList typeList = AllZoneUtil.getPlayerGraveyard(player); - typeList = typeList.getValidCards(cost.getExileFromGraveType().split(","), sa.getActivatingPlayer(), sa.getSourceCard()); - Card target = sa.getTargetCard(); - if (target != null && target.getController().isPlayer(player)) // don't exile the card we're pumping - typeList.remove(target); - - if (cost.getExileFromGraveAmount() > typeList.size()) - return false; - } else if (cost.getExileFromGraveThis() && !AllZoneUtil.isCardInPlayerGraveyard(player, card)) - return false; - } - - if (cost.getExileFromTopCost()) { - if (!cost.getExileFromTopThis()) { - CardList typeList = AllZoneUtil.getPlayerCardsInLibrary(player); - typeList = typeList.getValidCards(cost.getExileFromTopType().split(","), sa.getActivatingPlayer(), sa.getSourceCard()); - Card target = sa.getTargetCard(); - if (target != null && target.getController().isPlayer(player)) // don't exile the card we're pumping - typeList.remove(target); - - if (cost.getExileFromTopAmount() > typeList.size()) - return false; - } else if (cost.getExileFromTopThis() && !AllZoneUtil.isCardInPlayerLibrary(player, card)) - return false; - } - - if (cost.getReturnCost()) { - // if there's a return in the cost, just because we can Pay it doesn't mean we want to. - if (!cost.getReturnThis()) { - CardList typeList = AllZoneUtil.getPlayerCardsInPlay(player); - typeList = typeList.getValidCards(cost.getReturnType().split(","), sa.getActivatingPlayer(), sa.getSourceCard()); - Card target = sa.getTargetCard(); - if (target != null && target.getController().isPlayer(player)) // don't bounce the card we're pumping - typeList.remove(target); - - if (cost.getReturnAmount() > typeList.size()) - return false; - } else if (!AllZoneUtil.isCardInPlay(card)) - return false; - } - - return true; + return Cost_Payment.canPayAdditionalCosts(sa.getPayCosts(), sa); } /** @@ -651,7 +505,7 @@ public class ComputerUtil { for (Ability_Mana m : manaAbilities) { if (used) break; //mana source already used in the test - + m.setActivatingPlayer(player); //if the AI can't pay the additional costs skip the mana ability if (m.getPayCosts() != null) { if (!canPayAdditionalCosts(m, player)) @@ -799,15 +653,14 @@ public class ComputerUtil { for (Ability_Mana m : manaAbilities) { Cost cost = m.getPayCosts(); + needsLimitedResources |= !cost.isReusuableResource(); //if the AI can't pay the additional costs skip the mana ability + m.setActivatingPlayer(AllZone.getComputerPlayer()); if (cost != null) { if (!canPayAdditionalCosts(m, player)) continue; - if (cost.getSubCounter() || cost.getLifeCost()) - needsLimitedResources = true; - } else if (card.isTapped()) - continue; + } //don't use abilities with dangerous drawbacks if (m.getSubAbility() != null) { @@ -837,15 +690,12 @@ public class ComputerUtil { for (Ability_Mana m : manaAbilities) { Cost cost = m.getPayCosts(); - + needsLimitedResources |= !cost.isReusuableResource(); //if the AI can't pay the additional costs skip the mana ability if (cost != null) { if (!canPayAdditionalCosts(m, player)) continue; - if (cost.getSubCounter() || cost.getLifeCost()) - needsLimitedResources = true; - } else if (card.isTapped()) - continue; + } //don't use abilities with dangerous drawbacks if (m.getSubAbility() != null) { @@ -1035,7 +885,7 @@ public class ComputerUtil { if (target != null && target.getController().isComputer() && typeList.contains(target)) typeList.remove(target); // don't sacrifice the card we're pumping - if (typeList.size() == 0) + if (typeList.size() < amount) return null; CardList sacList = new CardList(); @@ -1112,7 +962,7 @@ public class ComputerUtil { if (target != null && target.getController().isComputer() && typeList.contains(target)) typeList.remove(target); // don't exile the card we're pumping - if (typeList.size() == 0) + if (typeList.size() < amount) return null; CardListUtil.sortAttackLowFirst(typeList); @@ -1141,7 +991,7 @@ public class ComputerUtil { if (tap) typeList.remove(activate); - if (typeList.size() == 0 || amount >= typeList.size()) + if (typeList.size() < amount) return null; CardListUtil.sortAttackLowFirst(typeList); @@ -1167,7 +1017,7 @@ public class ComputerUtil { if (target != null && target.getController().isComputer() && typeList.contains(target)) // don't bounce the card we're pumping typeList.remove(target); - if (typeList.size() == 0) + if (typeList.size() < amount) return null; CardListUtil.sortAttackLowFirst(typeList); diff --git a/src/main/java/forge/GameAction.java b/src/main/java/forge/GameAction.java index be59cfbfc68..a52af4ba3a5 100644 --- a/src/main/java/forge/GameAction.java +++ b/src/main/java/forge/GameAction.java @@ -5,6 +5,8 @@ import forge.card.abilityFactory.AbilityFactory; import forge.card.abilityFactory.AbilityFactory_Attach; import forge.card.cardFactory.CardFactoryInterface; import forge.card.cardFactory.CardFactoryUtil; +import forge.card.cost.Cost; +import forge.card.cost.Cost_Payment; import forge.card.mana.ManaCost; import forge.card.mana.ManaPool; import forge.card.spellability.*; @@ -475,26 +477,30 @@ public class GameAction { return moveToStack(c); } + /** *

AI_discardNumType.

* * @param numDiscard a int. * @param uTypes an array of {@link java.lang.String} objects. * @param sa a {@link forge.card.spellability.SpellAbility} object. - * @return a boolean. + * @return a CardList of discarded cards. */ - public boolean AI_discardNumType(int numDiscard, String[] uTypes, SpellAbility sa) { + public CardList AI_discardNumType(int numDiscard, String[] uTypes, SpellAbility sa) { CardList hand = AllZoneUtil.getPlayerHand(AllZone.getComputerPlayer()); - CardList tHand = hand.getValidCards(uTypes, sa.getActivatingPlayer(), sa.getSourceCard()); + hand = hand.getValidCards(uTypes, sa.getActivatingPlayer(), sa.getSourceCard()); - if (tHand.size() >= numDiscard) { - CardListUtil.sortCMC(tHand); - tHand.reverse(); + if (hand.size() < numDiscard) + return null; + + CardList discard = new CardList(); + + CardListUtil.sortCMC(hand); + hand.reverse(); for (int i = 0; i < numDiscard; i++) - tHand.get(i).getController().discard(tHand.get(i), sa); - return true; - } - return false; + discard.add(hand.get(i)); + + return discard; } /** diff --git a/src/main/java/forge/GameActionUtil.java b/src/main/java/forge/GameActionUtil.java index 7d1cde67ade..0870ce4504c 100644 --- a/src/main/java/forge/GameActionUtil.java +++ b/src/main/java/forge/GameActionUtil.java @@ -3,6 +3,7 @@ package forge; import forge.card.abilityFactory.AbilityFactory; import forge.card.cardFactory.CardFactoryUtil; +import forge.card.cost.Cost; import forge.card.spellability.*; import forge.game.GameLossReason; import forge.gui.GuiUtils; diff --git a/src/main/java/forge/card/abilityFactory/AbilityFactory.java b/src/main/java/forge/card/abilityFactory/AbilityFactory.java index 3a791270c98..cfbec5693fe 100644 --- a/src/main/java/forge/card/abilityFactory/AbilityFactory.java +++ b/src/main/java/forge/card/abilityFactory/AbilityFactory.java @@ -2,6 +2,7 @@ package forge.card.abilityFactory; import forge.*; import forge.card.cardFactory.CardFactoryUtil; +import forge.card.cost.Cost; import forge.card.spellability.*; import java.util.ArrayList; @@ -73,7 +74,7 @@ public class AbilityFactory { /** *

Getter for the field abCost.

* - * @return a {@link forge.card.spellability.Cost} object. + * @return a {@link forge.card.cost.Cost} object. */ public Cost getAbCost() { return abCost; diff --git a/src/main/java/forge/card/abilityFactory/AbilityFactory_AlterLife.java b/src/main/java/forge/card/abilityFactory/AbilityFactory_AlterLife.java index 06417b412ff..2fcdc3c5553 100644 --- a/src/main/java/forge/card/abilityFactory/AbilityFactory_AlterLife.java +++ b/src/main/java/forge/card/abilityFactory/AbilityFactory_AlterLife.java @@ -1,6 +1,8 @@ package forge.card.abilityFactory; import forge.*; +import forge.card.cost.Cost; +import forge.card.cost.CostUtil; import forge.card.spellability.*; import java.util.ArrayList; @@ -200,33 +202,20 @@ public class AbilityFactory_AlterLife { //don't use it if no life to gain if (lifeAmount <= 0) return false; - if (abCost != null) { - if (abCost.getSacCost() && life > 4) { - if (abCost.getSacThis() && life > 6) + if (abCost != null) { + if (life > 5){ + if (!CostUtil.checkSacrificeCost(abCost, source)) + return false; + + if (!CostUtil.checkLifeCost(abCost, source, 4)) + return false; + + if (!CostUtil.checkDiscardCost(abCost, source)) return false; - else { - //only sacrifice something that's supposed to be sacrificed - String type = abCost.getSacType(); - CardList typeList = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer()); - typeList = typeList.getValidCards(type.split(","), source.getController(), source); - if (ComputerUtil.getCardPreference(source, "SacCost", typeList) == null) - return false; - } - } - if (abCost.getLifeCost() && life > 5) return false; - if (abCost.getDiscardCost() && life > 5) return false; - - if (abCost.getSubCounter()) { - //non +1/+1 counters should be used - if (abCost.getCounterType().equals(Counters.P1P1)) { - // A card has a 25% chance per counter to be able to pass through here - // 4+ counters will always pass. 0 counters will never - int currentNum = source.getCounters(abCost.getCounterType()); - double percent = .25 * (currentNum / abCost.getCounterNum()); - if (percent <= r.nextFloat()) - return false; - } } + + if (!CostUtil.checkRemoveCounterCost(abCost, source)) + return false; } if (!AllZone.getComputerPlayer().canGainLife()) @@ -507,8 +496,6 @@ public class AbilityFactory_AlterLife { Cost abCost = sa.getPayCosts(); final Card source = sa.getSourceCard(); HashMap params = af.getMapParams(); - int humanLife = AllZone.getHumanPlayer().getLife(); - int aiLife = AllZone.getComputerPlayer().getLife(); String amountStr = params.get("LifeAmount"); @@ -517,29 +504,17 @@ public class AbilityFactory_AlterLife { if (abCost != null) { // AI currently disabled for these costs - if (abCost.getSacCost()) { - if (amountStr.contains("X")) - return false; - if (!abCost.getSacThis()) { - //only sacrifice something that's supposed to be sacrificed - String type = abCost.getSacType(); - CardList typeList = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer()); - typeList = typeList.getValidCards(type.split(","), source.getController(), source); - if (ComputerUtil.getCardPreference(source, "SacCost", typeList) == null) - return false; - } - } - if (abCost.getLifeCost() && aiLife - abCost.getLifeAmount() < humanLife - amount) return false; - if (abCost.getDiscardCost()) return false; + if (!CostUtil.checkLifeCost(abCost, source, amount)) + return false; - if (abCost.getSubCounter()) { - // A card has a 25% chance per counter to be able to pass through here - // 4+ counters will always pass. 0 counters will never - int currentNum = source.getCounters(abCost.getCounterType()); - double percent = .25 * (currentNum / abCost.getCounterNum()); - if (percent <= r.nextFloat()) - return false; - } + if (!CostUtil.checkDiscardCost(abCost, source)) + return false; + + if (!CostUtil.checkSacrificeCost(abCost, source)) + return false; + + if (!CostUtil.checkRemoveCounterCost(abCost, source)) + return false; } if (!AllZone.getHumanPlayer().canLoseLife()) @@ -892,27 +867,17 @@ public class AbilityFactory_AlterLife { //int humanPoison = AllZone.getHumanPlayer().getPoisonCounters(); //int humanLife = AllZone.getHumanPlayer().getLife(); //int aiPoison = AllZone.getComputerPlayer().getPoisonCounters(); - int aiLife = AllZone.getComputerPlayer().getLife(); - String amountStr = params.get("Num"); // TODO handle proper calculation of X values based on Cost and what would be paid //final int amount = AbilityFactory.calculateAmount(af.getHostCard(), amountStr, sa); if (abCost != null) { // AI currently disabled for these costs - if (abCost.getSacCost()) { - if (amountStr.contains("X")) - return false; - if (!abCost.getSacThis()) { - //only sacrifice something that's supposed to be sacrificed - String type = abCost.getSacType(); - CardList typeList = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer()); - typeList = typeList.getValidCards(type.split(","), source.getController(), source); - if (ComputerUtil.getCardPreference(source, "SacCost", typeList) == null) - return false; - } - } - if (abCost.getLifeCost() && aiLife - abCost.getLifeAmount() <= 0) return false; + if (!CostUtil.checkLifeCost(abCost, source, 1)) + return false; + + if (!CostUtil.checkSacrificeCost(abCost, source)) + return false; } //Don't use poison before main 2 if possible diff --git a/src/main/java/forge/card/abilityFactory/AbilityFactory_Attach.java b/src/main/java/forge/card/abilityFactory/AbilityFactory_Attach.java index aca4723d96b..6f8ea3609cc 100644 --- a/src/main/java/forge/card/abilityFactory/AbilityFactory_Attach.java +++ b/src/main/java/forge/card/abilityFactory/AbilityFactory_Attach.java @@ -15,13 +15,12 @@ import forge.CombatUtil; import forge.Command; import forge.ComputerUtil; import forge.Constant; -import forge.Counters; import forge.MyRandom; import forge.Player; import forge.card.cardFactory.CardFactoryUtil; +import forge.card.cost.Cost; import forge.card.spellability.Ability_Sub; -import forge.card.spellability.Cost; import forge.card.spellability.Spell; import forge.card.spellability.SpellAbility; import forge.card.spellability.Spell_Permanent; @@ -520,25 +519,7 @@ public class AbilityFactory_Attach { if (abCost != null){ // No Aura spells have Additional Costs - - // AI currently disabled for these costs - if (abCost.getSacCost()){ - - } - if (abCost.getLifeCost()) return false; - if (abCost.getDiscardCost()) return false; - - if (abCost.getSubCounter()){ - //non +1/+1 counters should be used - if (abCost.getCounterType().equals(Counters.P1P1)){ - // A card has a 25% chance per counter to be able to pass through here - // 4+ counters will always pass. 0 counters will never - int currentNum = source.getCounters(abCost.getCounterType()); - double percent = .25 * (currentNum / abCost.getCounterNum()); - if (percent <= r.nextFloat()) - return false; - } - } + } // prevent run-away activations - first time will always return true @@ -552,7 +533,7 @@ public class AbilityFactory_Attach { return false; } - if (abCost.getMana().contains("X") && source.getSVar("X").equals("Count$xPaid")){ + if (abCost.getTotalMana().contains("X") && source.getSVar("X").equals("Count$xPaid")){ // Set PayX here to maximum value. (Endless Scream and Venarian Gold) int xPay = ComputerUtil.determineLeftoverMana(sa); diff --git a/src/main/java/forge/card/abilityFactory/AbilityFactory_ChangeZone.java b/src/main/java/forge/card/abilityFactory/AbilityFactory_ChangeZone.java index 7f083e28f3f..89fe676dfa5 100644 --- a/src/main/java/forge/card/abilityFactory/AbilityFactory_ChangeZone.java +++ b/src/main/java/forge/card/abilityFactory/AbilityFactory_ChangeZone.java @@ -2,6 +2,8 @@ package forge.card.abilityFactory; import forge.*; import forge.card.cardFactory.CardFactoryUtil; +import forge.card.cost.Cost; +import forge.card.cost.CostUtil; import forge.card.spellability.*; import forge.gui.GuiUtils; @@ -283,22 +285,14 @@ public class AbilityFactory_ChangeZone { if (abCost != null) { // AI currently disabled for these costs - if (abCost.getSacCost() && !abCost.getSacThis()) { - //only sacrifice something that's supposed to be sacrificed - String type = abCost.getSacType(); - CardList typeList = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer()); - typeList = typeList.getValidCards(type.split(","), source.getController(), source); - if (ComputerUtil.getCardPreference(source, "SacCost", typeList) == null) - return false; - } - if (abCost.getLifeCost()) { - if (AllZone.getComputerPlayer().getLife() - abCost.getLifeAmount() < 4) - return false; - } - if (abCost.getDiscardCost()) return false; - - if (abCost.getSubCounter()) ; // SubCounter is fine - + if (!CostUtil.checkSacrificeCost(abCost, source)) + return false; + + if (!CostUtil.checkLifeCost(abCost, source, 4)) + return false; + + if (!CostUtil.checkDiscardCost(abCost, source)) + return false; } Random r = MyRandom.random; @@ -897,28 +891,17 @@ public class AbilityFactory_ChangeZone { if (abCost != null) { // AI currently disabled for these costs - if (abCost.getSacCost() && !abCost.getSacThis()) { - //only sacrifice something that's supposed to be sacrificed - String type = abCost.getSacType(); - CardList typeList = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer()); - typeList = typeList.getValidCards(type.split(","), source.getController(), source); - if (ComputerUtil.getCardPreference(source, "SacCost", typeList) == null) - return false; - } - if (abCost.getLifeCost()) { - if (AllZone.getComputerPlayer().getLife() - abCost.getLifeAmount() < 4) - return false; - } - if (abCost.getDiscardCost()) return false; + if (!CostUtil.checkSacrificeCost(abCost, source)) + return false; + + if (!CostUtil.checkLifeCost(abCost, source, 4)) + return false; + + if (!CostUtil.checkDiscardCost(abCost, source)) + return false; - if (abCost.getSubCounter()) { - // A card has a 25% chance per counter to be able to pass through here - // 4+ counters will always pass. 0 counters will never - int currentNum = source.getCounters(abCost.getCounterType()); - double percent = .25 * (currentNum / abCost.getCounterNum()); - if (percent <= r.nextFloat()) - return false; - } + if (!CostUtil.checkRemoveCounterCost(abCost, source)) + return false; } // prevent run-away activations - first time will always return true @@ -1565,24 +1548,19 @@ public class AbilityFactory_ChangeZone { private static boolean changeZoneAllCanPlayAI(AbilityFactory af, SpellAbility sa) { // Change Zone All, can be any type moving from one zone to another Cost abCost = af.getAbCost(); - //Card source = af.getHostCard(); + Card source = sa.getSourceCard(); HashMap params = af.getMapParams(); String destination = params.get("Destination"); String origin = params.get("Origin"); + if (abCost != null) { // AI currently disabled for these costs - if (abCost.getSacCost()) { - // Sac is ok in general, but should add some decision making based off what we Sacrifice and what we might get - } - if (abCost.getLifeCost()) { - if (AllZone.getComputerPlayer().getLife() - abCost.getLifeAmount() < 4) - return false; - } - if (abCost.getDiscardCost()) return false; - - if (abCost.getSubCounter()) - ; // subcounter is fine + if (!CostUtil.checkLifeCost(abCost, source, 4)) + return false; + + if (!CostUtil.checkDiscardCost(abCost, source)) + return false; } diff --git a/src/main/java/forge/card/abilityFactory/AbilityFactory_CounterMagic.java b/src/main/java/forge/card/abilityFactory/AbilityFactory_CounterMagic.java index 56262aa0e16..aa4300ca9bc 100644 --- a/src/main/java/forge/card/abilityFactory/AbilityFactory_CounterMagic.java +++ b/src/main/java/forge/card/abilityFactory/AbilityFactory_CounterMagic.java @@ -2,6 +2,8 @@ package forge.card.abilityFactory; import forge.*; import forge.card.cardFactory.CardFactoryUtil; +import forge.card.cost.Cost; +import forge.card.cost.CostUtil; import forge.card.spellability.*; import java.util.ArrayList; @@ -171,18 +173,10 @@ public class AbilityFactory_CounterMagic { if (abCost != null) { // AI currently disabled for these costs - if (abCost.getSacCost() && !abCost.getSacThis()) { - //only sacrifice something that's supposed to be sacrificed - String type = abCost.getSacType(); - CardList typeList = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer()); - typeList = typeList.getValidCards(type.split(","), source.getController(), source); - if (ComputerUtil.getCardPreference(source, "SacCost", typeList) == null) - return false; - } - if (abCost.getLifeCost()) { - if (AllZone.getComputerPlayer().getLife() - abCost.getLifeAmount() < 4) - return false; - } + if (!CostUtil.checkSacrificeCost(abCost, source)) + return false; + if (!CostUtil.checkLifeCost(abCost, source, 4)) + return false; } Target tgt = sa.getTarget(); diff --git a/src/main/java/forge/card/abilityFactory/AbilityFactory_Counters.java b/src/main/java/forge/card/abilityFactory/AbilityFactory_Counters.java index 2b2c66cbac8..865499f19ad 100644 --- a/src/main/java/forge/card/abilityFactory/AbilityFactory_Counters.java +++ b/src/main/java/forge/card/abilityFactory/AbilityFactory_Counters.java @@ -2,6 +2,8 @@ package forge.card.abilityFactory; import forge.*; import forge.card.cardFactory.CardFactoryUtil; +import forge.card.cost.Cost; +import forge.card.cost.CostUtil; import forge.card.spellability.*; import forge.gui.GuiUtils; import forge.gui.input.Input; @@ -221,25 +223,20 @@ public class AbilityFactory_Counters { if (abCost != null) { // AI currently disabled for these costs - if (abCost.getSacCost() && (!abCost.getSacThis() || source.isCreature())) { - //only sacrifice something that's supposed to be sacrificed - String sacType = abCost.getSacType(); - CardList typeList = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer()); - typeList = typeList.getValidCards(sacType.split(","), source.getController(), source); - if (ComputerUtil.getCardPreference(source, "SacCost", typeList) == null) - return false; - } - if (abCost.getLifeCost()) return false; - if (abCost.getDiscardCost()) return false; + if (!CostUtil.checkLifeCost(abCost, source, 4)) + return false; - if (abCost.getSubCounter()) { - // A card has a 25% chance per counter to be able to pass through here - // 4+ counters will always pass. 0 counters will never - int currentNum = source.getCounters(abCost.getCounterType()); - double percent = .25 * (currentNum / abCost.getCounterNum()); - if (percent <= r.nextFloat()) - return false; - } + if (!CostUtil.checkDiscardCost(abCost, source)) + return false; + + if (!CostUtil.checkSacrificeCost(abCost, source)) + return false; + + if (CostUtil.checkCreatureSacrificeCost(abCost, source)) + return false; + + if (!CostUtil.checkRemoveCounterCost(abCost, source)) + return false; } // TODO handle proper calculation of X values based on Cost @@ -743,25 +740,17 @@ public class AbilityFactory_Counters { if (abCost != null) { // AI currently disabled for these costs - if (abCost.getSacCost() && !abCost.getSacThis()) { - //only sacrifice something that's supposed to be sacrificed - String sacType = abCost.getSacType(); - CardList typeList = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer()); - typeList = typeList.getValidCards(sacType.split(","), source.getController(), source); - if (ComputerUtil.getCardPreference(source, "SacCost", typeList) == null) - return false; - } - if (abCost.getLifeCost()) return false; - if (abCost.getDiscardCost()) return false; + if (!CostUtil.checkLifeCost(abCost, source, 4)) + return false; - if (abCost.getSubCounter()) { - // A card has a 25% chance per counter to be able to pass through here - // 4+ counters will always pass. 0 counters will never - int currentNum = source.getCounters(abCost.getCounterType()); - double percent = .25 * (currentNum / abCost.getCounterNum()); - if (percent <= r.nextFloat()) - return false; - } + if (!CostUtil.checkDiscardCost(abCost, source)) + return false; + + if (!CostUtil.checkSacrificeCost(abCost, source)) + return false; + + if (!CostUtil.checkRemoveCounterCost(abCost, source)) + return false; } // TODO handle proper calculation of X values based on Cost @@ -1363,11 +1352,14 @@ public class AbilityFactory_Counters { if (abCost != null) { // AI currently disabled for these costs - if (abCost.getSacCost()) { + if (!CostUtil.checkLifeCost(abCost, source, 8)) + return false; + + if (!CostUtil.checkDiscardCost(abCost, source)) + return false; + + if (!CostUtil.checkSacrificeCost(abCost, source)) return false; - } - if (abCost.getLifeCost()) return false; - if (abCost.getDiscardCost()) return false; } if (tgt != null) { diff --git a/src/main/java/forge/card/abilityFactory/AbilityFactory_DealDamage.java b/src/main/java/forge/card/abilityFactory/AbilityFactory_DealDamage.java index 27fb6b5e271..1e3a39447eb 100644 --- a/src/main/java/forge/card/abilityFactory/AbilityFactory_DealDamage.java +++ b/src/main/java/forge/card/abilityFactory/AbilityFactory_DealDamage.java @@ -2,6 +2,8 @@ package forge.card.abilityFactory; import forge.*; import forge.card.cardFactory.CardFactoryUtil; +import forge.card.cost.Cost; +import forge.card.cost.CostUtil; import forge.card.spellability.*; import java.util.ArrayList; @@ -238,23 +240,14 @@ public class AbilityFactory_DealDamage { boolean rr = AF.isSpell(); // temporarily disabled until better AI - if (abCost.getSacCost() && !abCost.getSacThis() && AllZone.getHumanPlayer().getLife() - dmg > 0) { - //only sacrifice something that's supposed to be sacrificed - String sacType = abCost.getSacType(); - CardList typeList = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer()); - typeList = typeList.getValidCards(sacType.split(","), source.getController(), source); - if (ComputerUtil.getCardPreference(source, "SacCost", typeList) == null) - return false; - } - if (AF.getAbCost().getSubCounter()) { - // +1/+1 counters only if damage from this ability would kill the human, otherwise ok - if (AllZone.getHumanPlayer().getLife() - dmg > 0 && AF.getAbCost().getCounterType().equals(Counters.P1P1)) - return false; - } - if (AF.getAbCost().getLifeCost()) { - if (AllZone.getHumanPlayer().getLife() - dmg > 0) // only if damage from this ability would kill the human - return false; - } + if (!CostUtil.checkLifeCost(abCost, source, 4)) + return false; + + if (!CostUtil.checkSacrificeCost(abCost, source)) + return false; + + if (!CostUtil.checkRemoveCounterCost(abCost, source)) + return false; if (source.getName().equals("Stuffy Doll")) { // Now stuffy sits around for blocking @@ -805,18 +798,8 @@ public class AbilityFactory_DealDamage { //abCost stuff that should probably be centralized... if (abCost != null) { // AI currently disabled for some costs - if (abCost.getSacCost()) { - //OK - } - if (abCost.getLifeCost()) { - if (AllZone.getComputerPlayer().getLife() - abCost.getLifeAmount() < 4) - return false; - } - if (abCost.getDiscardCost()) ; //OK - - if (abCost.getSubCounter()) { - // OK - } + if (!CostUtil.checkLifeCost(abCost, source, 4)) + return false; } // TODO: if damage is dependant on mana paid, maybe have X be human's max life diff --git a/src/main/java/forge/card/abilityFactory/AbilityFactory_Debuff.java b/src/main/java/forge/card/abilityFactory/AbilityFactory_Debuff.java index 79d891927e8..175889e2e3b 100644 --- a/src/main/java/forge/card/abilityFactory/AbilityFactory_Debuff.java +++ b/src/main/java/forge/card/abilityFactory/AbilityFactory_Debuff.java @@ -2,6 +2,8 @@ package forge.card.abilityFactory; import forge.*; import forge.card.cardFactory.CardFactoryUtil; +import forge.card.cost.Cost; +import forge.card.cost.CostUtil; import forge.card.spellability.*; import java.util.*; @@ -194,28 +196,21 @@ public class AbilityFactory_Debuff { */ private static boolean debuffCanPlayAI(final AbilityFactory af, final SpellAbility sa) { // if there is no target and host card isn't in play, don't activate - if (af.getAbTgt() == null && !AllZoneUtil.isCardInPlay(af.getHostCard())) + Card source = sa.getSourceCard(); + if (sa.getTarget() == null && !AllZoneUtil.isCardInPlay(source)) return false; + Cost cost = sa.getPayCosts(); + // temporarily disabled until AI is improved - if (af.getAbCost().getSacCost() && sa.getSourceCard().isCreature()) return false; - if (af.getAbCost().getLifeCost()) { + if (!CostUtil.checkCreatureSacrificeCost(cost, source)) + return false; + + if (!CostUtil.checkLifeCost(cost, source, 40)) + return false; + + if (!CostUtil.checkRemoveCounterCost(cost, source)) return false; - } - if (af.getAbCost().getSubCounter()) { - // instead of never removing counters, we will have a random possibility of failure. - // all the other tests still need to pass if a counter will be removed - Counters count = af.getAbCost().getCounterType(); - double chance = .66; - if (count.equals(Counters.P1P1)) { // 10% chance to remove +1/+1 to pump - chance = .1; - } else if (count.equals(Counters.CHARGE)) { // 50% chance to remove charge to pump - chance = .5; - } - Random r = MyRandom.random; - if (r.nextFloat() > chance) - return false; - } HashMap params = af.getMapParams(); SpellAbility_Restriction restrict = sa.getRestrictions(); diff --git a/src/main/java/forge/card/abilityFactory/AbilityFactory_Destroy.java b/src/main/java/forge/card/abilityFactory/AbilityFactory_Destroy.java index 6ac6b519230..83e8e2926ce 100644 --- a/src/main/java/forge/card/abilityFactory/AbilityFactory_Destroy.java +++ b/src/main/java/forge/card/abilityFactory/AbilityFactory_Destroy.java @@ -2,6 +2,8 @@ package forge.card.abilityFactory; import forge.*; import forge.card.cardFactory.CardFactoryUtil; +import forge.card.cost.Cost; +import forge.card.cost.CostUtil; import forge.card.spellability.*; import java.util.ArrayList; @@ -156,23 +158,14 @@ public class AbilityFactory_Destroy { if (abCost != null) { // AI currently disabled for some costs - if (abCost.getSacCost() && !abCost.getSacThis()) { - //only sacrifice something that's supposed to be sacrificed - String sacType = abCost.getSacType(); - CardList typeList = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer()); - typeList = typeList.getValidCards(sacType.split(","), source.getController(), source); - if (ComputerUtil.getCardPreference(source, "SacCost", typeList) == null) - return false; - } - if (abCost.getLifeCost()) { - if (AllZone.getComputerPlayer().getLife() - abCost.getLifeAmount() < 4) - return false; - } - if (abCost.getDiscardCost()) return false; - - if (abCost.getSubCounter()) { - // OK - } + if (!CostUtil.checkSacrificeCost(abCost, source)) + return false; + + if (!CostUtil.checkLifeCost(abCost, source, 4)) + return false; + + if (!CostUtil.checkDiscardCost(abCost, source)) + return false; } // prevent run-away activations - first time will always return true @@ -607,18 +600,9 @@ public class AbilityFactory_Destroy { if (abCost != null) { // AI currently disabled for some costs - if (abCost.getSacCost()) { - //OK - } - if (abCost.getLifeCost()) { - if (AllZone.getComputerPlayer().getLife() - abCost.getLifeAmount() < 4) - return false; - } - if (abCost.getDiscardCost()) ;//OK - if (abCost.getSubCounter()) { - // OK - } + if (!CostUtil.checkLifeCost(abCost, source, 4)) + return false; } // prevent run-away activations - first time will always return true diff --git a/src/main/java/forge/card/abilityFactory/AbilityFactory_Mana.java b/src/main/java/forge/card/abilityFactory/AbilityFactory_Mana.java index bacf609db4f..0825da8223f 100644 --- a/src/main/java/forge/card/abilityFactory/AbilityFactory_Mana.java +++ b/src/main/java/forge/card/abilityFactory/AbilityFactory_Mana.java @@ -1,6 +1,7 @@ package forge.card.abilityFactory; import forge.*; +import forge.card.cost.Cost; import forge.card.spellability.*; import forge.gui.GuiUtils; import forge.gui.input.Input_PayManaCostUtil; diff --git a/src/main/java/forge/card/abilityFactory/AbilityFactory_PermanentState.java b/src/main/java/forge/card/abilityFactory/AbilityFactory_PermanentState.java index 47677634414..61bcdb185fe 100644 --- a/src/main/java/forge/card/abilityFactory/AbilityFactory_PermanentState.java +++ b/src/main/java/forge/card/abilityFactory/AbilityFactory_PermanentState.java @@ -2,6 +2,8 @@ package forge.card.abilityFactory; import forge.*; import forge.card.cardFactory.CardFactoryUtil; +import forge.card.cost.Cost; +import forge.card.cost.CostUtil; import forge.card.spellability.*; import forge.gui.GuiUtils; @@ -176,13 +178,13 @@ public class AbilityFactory_PermanentState { */ private static boolean untapCanPlayAI(final AbilityFactory af, SpellAbility sa) { // AI cannot use this properly until he can use SAs during Humans turn - - if (af.getAbCost().getAddCounter()) - if (af.getAbCost().getCounterType().equals(Counters.M1M1)) - return false; - - Target tgt = af.getAbTgt(); - //Card source = sa.getSourceCard(); + Target tgt = sa.getTarget(); + Card source = sa.getSourceCard(); + Cost cost = sa.getPayCosts(); + + + if (!CostUtil.checkAddM1M1CounterCost(cost, source)) + return false; Random r = MyRandom.random; boolean randomReturn = r.nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn() + 1); diff --git a/src/main/java/forge/card/abilityFactory/AbilityFactory_PreventDamage.java b/src/main/java/forge/card/abilityFactory/AbilityFactory_PreventDamage.java index 3d4498bffd2..348688921bf 100644 --- a/src/main/java/forge/card/abilityFactory/AbilityFactory_PreventDamage.java +++ b/src/main/java/forge/card/abilityFactory/AbilityFactory_PreventDamage.java @@ -2,6 +2,8 @@ package forge.card.abilityFactory; import forge.*; import forge.card.cardFactory.CardFactoryUtil; +import forge.card.cost.Cost; +import forge.card.cost.CostUtil; import forge.card.spellability.*; import java.util.ArrayList; @@ -178,12 +180,20 @@ public class AbilityFactory_PreventDamage { final Card hostCard = af.getHostCard(); boolean chance = false; + Cost cost = sa.getPayCosts(); + // temporarily disabled until better AI - if (af.getAbCost().getSacCost()) return false; - if (af.getAbCost().getSubCounter()) - if (af.getAbCost().getCounterType().equals(Counters.P1P1)) - return false; - if (af.getAbCost().getLifeCost()) return false; + if (!CostUtil.checkLifeCost(cost, hostCard, 4)) + return false; + + if (!CostUtil.checkDiscardCost(cost, hostCard)) + return false; + + if (!CostUtil.checkSacrificeCost(cost, hostCard)) + return false; + + if (!CostUtil.checkRemoveCounterCost(cost, hostCard)) + return false; Target tgt = af.getAbTgt(); if (tgt == null) { diff --git a/src/main/java/forge/card/abilityFactory/AbilityFactory_Protection.java b/src/main/java/forge/card/abilityFactory/AbilityFactory_Protection.java index 455916e2ce4..23a447521d9 100644 --- a/src/main/java/forge/card/abilityFactory/AbilityFactory_Protection.java +++ b/src/main/java/forge/card/abilityFactory/AbilityFactory_Protection.java @@ -2,6 +2,8 @@ package forge.card.abilityFactory; import forge.*; import forge.card.cardFactory.CardFactoryUtil; +import forge.card.cost.Cost; +import forge.card.cost.CostUtil; import forge.card.spellability.*; import forge.gui.GuiUtils; @@ -209,28 +211,20 @@ public class AbilityFactory_Protection { if(af.getAbTgt() == null && !AllZoneUtil.isCardInPlay(hostCard)) return false; - // temporarily disabled until AI is improved - if(af.getAbCost().getSacCost() && sa.getSourceCard().isCreature()) return false; - if(af.getAbCost().getLifeCost()) { - if(!af.isCurse()) return false; //Use life only to kill creatures - if(AllZone.getComputerPlayer().getLife() - af.getAbCost().getLifeAmount() < 4) - return false; - } - if(af.getAbCost().getSubCounter()) { - // instead of never removing counters, we will have a random possibility of failure. - // all the other tests still need to pass if a counter will be removed - Counters count = af.getAbCost().getCounterType(); - double chance = .66; - if(count.equals(Counters.P1P1)) { // 10% chance to remove +1/+1 to protect - chance = .1; - } - else if(count.equals(Counters.CHARGE)) { // 50% chance to remove charge to protect - chance = .5; - } - Random r = MyRandom.random; - if(r.nextFloat() > chance) - return false; - } + Cost cost = sa.getPayCosts(); + + // temporarily disabled until better AI + if (!CostUtil.checkLifeCost(cost, hostCard, 4)) + return false; + + if (!CostUtil.checkDiscardCost(cost, hostCard)) + return false; + + if (!CostUtil.checkCreatureSacrificeCost(cost, hostCard)) + return false; + + if (!CostUtil.checkRemoveCounterCost(cost, hostCard)) + return false; // Phase Restrictions if(AllZone.getStack().size() == 0 && AllZone.getPhase().isBefore(Constant.Phase.Combat_FirstStrikeDamage)) { @@ -768,14 +762,20 @@ public class AbilityFactory_Protection { if(af.getAbTgt() == null && !AllZoneUtil.isCardInPlay(hostCard)) return false; - // temporarily disabled until AI is improved - if(af.getAbCost().getSacCost()) return false; - if(af.getAbCost().getLifeCost()) { - return false; - } - if(af.getAbCost().getSubCounter()) { - return false; - } + Cost cost = sa.getPayCosts(); + + // temporarily disabled until better AI + if (!CostUtil.checkLifeCost(cost, hostCard, 4)) + return false; + + if (!CostUtil.checkDiscardCost(cost, hostCard)) + return false; + + if (!CostUtil.checkSacrificeCost(cost, hostCard)) + return false; + + if (!CostUtil.checkRemoveCounterCost(cost, hostCard)) + return false; return false; }//protectAllCanPlayAI() diff --git a/src/main/java/forge/card/abilityFactory/AbilityFactory_Pump.java b/src/main/java/forge/card/abilityFactory/AbilityFactory_Pump.java index c55060af43f..00d0969ef1b 100644 --- a/src/main/java/forge/card/abilityFactory/AbilityFactory_Pump.java +++ b/src/main/java/forge/card/abilityFactory/AbilityFactory_Pump.java @@ -2,6 +2,8 @@ package forge.card.abilityFactory; import forge.*; import forge.card.cardFactory.CardFactoryUtil; +import forge.card.cost.Cost; +import forge.card.cost.CostUtil; import forge.card.spellability.*; import java.util.ArrayList; @@ -303,30 +305,20 @@ public class AbilityFactory_Pump { if (AF.getAbTgt() == null && !AllZoneUtil.isCardInPlay(hostCard)) return false; + Cost cost = sa.getPayCosts(); + // temporarily disabled until AI is improved - if (AF.getAbCost().getSacCost() && sa.getSourceCard().isCreature()) return false; - if (AF.getAbCost().getLifeCost()) { - if (!AF.isCurse()) return false; //Use life only to kill creatures - if (AllZone.getComputerPlayer().getLife() - AF.getAbCost().getLifeAmount() < 4) - return false; - } - if (AF.getAbCost().getDiscardCost() && !AF.isCurse()) { - return false; - } - if (AF.getAbCost().getSubCounter()) { - // instead of never removing counters, we will have a random possibility of failure. - // all the other tests still need to pass if a counter will be removed - Counters count = AF.getAbCost().getCounterType(); - double chance = .66; - if (count.equals(Counters.P1P1)) { // 10% chance to remove +1/+1 to pump - chance = .1; - } else if (count.equals(Counters.CHARGE)) { // 50% chance to remove charge to pump - chance = .5; - } - Random r = MyRandom.random; - if (r.nextFloat() > chance) - return false; - } + if (!CostUtil.checkLifeCost(cost, hostCard, 4)) + return false; + + if (!CostUtil.checkDiscardCost(cost, hostCard)) + return false; + + if (!CostUtil.checkCreatureSacrificeCost(cost, hostCard)) + return false; + + if (!CostUtil.checkRemoveCounterCost(cost, hostCard)) + return false; SpellAbility_Restriction restrict = sa.getRestrictions(); diff --git a/src/main/java/forge/card/abilityFactory/AbilityFactory_Regenerate.java b/src/main/java/forge/card/abilityFactory/AbilityFactory_Regenerate.java index f1aa42a7cff..81793d24b79 100644 --- a/src/main/java/forge/card/abilityFactory/AbilityFactory_Regenerate.java +++ b/src/main/java/forge/card/abilityFactory/AbilityFactory_Regenerate.java @@ -2,6 +2,8 @@ package forge.card.abilityFactory; import forge.*; import forge.card.cardFactory.CardFactoryUtil; +import forge.card.cost.Cost; +import forge.card.cost.CostUtil; import forge.card.spellability.*; import java.util.ArrayList; @@ -183,18 +185,14 @@ public class AbilityFactory_Regenerate { Cost abCost = af.getAbCost(); if (abCost != null) { // AI currently disabled for these costs - if (abCost.getSacCost() && !abCost.getSacThis()) { - //only sacrifice something that's supposed to be sacrificed - String type = abCost.getSacType(); - CardList typeList = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer()); - typeList = typeList.getValidCards(type.split(","), hostCard.getController(), hostCard); - if (ComputerUtil.getCardPreference(hostCard, "SacCost", typeList) == null) - return false; - } - if (abCost.getLifeCost()) { - if (AllZone.getComputerPlayer().getLife() - abCost.getLifeAmount() < 4) - return false; - } + if (!CostUtil.checkLifeCost(abCost, hostCard, 4)) + return false; + + if (!CostUtil.checkSacrificeCost(abCost, hostCard)) + return false; + + if (CostUtil.checkCreatureSacrificeCost(abCost, hostCard)) + return false; } Target tgt = sa.getTarget(); @@ -550,18 +548,14 @@ public class AbilityFactory_Regenerate { Cost abCost = af.getAbCost(); if (abCost != null) { // AI currently disabled for these costs - if (abCost.getSacCost() && !abCost.getSacThis()) { - //only sacrifice something that's supposed to be sacrificed - String type = abCost.getSacType(); - CardList typeList = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer()); - typeList = typeList.getValidCards(type.split(","), hostCard.getController(), hostCard); - if (ComputerUtil.getCardPreference(hostCard, "SacCost", typeList) == null) - return false; - } - if (abCost.getLifeCost()) { - if (AllZone.getComputerPlayer().getLife() - abCost.getLifeAmount() < 4) - return false; - } + if (!CostUtil.checkSacrificeCost(abCost, hostCard)) + return false; + + if (CostUtil.checkCreatureSacrificeCost(abCost, hostCard)) + return false; + + if (!CostUtil.checkLifeCost(abCost, hostCard, 4)) + return false; } // filter AIs battlefield by what I can target diff --git a/src/main/java/forge/card/abilityFactory/AbilityFactory_Reveal.java b/src/main/java/forge/card/abilityFactory/AbilityFactory_Reveal.java index 7967e75a4ef..ab481920c3a 100644 --- a/src/main/java/forge/card/abilityFactory/AbilityFactory_Reveal.java +++ b/src/main/java/forge/card/abilityFactory/AbilityFactory_Reveal.java @@ -1,6 +1,8 @@ package forge.card.abilityFactory; import forge.*; +import forge.card.cost.Cost; +import forge.card.cost.CostUtil; import forge.card.spellability.*; import forge.gui.GuiUtils; @@ -595,22 +597,22 @@ public class AbilityFactory_Reveal { */ private static boolean revealHandCanPlayAI(final AbilityFactory af, SpellAbility sa) { // AI cannot use this properly until he can use SAs during Humans turn - Cost abCost = af.getAbCost(); + Cost abCost = sa.getPayCosts(); + Card source = sa.getSourceCard(); if (abCost != null) { // AI currently disabled for these costs - if (abCost.getSacCost()) { + if (!CostUtil.checkLifeCost(abCost, source, 4)) return false; - } - if (abCost.getLifeCost()) { - if (AllZone.getComputerPlayer().getLife() - abCost.getLifeAmount() < 4) - return false; - } - if (abCost.getDiscardCost()) return false; - if (abCost.getSubCounter()) { - if (abCost.getCounterType().equals(Counters.P1P1)) return false; // Other counters should be used - } + if (!CostUtil.checkDiscardCost(abCost, source)) + return false; + + if (!CostUtil.checkSacrificeCost(abCost, source)) + return false; + + if (!CostUtil.checkRemoveCounterCost(abCost, source)) + return false; } diff --git a/src/main/java/forge/card/abilityFactory/AbilityFactory_Sacrifice.java b/src/main/java/forge/card/abilityFactory/AbilityFactory_Sacrifice.java index a7b275d5f25..c597c58232d 100644 --- a/src/main/java/forge/card/abilityFactory/AbilityFactory_Sacrifice.java +++ b/src/main/java/forge/card/abilityFactory/AbilityFactory_Sacrifice.java @@ -2,6 +2,8 @@ package forge.card.abilityFactory; import forge.*; import forge.card.cardFactory.CardFactoryUtil; +import forge.card.cost.Cost; +import forge.card.cost.CostUtil; import forge.card.spellability.*; import java.util.ArrayList; @@ -347,15 +349,10 @@ public class AbilityFactory_Sacrifice { msg = valid; msg = "Sacrifice a " + msg; - - boolean remSacrificed = params.containsKey("RememberSacrificed"); - if (remSacrificed) - card.clearRemembered(); if (valid.equals("Self")) { if (AllZone.getZone(sa.getSourceCard()).is(Constant.Zone.Battlefield)) - if (AllZone.getGameAction().sacrifice(sa.getSourceCard()) && remSacrificed) - card.addRemembered(sa.getSourceCard()); + AllZone.getGameAction().sacrifice(sa.getSourceCard()); } //TODO - maybe this can be done smarter... else if (valid.equals("Card.AttachedBy")) { @@ -592,18 +589,8 @@ public class AbilityFactory_Sacrifice { if (abCost != null) { // AI currently disabled for some costs - if (abCost.getSacCost()) { - //OK - } - if (abCost.getLifeCost()) { - if (AllZone.getComputerPlayer().getLife() - abCost.getLifeAmount() < 4) - return false; - } - if (abCost.getDiscardCost()) ;//OK - - if (abCost.getSubCounter()) { - // OK - } + if (!CostUtil.checkLifeCost(abCost, source, 4)) + return false; } // prevent run-away activations - first time will always return true diff --git a/src/main/java/forge/card/abilityFactory/AbilityFactory_Token.java b/src/main/java/forge/card/abilityFactory/AbilityFactory_Token.java index 8d8ec68071b..cbed1caacd0 100644 --- a/src/main/java/forge/card/abilityFactory/AbilityFactory_Token.java +++ b/src/main/java/forge/card/abilityFactory/AbilityFactory_Token.java @@ -10,16 +10,16 @@ import forge.CardList; import forge.Command; import forge.ComputerUtil; import forge.Constant; -import forge.Counters; import forge.MyRandom; import forge.Player; import forge.card.cardFactory.CardFactoryUtil; import forge.card.spellability.Ability_Activated; import forge.card.spellability.Ability_Sub; -import forge.card.spellability.Cost; import forge.card.spellability.Spell; import forge.card.spellability.SpellAbility; import forge.card.spellability.Target; +import forge.card.cost.Cost; +import forge.card.cost.CostUtil; import forge.card.trigger.Trigger; import forge.card.trigger.TriggerHandler; @@ -266,28 +266,17 @@ public class AbilityFactory_Token extends AbilityFactory { } if (cost != null) { - if (cost.getSacCost() && !cost.getSacThis()) { - //only sacrifice something that's supposed to be sacrificed - String type = cost.getSacType(); - CardList typeList = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer()); - typeList = typeList.getValidCards(type.split(","), source.getController(), source); - if (ComputerUtil.getCardPreference(source, "SacCost", typeList) == null) - return false; - } - if (cost.getSubCounter()) { - if (cost.getCounterType().equals(Counters.P1P1)) { - // A card has a 25% chance per counter to be able to pass through here - // 4+ counters will always pass. 0 counters will never - int currentNum = source.getCounters(cost.getCounterType()); - double percent = .25 * (currentNum / cost.getCounterNum()); - if (percent <= r.nextFloat()) - return false; - } - } - if (cost.getLifeCost()) { - if (AllZone.getComputerPlayer().getLife() - cost.getLifeAmount() < 4) - return false; - } + if (!CostUtil.checkLifeCost(cost, source, 4)) + return false; + + if (!CostUtil.checkDiscardCost(cost, source)) + return false; + + if (!CostUtil.checkSacrificeCost(cost, source)) + return false; + + if (!CostUtil.checkRemoveCounterCost(cost, source)) + return false; } if (tokenAmount.equals("X")) { diff --git a/src/main/java/forge/card/abilityFactory/AbilityFactory_ZoneAffecting.java b/src/main/java/forge/card/abilityFactory/AbilityFactory_ZoneAffecting.java index f388b29ab21..30dcd4c47a1 100644 --- a/src/main/java/forge/card/abilityFactory/AbilityFactory_ZoneAffecting.java +++ b/src/main/java/forge/card/abilityFactory/AbilityFactory_ZoneAffecting.java @@ -2,6 +2,8 @@ package forge.card.abilityFactory; import forge.*; import forge.card.cardFactory.CardFactoryUtil; +import forge.card.cost.Cost; +import forge.card.cost.CostUtil; import forge.card.spellability.*; import forge.gui.GuiUtils; @@ -186,24 +188,23 @@ public class AbilityFactory_ZoneAffecting { private static boolean drawCanPlayAI(final AbilityFactory af, SpellAbility sa) { HashMap params = af.getMapParams(); - Target tgt = af.getAbTgt(); + Target tgt = sa.getTarget(); Card source = sa.getSourceCard(); - Cost abCost = af.getAbCost(); + Cost abCost = sa.getPayCosts(); if (abCost != null) { // AI currently disabled for these costs - if (abCost.getSacCost() && source.isCreature()) { + if (CostUtil.checkCreatureSacrificeCost(abCost, source)) + return false; + + if (!CostUtil.checkLifeCost(abCost, source, 4)) return false; - } - if (abCost.getLifeCost()) { - if (AllZone.getComputerPlayer().getLife() - abCost.getLifeAmount() < 4) - return false; - } - if (abCost.getDiscardCost()) return false; - if (abCost.getSubCounter()) { - if (abCost.getCounterType().equals(Counters.P1P1)) return false; // Other counters should be used - } + if (!CostUtil.checkDiscardCost(abCost, source)) + return false; + + if (!CostUtil.checkRemoveCounterCost(abCost, source)) + return false; } @@ -595,18 +596,17 @@ public class AbilityFactory_ZoneAffecting { if (abCost != null) { // AI currently disabled for these costs - if (abCost.getSacCost()) { + if (!CostUtil.checkLifeCost(abCost, source, 4)) return false; - } - if (abCost.getLifeCost()) { - if (AllZone.getComputerPlayer().getLife() - abCost.getLifeAmount() < 4) - return false; - } - if (abCost.getDiscardCost()) return false; - if (abCost.getSubCounter()) { - if (abCost.getCounterType().equals(Counters.P1P1)) return false; // Other counters should be used - } + if (!CostUtil.checkDiscardCost(abCost, source)) + return false; + + if (!CostUtil.checkSacrificeCost(abCost, source)) + return false; + + if (!CostUtil.checkRemoveCounterCost(abCost, source)) + return false; } @@ -1105,24 +1105,23 @@ public class AbilityFactory_ZoneAffecting { private static boolean discardCanPlayAI(final AbilityFactory af, SpellAbility sa) { HashMap params = af.getMapParams(); - Target tgt = af.getAbTgt(); + Target tgt = sa.getTarget(); Card source = sa.getSourceCard(); - Cost abCost = af.getAbCost(); + Cost abCost = sa.getPayCosts(); if (abCost != null) { // AI currently disabled for these costs - if (abCost.getSacCost()) { + if (!CostUtil.checkSacrificeCost(abCost, source)) return false; - } - if (abCost.getLifeCost()) { - if (AllZone.getComputerPlayer().getLife() - abCost.getLifeAmount() < 4) - return false; - } - if (abCost.getDiscardCost()) return false; - if (abCost.getSubCounter()) { - if (abCost.getCounterType().equals(Counters.P1P1)) return false; // Other counters should be used - } + if (!CostUtil.checkLifeCost(abCost, source, 4)) + return false; + + if (!CostUtil.checkDiscardCost(abCost, source)) + return false; + + if (!CostUtil.checkRemoveCounterCost(abCost, source)) + return false; } diff --git a/src/main/java/forge/card/cardFactory/AbstractCardFactory.java b/src/main/java/forge/card/cardFactory/AbstractCardFactory.java index e9bf5375c3e..2fa153dc33e 100644 --- a/src/main/java/forge/card/cardFactory/AbstractCardFactory.java +++ b/src/main/java/forge/card/cardFactory/AbstractCardFactory.java @@ -24,6 +24,7 @@ import forge.FileUtil; import forge.Player; import forge.PlayerZone; import forge.card.abilityFactory.AbilityFactory; +import forge.card.cost.Cost; import forge.card.spellability.*; import forge.card.trigger.Trigger; import forge.game.GameLossReason; diff --git a/src/main/java/forge/card/cardFactory/CardFactoryUtil.java b/src/main/java/forge/card/cardFactory/CardFactoryUtil.java index 7117ecad259..b2ff2e51083 100644 --- a/src/main/java/forge/card/cardFactory/CardFactoryUtil.java +++ b/src/main/java/forge/card/cardFactory/CardFactoryUtil.java @@ -2,6 +2,7 @@ package forge.card.cardFactory; import com.esotericsoftware.minlog.Log; import forge.*; +import forge.card.cost.Cost; import forge.card.mana.ManaCost; import forge.card.spellability.*; import forge.card.trigger.Trigger; @@ -907,7 +908,7 @@ public class CardFactoryUtil { * @param sourceCard * a {@link forge.Card} object. * @param cost - * a {@link forge.card.spellability.Cost} object. + * a {@link forge.card.cost.Cost} object. * @param orgManaCost * a {@link java.lang.String} object. * @param a @@ -1258,7 +1259,7 @@ public class CardFactoryUtil { * @param extrinsicKeywords * an array of {@link java.lang.String} objects. * @param abCost - * a {@link forge.card.spellability.Cost} object. + * a {@link forge.card.cost.Cost} object. * @return a {@link forge.card.spellability.SpellAbility} object. */ public static SpellAbility eqPump_Equip(final Card sourceCard, final int Power, final int Tough, @@ -1370,7 +1371,7 @@ public class CardFactoryUtil { * @param extrinsicKeywords * an array of {@link java.lang.String} objects. * @param abCost - * a {@link forge.card.spellability.Cost} object. + * a {@link forge.card.cost.Cost} object. * @return a {@link forge.Command} object. */ public static Command eqPump_onEquip(final Card sourceCard, final int Power, final int Tough, @@ -1414,7 +1415,7 @@ public class CardFactoryUtil { * @param extrinsicKeywords * an array of {@link java.lang.String} objects. * @param abCost - * a {@link forge.card.spellability.Cost} object. + * a {@link forge.card.cost.Cost} object. * @return a {@link forge.Command} object. */ public static Command eqPump_unEquip(final Card sourceCard, final int Power, final int Tough, diff --git a/src/main/java/forge/card/cardFactory/CardFactory_Auras.java b/src/main/java/forge/card/cardFactory/CardFactory_Auras.java index 250b146bc7b..a09978753d0 100644 --- a/src/main/java/forge/card/cardFactory/CardFactory_Auras.java +++ b/src/main/java/forge/card/cardFactory/CardFactory_Auras.java @@ -13,8 +13,8 @@ import forge.Command; import forge.Constant; import forge.Player; import forge.PlayerZone; +import forge.card.cost.Cost; import forge.card.spellability.Ability; -import forge.card.spellability.Cost; import forge.card.spellability.Spell; import forge.card.spellability.SpellAbility; import forge.card.spellability.Spell_Permanent; diff --git a/src/main/java/forge/card/cardFactory/CardFactory_Creatures.java b/src/main/java/forge/card/cardFactory/CardFactory_Creatures.java index 56a528e678a..438e91182cd 100644 --- a/src/main/java/forge/card/cardFactory/CardFactory_Creatures.java +++ b/src/main/java/forge/card/cardFactory/CardFactory_Creatures.java @@ -4,6 +4,7 @@ package forge.card.cardFactory; import com.esotericsoftware.minlog.Log; import forge.*; import forge.card.abilityFactory.AbilityFactory; +import forge.card.cost.Cost; import forge.card.spellability.*; import forge.card.trigger.Trigger; import forge.card.trigger.TriggerHandler; diff --git a/src/main/java/forge/card/cardFactory/CardFactory_Equipment.java b/src/main/java/forge/card/cardFactory/CardFactory_Equipment.java index ffdadf3f709..f8ec2fd0b40 100644 --- a/src/main/java/forge/card/cardFactory/CardFactory_Equipment.java +++ b/src/main/java/forge/card/cardFactory/CardFactory_Equipment.java @@ -2,6 +2,7 @@ package forge.card.cardFactory; import forge.*; +import forge.card.cost.Cost; import forge.card.spellability.*; import forge.card.trigger.Trigger; import forge.card.trigger.TriggerHandler; diff --git a/src/main/java/forge/card/cardFactory/CardFactory_Instants.java b/src/main/java/forge/card/cardFactory/CardFactory_Instants.java index 90b666b8184..c0c65a1cc5b 100644 --- a/src/main/java/forge/card/cardFactory/CardFactory_Instants.java +++ b/src/main/java/forge/card/cardFactory/CardFactory_Instants.java @@ -1,6 +1,7 @@ package forge.card.cardFactory; import forge.*; +import forge.card.cost.Cost; import forge.card.spellability.*; import forge.gui.GuiUtils; import forge.gui.input.Input; diff --git a/src/main/java/forge/card/cardFactory/CardFactory_Lands.java b/src/main/java/forge/card/cardFactory/CardFactory_Lands.java index 02c490f150d..9b88eea07ea 100644 --- a/src/main/java/forge/card/cardFactory/CardFactory_Lands.java +++ b/src/main/java/forge/card/cardFactory/CardFactory_Lands.java @@ -1,6 +1,7 @@ package forge.card.cardFactory; import forge.*; +import forge.card.cost.Cost; import forge.card.spellability.*; import forge.gui.GuiUtils; import forge.gui.input.Input; diff --git a/src/main/java/forge/card/cardFactory/CardFactory_Planeswalkers.java b/src/main/java/forge/card/cardFactory/CardFactory_Planeswalkers.java index a70668d2b29..12821dfdcfe 100644 --- a/src/main/java/forge/card/cardFactory/CardFactory_Planeswalkers.java +++ b/src/main/java/forge/card/cardFactory/CardFactory_Planeswalkers.java @@ -3,6 +3,7 @@ package forge.card.cardFactory; import com.esotericsoftware.minlog.Log; import forge.*; +import forge.card.cost.Cost; import forge.card.spellability.*; import forge.gui.GuiUtils; import forge.gui.input.Input; diff --git a/src/main/java/forge/card/cardFactory/CardFactory_Sorceries.java b/src/main/java/forge/card/cardFactory/CardFactory_Sorceries.java index 021b46d6f0a..2729a671d4a 100644 --- a/src/main/java/forge/card/cardFactory/CardFactory_Sorceries.java +++ b/src/main/java/forge/card/cardFactory/CardFactory_Sorceries.java @@ -2,6 +2,7 @@ package forge.card.cardFactory; import com.esotericsoftware.minlog.Log; import forge.*; +import forge.card.cost.Cost; import forge.card.spellability.*; import forge.gui.GuiUtils; import forge.gui.input.Input; diff --git a/src/main/java/forge/card/cost/Cost.java b/src/main/java/forge/card/cost/Cost.java new file mode 100644 index 00000000000..a68c1193193 --- /dev/null +++ b/src/main/java/forge/card/cost/Cost.java @@ -0,0 +1,471 @@ +package forge.card.cost; + +import java.util.ArrayList; +import java.util.regex.Pattern; + +import forge.AllZone; +import forge.Card; +import forge.Constant; +import forge.Counters; +import forge.card.mana.ManaCost; +import forge.card.spellability.SpellAbility; + +/** + *

Cost class.

+ * + * @author Forge + * @version $Id$ + */ +public class Cost { + private boolean isAbility = true; + ArrayList costParts = new ArrayList(); + + public ArrayList getCostParts(){ return costParts; } + + private boolean sacCost = false; + + /** + *

Getter for the field sacCost.

+ * + * @return a boolean. + */ + public boolean getSacCost() { + return sacCost; + } + + private boolean tapCost = false; + + /** + *

getTap.

+ * + * @return a boolean. + */ + public boolean getTap() { + return tapCost; + } + + /** + *

hasNoManaCost.

+ * + * @return a boolean. + */ + public boolean hasNoManaCost() { + for(CostPart part : costParts) + if (part instanceof CostMana) + return false; + + return true; + } + + /** + *

isOnlyManaCost.

+ * + * @return a boolean. + */ + public boolean isOnlyManaCost() { + // Only used by Morph and Equip... why do we need this? + for(CostPart part : costParts) + if (!(part instanceof CostMana)) + return false; + + return true; + } + + /** + *

getTotalMana.

+ * + * @return a {@link java.lang.String} object. + */ + public String getTotalMana() { + for(CostPart part : costParts) + if (part instanceof CostMana) + return part.toString(); + + return "0"; + } + + private String name; + + // Parsing Strings + private final static String tapXStr = "tapXType<"; + private final static String subStr = "SubCounter<"; + private final static String addStr = "AddCounter<"; + private final static String lifeStr = "PayLife<"; + private final static String discStr = "Discard<"; + private final static String sacStr = "Sac<"; + private final static String exileStr = "Exile<"; + private final static String exileFromHandStr = "ExileFromHand<"; + private final static String exileFromGraveStr = "ExileFromGrave<"; + private final static String exileFromTopStr = "ExileFromTop<"; + private final static String returnStr = "Return<"; + + /** + *

Constructor for Cost.

+ * + * @param parse a {@link java.lang.String} object. + * @param cardName a {@link java.lang.String} object. + * @param bAbility a boolean. + */ + public Cost(String parse, String cardName, boolean bAbility) { + isAbility = bAbility; + // when adding new costs for cost string, place them here + name = cardName; + + while (parse.contains(tapXStr)) { + String[] splitStr = abCostParse(parse, tapXStr, 3); + parse = abUpdateParse(parse, tapXStr); + + String description = splitStr.length > 2 ? splitStr[2] : null; + costParts.add(new CostTapType(splitStr[0], splitStr[1], description)); + } + + while (parse.contains(subStr)) { + // SubCounter + String[] splitStr = abCostParse(parse, subStr, 4); + parse = abUpdateParse(parse, subStr); + + String type = splitStr.length > 2 ? splitStr[2] : null; + String description = splitStr.length > 3 ? splitStr[3] : null; + + costParts.add(new CostRemoveCounter(splitStr[0], Counters.valueOf(splitStr[1]), type, description)); + } + + + while (parse.contains(addStr)) { + // AddCounter + String[] splitStr = abCostParse(parse, addStr, 2); + parse = abUpdateParse(parse, addStr); + + costParts.add(new CostPutCounter(splitStr[0], Counters.valueOf(splitStr[1]))); + } + + // While no card has "PayLife<2> PayLife<3> there might be a card that Changes Cost by adding a Life Payment + while (parse.contains(lifeStr)) { + // PayLife + String[] splitStr = abCostParse(parse, lifeStr, 1); + parse = abUpdateParse(parse, lifeStr); + + costParts.add(new CostPayLife(splitStr[0])); + } + + + while (parse.contains(discStr)) { + // Discard + String[] splitStr = abCostParse(parse, discStr, 3); + parse = abUpdateParse(parse, discStr); + + String description = splitStr.length > 2 ? splitStr[2] : null; + costParts.add(new CostDiscard(splitStr[0], splitStr[1], description)); + } + + + while (parse.contains(sacStr)) { + sacCost = true; + String[] splitStr = abCostParse(parse, sacStr, 3); + parse = abUpdateParse(parse, sacStr); + + String description = splitStr.length > 2 ? splitStr[2] : null; + costParts.add(new CostSacrifice(splitStr[0], splitStr[1], description)); + } + + + while (parse.contains(exileStr)) { + String[] splitStr = abCostParse(parse, exileStr, 3); + parse = abUpdateParse(parse, exileStr); + + String description = splitStr.length > 2 ? splitStr[2] : null; + costParts.add(new CostExile(splitStr[0], splitStr[1], description, Constant.Zone.Battlefield)); + } + + + while (parse.contains(exileFromHandStr)) { + String[] splitStr = abCostParse(parse, exileFromHandStr, 3); + parse = abUpdateParse(parse, exileFromHandStr); + + String description = splitStr.length > 2 ? splitStr[2] : null; + costParts.add(new CostExile(splitStr[0], splitStr[1], description, Constant.Zone.Hand)); + } + + + while (parse.contains(exileFromGraveStr)) { + String[] splitStr = abCostParse(parse, exileFromGraveStr, 3); + parse = abUpdateParse(parse, exileFromGraveStr); + + String description = splitStr.length > 2 ? splitStr[2] : null; + costParts.add(new CostExile(splitStr[0], splitStr[1], description, Constant.Zone.Graveyard)); + } + + + while (parse.contains(exileFromTopStr)) { + String[] splitStr = abCostParse(parse, exileFromTopStr, 3); + parse = abUpdateParse(parse, exileFromTopStr); + + String description = splitStr.length > 2 ? splitStr[2] : null; + costParts.add(new CostExile(splitStr[0], splitStr[1], description, Constant.Zone.Library)); + } + + + while (parse.contains(returnStr)) { + String[] splitStr = abCostParse(parse, returnStr, 3); + parse = abUpdateParse(parse, returnStr); + + String description = splitStr.length > 2 ? splitStr[2] : null; + costParts.add(new CostReturn(splitStr[0], splitStr[1], description)); + } + + // These won't show up with multiples + if (parse.contains("Untap")) { + parse = parse.replace("Untap", "").trim(); + costParts.add(0, new CostUntap()); + } + + if (parse.contains("Q")) { + parse = parse.replace("Q", "").trim(); + costParts.add(0, new CostUntap()); + } + + if (parse.contains("T")) { + tapCost = true; + parse = parse.replace("T", "").trim(); + costParts.add(0, new CostTap()); + } + + String stripXCost = parse.replaceAll("X", ""); + + int amountX = parse.length() - stripXCost.length(); + + String mana = stripXCost.trim(); + if (mana.equals("")) + mana = "0"; + + if (amountX > 0 || !mana.equals("0")) + costParts.add(0, new CostMana(mana, amountX)); + } + + /** + *

abCostParse.

+ * + * @param parse a {@link java.lang.String} object. + * @param subkey a {@link java.lang.String} object. + * @param numParse a int. + * @return an array of {@link java.lang.String} objects. + */ + private String[] abCostParse(String parse, String subkey, int numParse) { + int startPos = parse.indexOf(subkey); + int endPos = parse.indexOf(">", startPos); + String str = parse.substring(startPos, endPos); + + str = str.replace(subkey, ""); + + String[] splitStr = str.split("/", numParse); + return splitStr; + } + + /** + *

abUpdateParse.

+ * + * @param parse a {@link java.lang.String} object. + * @param subkey a {@link java.lang.String} object. + * @return a {@link java.lang.String} object. + */ + private String abUpdateParse(String parse, String subkey) { + int startPos = parse.indexOf(subkey); + int endPos = parse.indexOf(">", startPos); + String str = parse.substring(startPos, endPos + 1); + return parse.replace(str, "").trim(); + } + + /** + *

changeCost.

+ * + * @param sa a {@link forge.card.spellability.SpellAbility} object. + */ + public void changeCost(SpellAbility sa) { + // TODO: Change where ChangeCost happens + for(CostPart part : costParts){ + if (part instanceof CostMana){ + CostMana costMana = (CostMana)part; + + if (getTotalMana() != "0") { // 11/15/10 use getTotalMana() to account for X reduction + String mana = getTotalMana(); + costMana.setAdjustedMana(AllZone.getGameAction().getSpellCostChange(sa, new ManaCost(mana)).toString()); + } + } + } + } + + /** + *

refundPaidCost.

+ * + * @param source a {@link forge.Card} object. + */ + public void refundPaidCost(Card source) { + // prereq: isUndoable is called first + for(CostPart part : costParts) + part.refund(source); + } + + /** + *

isUndoable.

+ * + * @return a boolean. + */ + public boolean isUndoable() { + for(CostPart part : costParts) + if (!part.isUndoable()) + return false; + + return true; + } + + /** + *

isReusuableResource.

+ * + * @return a boolean. + */ + public boolean isReusuableResource() { + for(CostPart part : costParts) + if (!part.isReusable()) + return false; + + return isAbility; + } + + /** + *

toString.

+ * + * @return a {@link java.lang.String} object. + */ + public String toString() { + if (isAbility) + return abilityToString(); + else + return spellToString(true); + } + + // maybe add a conversion method that turns the amounts into words 1=a(n), 2=two etc. + + /** + *

toStringAlt.

+ * + * @return a {@link java.lang.String} object. + */ + public String toStringAlt() { + return spellToString(false); + } + + /** + *

spellToString.

+ * + * @param bFlag a boolean. + * @return a {@link java.lang.String} object. + */ + private String spellToString(boolean bFlag) { + StringBuilder cost = new StringBuilder(); + boolean first = true; + + if (bFlag) + cost.append("As an additional cost to cast ").append(name).append(", "); + else{ + // usually no additional mana cost for spells + // only three Alliances cards have additional mana costs, but they are basically kicker/multikicker + /* + if (!getTotalMana().equals("0")) { + cost.append("pay ").append(getTotalMana()); + first = false; + } + */ + } + + for(CostPart part : costParts){ + if (part instanceof CostMana) + continue; + if (!first) + cost.append("and "); + cost.append(part.toString()).append(" "); + first = false; + } + + if (first) + return ""; + + if (bFlag) + cost.append(".").append("\n"); + + return cost.toString(); + } + + /** + *

abilityToString.

+ * + * @return a {@link java.lang.String} object. + */ + private String abilityToString() { + StringBuilder cost = new StringBuilder(); + boolean first = true; + + for(CostPart part : costParts){ + if (!first) + cost.append(", "); + cost.append(part.toString()); + first = false; + } + + if (first) // No costs, append 0 + cost.append("0"); + + cost.append(": "); + return cost.toString(); + } + + // TODO: If a Cost needs to pay more than 10 of something, fill this array as appropriate + /** Constant numNames="{zero, a, two, three, four, five, six, "{trunked} */ + private static final String[] numNames = {"zero", "a", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"}; + /** Constant vowelPattern */ + private static final Pattern vowelPattern = Pattern.compile("^[aeiou]", Pattern.CASE_INSENSITIVE); + + + public static String convertAmountTypeToWords(Integer i, String amount, String type){ + if (i == null) + return convertAmountTypeToWords(amount, type); + + return convertIntAndTypeToWords(i.intValue(), type); + } + + /** + *

convertIntAndTypeToWords.

+ * + * @param i a int. + * @param type a {@link java.lang.String} object. + * @return a {@link java.lang.String} object. + */ + public static String convertIntAndTypeToWords(int i, String type) { + StringBuilder sb = new StringBuilder(); + + if (i >= numNames.length) { + sb.append(i); + } else if (1 == i && vowelPattern.matcher(type).find()) + sb.append("an"); + else + sb.append(numNames[i]); + + sb.append(" "); + sb.append(type); + if (1 != i) + sb.append("s"); + + return sb.toString(); + } + + + public static String convertAmountTypeToWords(String amount, String type) { + StringBuilder sb = new StringBuilder(); + + sb.append(amount); + sb.append(" "); + sb.append(type); + + + return sb.toString(); + } +} diff --git a/src/main/java/forge/card/cost/CostDiscard.java b/src/main/java/forge/card/cost/CostDiscard.java new file mode 100644 index 00000000000..83540ed35a2 --- /dev/null +++ b/src/main/java/forge/card/cost/CostDiscard.java @@ -0,0 +1,190 @@ +package forge.card.cost; + +import forge.AllZone; +import forge.AllZoneUtil; +import forge.Card; +import forge.CardList; +import forge.CardListUtil; +import forge.Constant; +import forge.Player; +import forge.card.abilityFactory.AbilityFactory; +import forge.card.spellability.SpellAbility; + +public class CostDiscard extends CostPartWithList { + // Discard + + public CostDiscard(String amount, String type, String description){ + this.amount = amount; + this.type = type; + this.typeDescription = description; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Discard"); + + Integer i = convertAmount(); + + if (getThis()) { + sb.append(" ").append(type); + } + else if (type.equals("Hand")) { + sb.append(" your hand"); + } + else if (type.equals("LastDrawn")) { + sb.append(" last drawn card"); + } + else { + StringBuilder desc = new StringBuilder(); + + if (type.equals("Card") || type.equals("Random")) + desc.append("Card"); + else + desc.append(typeDescription == null ? type : typeDescription).append(" card"); + + sb.append(Cost.convertAmountTypeToWords(i, amount, desc.toString())); + + if (type.equals("Random")) + sb.append(" at random"); + } + return sb.toString(); + + } + + @Override + public void refund(Card source) { + // TODO Auto-generated method stub + + } + + @Override + public boolean canPay(SpellAbility ability, Card source, Player activator, Cost cost) { + CardList handList = AllZoneUtil.getPlayerHand(activator); + String type = getType(); + Integer amount = convertAmount(); + + if (getThis()) { + if (!AllZone.getZone(source).is(Constant.Zone.Hand)) + return false; + } else if (type.equals("Hand")) { + // this will always work + } else if (type.equals("LastDrawn")) { + Card c = activator.getLastDrawnCard(); + return handList.contains(c); + } else { + if (!type.equals("Random")) { + handList = handList.getValidCards(type.split(";"), activator, source); + } + if (amount != null && amount > handList.size()) { + // not enough cards in hand to pay + return false; + } + } + + return true; + } + + @Override + public void payAI(SpellAbility ability, Card source, Cost_Payment payment) { + Player activator = ability.getActivatingPlayer(); + for (Card c : list) + activator.discard(c, ability); + } + + @Override + public boolean payHuman(SpellAbility ability, Card source, Cost_Payment payment) { + Player activator = ability.getActivatingPlayer(); + CardList handList = AllZoneUtil.getPlayerHand(activator); + String discType = getType(); + String amount = getAmount(); + + if (getThis()) { + activator.discard(source, ability); + payment.setPaidManaPart(this, true); + } else if (discType.equals("Hand")) { + activator.discardHand(ability); + payment.setPaidManaPart(this, true); + } else if (discType.equals("LastDrawn")) { + if (handList.contains(activator.getLastDrawnCard())) { + activator.discard(activator.getLastDrawnCard(), ability); + payment.setPaidManaPart(this, true); + } + } else { + Integer c = convertAmount(); + + if (discType.equals("Random")) { + if (c == null){ + String sVar = source.getSVar(amount); + // Generalize this + if (sVar.equals("XChoice")){ + c = CostUtil.chooseXValue(source, handList.size()); + } + else{ + c = AbilityFactory.calculateAmount(source, amount, ability); + } + } + + activator.discardRandom(c, ability); + payment.setPaidManaPart(this, true); + } else { + String validType[] = discType.split(";"); + handList = handList.getValidCards(validType, activator, ability.getSourceCard()); + + if (c == null){ + String sVar = source.getSVar(amount); + // Generalize this + if (sVar.equals("XChoice")){ + c = CostUtil.chooseXValue(source, handList.size()); + } + else{ + c = AbilityFactory.calculateAmount(source, amount, ability); + } + } + + CostUtil.setInput(Cost_Input.input_discardCost(discType, handList, ability, payment, this, c)); + return false; + } + } + return true; + } + + @Override + public boolean decideAIPayment(SpellAbility ability, Card source, Cost_Payment payment) { + String type = getType(); + Player activator = ability.getActivatingPlayer(); + CardList hand = AllZoneUtil.getPlayerHand(activator); + resetList(); + if (type.equals("LastDrawn")){ + if (!hand.contains(activator.getLastDrawnCard())) + return false; + list.add(activator.getLastDrawnCard()); + } + + else if (getThis()){ + if (!hand.contains(source)) + return false; + + list.add(source); + } + + else if (type.equals("Hand")){ + list.addAll(hand); + } + + else{ + Integer c = convertAmount(); + if (c == null){ + c = AbilityFactory.calculateAmount(source, amount, ability); + } + + if (type.equals("Random")){ + list = CardListUtil.getRandomSubList(hand, c); + } + else{ + list = AllZone.getGameAction().AI_discardNumType(c, type.split(";"), ability); + } + } + return list != null; + } +} diff --git a/src/main/java/forge/card/cost/CostExile.java b/src/main/java/forge/card/cost/CostExile.java new file mode 100644 index 00000000000..63506f2ef9b --- /dev/null +++ b/src/main/java/forge/card/cost/CostExile.java @@ -0,0 +1,127 @@ +package forge.card.cost; + +import forge.AllZone; +import forge.AllZoneUtil; +import forge.Card; +import forge.CardList; +import forge.ComputerUtil; +import forge.Constant; +import forge.Player; +import forge.PlayerZone; +import forge.card.abilityFactory.AbilityFactory; +import forge.card.spellability.SpellAbility; + +public class CostExile extends CostPartWithList { + //Exile + //ExileFromHand + //ExileFromGraveyard + //ExileFromLibrary + + private String from = Constant.Zone.Battlefield; + + public String getFrom() { + return from; + } + + public CostExile(String amount, String type, String description, String from){ + this.amount = amount; + this.type = type; + this.typeDescription = description; + if (from != null) + this.from = from; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Exile "); + + if (getThis()) { + sb.append(type); + } + else{ + Integer i = convertAmount(); + String desc = typeDescription == null ? type : typeDescription; + + sb.append(Cost.convertAmountTypeToWords(i, amount, desc)); + } + + if (from.equals("Battlefield")){ + if (!getThis()) + sb.append(" you control"); + } + else{ + sb.append(" from your ").append(from); + } + + return sb.toString(); + } + + @Override + public void refund(Card source) { + // TODO Auto-generated method stub + + } + + @Override + public boolean canPay(SpellAbility ability, Card source, Player activator, Cost cost) { + PlayerZone zone = AllZone.getZone(getFrom(), activator); + if (!getThis()) { + CardList typeList = AllZoneUtil.getCardsInZone(zone); + + typeList = typeList.getValidCards(getType().split(";"), activator, source); + + Integer amount = convertAmount(); + if (amount != null && typeList.size() < amount) + return false; + } else if (!AllZoneUtil.isCardInZone(zone, source)) + return false; + + return true; + } + + @Override + public void payAI(SpellAbility ability, Card source, Cost_Payment payment) { + for (Card c : list) + AllZone.getGameAction().exile(c); + } + + @Override + public boolean payHuman(SpellAbility ability, Card source, Cost_Payment payment) { + String amount = getAmount(); + Integer c = convertAmount(); + CardList list = AllZoneUtil.getCardsInZone(getFrom(), ability.getActivatingPlayer()); + if (c == null){ + String sVar = source.getSVar(amount); + // Generalize this + if (sVar.equals("XChoice")){ + c = CostUtil.chooseXValue(source, list.size()); + } + else{ + c = AbilityFactory.calculateAmount(source, amount, ability); + } + } + if (getThis()) + CostUtil.setInput(Cost_Input.exileThis(ability, payment, this)); + else + CostUtil.setInput(Cost_Input.exileType(ability, this, getType(), payment, c)); + return false; + } + + @Override + public boolean decideAIPayment(SpellAbility ability, Card source, Cost_Payment payment) { + resetList(); + if (getThis()) + list.add(source); + else{ + Integer c = convertAmount(); + if (c == null){ + c = AbilityFactory.calculateAmount(source, amount, ability); + } + list = ComputerUtil.chooseExileFrom(getFrom(), getType(), source, ability.getTargetCard(), c); + if (list == null) + return false; + } + return true; + } +} diff --git a/src/main/java/forge/card/cost/CostMana.java b/src/main/java/forge/card/cost/CostMana.java new file mode 100644 index 00000000000..cd8c580e4d4 --- /dev/null +++ b/src/main/java/forge/card/cost/CostMana.java @@ -0,0 +1,104 @@ +package forge.card.cost; + +import com.google.common.base.Strings; + +import forge.Card; +import forge.ComputerUtil; +import forge.Player; +import forge.card.abilityFactory.AbilityFactory; +import forge.card.spellability.SpellAbility; + +public class CostMana extends CostPart { + //"Leftover" + private String mana = ""; + private int amountX = 0; + private String adjustedMana = ""; + + public String getMana() { + // Only used for Human to pay for non-X cost first + return mana; + } + + public void setMana(String sCost) { + mana = sCost; + } + + public boolean hasNoXManaCost() { + return amountX == 0; + } + + public int getXMana() { + return amountX; + } + + public void setXMana(int xCost) { + amountX = xCost; + } + + public String getAdjustedMana() { + return adjustedMana; + } + + public void setAdjustedMana(String adjustedMana) { + this.adjustedMana = adjustedMana; + } + + public CostMana(String mana, int amount){ + this.mana = mana.trim(); + this.amountX = amount; + this.isUndoable = true; + this.isReusable = true; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + + sb.append(Strings.repeat("X ", amountX)); + if (!mana.equals("0")) + sb.append(mana); + + return sb.toString().trim(); + } + + @Override + public void refund(Card source) { + // TODO Auto-generated method stub + + } + + @Override + public boolean canPay(SpellAbility ability, Card source, Player activator, Cost cost) { + // For now, this will always return true. But this should probably be checked at some point + return true; + } + + @Override + public void payAI(SpellAbility ability, Card source, Cost_Payment payment) { + ComputerUtil.payManaCost(ability); + } + + @Override + public boolean payHuman(SpellAbility ability, Card source, Cost_Payment payment) { + int manaToAdd = 0; + if (!hasNoXManaCost()) { + // if X cost is a defined value, other than xPaid + if (!source.getSVar("X").equals("Count$xPaid")) { + // this currently only works for things about Targeted object + manaToAdd = AbilityFactory.calculateAmount(source, "X", ability) * getXMana(); + } + } + if (!getMana().equals("0") || manaToAdd > 0) + CostUtil.setInput(Cost_Input.input_payMana(ability, payment, this, manaToAdd)); + else if (getXMana() > 0) + CostUtil.setInput(Cost_Input.input_payXMana(ability, payment, this, getXMana())); + + // We return false here because the Inputs set above should recall payment.payCosts() + return false; + } + + @Override + public boolean decideAIPayment(SpellAbility ability, Card source, Cost_Payment payment) { + return true; + } +} diff --git a/src/main/java/forge/card/cost/CostPart.java b/src/main/java/forge/card/cost/CostPart.java new file mode 100644 index 00000000000..bcb491215b5 --- /dev/null +++ b/src/main/java/forge/card/cost/CostPart.java @@ -0,0 +1,68 @@ +package forge.card.cost; + +import forge.Card; +import forge.Player; +import forge.card.spellability.SpellAbility; + +public abstract class CostPart { + protected boolean isReusable = false; + protected boolean isUndoable = false; + protected boolean optional = false; + protected String optionalType = null; + protected String amount = "1"; + protected String type = "Card"; + protected String typeDescription = null; + + public String getAmount() { + return amount; + } + + public String getType() { + return type; + } + + public boolean getThis() { + return type.equals("CARDNAME"); + } + + public String getTypeDescription() { + return typeDescription; + } + + public String getDescriptiveType(){ + return typeDescription == null ? type : typeDescription; + } + + public boolean isReusable(){ + return isReusable; + } + + public boolean isUndoable(){ + return isUndoable; + } + + public String getOptionalType() { + return optionalType; + } + + public void setOptionalType(String optionalType) { + this.optionalType = optionalType; + } + + public Integer convertAmount(){ + Integer i = null; + try{ + i = Integer.parseInt(amount); + }catch(NumberFormatException e){} + return i; + } + + public abstract boolean canPay(SpellAbility ability, Card source, Player activator, Cost cost); + + public abstract boolean decideAIPayment(SpellAbility ability, Card source, Cost_Payment payment); + public abstract void payAI(SpellAbility ability, Card source, Cost_Payment payment); + public abstract boolean payHuman(SpellAbility ability, Card source, Cost_Payment payment); + + public abstract String toString(); + public abstract void refund(Card source); +} diff --git a/src/main/java/forge/card/cost/CostPartWithList.java b/src/main/java/forge/card/cost/CostPartWithList.java new file mode 100644 index 00000000000..a1bb0ee1df9 --- /dev/null +++ b/src/main/java/forge/card/cost/CostPartWithList.java @@ -0,0 +1,12 @@ +package forge.card.cost; + +import forge.Card; +import forge.CardList; + +public abstract class CostPartWithList extends CostPart { + protected CardList list = null; + public CardList getList() { return list; } + public void setList(CardList setList) { list = setList; } + public void resetList() { list = new CardList(); } + public void addToList(Card c) { list.add(c); } +} diff --git a/src/main/java/forge/card/cost/CostPayLife.java b/src/main/java/forge/card/cost/CostPayLife.java new file mode 100644 index 00000000000..b256111231c --- /dev/null +++ b/src/main/java/forge/card/cost/CostPayLife.java @@ -0,0 +1,109 @@ +package forge.card.cost; + +import forge.AllZone; +import forge.Card; +import forge.GameActionUtil; +import forge.Player; +import forge.card.abilityFactory.AbilityFactory; +import forge.card.spellability.SpellAbility; + +public class CostPayLife extends CostPart { + private int lastPaidAmount = 0; + + public int getLastPaidAmount(){ + return lastPaidAmount; + } + + public void setLastPaidAmount(int paidAmount){ + lastPaidAmount = paidAmount; + } + + public CostPayLife(String amount){ + this.amount = amount; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Pay ").append(amount).append(" Life"); + return sb.toString(); + } + + @Override + public void refund(Card source) { + // Really should be activating player + source.getController().payLife(lastPaidAmount * -1, null); + } + + @Override + public boolean canPay(SpellAbility ability, Card source, Player activator, Cost cost) { + Integer amount = convertAmount(); + if (amount != null && !activator.canPayLife(amount)) { + return false; + } + + return true; + } + + @Override + public void payAI(SpellAbility ability, Card source, Cost_Payment payment) { + AllZone.getComputerPlayer().payLife(getLastPaidAmount(), null); + } + + @Override + public boolean payHuman(SpellAbility ability, Card source, Cost_Payment payment) { + String amount = getAmount(); + int life = ability.getActivatingPlayer().getLife(); + + Integer c = convertAmount(); + if (c == null){ + String sVar = source.getSVar(amount); + // Generalize this + if (sVar.equals("XChoice")){ + c = CostUtil.chooseXValue(source, life); + } + else{ + c = AbilityFactory.calculateAmount(source, amount, ability); + } + } + + StringBuilder sb = new StringBuilder(); + sb.append(source.getName()).append(" - Pay ").append(c).append(" Life?"); + + if (GameActionUtil.showYesNoDialog(source, sb.toString())) { + AllZone.getHumanPlayer().payLife(c, null); + setLastPaidAmount(c); + payment.setPaidManaPart(this, true); + } else { + payment.setCancel(true); + payment.getRequirements().finishPaying(); + return false; + } + return true; + } + + @Override + public boolean decideAIPayment(SpellAbility ability, Card source, Cost_Payment payment) { + Player activator = ability.getActivatingPlayer(); + + Integer c = convertAmount(); + if (c == null){ + String sVar = source.getSVar(amount); + // Generalize this + if (sVar.equals("XChoice")){ + // This decision should be locked in the AF and be calculable at this state + //c = chooseXValue(life); + // Figure out what to do here + } + else{ + c = AbilityFactory.calculateAmount(source, amount, ability); + } + } + if (!activator.canPayLife(c)){ + return false; + } + + setLastPaidAmount(c); + return true; + } +} diff --git a/src/main/java/forge/card/cost/CostPutCounter.java b/src/main/java/forge/card/cost/CostPutCounter.java new file mode 100644 index 00000000000..f981996efee --- /dev/null +++ b/src/main/java/forge/card/cost/CostPutCounter.java @@ -0,0 +1,80 @@ +package forge.card.cost; + +import forge.Card; +import forge.Counters; +import forge.Player; +import forge.card.abilityFactory.AbilityFactory; +import forge.card.spellability.SpellAbility; + +public class CostPutCounter extends CostPart { + // Put Counter doesn't really have a "Valid" portion of the cost + private Counters counter; + private int lastPaidAmount = 0; + + public Counters getCounter() { + return counter; + } + + public void setLastPaidAmount(int paidAmount){ + lastPaidAmount = paidAmount; + } + + public CostPutCounter(String amount, Counters counter){ + this.type = "CARDNAME"; + isReusable = true; + this.amount = amount; + this.counter = counter; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if (counter.getName().equals("Loyalty")) + sb.append("+").append(amount); + else { + sb.append("Put "); + Integer i = convertAmount(); + sb.append(Cost.convertAmountTypeToWords(i, amount, counter.getName())); + + sb.append(" on ").append(type); + } + return sb.toString(); + } + + @Override + public void refund(Card source) { + source.subtractCounter(counter, lastPaidAmount); + } + + @Override + public boolean canPay(SpellAbility ability, Card source, Player activator, Cost cost) { + // PutCounter can always be paid as a Cost + return true; + } + + @Override + public void payAI(SpellAbility ability, Card source, Cost_Payment payment) { + Integer c = convertAmount(); + if (c == null){ + c = AbilityFactory.calculateAmount(source, amount, ability); + } + source.addCounterFromNonEffect(getCounter(), c); + } + + @Override + public boolean payHuman(SpellAbility ability, Card source, Cost_Payment payment) { + Integer c = convertAmount(); + if (c == null){ + c = AbilityFactory.calculateAmount(source, amount, ability); + } + + source.addCounterFromNonEffect(getCounter(), c); + payment.setPaidManaPart(this, true); + return true; + } + + @Override + public boolean decideAIPayment(SpellAbility ability, Card source, Cost_Payment payment) { + return true; + } +} diff --git a/src/main/java/forge/card/cost/CostRemoveCounter.java b/src/main/java/forge/card/cost/CostRemoveCounter.java new file mode 100644 index 00000000000..d0970404b62 --- /dev/null +++ b/src/main/java/forge/card/cost/CostRemoveCounter.java @@ -0,0 +1,126 @@ +package forge.card.cost; + +import forge.Card; +import forge.Counters; +import forge.Player; +import forge.card.abilityFactory.AbilityFactory; +import forge.card.spellability.SpellAbility; + +public class CostRemoveCounter extends CostPart { + // SubCounter + + private Counters counter; + private int lastPaidAmount = 0; + + + public Counters getCounter() { + return counter; + } + + public void setLastPaidAmount(int paidAmount){ + lastPaidAmount = paidAmount; + } + + public CostRemoveCounter(String amount, Counters counter, String type, String description){ + isReusable = true; + this.amount = amount; + this.counter = counter; + + if (type != null) + this.type = type; + else + this.type = "CARDNAME"; + + this.typeDescription = description; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if (counter.getName().equals("Loyalty")) + sb.append("-").append(amount); + else { + sb.append("Remove "); + Integer i = convertAmount(); + sb.append(Cost.convertAmountTypeToWords(i, amount, counter.getName())); + + sb.append(" from ").append(typeDescription == null ? type : typeDescription); + } + return sb.toString(); + } + + @Override + public void refund(Card source) { + source.addCounterFromNonEffect(counter, lastPaidAmount); + } + + @Override + public boolean canPay(SpellAbility ability, Card source, Player activator, Cost cost) { + Counters c = getCounter(); + + Integer amount = convertAmount(); + if (amount != null && source.getCounters(c) - amount < 0) { + return false; + } + + return true; + } + + @Override + public void payAI(SpellAbility ability, Card source, Cost_Payment payment) { + Integer c = convertAmount(); + if (c == null){ + c = AbilityFactory.calculateAmount(source, amount, ability); + } + source.subtractCounter(getCounter(), c); + } + + @Override + public boolean payHuman(SpellAbility ability, Card source, Cost_Payment payment) { + String amount = getAmount(); + Counters type = getCounter(); + Integer c = convertAmount(); + int maxCounters = source.getCounters(type); + + if (amount.equals("All")){ + c = maxCounters; + } + else{ + if (c == null){ + String sVar = source.getSVar(amount); + // Generalize this + if (sVar.equals("XChoice")){ + c = CostUtil.chooseXValue(source, maxCounters); + } + else{ + c = AbilityFactory.calculateAmount(source, amount, ability); + } + } + } + + if (maxCounters >= c) { + source.setSVar("CostCountersRemoved", "Number$"+Integer.toString(c)); + source.subtractCounter(type, c); + setLastPaidAmount(c); + payment.setPaidManaPart(this, true); + } else { + payment.setCancel(true); + payment.getRequirements().finishPaying(); + return false; + } + return true; + } + + @Override + public boolean decideAIPayment(SpellAbility ability, Card source, Cost_Payment payment) { + Integer c = convertAmount(); + if (c == null){ + c = AbilityFactory.calculateAmount(source, amount, ability); + } + if (c > source.getCounters(getCounter())) { + System.out.println("Not enough " + getCounter() + " on " + source.getName()); + return false; + } + return true; + } +} diff --git a/src/main/java/forge/card/cost/CostReturn.java b/src/main/java/forge/card/cost/CostReturn.java new file mode 100644 index 00000000000..08a668b12e6 --- /dev/null +++ b/src/main/java/forge/card/cost/CostReturn.java @@ -0,0 +1,113 @@ +package forge.card.cost; + +import forge.AllZone; +import forge.AllZoneUtil; +import forge.Card; +import forge.CardList; +import forge.ComputerUtil; +import forge.Player; +import forge.card.abilityFactory.AbilityFactory; +import forge.card.spellability.SpellAbility; + +public class CostReturn extends CostPartWithList { + // Return + + public CostReturn(String amount, String type, String description){ + this.amount = amount; + this.type = type; + this.typeDescription = description; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Return "); + + Integer i = convertAmount(); + String pronoun = "its"; + + if (getThis()) + sb.append(type); + else { + String desc = typeDescription == null ? type : typeDescription; + if (i != null){ + sb.append(Cost.convertIntAndTypeToWords(i, desc)); + if (i > 1) + pronoun = "their"; + } + else + sb.append(Cost.convertAmountTypeToWords(amount, desc)); + + sb.append(" you control"); + } + sb.append(" to ").append(pronoun).append(" owner's hand"); + return sb.toString(); + } + + @Override + public void refund(Card source) { + // TODO Auto-generated method stub + + } + + @Override + public boolean canPay(SpellAbility ability, Card source, Player activator, Cost cost) { + if (!getThis()) { + CardList typeList = AllZoneUtil.getPlayerCardsInPlay(activator); + typeList = typeList.getValidCards(getType().split(";"), activator, source); + + Integer amount = convertAmount(); + if (amount != null && typeList.size() < amount) + return false; + } else if (!AllZoneUtil.isCardInPlay(source)) + return false; + + return true; + } + + @Override + public void payAI(SpellAbility ability, Card source, Cost_Payment payment) { + for (Card c : list) + AllZone.getGameAction().moveToHand(c); + } + + @Override + public boolean payHuman(SpellAbility ability, Card source, Cost_Payment payment) { + String amount = getAmount(); + Integer c = convertAmount(); + Player activator = ability.getActivatingPlayer(); + CardList list = AllZoneUtil.getPlayerCardsInPlay(activator); + if (c == null) { + String sVar = source.getSVar(amount); + // Generalize this + if (sVar.equals("XChoice")) { + c = CostUtil.chooseXValue(source, list.size()); + } else { + c = AbilityFactory.calculateAmount(source, amount, ability); + } + } + if (getThis()) + CostUtil.setInput(Cost_Input.returnThis(ability, payment, this)); + else + CostUtil.setInput(Cost_Input.returnType(ability, getType(), payment, this, c)); + return false; + } + + @Override + public boolean decideAIPayment(SpellAbility ability, Card source, Cost_Payment payment) { + resetList(); + if (getThis()) + list.add(source); + else{ + Integer c = convertAmount(); + if (c == null){ + c = AbilityFactory.calculateAmount(source, amount, ability); + } + + list = ComputerUtil.chooseReturnType(getType(), source, ability.getTargetCard(), c); + if (list == null) + return false; + } + return true; + } +} diff --git a/src/main/java/forge/card/cost/CostSacrifice.java b/src/main/java/forge/card/cost/CostSacrifice.java new file mode 100644 index 00000000000..42abd25058f --- /dev/null +++ b/src/main/java/forge/card/cost/CostSacrifice.java @@ -0,0 +1,132 @@ +package forge.card.cost; + +import forge.AllZone; +import forge.AllZoneUtil; +import forge.Card; +import forge.CardList; +import forge.ComputerUtil; +import forge.Player; +import forge.card.abilityFactory.AbilityFactory; +import forge.card.spellability.SpellAbility; + +public class CostSacrifice extends CostPartWithList { + + public CostSacrifice(String amount, String type, String description){ + this.amount = amount; + this.type = type; + this.typeDescription = description; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Sacrifice "); + + Integer i = convertAmount(); + + if (getThis()) + sb.append(type); + else{ + String desc = typeDescription == null ? type : typeDescription; + if (i != null) + sb.append(Cost.convertIntAndTypeToWords(i, desc)); + else + sb.append(Cost.convertAmountTypeToWords(amount, desc)); + } + return sb.toString(); + } + + @Override + public void refund(Card source) { + // TODO Auto-generated method stub + + } + + @Override + public boolean canPay(SpellAbility ability, Card source, Player activator, Cost cost) { + // You can always sac all + if (!getThis()) { + CardList typeList = AllZoneUtil.getPlayerCardsInPlay(activator); + typeList = typeList.getValidCards(getType().split(";"), activator, source); + + Integer amount = convertAmount(); + + if (amount != null && typeList.size() < amount) + return false; + + // If amount is null, it's either "ALL" or "X" + // if X is defined, it needs to be calculated and checked, if X is choice, it can be Paid even if it's 0 + } else if (!AllZoneUtil.isCardInPlay(source)) + return false; + + return true; + } + + @Override + public void payAI(SpellAbility ability, Card source, Cost_Payment payment) { + for (Card c : list) + AllZone.getGameAction().sacrifice(c); + } + + @Override + public boolean payHuman(SpellAbility ability, Card source, Cost_Payment payment) { + String amount = getAmount(); + String type = getType(); + Player activator = ability.getActivatingPlayer(); + CardList list = AllZoneUtil.getPlayerCardsInPlay(activator); + list = list.getValidCards(type.split(";"), activator, source); + + if (getThis()){ + CostUtil.setInput(Cost_Input.sacrificeThis(ability, payment, this)); + } + else if (amount.equals("All")){ + Cost_Input.sacrificeAll(ability, payment, this, list); + return true; + } + else{ + Integer c = convertAmount(); + if (c == null){ + String sVar = source.getSVar(amount); + // Generalize this + if (sVar.equals("XChoice")){ + c = CostUtil.chooseXValue(source, list.size()); + } + else{ + c = AbilityFactory.calculateAmount(source, amount, ability); + } + } + + CostUtil.setInput(Cost_Input.sacrificeFromList(ability, payment, this, list, c)); + } + + return false; + } + + @Override + public boolean decideAIPayment(SpellAbility ability, Card source, Cost_Payment payment) { + resetList(); + Player activator = ability.getActivatingPlayer(); + if (getThis()){ + list.add(source); + } + else if (amount.equals("All")) { + CardList typeList = AllZoneUtil.getPlayerCardsInPlay(activator); + typeList = typeList.getValidCards(type.split(","), activator, source); + // Does the AI want to use Sacrifice All? + return false; + } else{ + Integer c = convertAmount(); + if (c == null){ + if (source.getSVar(amount).equals("XChoice")){ + return false; + } + + c = AbilityFactory.calculateAmount(source, amount, ability); + } + list = ComputerUtil.chooseSacrificeType(type, source, ability.getTargetCard(), c); + if (list == null) + return false; + } + return true; + } +} diff --git a/src/main/java/forge/card/cost/CostTap.java b/src/main/java/forge/card/cost/CostTap.java new file mode 100644 index 00000000000..2998e8d877c --- /dev/null +++ b/src/main/java/forge/card/cost/CostTap.java @@ -0,0 +1,47 @@ +package forge.card.cost; + +import forge.Card; +import forge.Player; +import forge.card.spellability.SpellAbility; + +public class CostTap extends CostPart { + public CostTap(){ + isReusable = true; + isUndoable = true; + } + + @Override + public String toString() { + return "Tap"; + } + + @Override + public void refund(Card source) { + source.untap(); + } + + @Override + public boolean canPay(SpellAbility ability, Card source, Player activator, Cost cost) { + return !(source.isTapped() || source.isSick()); + } + + @Override + public void payAI(SpellAbility ability, Card source, Cost_Payment payment) { + source.tap(); + } + + @Override + public boolean payHuman(SpellAbility ability, Card source, Cost_Payment payment) { + //if (!canPay(ability, source, ability.getActivatingPlayer(), payment.getCost())) + // return false; + + source.tap(); + payment.setPaidManaPart(this, true); + return true; + } + + @Override + public boolean decideAIPayment(SpellAbility ability, Card source, Cost_Payment payment) { + return true; + } +} diff --git a/src/main/java/forge/card/cost/CostTapType.java b/src/main/java/forge/card/cost/CostTapType.java new file mode 100644 index 00000000000..e528538814a --- /dev/null +++ b/src/main/java/forge/card/cost/CostTapType.java @@ -0,0 +1,113 @@ +package forge.card.cost; + +import forge.AllZoneUtil; +import forge.Card; +import forge.CardList; +import forge.ComputerUtil; +import forge.Player; +import forge.card.abilityFactory.AbilityFactory; +import forge.card.spellability.SpellAbility; + +public class CostTapType extends CostPartWithList { + public CostTapType(String amount, String type, String description){ + list = new CardList(); + isReusable = true; + this.amount = amount; + this.type = type; + this.typeDescription = description; + } + + public String getDescription(){ + return typeDescription == null ? type : typeDescription; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Tap "); + + Integer i = convertAmount(); + String desc = getDescription(); + + sb.append(Cost.convertAmountTypeToWords(i, amount, "untapped " + desc)); + + sb.append(" you control"); + + return sb.toString(); + } + + public void addToTappedList(Card c) { + list.add(c); + } + + @Override + public void refund(Card source) { + for (Card c : list){ + c.untap(); + } + + list.clear(); + } + + @Override + public boolean canPay(SpellAbility ability, Card source, Player activator, Cost cost) { + CardList typeList = AllZoneUtil.getPlayerCardsInPlay(activator); + + typeList = typeList.getValidCards(getType().split(";"), activator, source); + + if (cost.getTap()) + typeList.remove(source); + typeList = typeList.filter(AllZoneUtil.untapped); + + Integer amount = convertAmount(); + if (typeList.size() == 0 || (amount != null && typeList.size() < amount)) + return false; + + return true; + } + + @Override + public void payAI(SpellAbility ability, Card source, Cost_Payment payment) { + for (Card c : list) + c.tap(); + } + + @Override + public boolean payHuman(SpellAbility ability, Card source, Cost_Payment payment) { + CardList typeList = AllZoneUtil.getPlayerCardsInPlay(source.getController()); + typeList = typeList.getValidCards(getType().split(";"), ability.getActivatingPlayer(), ability.getSourceCard()); + String amount = getAmount(); + Integer c = convertAmount(); + if (c == null){ + String sVar = source.getSVar(amount); + // Generalize this + if (sVar.equals("XChoice")){ + c = CostUtil.chooseXValue(source, typeList.size()); + } + else{ + c = AbilityFactory.calculateAmount(source, amount, ability); + } + } + + CostUtil.setInput(Cost_Input.input_tapXCost(this, typeList, ability, payment, c)); + return false; + } + + @Override + public boolean decideAIPayment(SpellAbility ability, Card source, Cost_Payment payment) { + boolean tap = payment.getCost().getTap(); + Integer c = convertAmount(); + if (c == null){ + // Determine Amount + } + + list = ComputerUtil.chooseTapType(getType(), source, tap, c); + + if (list == null) { + System.out.println("Couldn't find a valid card to tap for: " + source.getName()); + return false; + } + + return true; + } +} diff --git a/src/main/java/forge/card/cost/CostUntap.java b/src/main/java/forge/card/cost/CostUntap.java new file mode 100644 index 00000000000..632b558b228 --- /dev/null +++ b/src/main/java/forge/card/cost/CostUntap.java @@ -0,0 +1,47 @@ +package forge.card.cost; + +import forge.Card; +import forge.Player; +import forge.card.spellability.SpellAbility; + +public class CostUntap extends CostPart { + public CostUntap(){ + isReusable = true; + isUndoable = true; + } + + @Override + public String toString() { + return "Untap"; + } + + @Override + public void refund(Card source) { + source.tap(); + } + + @Override + public boolean canPay(SpellAbility ability, Card source, Player activator, Cost cost) { + return !(source.isUntapped() || source.isSick()); + } + + @Override + public void payAI(SpellAbility ability, Card source, Cost_Payment payment) { + source.untap(); + } + + @Override + public boolean payHuman(SpellAbility ability, Card source, Cost_Payment payment) { + //if (!canPay(ability, source, ability.getActivatingPlayer(), payment.getCost())) + // return false; + + source.untap(); + payment.setPaidManaPart(this, true); + return true; + } + + @Override + public boolean decideAIPayment(SpellAbility ability, Card source, Cost_Payment payment) { + return true; + } +} diff --git a/src/main/java/forge/card/cost/CostUtil.java b/src/main/java/forge/card/cost/CostUtil.java new file mode 100644 index 00000000000..517f9c676c0 --- /dev/null +++ b/src/main/java/forge/card/cost/CostUtil.java @@ -0,0 +1,153 @@ +package forge.card.cost; + +import java.util.Random; + +import forge.AllZone; +import forge.AllZoneUtil; +import forge.Card; +import forge.CardList; +import forge.ComputerUtil; +import forge.Counters; +import forge.card.abilityFactory.AbilityFactory; +import forge.card.spellability.SpellAbility; +import forge.gui.GuiUtils; +import forge.gui.input.Input; + +public class CostUtil { + static private Random r = new Random(); + static private double P1P1Percent = .1; + static private double OtherPercent = .25; + + static public boolean checkSacrificeCost(Cost cost, Card source){ + for(CostPart part : cost.getCostParts()){ + if (part instanceof CostSacrifice){ + CostSacrifice sac = (CostSacrifice)part; + + String type = sac.getType(); + CardList typeList = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer()); + typeList = typeList.getValidCards(type.split(","), source.getController(), source); + if (ComputerUtil.getCardPreference(source, "SacCost", typeList) == null) + return false; + } + } + return true; + } + + static public boolean checkCreatureSacrificeCost(Cost cost, Card source){ + for(CostPart part : cost.getCostParts()){ + if (part instanceof CostSacrifice){ + CostSacrifice sac = (CostSacrifice)part; + if (sac.getThis() && source.isCreature()) + return false; + } + } + return true; + } + + static public boolean checkLifeCost(Cost cost, Card source, int remainingLife){ + for(CostPart part : cost.getCostParts()){ + if (part instanceof CostPayLife){ + CostPayLife payLife = (CostPayLife)part; + if (AllZone.getComputerPlayer().getLife() - payLife.convertAmount() < remainingLife) + return false; + } + } + return true; + } + + static public boolean checkDiscardCost(Cost cost, Card source){ + for(CostPart part : cost.getCostParts()){ + if (part instanceof CostDiscard) + return false; + } + return true; + } + + static public boolean checkRemoveCounterCost(Cost cost, Card source){ + for(CostPart part : cost.getCostParts()){ + if (part instanceof CostRemoveCounter){ + CostRemoveCounter remCounter = (CostRemoveCounter)part; + + // A card has a 25% chance per counter to be able to pass through here + // 4+ counters will always pass. 0 counters will never + Counters type = remCounter.getCounter(); + double percent = type.name().equals("P1P1") ? P1P1Percent : OtherPercent; + int currentNum = source.getCounters(type); + + double chance = percent * (currentNum / part.convertAmount()); + if (chance <= r.nextFloat()) + return false; + } + } + + return true; + } + + static public boolean checkAddM1M1CounterCost(Cost cost, Card source){ + for(CostPart part : cost.getCostParts()){ + if (part instanceof CostPutCounter){ + CostPutCounter addCounter = (CostPutCounter)part; + Counters type = addCounter.getCounter(); + + if (type.equals(Counters.M1M1)) + return false; + } + } + + return true; + } + + static public boolean hasDiscardHandCost(Cost cost){ + for(CostPart part : cost.getCostParts()){ + if (part instanceof CostDiscard){ + CostDiscard disc = (CostDiscard)part; + if (disc.getType().equals("Hand")) + return true; + } + } + return false; + } + + static public Integer determineAmount(CostPart part, Card source, SpellAbility ability, int maxChoice){ + String amount = part.getAmount(); + Integer c = part.convertAmount(); + if (c == null){ + String sVar = source.getSVar(amount); + // Generalize this + if (sVar.equals("XChoice")){ + c = CostUtil.chooseXValue(source, maxChoice); + } + else{ + c = AbilityFactory.calculateAmount(source, amount, ability); + } + } + return c; + } + + static public int chooseXValue(Card card, int maxValue){ + String chosen = card.getSVar("ChosenX"); + if (chosen.length() > 0){ + return AbilityFactory.calculateAmount(card, "ChosenX", null); + } + + Integer[] choiceArray = new Integer[maxValue+1]; + for(int i = 0; i < choiceArray.length; i++){ + choiceArray[i] = i; + } + Object o = GuiUtils.getChoice(card.toString() + " - Choose a Value for X", choiceArray); + int chosenX = (Integer)o; + card.setSVar("ChosenX", "Number$"+Integer.toString(chosenX)); + + return chosenX; + } + + /** + *

setInput.

+ * + * @param in a {@link forge.gui.input.Input} object. + */ + static public void setInput(Input in) { + // Just a shortcut.. + AllZone.getInputControl().setInput(in, true); + } +} diff --git a/src/main/java/forge/card/cost/Cost_Input.java b/src/main/java/forge/card/cost/Cost_Input.java new file mode 100644 index 00000000000..becc4f9e85e --- /dev/null +++ b/src/main/java/forge/card/cost/Cost_Input.java @@ -0,0 +1,678 @@ +package forge.card.cost; + +import javax.swing.JOptionPane; + +import forge.AllZone; +import forge.AllZoneUtil; +import forge.ButtonUtil; +import forge.Card; +import forge.CardList; +import forge.Constant; +import forge.Phase; +import forge.Player; +import forge.PlayerZone; +import forge.card.mana.ManaCost; +import forge.card.spellability.SpellAbility; +import forge.gui.input.Input; +import forge.gui.input.Input_PayManaCostUtil; + +public class Cost_Input { + // ****************************************************************************** + // *********** Inputs used by Cost_Payment below here *************************** + // ****************************************************************************** + + /** + *

input_payMana.

+ * + * @param sa a {@link forge.card.spellability.SpellAbility} object. + * @param payment a {@link forge.card.cost.Cost_Payment} object. + * @param manaToAdd a int. + * @return a {@link forge.gui.input.Input} object. + */ + public static Input input_payMana(final SpellAbility sa, final Cost_Payment payment, final CostMana costMana, final int manaToAdd) { + final ManaCost manaCost; + + if (Phase.getGameBegins() == 1) { + if (sa.getSourceCard().isCopiedSpell() && sa.isSpell()) { + manaCost = new ManaCost("0"); + } else { + String mana = costMana.getMana(); + manaCost = new ManaCost(mana); + manaCost.increaseColorlessMana(manaToAdd); + } + } else { + System.out.println("Is input_payMana ever called when the Game isn't in progress?"); + manaCost = new ManaCost(sa.getManaCost()); + } + + Input payMana = new Input() { + private ManaCost mana = manaCost; + private static final long serialVersionUID = 3467312982164195091L; + + private final String originalManaCost = costMana.getMana(); + + private int phyLifeToLose = 0; + + private void resetManaCost() { + mana = new ManaCost(originalManaCost); + phyLifeToLose = 0; + } + + @Override + public void selectCard(Card card, PlayerZone zone) { + // prevent cards from tapping themselves if ability is a tapability, although it should already be tapped + if (sa.getSourceCard().equals(card) && sa.isTapAbility()) { + return; + } + + mana = Input_PayManaCostUtil.activateManaAbility(sa, card, mana); + + if (mana.isPaid()) + done(); + else if (AllZone.getInputControl().getInput() == this) + showMessage(); + } + + @Override + public void selectPlayer(Player player) { + if (player.isHuman()) { + if (manaCost.payPhyrexian()) { + phyLifeToLose += 2; + } + + showMessage(); + } + } + + private void done() { + Card source = sa.getSourceCard(); + if (phyLifeToLose > 0) + AllZone.getHumanPlayer().payLife(phyLifeToLose, source); + source.setColorsPaid(mana.getColorsPaid()); + source.setSunburstValue(mana.getSunburst()); + resetManaCost(); + stop(); + + if (costMana.hasNoXManaCost() || manaToAdd > 0){ + payment.paidCost(costMana); + } + else{ + source.setXManaCostPaid(0); + CostUtil.setInput(input_payXMana(sa, payment, costMana, costMana.getXMana())); + } + + } + + @Override + public void selectButtonCancel() { + stop(); + resetManaCost(); + payment.cancelCost(); + AllZone.getHumanBattlefield().updateObservers(); + } + + @Override + public void showMessage() { + ButtonUtil.enableOnlyCancel(); + String displayMana = mana.toString().replace("X", "").trim(); + AllZone.getDisplay().showMessage("Pay Mana Cost: " + displayMana); + + StringBuilder msg = new StringBuilder("Pay Mana Cost: " + displayMana); + if (phyLifeToLose > 0) { + msg.append(" ("); + msg.append(phyLifeToLose); + msg.append(" life paid for phyrexian mana)"); + } + + if (mana.containsPhyrexianMana()) { + msg.append("\n(Click on your life total to pay life for phyrexian mana.)"); + } + + AllZone.getDisplay().showMessage(msg.toString()); + if (mana.isPaid()) + done(); + } + }; + return payMana; + } + + /** + *

input_payXMana.

+ * @param sa a {@link forge.card.spellability.SpellAbility} object. + * @param payment a {@link forge.card.cost.Cost_Payment} object. + * @param costMana TODO + * @param numX a int. + * + * @return a {@link forge.gui.input.Input} object. + */ + public static Input input_payXMana(final SpellAbility sa, final Cost_Payment payment, final CostMana costMana, final int numX) { + Input payX = new Input() { + private static final long serialVersionUID = -6900234444347364050L; + int xPaid = 0; + ManaCost manaCost = new ManaCost(Integer.toString(numX)); + + @Override + public void showMessage() { + if (manaCost.toString().equals(Integer.toString(numX))) // Can only cancel if partially paid an X value + ButtonUtil.enableAll(); + else + ButtonUtil.enableOnlyCancel(); + + AllZone.getDisplay().showMessage("Pay X Mana Cost for " + sa.getSourceCard().getName() + "\n" + xPaid + " Paid so far."); + } + + // selectCard + @Override + public void selectCard(Card card, PlayerZone zone) { + if (sa.getSourceCard().equals(card) && sa.isTapAbility()) { + // this really shouldn't happen but just in case + return; + } + + manaCost = Input_PayManaCostUtil.activateManaAbility(sa, card, manaCost); + if (manaCost.isPaid()) { + manaCost = new ManaCost(Integer.toString(numX)); + xPaid++; + } + + if (AllZone.getInputControl().getInput() == this) + showMessage(); + } + + @Override + public void selectButtonCancel() { + stop(); + payment.cancelCost(); + AllZone.getHumanBattlefield().updateObservers(); + } + + @Override + public void selectButtonOK() { + stop(); + payment.getCard().setXManaCostPaid(xPaid); + payment.paidCost(costMana); + } + + }; + + return payX; + } + + /** + *

input_discardCost.

+ * @param discType a {@link java.lang.String} object. + * @param handList a {@link forge.CardList} object. + * @param sa a {@link forge.card.spellability.SpellAbility} object. + * @param payment a {@link forge.card.cost.Cost_Payment} object. + * @param part TODO + * @param nNeeded a int. + * + * @return a {@link forge.gui.input.Input} object. + */ + public static Input input_discardCost(final String discType, final CardList handList, SpellAbility sa, final Cost_Payment payment, final CostPart part, final int nNeeded) { + final SpellAbility sp = sa; + Input target = new Input() { + private static final long serialVersionUID = -329993322080934435L; + + int nDiscard = 0; + + @Override + public void showMessage() { + boolean any = discType.equals("Any") ? true : false; + if (AllZone.getHumanHand().size() == 0) stop(); + StringBuilder type = new StringBuilder(""); + if (any || !discType.equals("Card")) { + type.append(" ").append(discType); + } + StringBuilder sb = new StringBuilder(); + sb.append("Select "); + if (any) { + sb.append("any "); + } else { + sb.append("a ").append(type.toString()).append(" "); + } + sb.append("card to discard."); + if (nNeeded > 1) { + sb.append(" You have "); + sb.append(nNeeded - nDiscard); + sb.append(" remaining."); + } + AllZone.getDisplay().showMessage(sb.toString()); + ButtonUtil.enableOnlyCancel(); + } + + @Override + public void selectButtonCancel() { + cancel(); + } + + @Override + public void selectCard(Card card, PlayerZone zone) { + if (zone.is(Constant.Zone.Hand) && handList.contains(card)) { + // send in CardList for Typing + card.getController().discard(card, sp); + handList.remove(card); + nDiscard++; + + //in case no more cards in hand + if (nDiscard == nNeeded) + done(); + else if (AllZone.getHumanHand().size() == 0) // this really shouldn't happen + cancel(); + else + showMessage(); + } + } + + public void cancel() { + stop(); + payment.cancelCost(); + } + + public void done() { + stop(); + payment.paidCost(part); + } + }; + + return target; + }//input_discard() + + /** + *

sacrificeThis.

+ * + * @param sa a {@link forge.card.spellability.SpellAbility} object. + * @param payment a {@link forge.card.cost.Cost_Payment} object. + * @param part TODO + * @return a {@link forge.gui.input.Input} object. + */ + public static Input sacrificeThis(final SpellAbility sa, final Cost_Payment payment, final CostPart part) { + Input target = new Input() { + private static final long serialVersionUID = 2685832214519141903L; + + @Override + public void showMessage() { + Card card = sa.getSourceCard(); + if (card.getController().isHuman() && AllZoneUtil.isCardInPlay(card)) { + StringBuilder sb = new StringBuilder(); + sb.append(card.getName()); + sb.append(" - Sacrifice?"); + Object[] possibleValues = {"Yes", "No"}; + Object choice = JOptionPane.showOptionDialog(null, sb.toString(), card.getName() + " - Cost", + JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, + null, possibleValues, possibleValues[0]); + if (choice.equals(0)) { + payment.getAbility().addCostToHashList(card, "Sacrificed"); + AllZone.getGameAction().sacrifice(card); + stop(); + payment.paidCost(part); + } else { + stop(); + payment.cancelCost(); + } + } + } + }; + + return target; + }//input_sacrifice() + + /** + *

sacrificeType.

+ * @param sa a {@link forge.card.spellability.SpellAbility} object. + * @param payment a {@link forge.card.cost.Cost_Payment} object. + * @param part TODO + * @param typeList TODO + * @param num TODO + * + * @return a {@link forge.gui.input.Input} object. + */ + public static Input sacrificeFromList(final SpellAbility sa, final Cost_Payment payment, final CostSacrifice part, final CardList typeList, final int nNeeded) { + Input target = new Input() { + private static final long serialVersionUID = 2685832214519141903L; + private int nSacrifices = 0; + + @Override + public void showMessage() { + StringBuilder msg = new StringBuilder("Sacrifice "); + int nLeft = nNeeded - nSacrifices; + msg.append(nLeft).append(" "); + msg.append(part.getDescriptiveType()); + if (nLeft > 1) { + msg.append("s"); + } + + AllZone.getDisplay().showMessage(msg.toString()); + ButtonUtil.enableOnlyCancel(); + } + + @Override + public void selectButtonCancel() { + cancel(); + } + + @Override + public void selectCard(Card card, PlayerZone zone) { + if (typeList.contains(card)) { + nSacrifices++; + payment.getAbility().addCostToHashList(card, "Sacrificed"); + AllZone.getGameAction().sacrifice(card); + typeList.remove(card); + //in case nothing else to sacrifice + if (nSacrifices == nNeeded) + done(); + else if (typeList.size() == 0) // this really shouldn't happen + cancel(); + else + showMessage(); + } + } + + public void done() { + stop(); + payment.paidCost(part); + } + + public void cancel() { + stop(); + payment.cancelCost(); + } + }; + + return target; + }//sacrificeType() + + /** + *

sacrificeAllType.

+ * + * @param sa a {@link forge.card.spellability.SpellAbility} object. + * @param payment a {@link forge.card.cost.Cost_Payment} object. + * @param part TODO + * @param typeList TODO + */ + public static void sacrificeAll(final SpellAbility sa, final Cost_Payment payment, CostPart part, CardList typeList) { + // TODO Ask First + for (Card card : typeList) { + payment.getAbility().addCostToHashList(card, "Sacrificed"); + AllZone.getGameAction().sacrifice(card); + } + + payment.setPaidManaPart(part, true); + } + + /** + *

exileThis.

+ * + * @param sa a {@link forge.card.spellability.SpellAbility} object. + * @param payment a {@link forge.card.cost.Cost_Payment} object. + * @param costExile TODO + * @return a {@link forge.gui.input.Input} object. + */ + public static Input exileThis(final SpellAbility sa, final Cost_Payment payment, final CostExile part) { + Input target = new Input() { + private static final long serialVersionUID = 678668673002725001L; + + @Override + public void showMessage() { + Card card = sa.getSourceCard(); + if (sa.getActivatingPlayer().isHuman() && AllZoneUtil.isCardInZone(AllZone.getZone(part.getFrom(), sa.getActivatingPlayer()), card)) { + StringBuilder sb = new StringBuilder(); + sb.append(card.getName()); + sb.append(" - Exile?"); + Object[] possibleValues = {"Yes", "No"}; + Object choice = JOptionPane.showOptionDialog(null, sb.toString(), card.getName() + " - Cost", + JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, + null, possibleValues, possibleValues[0]); + if (choice.equals(0)) { + payment.getAbility().addCostToHashList(card, "Exiled"); + AllZone.getGameAction().exile(card); + stop(); + payment.paidCost(part); + } else { + stop(); + payment.cancelCost(); + } + } + } + }; + + return target; + }//input_exile() + + + /** + *

exileType.

+ * + * @param sa a {@link forge.card.spellability.SpellAbility} object. + * @param costExile TODO + * @param type a {@link java.lang.String} object. + * @param payment a {@link forge.card.cost.Cost_Payment} object. + * @return a {@link forge.gui.input.Input} object. + */ + public static Input exileType(final SpellAbility sa, final CostExile part, final String type, final Cost_Payment payment, final int nNeeded) { + Input target = new Input() { + private static final long serialVersionUID = 1403915758082824694L; + + private CardList typeList; + private int nExiles = 0; + + @Override + public void showMessage() { + StringBuilder msg = new StringBuilder("Exile "); + int nLeft = nNeeded - nExiles; + msg.append(nLeft).append(" "); + msg.append(type); + if (nLeft > 1) { + msg.append("s"); + } + + typeList = AllZoneUtil.getPlayerCardsInPlay(sa.getSourceCard().getController()); + typeList = typeList.getValidCards(type.split(";"), sa.getActivatingPlayer(), sa.getSourceCard()); + AllZone.getDisplay().showMessage(msg.toString()); + ButtonUtil.enableOnlyCancel(); + } + + @Override + public void selectButtonCancel() { + cancel(); + } + + @Override + public void selectCard(Card card, PlayerZone zone) { + if (typeList.contains(card)) { + nExiles++; + payment.getAbility().addCostToHashList(card, "Exiled"); + AllZone.getGameAction().exile(card); + typeList.remove(card); + //in case nothing else to exile + if (nExiles == nNeeded) + done(); + else if (typeList.size() == 0) // this really shouldn't happen + cancel(); + else + showMessage(); + } + } + + public void done() { + stop(); + payment.paidCost(part); + } + + public void cancel() { + stop(); + payment.cancelCost(); + } + }; + + return target; + }//exileType() + + /** + *

input_tapXCost.

+ * + * @param nCards a int. + * @param cardType a {@link java.lang.String} object. + * @param cardList a {@link forge.CardList} object. + * @param sa a {@link forge.card.spellability.SpellAbility} object. + * @param payment a {@link forge.card.cost.Cost_Payment} object. + * @return a {@link forge.gui.input.Input} object. + */ + public static Input input_tapXCost(final CostTapType tapType, final CardList cardList, SpellAbility sa, final Cost_Payment payment, final int nCards) { + Input target = new Input() { + + private static final long serialVersionUID = 6438988130447851042L; + int nTapped = 0; + + @Override + public void showMessage() { + if (cardList.size() == 0) stop(); + + int left = nCards - nTapped; + AllZone.getDisplay().showMessage("Select a " + tapType.getDescription() + " to tap (" + left + " left)"); + ButtonUtil.enableOnlyCancel(); + } + + @Override + public void selectButtonCancel() { + cancel(); + } + + @Override + public void selectCard(Card card, PlayerZone zone) { + if (zone.is(Constant.Zone.Battlefield) && cardList.contains(card) && card.isUntapped()) { + // send in CardList for Typing + card.tap(); + tapType.addToList(card); + cardList.remove(card); + payment.getAbility().addCostToHashList(card, "Tapped"); + nTapped++; + + if (nTapped == nCards) + done(); + else if (cardList.size() == 0) // this really shouldn't happen + cancel(); + else + showMessage(); + } + } + + public void cancel() { + stop(); + payment.cancelCost(); + } + + public void done() { + stop(); + payment.paidCost(tapType); + } + }; + + return target; + }//input_tapXCost() + + /** + *

returnThis.

+ * + * @param sa a {@link forge.card.spellability.SpellAbility} object. + * @param payment a {@link forge.card.cost.Cost_Payment} object. + * @param part TODO + * @return a {@link forge.gui.input.Input} object. + */ + public static Input returnThis(final SpellAbility sa, final Cost_Payment payment, final CostPart part) { + Input target = new Input() { + private static final long serialVersionUID = 2685832214519141903L; + + @Override + public void showMessage() { + Card card = sa.getSourceCard(); + if (card.getController().isHuman() && AllZoneUtil.isCardInPlay(card)) { + StringBuilder sb = new StringBuilder(); + sb.append(card.getName()); + sb.append(" - Return to Hand?"); + Object[] possibleValues = {"Yes", "No"}; + Object choice = JOptionPane.showOptionDialog(null, sb.toString(), card.getName() + " - Cost", + JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, + null, possibleValues, possibleValues[0]); + if (choice.equals(0)) { + AllZone.getGameAction().moveToHand(card); + stop(); + payment.paidCost(part); + } else { + stop(); + payment.cancelCost(); + } + } + } + }; + + return target; + }//input_sacrifice() + + + /** + *

returnType.

+ * + * @param sa a {@link forge.card.spellability.SpellAbility} object. + * @param type a {@link java.lang.String} object. + * @param payment a {@link forge.card.cost.Cost_Payment} object. + * @param part TODO + * @return a {@link forge.gui.input.Input} object. + */ + public static Input returnType(final SpellAbility sa, final String type, final Cost_Payment payment, final CostPart part, final int nNeeded) { + Input target = new Input() { + private static final long serialVersionUID = 2685832214519141903L; + private CardList typeList; + private int nReturns = 0; + + @Override + public void showMessage() { + StringBuilder msg = new StringBuilder("Return "); + int nLeft = nNeeded - nReturns; + msg.append(nLeft).append(" "); + msg.append(type); + if (nLeft > 1) { + msg.append("s"); + } + + typeList = AllZoneUtil.getPlayerCardsInPlay(sa.getSourceCard().getController()); + typeList = typeList.getValidCards(type.split(";"), sa.getActivatingPlayer(), sa.getSourceCard()); + AllZone.getDisplay().showMessage(msg.toString()); + ButtonUtil.enableOnlyCancel(); + } + + @Override + public void selectButtonCancel() { + cancel(); + } + + @Override + public void selectCard(Card card, PlayerZone zone) { + if (typeList.contains(card)) { + nReturns++; + AllZone.getGameAction().moveToHand(card); + typeList.remove(card); + //in case nothing else to return + if (nReturns == nNeeded) + done(); + else if (typeList.size() == 0) // this really shouldn't happen + cancel(); + else + showMessage(); + } + } + + public void done() { + stop(); + payment.paidCost(part); + } + + public void cancel() { + stop(); + payment.cancelCost(); + } + }; + + return target; + }//returnType() +} diff --git a/src/main/java/forge/card/cost/Cost_Payment.java b/src/main/java/forge/card/cost/Cost_Payment.java new file mode 100644 index 00000000000..16de246cbac --- /dev/null +++ b/src/main/java/forge/card/cost/Cost_Payment.java @@ -0,0 +1,235 @@ +package forge.card.cost; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import forge.AllZone; +import forge.Card; +import forge.Player; +import forge.card.spellability.SpellAbility; +import forge.card.spellability.SpellAbility_Requirements; + + +/** + *

Cost_Payment class.

+ * + * @author Forge + * @version $Id$ + */ +public class Cost_Payment { + private Cost cost = null; + private SpellAbility ability = null; + private Card card = null; + private SpellAbility_Requirements req = null; + private boolean bCancel = false; + private Map paidCostParts = new HashMap(); + + /** + *

Getter for the field cost.

+ * + * @return a {@link forge.card.cost.Cost} object. + */ + public Cost getCost() { + return cost; + } + + /** + *

Getter for the field ability.

+ * + * @return a {@link forge.card.spellability.SpellAbility} object. + */ + public SpellAbility getAbility() { + return ability; + } + + /** + *

Getter for the field card.

+ * + * @return a {@link forge.Card} object. + */ + public Card getCard() { + return card; + } + + /** + *

setRequirements.

+ * + * @param reqs a {@link forge.card.spellability.SpellAbility_Requirements} object. + */ + public void setRequirements(SpellAbility_Requirements reqs) { + req = reqs; + } + + public SpellAbility_Requirements getRequirements(){ + return req; + } + + /** + *

setCancel.

+ * + * @param cancel a boolean. + */ + public void setCancel(boolean cancel) { + bCancel = cancel; + } + + /** + *

isCanceled.

+ * + * @return a boolean. + */ + public boolean isCanceled() { + return bCancel; + } + + /** + *

Constructor for Cost_Payment.

+ * + * @param cost a {@link forge.card.cost.Cost} object. + * @param abil a {@link forge.card.spellability.SpellAbility} object. + */ + public Cost_Payment(Cost cost, SpellAbility abil) { + this.cost = cost; + this.ability = abil; + card = abil.getSourceCard(); + + for(CostPart part : cost.getCostParts()){ + paidCostParts.put(part, false); + } + } + + /** + *

canPayAdditionalCosts.

+ * + * @param cost a {@link forge.card.cost.Cost} object. + * @param ability a {@link forge.card.spellability.SpellAbility} object. + * @return a boolean. + */ + public static boolean canPayAdditionalCosts(Cost cost, SpellAbility ability) { + if (cost == null) + return true; + + Player activator = ability.getActivatingPlayer(); + final Card card = ability.getSourceCard(); + + for(CostPart part : cost.getCostParts()){ + if (!part.canPay(ability, card, activator, cost)) + return false; + } + + return true; + } + + public void setPaidManaPart(CostPart part, boolean bPaid){ + paidCostParts.put(part, bPaid); + } + + public void paidCost(CostPart part){ + setPaidManaPart(part, true); + payCost(); + } + + public void cancelCost(){ + setCancel(true); + req.finishPaying(); + } + + /** + *

payCost.

+ * + * @return a boolean. + */ + public boolean payCost() { + // Nothing actually ever checks this return value, is it needed? + if (bCancel) { + req.finishPaying(); + return false; + } + + for(CostPart part : cost.getCostParts()){ + // This portion of the cost is already paid for, keep moving + if (paidCostParts.get(part)) + continue; + + if (!part.payHuman(ability, card, this)) + return false; + } + + resetUndoList(); + req.finishPaying(); + return true; + } + + /** + *

isAllPaid.

+ * + * @return a boolean. + */ + public boolean isAllPaid() { + for(CostPart part : paidCostParts.keySet()){ + if (!paidCostParts.get(part)) + return false; + } + + return true; + } + + /** + *

resetUndoList.

+ */ + public void resetUndoList() { + for(CostPart part : paidCostParts.keySet()){ + if (part instanceof CostPartWithList){ + ((CostPartWithList)part).resetList(); + } + } + } + + /** + *

cancelPayment.

+ */ + public void cancelPayment() { + for(CostPart part : paidCostParts.keySet()){ + if (paidCostParts.get(part) && part.isUndoable()) + part.refund(card); + } + + // Move this to CostMana + AllZone.getManaPool().unpaid(ability, false); + } + + /** + *

payComputerCosts.

+ * + * @return a boolean. + */ + public boolean payComputerCosts() { + // canPayAdditionalCosts now Player Agnostic + + // Just in case it wasn't set, but honestly it shouldn't have gotten here without being set + Player activator = AllZone.getComputerPlayer(); + ability.setActivatingPlayer(activator); + + Card source = ability.getSourceCard(); + ArrayList parts = cost.getCostParts(); + + // Set all of the decisions before attempting to pay anything + for(CostPart part : parts){ + if (!part.decideAIPayment(ability, source, this)) + return false; + } + + for(CostPart part : parts){ + part.payAI(ability, ability.getSourceCard(), this); + } + return true; + } + + /** + *

changeCost.

+ */ + public void changeCost() { + cost.changeCost(ability); + } +} diff --git a/src/main/java/forge/card/spellability/Ability_Activated.java b/src/main/java/forge/card/spellability/Ability_Activated.java index 614f947ed24..06c5354fd12 100644 --- a/src/main/java/forge/card/spellability/Ability_Activated.java +++ b/src/main/java/forge/card/spellability/Ability_Activated.java @@ -1,6 +1,8 @@ package forge.card.spellability; import forge.*; +import forge.card.cost.Cost; +import forge.card.cost.Cost_Payment; /** @@ -27,7 +29,7 @@ abstract public class Ability_Activated extends SpellAbility implements java.io. *

Constructor for Ability_Activated.

* * @param sourceCard a {@link forge.Card} object. - * @param abCost a {@link forge.card.spellability.Cost} object. + * @param abCost a {@link forge.card.cost.Cost} object. * @param tgt a {@link forge.card.spellability.Target} object. */ public Ability_Activated(Card sourceCard, Cost abCost, Target tgt) { diff --git a/src/main/java/forge/card/spellability/Ability_Mana.java b/src/main/java/forge/card/spellability/Ability_Mana.java index 46deedfd37d..83c51a23b63 100644 --- a/src/main/java/forge/card/spellability/Ability_Mana.java +++ b/src/main/java/forge/card/spellability/Ability_Mana.java @@ -4,6 +4,7 @@ import forge.AllZone; import forge.AllZoneUtil; import forge.Card; import forge.Player; +import forge.card.cost.Cost; import forge.card.mana.ManaPool; import java.util.HashMap; @@ -51,7 +52,7 @@ abstract public class Ability_Mana extends Ability_Activated implements java.io. *

Constructor for Ability_Mana.

* * @param sourceCard a {@link forge.Card} object. - * @param cost a {@link forge.card.spellability.Cost} object. + * @param cost a {@link forge.card.cost.Cost} object. * @param produced a {@link java.lang.String} object. */ public Ability_Mana(Card sourceCard, Cost cost, String produced) { @@ -62,7 +63,7 @@ abstract public class Ability_Mana extends Ability_Activated implements java.io. *

Constructor for Ability_Mana.

* * @param sourceCard a {@link forge.Card} object. - * @param cost a {@link forge.card.spellability.Cost} object. + * @param cost a {@link forge.card.cost.Cost} object. * @param produced a {@link java.lang.String} object. * @param num a int. */ @@ -189,7 +190,7 @@ abstract public class Ability_Mana extends Ability_Activated implements java.io. * @return a boolean. */ public boolean isSacrifice() { - return this.getPayCosts().getSacCost(); + return payCosts.getSacCost(); } /** diff --git a/src/main/java/forge/card/spellability/Cost.java b/src/main/java/forge/card/spellability/Cost.java deleted file mode 100644 index 0a8293990fe..00000000000 --- a/src/main/java/forge/card/spellability/Cost.java +++ /dev/null @@ -1,1285 +0,0 @@ -package forge.card.spellability; - -import forge.AllZone; -import forge.Card; -import forge.Counters; -import forge.card.mana.ManaCost; - -import java.util.regex.Pattern; - -/** - *

Cost class.

- * - * @author Forge - * @version $Id$ - */ -public class Cost { - private boolean isAbility = true; - - private boolean sacCost = false; - - /** - *

Getter for the field sacCost.

- * - * @return a boolean. - */ - public boolean getSacCost() { - return sacCost; - } - - private String sacType = ""; // or CARDNAME - - /** - *

Getter for the field sacType.

- * - * @return a {@link java.lang.String} object. - */ - public String getSacType() { - return sacType; - } - - private boolean sacThis = false; - - /** - *

Getter for the field sacThis.

- * - * @return a boolean. - */ - public boolean getSacThis() { - return sacThis; - } - - private int sacAmount = 0; - - /** - *

Getter for the field sacAmount.

- * - * @return a int. - */ - public int getSacAmount() { - return sacAmount; - } - - private boolean sacX = false; - - /** - *

isSacX.

- * - * @return a boolean. - */ - public boolean isSacX() { - return sacX; - } - - private boolean sacAll = false; - - /** - *

isSacAll.

- * - * @return a boolean. - */ - public boolean isSacAll() { - return sacAll; - } - - private boolean exileCost = false; - - /** - *

Getter for the field exileCost.

- * - * @return a boolean. - */ - public boolean getExileCost() { - return exileCost; - } - - private String exileType = ""; // or CARDNAME - - /** - *

Getter for the field exileType.

- * - * @return a {@link java.lang.String} object. - */ - public String getExileType() { - return exileType; - } - - private boolean exileThis = false; - - /** - *

Getter for the field exileThis.

- * - * @return a boolean. - */ - public boolean getExileThis() { - return exileThis; - } - - private int exileAmount = 0; - - /** - *

Getter for the field exileAmount.

- * - * @return a int. - */ - public int getExileAmount() { - return exileAmount; - } - - private boolean exileFromHandCost = false; - - /** - *

Getter for the field exileFromHandCost.

- * - * @return a boolean. - */ - public boolean getExileFromHandCost() { - return exileFromHandCost; - } - - private String exileFromHandType = ""; // or CARDNAME - - /** - *

Getter for the field exileFromHandType.

- * - * @return a {@link java.lang.String} object. - */ - public String getExileFromHandType() { - return exileFromHandType; - } - - private boolean exileFromHandThis = false; - - /** - *

Getter for the field exileFromHandThis.

- * - * @return a boolean. - */ - public boolean getExileFromHandThis() { - return exileFromHandThis; - } - - private int exileFromHandAmount = 0; - - /** - *

Getter for the field exileFromHandAmount.

- * - * @return a int. - */ - public int getExileFromHandAmount() { - return exileFromHandAmount; - } - - private boolean exileFromGraveCost = false; - - /** - *

Getter for the field exileFromGraveCost.

- * - * @return a boolean. - */ - public boolean getExileFromGraveCost() { - return exileFromGraveCost; - } - - private String exileFromGraveType = ""; // or CARDNAME - - /** - *

Getter for the field exileFromGraveType.

- * - * @return a {@link java.lang.String} object. - */ - public String getExileFromGraveType() { - return exileFromGraveType; - } - - private boolean exileFromGraveThis = false; - - /** - *

Getter for the field exileFromGraveThis.

- * - * @return a boolean. - */ - public boolean getExileFromGraveThis() { - return exileFromGraveThis; - } - - private int exileFromGraveAmount = 0; - - /** - *

Getter for the field exileFromGraveAmount.

- * - * @return a int. - */ - public int getExileFromGraveAmount() { - return exileFromGraveAmount; - } - - private boolean exileFromTopCost = false; - - /** - *

Getter for the field exileFromTopCost.

- * - * @return a boolean. - */ - public boolean getExileFromTopCost() { - return exileFromTopCost; - } - - private String exileFromTopType = ""; // or CARDNAME - - /** - *

Getter for the field exileFromTopType.

- * - * @return a {@link java.lang.String} object. - */ - public String getExileFromTopType() { - return exileFromTopType; - } - - private boolean exileFromTopThis = false; - - /** - *

Getter for the field exileFromTopThis.

- * - * @return a boolean. - */ - public boolean getExileFromTopThis() { - return exileFromTopThis; - } - - private int exileFromTopAmount = 0; - - /** - *

Getter for the field exileFromTopAmount.

- * - * @return a int. - */ - public int getExileFromTopAmount() { - return exileFromTopAmount; - } - - private boolean tapCost = false; - - /** - *

getTap.

- * - * @return a boolean. - */ - public boolean getTap() { - return tapCost; - } - - // future expansion of Ability_Cost class: tap untapped type - private boolean tapXTypeCost = false; - - /** - *

Getter for the field tapXTypeCost.

- * - * @return a boolean. - */ - public boolean getTapXTypeCost() { - return tapXTypeCost; - } - - private int tapXTypeAmount = 0; - - /** - *

Getter for the field tapXTypeAmount.

- * - * @return a int. - */ - public int getTapXTypeAmount() { - return tapXTypeAmount; - } - - private String tapXType = ""; - - /** - *

Getter for the field tapXType.

- * - * @return a {@link java.lang.String} object. - */ - public String getTapXType() { - return tapXType; - } - - private boolean untapCost = false; - - /** - *

getUntap.

- * - * @return a boolean. - */ - public boolean getUntap() { - return untapCost; - } - - private boolean subtractCounterCost = false; - - /** - *

getSubCounter.

- * - * @return a boolean. - */ - public boolean getSubCounter() { - return subtractCounterCost; - } - - private boolean addCounterCost = false; - - /** - *

getAddCounter.

- * - * @return a boolean. - */ - public boolean getAddCounter() { - return addCounterCost; - } - - private int counterAmount = 0; - - /** - *

getCounterNum.

- * - * @return a int. - */ - public int getCounterNum() { - return counterAmount; - } - - private Counters counterType; - - /** - *

Getter for the field counterType.

- * - * @return a {@link forge.Counters} object. - */ - public Counters getCounterType() { - return counterType; - } - - private boolean lifeCost = false; - - /** - *

Getter for the field lifeCost.

- * - * @return a boolean. - */ - public boolean getLifeCost() { - return lifeCost; - } - - private int lifeAmount = 0; - - /** - *

Getter for the field lifeAmount.

- * - * @return a int. - */ - public int getLifeAmount() { - return lifeAmount; - } - - private boolean discardCost = false; - - /** - *

Getter for the field discardCost.

- * - * @return a boolean. - */ - public boolean getDiscardCost() { - return discardCost; - } - - private int discardAmount = 0; - - /** - *

Getter for the field discardAmount.

- * - * @return a int. - */ - public int getDiscardAmount() { - return discardAmount; - } - - private String discardType = ""; - - /** - *

Getter for the field discardType.

- * - * @return a {@link java.lang.String} object. - */ - public String getDiscardType() { - return discardType; - } - - private boolean discardThis = false; - - /** - *

Getter for the field discardThis.

- * - * @return a boolean. - */ - public boolean getDiscardThis() { - return discardThis; - } - - private boolean returnCost = false; // Return something to owner's hand - - /** - *

Getter for the field returnCost.

- * - * @return a boolean. - */ - public boolean getReturnCost() { - return returnCost; - } - - private String returnType = ""; // or CARDNAME - - /** - *

Getter for the field returnType.

- * - * @return a {@link java.lang.String} object. - */ - public String getReturnType() { - return returnType; - } - - private boolean returnThis = false; - - /** - *

Getter for the field returnThis.

- * - * @return a boolean. - */ - public boolean getReturnThis() { - return returnThis; - } - - private int returnAmount = 0; - - /** - *

Getter for the field returnAmount.

- * - * @return a int. - */ - public int getReturnAmount() { - return returnAmount; - } - - /** - *

hasNoManaCost.

- * - * @return a boolean. - */ - public boolean hasNoManaCost() { - return manaCost.equals("") || manaCost.equals("0"); - } - - private String manaCost = ""; - - /** - *

getMana.

- * - * @return a {@link java.lang.String} object. - */ - public String getMana() { - return manaCost; - } // Only used for Human to pay for non-X cost first - - /** - *

setMana.

- * - * @param sCost a {@link java.lang.String} object. - */ - public void setMana(String sCost) { - manaCost = sCost; - } - - /** - *

hasNoXManaCost.

- * - * @return a boolean. - */ - public boolean hasNoXManaCost() { - return manaXCost == 0; - } - - private int manaXCost = 0; - - /** - *

getXMana.

- * - * @return a int. - */ - public int getXMana() { - return manaXCost; - } - - /** - *

setXMana.

- * - * @param xCost a int. - */ - public void setXMana(int xCost) { - manaXCost = xCost; - } - - /** - *

isOnlyManaCost.

- * - * @return a boolean. - */ - public boolean isOnlyManaCost() { - return !sacCost && !exileCost && !exileFromHandCost && !exileFromGraveCost && !exileFromTopCost && !tapCost && - !tapXTypeCost && !untapCost && !subtractCounterCost && !addCounterCost && !lifeCost && !discardCost && !returnCost; - } - - /** - *

getTotalMana.

- * - * @return a {@link java.lang.String} object. - */ - public String getTotalMana() { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < manaXCost; i++) - sb.append("X "); - - if (!hasNoManaCost()) - sb.append(manaCost); - - if (sb.toString().equals("")) - return "0"; - - return sb.toString().trim(); - } - - - private String name; - - /** - *

Constructor for Cost.

- * - * @param parse a {@link java.lang.String} object. - * @param cardName a {@link java.lang.String} object. - * @param bAbility a boolean. - */ - public Cost(String parse, String cardName, boolean bAbility) { - isAbility = bAbility; - // when adding new costs for cost string, place them here - name = cardName; - - String tapXStr = "tapXType<"; - if (parse.contains(tapXStr)) { - tapXTypeCost = true; - String[] splitStr = abCostParse(parse, tapXStr, 2); - parse = abUpdateParse(parse, tapXStr); - - tapXTypeAmount = Integer.parseInt(splitStr[0]); - tapXType = splitStr[1]; - } - - String subStr = "SubCounter<"; - if (parse.contains(subStr)) { - // SubCounter - subtractCounterCost = true; - String[] splitStr = abCostParse(parse, subStr, 2); - parse = abUpdateParse(parse, subStr); - - counterAmount = Integer.parseInt(splitStr[0]); - counterType = Counters.valueOf(splitStr[1]); - } - - String addStr = "AddCounter<"; - if (parse.contains(addStr)) { - // AddCounter - addCounterCost = true; - String[] splitStr = abCostParse(parse, addStr, 2); - parse = abUpdateParse(parse, addStr); - - counterAmount = Integer.parseInt(splitStr[0]); - counterType = Counters.valueOf(splitStr[1]); - } - - String lifeStr = "PayLife<"; - if (parse.contains(lifeStr)) { - // PayLife - lifeCost = true; - String[] splitStr = abCostParse(parse, lifeStr, 1); - parse = abUpdateParse(parse, lifeStr); - - lifeAmount = Integer.parseInt(splitStr[0]); - } - - String discStr = "Discard<"; - if (parse.contains(discStr)) { - // Discard - discardCost = true; - String[] splitStr = abCostParse(parse, discStr, 2); - parse = abUpdateParse(parse, discStr); - - discardAmount = Integer.parseInt(splitStr[0]); - discardType = splitStr[1]; - discardThis = (discardType.equals("CARDNAME")); - } - - String sacStr = "Sac<"; - if (parse.contains(sacStr)) { - // TODO: maybe separate SacThis from SacType? not sure if any card would use both - sacCost = true; - String[] splitStr = abCostParse(parse, sacStr, 2); - parse = abUpdateParse(parse, sacStr); - - if (splitStr[0].equals("X")) sacX = true; - else if (splitStr[0].equals("All")) sacAll = true; - else sacAmount = Integer.parseInt(splitStr[0]); - sacType = splitStr[1]; - sacThis = (sacType.equals("CARDNAME")); - } - - String exileStr = "Exile<"; - if (parse.contains(exileStr)) { - exileCost = true; - String[] splitStr = abCostParse(parse, exileStr, 2); - parse = abUpdateParse(parse, exileStr); - - exileAmount = Integer.parseInt(splitStr[0]); - exileType = splitStr[1]; - exileThis = (exileType.equals("CARDNAME")); - } - - String exileFromHandStr = "ExileFromHand<"; - if (parse.contains(exileFromHandStr)) { - exileFromHandCost = true; - String[] splitStr = abCostParse(parse, exileFromHandStr, 2); - parse = abUpdateParse(parse, exileFromHandStr); - - exileFromHandAmount = Integer.parseInt(splitStr[0]); - exileFromHandType = splitStr[1]; - exileFromHandThis = (exileFromHandType.equals("CARDNAME")); - } - - String exileFromGraveStr = "ExileFromGrave<"; - if (parse.contains(exileFromGraveStr)) { - exileFromGraveCost = true; - String[] splitStr = abCostParse(parse, exileFromGraveStr, 2); - parse = abUpdateParse(parse, exileFromGraveStr); - - exileFromGraveAmount = Integer.parseInt(splitStr[0]); - exileFromGraveType = splitStr[1]; - exileFromGraveThis = (exileFromGraveType.equals("CARDNAME")); - } - - String exileFromTopStr = "ExileFromTop<"; - if (parse.contains(exileFromTopStr)) { - exileFromTopCost = true; - String[] splitStr = abCostParse(parse, exileFromTopStr, 2); - parse = abUpdateParse(parse, exileFromTopStr); - - exileFromTopAmount = Integer.parseInt(splitStr[0]); - exileFromTopType = splitStr[1]; - exileFromTopThis = false; - } - - String returnStr = "Return<"; - if (parse.contains(returnStr)) { - returnCost = true; - String[] splitStr = abCostParse(parse, returnStr, 2); - parse = abUpdateParse(parse, returnStr); - - returnAmount = Integer.parseInt(splitStr[0]); - returnType = splitStr[1]; - returnThis = (returnType.equals("CARDNAME")); - } - - if (parse.contains("Untap")) { - untapCost = true; - parse = parse.replace("Untap", "").trim(); - } - - if (parse.contains("Q")) { - untapCost = true; - parse = parse.replace("Q", "").trim(); - } - - if (parse.contains("T")) { - tapCost = true; - parse = parse.replace("T", ""); - parse = parse.trim(); - } - - String stripXCost = parse.replaceAll("X", ""); - - manaXCost = parse.length() - stripXCost.length(); - - manaCost = stripXCost.trim(); - if (manaCost.equals("")) - manaCost = "0"; - } - - /** - *

abCostParse.

- * - * @param parse a {@link java.lang.String} object. - * @param subkey a {@link java.lang.String} object. - * @param numParse a int. - * @return an array of {@link java.lang.String} objects. - */ - String[] abCostParse(String parse, String subkey, int numParse) { - int startPos = parse.indexOf(subkey); - int endPos = parse.indexOf(">", startPos); - String str = parse.substring(startPos, endPos); - - str = str.replace(subkey, ""); - - String[] splitStr = str.split("/", numParse); - return splitStr; - } - - /** - *

abUpdateParse.

- * - * @param parse a {@link java.lang.String} object. - * @param subkey a {@link java.lang.String} object. - * @return a {@link java.lang.String} object. - */ - String abUpdateParse(String parse, String subkey) { - int startPos = parse.indexOf(subkey); - int endPos = parse.indexOf(">", startPos); - String str = parse.substring(startPos, endPos + 1); - return parse.replace(str, "").trim(); - } - - /** - *

changeCost.

- * - * @param sa a {@link forge.card.spellability.SpellAbility} object. - */ - public void changeCost(SpellAbility sa) { - if (getTotalMana() != "0") { // 11/15/10 use getTotalMana() to account for X reduction - String mana = getTotalMana(); - manaCost = AllZone.getGameAction().getSpellCostChange(sa, new ManaCost(mana)).toString(); - } - } - - /** - *

refundPaidCost.

- * - * @param source a {@link forge.Card} object. - */ - public void refundPaidCost(Card source) { - // prereq: isUndoable is called first - if (tapCost) - source.untap(); - else if (untapCost) - source.tap(); - - if (subtractCounterCost) - source.addCounterFromNonEffect(counterType, counterAmount); - else if (addCounterCost) - source.subtractCounter(counterType, counterAmount); - - // refund chained mana abilities? - } - - /** - *

isUndoable.

- * - * @return a boolean. - */ - public boolean isUndoable() { - return !(sacCost || exileCost || exileFromHandCost || exileFromGraveCost || tapXTypeCost || discardCost || - returnCost || lifeCost || exileFromTopCost) && hasNoXManaCost() && hasNoManaCost(); - } - - - /** - *

isReusuableResource.

- * - * @return a boolean. - */ - public boolean isReusuableResource() { - return !(sacCost || exileCost || exileFromHandCost || tapXTypeCost || discardCost || - returnCost || lifeCost) && isAbility; - // TODO: add/sub counter? Maybe check if it's we're adding a positive counter, or removing a negative counter - } - - /** - *

toString.

- * - * @return a {@link java.lang.String} object. - */ - public String toString() { - if (isAbility) - return abilityToString(); - else - return spellToString(true); - } - - // maybe add a conversion method that turns the amounts into words 1=a(n), 2=two etc. - - /** - *

toStringAlt.

- * - * @return a {@link java.lang.String} object. - */ - public String toStringAlt() { - return spellToString(false); - } - - /** - *

spellToString.

- * - * @param bFlag a boolean. - * @return a {@link java.lang.String} object. - */ - private String spellToString(boolean bFlag) { - StringBuilder cost = new StringBuilder(); - - if (bFlag) - cost.append("As an additional cost to cast ").append(name).append(", "); - - boolean first = true; - - if (!bFlag) { - // usually no additional mana cost for spells - // only three Alliances cards have additional mana costs, but they are basically kicker/multikicker - if (!getTotalMana().equals("0")) { - cost.append("pay ").append(getTotalMana()); - first = false; - } - } - - if (tapCost || untapCost) { - // tap cost for spells will not be in this form. - } - - if (subtractCounterCost || addCounterCost) { - // add counterCost only appears in this form, which is currently on supported: - // put a -1/-1 counter on a creature you control. - - // subtractCounter for spells will not be in this form - - } - - if (lifeCost) { - if (first) - cost.append("pay "); - else - cost.append("and pay "); - cost.append(lifeAmount); - cost.append(" Life"); - - first = false; - } - - if (discardCost) { - cost.append(discardString(first)); - first = false; - } - - if (sacCost) { - cost.append(sacString(first)); - first = false; - } - - if (exileCost) { - cost.append(exileString(first)); - first = false; - } - - if (exileFromHandCost) { - cost.append(exileFromHandString(first)); - first = false; - } - - if (exileFromGraveCost) { - cost.append(exileFromGraveString(first)); - first = false; - } - - if (exileFromTopCost) { - cost.append(exileFromTopString(first)); - first = false; - } - - if (returnCost) { - cost.append(returnString(first)); - first = false; - } - - if (first) - return ""; - - if (bFlag) - cost.append(".").append("\n"); - - return cost.toString(); - } - - /** - *

abilityToString.

- * - * @return a {@link java.lang.String} object. - */ - private String abilityToString() { - StringBuilder cost = new StringBuilder(); - boolean first = true; - if (manaXCost > 0) { - for (int i = 0; i < manaXCost; i++) { - cost.append("X").append(" "); - } - first = false; - } - - if (!(manaCost.equals("0") || manaCost.equals(""))) { - cost.append(manaCost); - first = false; - } - - if (tapCost) { - if (first) - cost.append("Tap"); - else - cost.append(", tap"); - first = false; - } - - if (untapCost) { - if (first) - cost.append("Untap "); - else - cost.append(", untap"); - first = false; - } - - if (tapXTypeCost) { - if (first) - cost.append("Tap "); - else - cost.append(", tap "); - cost.append(convertIntAndTypeToWords(tapXTypeAmount, "untapped " + tapXType)); - cost.append(" you control"); -// cost.append(tapXType); // needs IsValid String converter - first = false; - } - - if (subtractCounterCost) { - if (counterType.getName().equals("Loyalty")) - cost.append("-").append(counterAmount); - else { - if (first) - cost.append("Remove "); - else - cost.append(", remove "); - - cost.append(convertIntAndTypeToWords(counterAmount, counterType.getName() + " counter")); - - cost.append(" from "); - cost.append(name); - } - - first = false; - } - - if (addCounterCost) { - if (counterType.getName().equals("Loyalty")) - cost.append("+").append(counterAmount); - else { - if (first) - cost.append("Put "); - else - cost.append(", put "); - - cost.append(convertIntAndTypeToWords(counterAmount, counterType.getName() + " counter")); - - cost.append(" on "); - cost.append(name); - } - first = false; - } - - if (lifeCost) { - if (first) - cost.append("Pay "); - else - cost.append(", Pay "); - cost.append(lifeAmount); - cost.append(" Life"); - - first = false; - } - - if (discardCost) { - cost.append(discardString(first)); - first = false; - } - - if (sacCost) { - cost.append(sacString(first)); - first = false; - } - - if (exileCost) { - cost.append(exileString(first)); - first = false; - } - - if (exileFromHandCost) { - cost.append(exileFromHandString(first)); - first = false; - } - - if (exileFromGraveCost) { - cost.append(exileFromGraveString(first)); - first = false; - } - - if (exileFromTopCost) { - cost.append(exileFromTopString(first)); - first = false; - } - - if (returnCost) { - cost.append(returnString(first)); - first = false; - } - - if (first) // No costs, append 0 - cost.append("0"); - - cost.append(": "); - return cost.toString(); - } - - /** - *

discardString.

- * - * @param first a boolean. - * @return a {@link java.lang.String} object. - */ - public String discardString(boolean first) { - StringBuilder cost = new StringBuilder(); - if (first) { - if (isAbility) - cost.append("Discard "); - else - cost.append("discard "); - } else { - if (isAbility) - cost.append(", discard "); - else - cost.append("and discard "); - } - - if (discardThis) { - cost.append(name); - } else if (discardType.equals("Hand")) { - cost.append("your hand"); - } else if (discardType.equals("LastDrawn")) { - cost.append("last drawn card"); - } else { - if (!discardType.equals("Any") && !discardType.equals("Card") && !discardType.equals("Random")) { - cost.append(convertIntAndTypeToWords(discardAmount, discardType + " card")); - } else - cost.append(convertIntAndTypeToWords(discardAmount, "card")); - - if (discardType.equals("Random")) - cost.append(" at random"); - } - return cost.toString(); - } - - /** - *

sacString.

- * - * @param first a boolean. - * @return a {@link java.lang.String} object. - */ - public String sacString(boolean first) { - StringBuilder cost = new StringBuilder(); - if (first) { - if (isAbility) - cost.append("Sacrifice "); - else - cost.append("sacrifice "); - } else { - cost.append(", sacrifice "); - } - - if (sacType.equals("CARDNAME")) - cost.append(name); - else - cost.append(convertIntAndTypeToWords(sacAmount, sacType)); - - return cost.toString(); - } - - /** - *

exileString.

- * - * @param first a boolean. - * @return a {@link java.lang.String} object. - */ - public String exileString(boolean first) { - StringBuilder cost = new StringBuilder(); - if (first) { - if (isAbility) - cost.append("Exile "); - else - cost.append("exile "); - } else { - cost.append(", exile "); - } - - if (exileType.equals("CARDNAME")) - cost.append(name); - else - cost.append(convertIntAndTypeToWords(exileAmount, exileType)); - - return cost.toString(); - } - - /** - *

exileFromHandString.

- * - * @param first a boolean. - * @return a {@link java.lang.String} object. - */ - public String exileFromHandString(boolean first) { - StringBuilder cost = new StringBuilder(); - if (first) { - if (isAbility) - cost.append("Exile "); - else - cost.append("exile "); - } else { - cost.append(", exile "); - } - - if (exileType.equals("CARDNAME")) - cost.append(name); - else { - cost.append(convertIntAndTypeToWords(exileFromHandAmount, exileFromHandType)); - cost.append(" from your hand"); - } - return cost.toString(); - } - - /** - *

exileFromGraveString.

- * - * @param first a boolean. - * @return a {@link java.lang.String} object. - */ - public String exileFromGraveString(boolean first) { - StringBuilder cost = new StringBuilder(); - if (first) { - if (isAbility) - cost.append("Exile "); - else - cost.append("exile "); - } else { - cost.append(", exile "); - } - - if (exileType.equals("CARDNAME")) - cost.append(name); - else { - cost.append(convertIntAndTypeToWords(exileFromGraveAmount, exileFromGraveType)); - cost.append(" from your graveyard"); - } - return cost.toString(); - } - - /** - *

exileFromTopString.

- * - * @param first a boolean. - * @return a {@link java.lang.String} object. - */ - public String exileFromTopString(boolean first) { - StringBuilder cost = new StringBuilder(); - if (first) { - if (isAbility) - cost.append("Exile "); - else - cost.append("exile "); - } else { - cost.append(", Exile "); - } - - if (exileType.equals("CARDNAME")) - cost.append(name).append(" from the top of you library"); - else { - cost.append("the top "); - cost.append(convertIntAndTypeToWords(exileFromTopAmount, exileFromTopType)); - cost.append(" of your library"); - } - return cost.toString(); - } - - /** - *

returnString.

- * - * @param first a boolean. - * @return a {@link java.lang.String} object. - */ - public String returnString(boolean first) { - StringBuilder cost = new StringBuilder(); - if (first) { - if (isAbility) - cost.append("Return "); - else - cost.append("return "); - } else { - cost.append(", return "); - } - String pronoun = "its"; - if (returnType.equals("CARDNAME")) - cost.append(name); - else { - cost.append(convertIntAndTypeToWords(returnAmount, returnType)); - - if (returnAmount > 1) { - pronoun = "their"; - } - cost.append(" you control"); - } - cost.append(" to ").append(pronoun).append(" owner's hand"); - return cost.toString(); - } - - // TODO: If an Ability_Cost needs to pay more than 10 of something, fill this array as appropriate - /** Constant numNames="{zero, a, two, three, four, five, six, "{trunked} */ - private static final String[] numNames = {"zero", "a", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"}; - /** Constant vowelPattern */ - private static final Pattern vowelPattern = Pattern.compile("^[aeiou]", Pattern.CASE_INSENSITIVE); - - - /** - *

convertIntAndTypeToWords.

- * - * @param i a int. - * @param type a {@link java.lang.String} object. - * @return a {@link java.lang.String} object. - */ - private String convertIntAndTypeToWords(int i, String type) { - StringBuilder sb = new StringBuilder(); - - if (i >= numNames.length) { - sb.append(i); - } else if (1 == i && vowelPattern.matcher(type).find()) - sb.append("an"); - else - sb.append(numNames[i]); - - sb.append(" "); - sb.append(type); - if (1 != i) - sb.append("s"); - - return sb.toString(); - } -} diff --git a/src/main/java/forge/card/spellability/Cost_Payment.java b/src/main/java/forge/card/spellability/Cost_Payment.java deleted file mode 100644 index ed483e6f37c..00000000000 --- a/src/main/java/forge/card/spellability/Cost_Payment.java +++ /dev/null @@ -1,1877 +0,0 @@ -package forge.card.spellability; - -import forge.*; -import forge.card.abilityFactory.AbilityFactory; -import forge.card.mana.ManaCost; -import forge.gui.GuiUtils; -import forge.gui.input.Input; -import forge.gui.input.Input_PayManaCostUtil; - -import javax.swing.*; - -/** - *

Cost_Payment class.

- * - * @author Forge - * @version $Id$ - */ -public class Cost_Payment { - private Cost cost = null; - private SpellAbility ability = null; - private Card card = null; - private SpellAbility_Requirements req = null; - - /** - *

Getter for the field cost.

- * - * @return a {@link forge.card.spellability.Cost} object. - */ - public Cost getCost() { - return cost; - } - - /** - *

Getter for the field ability.

- * - * @return a {@link forge.card.spellability.SpellAbility} object. - */ - public SpellAbility getAbility() { - return ability; - } - - /** - *

Getter for the field card.

- * - * @return a {@link forge.Card} object. - */ - public Card getCard() { - return card; - } - - /** - *

setRequirements.

- * - * @param reqs a {@link forge.card.spellability.SpellAbility_Requirements} object. - */ - public void setRequirements(SpellAbility_Requirements reqs) { - req = reqs; - } - - /** - *

setCancel.

- * - * @param cancel a boolean. - */ - public void setCancel(boolean cancel) { - bCancel = cancel; - } - - /** - *

isCanceled.

- * - * @return a boolean. - */ - public boolean isCanceled() { - return bCancel; - } - - // No default values so an error will be kicked if not set properly in constructor - private boolean payTap; - private boolean payUntap; - private boolean payMana; - private boolean payXMana; - private boolean paySubCounter; - private boolean payAddCounter; - private boolean paySac; - private boolean payExile; - private boolean payExileFromHand; - private boolean payExileFromGrave; - private boolean payExileFromTop; - private boolean payLife; - private boolean payDiscard; - private boolean payTapXType; - private boolean payReturn; - - private boolean bCancel = false; - private boolean bXDefined = true; - - private CardList payTapXTypeTappedList = new CardList(); - - /** - *

addPayTapXTypeTappedList.

- * - * @param c a {@link forge.Card} object. - */ - private void addPayTapXTypeTappedList(Card c) { - payTapXTypeTappedList.add(c); - } - - /** - *

Setter for the field payMana.

- * - * @param bPay a boolean. - */ - public void setPayMana(boolean bPay) { - payMana = bPay; - } - - /** - *

Setter for the field payXMana.

- * - * @param bPay a boolean. - */ - public void setPayXMana(boolean bPay) { - payXMana = bPay; - } - - /** - *

Setter for the field payDiscard.

- * - * @param bSac a boolean. - */ - public void setPayDiscard(boolean bSac) { - payDiscard = bSac; - } - - /** - *

Setter for the field paySac.

- * - * @param bSac a boolean. - */ - public void setPaySac(boolean bSac) { - paySac = bSac; - } - - /** - *

Setter for the field payExile.

- * - * @param bExile a boolean. - */ - public void setPayExile(boolean bExile) { - payExile = bExile; - } - - /** - *

Setter for the field payExileFromHand.

- * - * @param bExileFromHand a boolean. - */ - public void setPayExileFromHand(boolean bExileFromHand) { - payExileFromHand = bExileFromHand; - } - - /** - *

Setter for the field payExileFromGrave.

- * - * @param bExileFromGrave a boolean. - */ - public void setPayExileFromGrave(boolean bExileFromGrave) { - payExileFromGrave = bExileFromGrave; - } - - /** - *

Setter for the field payExileFromTop.

- * - * @param bExileFromTop a boolean. - */ - public void setPayExileFromTop(boolean bExileFromTop) { - payExileFromTop = bExileFromTop; - } - - /** - *

Setter for the field payTapXType.

- * - * @param bTapX a boolean. - */ - public void setPayTapXType(boolean bTapX) { - payTapXType = bTapX; - } - - /** - *

Setter for the field payReturn.

- * - * @param bReturn a boolean. - */ - public void setPayReturn(boolean bReturn) { - payReturn = bReturn; - } - - /** - *

Constructor for Cost_Payment.

- * - * @param cost a {@link forge.card.spellability.Cost} object. - * @param abil a {@link forge.card.spellability.SpellAbility} object. - */ - public Cost_Payment(Cost cost, SpellAbility abil) { - this.cost = cost; - this.ability = abil; - card = this.ability.getSourceCard(); - payTap = !cost.getTap(); - payUntap = !cost.getUntap(); - payMana = cost.hasNoManaCost(); - payXMana = cost.hasNoXManaCost(); - paySubCounter = !cost.getSubCounter(); - payAddCounter = !cost.getAddCounter(); - paySac = !cost.getSacCost(); - payExile = !cost.getExileCost(); - payExileFromHand = !cost.getExileFromHandCost(); - payExileFromGrave = !cost.getExileFromGraveCost(); - payExileFromTop = !cost.getExileFromTopCost(); - payLife = !cost.getLifeCost(); - payDiscard = !cost.getDiscardCost(); - payTapXType = !cost.getTapXTypeCost(); - payReturn = !cost.getReturnCost(); - } - - /** - *

canPayAdditionalCosts.

- * - * @param cost a {@link forge.card.spellability.Cost} object. - * @param ability a {@link forge.card.spellability.SpellAbility} object. - * @return a boolean. - */ - public static boolean canPayAdditionalCosts(Cost cost, SpellAbility ability) { - if (cost == null) - return true; - - final Card card = ability.getSourceCard(); - if (cost.getTap() && (card.isTapped() || card.isSick())) - return false; - - if (cost.getUntap() && (card.isUntapped() || card.isSick())) - return false; - - if (cost.getTapXTypeCost()) { - CardList typeList = AllZoneUtil.getPlayerCardsInPlay(card.getController()); - - typeList = typeList.getValidCards(cost.getTapXType().split(";"), ability.getActivatingPlayer(), ability.getSourceCard()); - - if (cost.getTap()) { - typeList = typeList.filter(new CardListFilter() { - public boolean addCard(Card c) { - return !c.equals(card) && c.isUntapped(); - } - }); - } - if (typeList.size() == 0) - return false; - } - - int countersLeft = 0; - if (cost.getSubCounter()) { - Counters c = cost.getCounterType(); - countersLeft = card.getCounters(c) - cost.getCounterNum(); - if (countersLeft < 0) { - return false; - } - } - - if (cost.getAddCounter()) { - // Adding Counters as a cost should always be able to be paid - } - - if (cost.getLifeCost()) { - if (!card.getController().canPayLife(cost.getLifeAmount())) return false; - } - - if (cost.getDiscardCost()) { - CardList handList = AllZoneUtil.getPlayerHand(card.getController()); - String discType = cost.getDiscardType(); - int discAmount = cost.getDiscardAmount(); - - if (cost.getDiscardThis()) { - if (!AllZone.getZone(card).getZoneName().equals(Constant.Zone.Hand)) - return false; - } else if (discType.equals("Hand")) { - // this will always work - } else if (discType.equals("LastDrawn")) { - Card c = card.getController().getLastDrawnCard(); - CardList hand = AllZoneUtil.getPlayerHand(card.getController()); - return hand.contains(c); - } else { - if (!discType.equals("Any") && !discType.equals("Random")) { - String validType[] = discType.split(";"); - - handList = handList.getValidCards(validType, ability.getActivatingPlayer(), ability.getSourceCard()); - } - if (discAmount > handList.size()) { - // not enough cards in hand to pay - return false; - } - } - } - - if (cost.getSacCost()) { - if (!cost.getSacThis()) { - CardList typeList = AllZoneUtil.getPlayerCardsInPlay(card.getController()); - - typeList = typeList.getValidCards(cost.getSacType().split(";"), ability.getActivatingPlayer(), ability.getSourceCard()); - - int amount = cost.isSacAll() ? typeList.size() : cost.getSacAmount(); - - if (typeList.size() < amount) - return false; - } else if (!AllZoneUtil.isCardInPlay(card)) - return false; - } - - if (cost.getExileCost()) { - if (!cost.getExileThis()) { - CardList typeList = AllZoneUtil.getPlayerCardsInPlay(card.getController()); - - typeList = typeList.getValidCards(cost.getExileType().split(";"), ability.getActivatingPlayer(), ability.getSourceCard()); - if (typeList.size() < cost.getExileAmount()) - return false; - } else if (!AllZoneUtil.isCardInPlay(card)) - return false; - } - - if (cost.getExileFromHandCost()) { - if (!cost.getExileFromHandThis()) { - CardList typeList = AllZoneUtil.getPlayerHand(card.getController()); - - typeList = typeList.getValidCards(cost.getExileFromHandType().split(";"), ability.getActivatingPlayer(), ability.getSourceCard()); - if (typeList.size() < cost.getExileFromHandAmount()) - return false; - } else if (!AllZoneUtil.isCardInPlayerHand(card.getController(), card)) - return false; - } - - if (cost.getExileFromGraveCost()) { - if (!cost.getExileFromGraveThis()) { - CardList typeList = AllZoneUtil.getPlayerGraveyard(card.getController()); - - typeList = typeList.getValidCards(cost.getExileFromGraveType().split(";"), ability.getActivatingPlayer(), ability.getSourceCard()); - if (typeList.size() < cost.getExileFromGraveAmount()) - return false; - } else if (!AllZoneUtil.isCardInPlayerGraveyard(card.getController(), card)) - return false; - } - - if (cost.getExileFromTopCost()) { - if (!cost.getExileFromTopThis()) { - CardList typeList = AllZoneUtil.getPlayerCardsInLibrary(card.getController()); - - typeList = typeList.getValidCards(cost.getExileFromTopType().split(";"), ability.getActivatingPlayer(), ability.getSourceCard()); - if (typeList.size() < cost.getExileFromTopAmount()) - return false; - } else if (!AllZoneUtil.isCardInPlayerLibrary(card.getController(), card)) - return false; - } - - if (cost.getReturnCost()) { - if (!cost.getReturnThis()) { - CardList typeList = AllZoneUtil.getPlayerCardsInPlay(card.getController()); - - typeList = typeList.getValidCards(cost.getReturnType().split(";"), ability.getActivatingPlayer(), ability.getSourceCard()); - if (typeList.size() < cost.getReturnAmount()) - return false; - } else if (!AllZoneUtil.isCardInPlay(card)) - return false; - } - - return true; - } - - /** - *

setInput.

- * - * @param in a {@link forge.gui.input.Input} object. - */ - public void setInput(Input in) { - AllZone.getInputControl().setInput(in, true); - } - - /** - *

payCost.

- * - * @return a boolean. - */ - public boolean payCost() { - if (bCancel) { - req.finishPaying(); - return false; - } - - if (!payTap && cost.getTap()) { - if (card.isUntapped()) { - card.tap(); - payTap = true; - } else - return false; - } - - if (!payUntap && cost.getUntap()) { - if (card.isTapped()) { - card.untap(); - payUntap = true; - } else - return false; - } - - int manaToAdd = 0; - if (bXDefined && !cost.hasNoXManaCost()) { - // if X cost is a defined value, other than xPaid - if (!card.getSVar("X").equals("Count$xPaid")) { - // this currently only works for things about Targeted object - manaToAdd = AbilityFactory.calculateAmount(card, "X", ability) * cost.getXMana(); - payXMana = true; // Since the X-cost is being lumped into the mana cost - payMana = false; - } - } - bXDefined = false; - - if (!payMana) { // pay mana here - setInput(input_payMana(getAbility(), this, manaToAdd)); - return false; - } - - if (!payXMana && !cost.hasNoXManaCost()) { // pay X mana here - card.setXManaCostPaid(0); - setInput(input_payXMana(getCost().getXMana(), getAbility(), this)); - return false; - } - - if (!payTapXType && cost.getTapXTypeCost()) { - CardList typeList = AllZoneUtil.getPlayerCardsInPlay(card.getController()); - typeList = typeList.getValidCards(cost.getTapXType().split(";"), ability.getActivatingPlayer(), ability.getSourceCard()); - - setInput(input_tapXCost(cost.getTapXTypeAmount(), cost.getTapXType(), typeList, ability, this)); - return false; - } - - if (!paySubCounter && cost.getSubCounter()) { // pay counters here. - Counters type = cost.getCounterType(); - if (card.getCounters(type) >= cost.getCounterNum()) { - card.subtractCounter(type, cost.getCounterNum()); - paySubCounter = true; - } else { - bCancel = true; - req.finishPaying(); - return false; - } - } - - if (!payAddCounter && cost.getAddCounter()) { // add counters here. - card.addCounterFromNonEffect(cost.getCounterType(), cost.getCounterNum()); - payAddCounter = true; - } - - if (!payLife && cost.getLifeCost()) { // pay life here - StringBuilder sb = new StringBuilder(); - sb.append(getCard().getName()); - sb.append(" - Pay "); - sb.append(cost.getLifeAmount()); - sb.append(" Life?"); - Object[] possibleValues = {"Yes", "No"}; - Object choice = JOptionPane.showOptionDialog(null, sb.toString(), getCard().getName() + " - Cost", - JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, - null, possibleValues, possibleValues[0]); - if (choice.equals(0)) { - AllZone.getHumanPlayer().payLife(cost.getLifeAmount(), null); - payLife = true; - } else { - bCancel = true; - req.finishPaying(); - return false; - } - } - - if (!payDiscard && cost.getDiscardCost()) { // discard here - CardList handList = AllZoneUtil.getPlayerHand(card.getController()); - String discType = cost.getDiscardType(); - int discAmount = cost.getDiscardAmount(); - - if (cost.getDiscardThis()) { - card.getController().discard(card, ability); - payDiscard = true; - } else if (discType.equals("Hand")) { - card.getController().discardHand(ability); - payDiscard = true; - } else if (discType.equals("LastDrawn")) { - if (handList.contains(card.getController().getLastDrawnCard())) { - card.getController().discard(card.getController().getLastDrawnCard(), ability); - payDiscard = true; - } - - } else { - if (discType.equals("Random")) { - card.getController().discardRandom(discAmount, ability); - payDiscard = true; - } else { - if (!discType.equals("Any")) { - String validType[] = discType.split(";"); - handList = handList.getValidCards(validType, ability.getActivatingPlayer(), ability.getSourceCard()); - } - setInput(input_discardCost(discAmount, discType, handList, ability, this)); - return false; - } - } - } - - if (!paySac && cost.getSacCost()) { // sacrifice stuff here - if (cost.getSacThis()) - setInput(sacrificeThis(ability, this)); - else if (cost.isSacAll()) - sacrificeAllType(ability, cost.getSacType(), this); - else if (cost.isSacX()) - setInput(sacrificeXType(ability, cost.getSacType(), this)); - else - setInput(sacrificeType(ability, cost.getSacType(), this)); - return false; - } - - if (!payExile && cost.getExileCost()) { // exile stuff here - if (cost.getExileThis()) - setInput(exileThis(ability, this)); - else - setInput(exileType(ability, cost.getExileType(), this)); - return false; - } - - if (!payExileFromHand && cost.getExileFromHandCost()) { // exile stuff here - if (cost.getExileFromHandThis()) - setInput(exileFromHandThis(ability, this)); - else - setInput(exileFromHandType(ability, cost.getExileFromHandType(), this)); - return false; - } - - if (!payExileFromGrave && cost.getExileFromGraveCost()) { // exile stuff here - if (cost.getExileFromGraveThis()) - setInput(exileFromGraveThis(ability, this)); - else - setInput(exileFromGraveType(ability, cost.getExileFromGraveType(), this)); - return false; - } - - if (!payExileFromTop && cost.getExileFromTopCost()) { // exile stuff here - if (cost.getExileFromTopThis()) - setInput(exileFromTopThis(ability, this)); - else - setInput(exileFromTopType(ability, cost.getExileFromTopType(), this)); - return false; - } - - if (!payReturn && cost.getReturnCost()) { // return stuff here - if (cost.getReturnThis()) - setInput(returnThis(ability, this)); - else - setInput(returnType(ability, cost.getReturnType(), this)); - return false; - } - - resetUndoList(); - req.finishPaying(); - return true; - } - - /** - *

isAllPaid.

- * - * @return a boolean. - */ - public boolean isAllPaid() { - // if you add a new Cost type add it here - return (payTap && payUntap && payMana && payXMana && paySubCounter && payAddCounter && - paySac && payExile && payLife && payDiscard && payTapXType && payReturn && - payExileFromHand && payExileFromGrave && payExileFromTop); - } - - /** - *

resetUndoList.

- */ - public void resetUndoList() { - // TODO: clear other undoLists here? - payTapXTypeTappedList.clear(); - } - - /** - *

cancelPayment.

- */ - public void cancelPayment() { - // unpay anything we can. - if (cost.getTap() && payTap) { - // untap if tapped - card.untap(); - } - if (cost.getUntap() && payUntap) { - // tap if untapped - card.tap(); - } - // refund mana - AllZone.getManaPool().unpaid(ability, false); - - if (cost.getTapXTypeCost()) { // Can't depend on payTapXType if canceling before tapping enough - - for (Card c : payTapXTypeTappedList) - c.untap(); - //needed? - payTapXTypeTappedList.clear(); - } - - // refund counters - if (cost.getSubCounter() && paySubCounter) { - card.addCounterFromNonEffect(cost.getCounterType(), cost.getCounterNum()); - } - - // remove added counters - if (cost.getAddCounter() && payAddCounter) { - card.subtractCounter(cost.getCounterType(), cost.getCounterNum()); - } - - // refund life - if (cost.getLifeCost() && payLife) { - card.getController().payLife(cost.getLifeAmount() * -1, null); - } - - // can't really undiscard things - - // can't really unsacrifice things - - //can't really unexile things - - // can't really unexile things from hand - - // can't really unreturn things - } - - /** - *

payComputerCosts.

- * - * @return a boolean. - */ - public boolean payComputerCosts() { - // ******** NOTE for Adding Costs ************ - // make sure ComputerUtil.canPayAdditionalCosts() is updated so the AI knows if they can Pay the cost - CardList sacCard = new CardList(); - CardList exileCard = new CardList(); - CardList exileFromHandCard = new CardList(); - CardList exileFromGraveCard = new CardList(); - CardList exileFromTopCard = new CardList(); - CardList tapXCard = new CardList(); - CardList returnCard = new CardList(); - ability.setActivatingPlayer(AllZone.getComputerPlayer()); - - // double check if something can be sacrificed here. Real check is in ComputerUtil.canPayAdditionalCosts() - if (cost.getSacCost()) { - int amount = cost.getSacAmount(); - if (cost.getSacThis()) - sacCard.add(card); - else if (cost.isSacAll()) { - CardList typeList = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer()); - typeList = typeList.getValidCards(cost.getSacType().split(","), card.getController(), card); - sacCard.addAll(typeList); - amount = sacCard.size(); - } else - sacCard = ComputerUtil.chooseSacrificeType(cost.getSacType(), card, ability.getTargetCard(), cost.getSacAmount()); - - if (sacCard.size() != amount) { - System.out.println("Couldn't find a valid card to sacrifice for: " + card.getName()); - return false; - } - } - - // double check if something can be exiled here. Real check is in ComputerUtil.canPayAdditionalCosts() - if (cost.getExileCost()) { - if (cost.getExileThis()) - exileCard.add(card); - else - exileCard = ComputerUtil.chooseExileType(cost.getExileType(), card, ability.getTargetCard(), cost.getExileAmount()); - - - if (exileCard.size() != cost.getExileAmount()) { - System.out.println("Couldn't find a valid card to exile for: " + card.getName()); - return false; - } - } - - // double check if something can be exiled here. Real check is in ComputerUtil.canPayAdditionalCosts() - if (cost.getExileFromHandCost()) { - if (cost.getExileFromHandThis()) - exileFromHandCard.add(card); - else - exileFromHandCard = ComputerUtil.chooseExileFromHandType(cost.getExileFromHandType(), card, ability.getTargetCard(), cost.getExileFromHandAmount()); - - if (exileFromHandCard.size() != cost.getExileFromHandAmount()) { - System.out.println("Couldn't find a valid card to exile for: " + card.getName()); - return false; - } - } - - if (cost.getExileFromGraveCost()) { - if (cost.getExileFromGraveThis()) - exileFromGraveCard.add(card); - else - exileFromGraveCard = ComputerUtil.chooseExileFromGraveType( - cost.getExileFromGraveType(), card, ability.getTargetCard(), cost.getExileFromGraveAmount()); - - if (exileFromGraveCard.size() != cost.getExileFromGraveAmount()) { - System.out.println("Couldn't find a valid card to exile for: " + card.getName()); - return false; - } - } - - if (cost.getExileFromTopCost()) { - if (cost.getExileFromTopThis()) - exileFromTopCard.add(card); - else - exileFromTopCard = AllZoneUtil.getPlayerCardsInLibrary(AllZone.getComputerPlayer(), cost.getExileFromTopAmount()); - - if (exileFromTopCard.size() != cost.getExileFromTopAmount()) { - System.out.println("Couldn't find a valid card to exile for: " + card.getName()); - return false; - } - } - - if (cost.getReturnCost()) { - if (cost.getReturnThis()) - returnCard.add(card); - else - returnCard = ComputerUtil.chooseReturnType(cost.getReturnType(), card, ability.getTargetCard(), cost.getReturnAmount()); - - if (returnCard.size() != cost.getReturnAmount()) { - System.out.println("Couldn't find a valid card to return for: " + card.getName()); - return false; - } - } - - if (cost.getDiscardThis()) { - if (!AllZoneUtil.getPlayerHand(card.getController()).contains(card.getController().getLastDrawnCard())) { - return false; - } - if (!AllZone.getZone(card).getZoneName().equals(Constant.Zone.Hand)) - return false; - } - - if (cost.getTapXTypeCost()) { - boolean tap = cost.getTap(); - - tapXCard = ComputerUtil.chooseTapType(cost.getTapXType(), card, tap, cost.getTapXTypeAmount()); - - if (tapXCard == null || tapXCard.size() != cost.getTapXTypeAmount()) { - System.out.println("Couldn't find a valid card to tap for: " + card.getName()); - return false; - } - } - - // double check if counters available? Real check is in ComputerUtil.canPayAdditionalCosts() - if (cost.getSubCounter() && cost.getCounterNum() > card.getCounters(cost.getCounterType())) { - System.out.println("Not enough " + cost.getCounterType() + " on " + card.getName()); - return false; - } - - if (cost.getTap()) - card.tap(); - - if (cost.getUntap()) - card.untap(); - - if (!cost.hasNoManaCost()) - ComputerUtil.payManaCost(ability); - - if (cost.getTapXTypeCost()) { - for (Card c : tapXCard) - c.tap(); - } - - if (cost.getSubCounter()) - card.subtractCounter(cost.getCounterType(), cost.getCounterNum()); - - if (cost.getAddCounter()) { - card.addCounterFromNonEffect(cost.getCounterType(), cost.getCounterNum()); - } - - if (cost.getLifeCost()) - AllZone.getComputerPlayer().payLife(cost.getLifeAmount(), null); - - if (cost.getDiscardCost()) { - String discType = cost.getDiscardType(); - int discAmount = cost.getDiscardAmount(); - - if (cost.getDiscardThis()) { - card.getController().discard(card, ability); - } else if (discType.equals("Hand")) { - card.getController().discardHand(ability); - } else { - if (discType.equals("Random")) { - card.getController().discardRandom(discAmount, ability); - } else { - if (!discType.equals("Any")) { - String validType[] = discType.split(";"); - AllZone.getGameAction().AI_discardNumType(discAmount, validType, ability); - } else { - AllZone.getComputerPlayer().discard(discAmount, ability, false); - } - } - } - } - - if (cost.getSacCost()) { - for (Card c : sacCard) - AllZone.getGameAction().sacrifice(c); - } - - if (cost.getExileCost()) { - for (Card c : exileCard) - AllZone.getGameAction().exile(c); - } - - if (cost.getExileFromHandCost()) { - for (Card c : exileFromHandCard) - AllZone.getGameAction().exile(c); - } - - if (cost.getExileFromGraveCost()) { - for (Card c : exileFromGraveCard) - AllZone.getGameAction().exile(c); - } - - if (cost.getExileFromTopCost()) { - for (Card c : exileFromTopCard) - AllZone.getGameAction().exile(c); - } - - if (cost.getReturnCost()) { - for (Card c : returnCard) - AllZone.getGameAction().moveToHand(c); - } - return true; - } - - /** - *

changeCost.

- */ - public void changeCost() { - cost.changeCost(ability); - } - - - // ****************************************************************************** - // *********** Inputs used by Cost_Payment below here *************************** - // ****************************************************************************** - - /** - *

input_payMana.

- * - * @param sa a {@link forge.card.spellability.SpellAbility} object. - * @param payment a {@link forge.card.spellability.Cost_Payment} object. - * @param manaToAdd a int. - * @return a {@link forge.gui.input.Input} object. - */ - public static Input input_payMana(final SpellAbility sa, final Cost_Payment payment, int manaToAdd) { - final ManaCost manaCost; - - if (Phase.getGameBegins() == 1) { - if (sa.getSourceCard().isCopiedSpell() && sa.isSpell()) { - manaCost = new ManaCost("0"); - } else { - String mana = payment.getCost().getMana().replace("X", "").trim(); - manaCost = new ManaCost(mana); - manaCost.increaseColorlessMana(manaToAdd); - } - } else { - manaCost = new ManaCost(sa.getManaCost()); - } - - Input payMana = new Input() { - private ManaCost mana = manaCost; - private static final long serialVersionUID = 3467312982164195091L; - - private final String originalManaCost = payment.getCost().getMana(); - - private int phyLifeToLose = 0; - - private void resetManaCost() { - mana = new ManaCost(originalManaCost); - phyLifeToLose = 0; - } - - @Override - public void selectCard(Card card, PlayerZone zone) { - // prevent cards from tapping themselves if ability is a tapability, although it should already be tapped - if (sa.getSourceCard().equals(card) && sa.isTapAbility()) { - return; - } - - mana = Input_PayManaCostUtil.activateManaAbility(sa, card, mana); - - if (mana.isPaid()) - done(); - else if (AllZone.getInputControl().getInput() == this) - showMessage(); - } - - @Override - public void selectPlayer(Player player) { - if (player.isHuman()) { - if (manaCost.payPhyrexian()) { - phyLifeToLose += 2; - } - - showMessage(); - - } - - } - - private void done() { - if (phyLifeToLose > 0) - AllZone.getHumanPlayer().payLife(phyLifeToLose, sa.getSourceCard()); - sa.getSourceCard().setColorsPaid(mana.getColorsPaid()); - sa.getSourceCard().setSunburstValue(mana.getSunburst()); - resetManaCost(); - payment.setPayMana(true); - stop(); - payment.payCost(); - } - - @Override - public void selectButtonCancel() { - resetManaCost(); - payment.setCancel(true); - payment.payCost(); - AllZone.getHumanBattlefield().updateObservers();//DO NOT REMOVE THIS, otherwise the cards don't always tap - stop(); - } - - @Override - public void showMessage() { - ButtonUtil.enableOnlyCancel(); - String displayMana = mana.toString().replace("X", "").trim(); - AllZone.getDisplay().showMessage("Pay Mana Cost: " + displayMana); - - StringBuilder msg = new StringBuilder("Pay Mana Cost: " + displayMana); - if (phyLifeToLose > 0) { - msg.append(" ("); - msg.append(phyLifeToLose); - msg.append(" life paid for phyrexian mana)"); - } - - if (mana.containsPhyrexianMana()) { - msg.append("\n(Click on your life total to pay life for phyrexian mana.)"); - } - - AllZone.getDisplay().showMessage(msg.toString()); - if (mana.isPaid()) - done(); - } - }; - return payMana; - } - - /** - *

input_payXMana.

- * - * @param numX a int. - * @param sa a {@link forge.card.spellability.SpellAbility} object. - * @param payment a {@link forge.card.spellability.Cost_Payment} object. - * @return a {@link forge.gui.input.Input} object. - */ - public static Input input_payXMana(final int numX, final SpellAbility sa, final Cost_Payment payment) { - Input payX = new Input() { - private static final long serialVersionUID = -6900234444347364050L; - int xPaid = 0; - ManaCost manaCost = new ManaCost(Integer.toString(numX)); - - @Override - public void showMessage() { - if (manaCost.toString().equals(Integer.toString(numX))) // Can only cancel if partially paid an X value - ButtonUtil.enableAll(); - else - ButtonUtil.enableOnlyCancel(); - - AllZone.getDisplay().showMessage("Pay X Mana Cost for " + sa.getSourceCard().getName() + "\n" + xPaid + " Paid so far."); - } - - // selectCard - @Override - public void selectCard(Card card, PlayerZone zone) { - if (sa.getSourceCard().equals(card) && sa.isTapAbility()) { - // this really shouldn't happen but just in case - return; - } - - manaCost = Input_PayManaCostUtil.activateManaAbility(sa, card, manaCost); - if (manaCost.isPaid()) { - manaCost = new ManaCost(Integer.toString(numX)); - xPaid++; - } - - if (AllZone.getInputControl().getInput() == this) - showMessage(); - } - - @Override - public void selectButtonCancel() { - payment.setCancel(true); - payment.payCost(); - AllZone.getHumanBattlefield().updateObservers();//DO NOT REMOVE THIS, otherwise the cards don't always tap - stop(); - } - - @Override - public void selectButtonOK() { - payment.setPayXMana(true); - payment.getCard().setXManaCostPaid(xPaid); - stop(); - payment.payCost(); - } - - }; - - return payX; - } - - - /** - *

input_discardCost.

- * - * @param nCards a int. - * @param discType a {@link java.lang.String} object. - * @param handList a {@link forge.CardList} object. - * @param sa a {@link forge.card.spellability.SpellAbility} object. - * @param payment a {@link forge.card.spellability.Cost_Payment} object. - * @return a {@link forge.gui.input.Input} object. - */ - public static Input input_discardCost(final int nCards, final String discType, final CardList handList, SpellAbility sa, final Cost_Payment payment) { - final SpellAbility sp = sa; - Input target = new Input() { - private static final long serialVersionUID = -329993322080934435L; - - int nDiscard = 0; - - @Override - public void showMessage() { - boolean any = discType.equals("Any") ? true : false; - if (AllZone.getHumanHand().size() == 0) stop(); - StringBuilder type = new StringBuilder(""); - if (any || !discType.equals("Card")) { - type.append(" ").append(discType); - } - StringBuilder sb = new StringBuilder(); - sb.append("Select "); - if (any) { - sb.append("any "); - } else { - sb.append("a ").append(type.toString()).append(" "); - } - sb.append("card to discard."); - if (nCards > 1) { - sb.append(" You have "); - sb.append(nCards - nDiscard); - sb.append(" remaining."); - } - AllZone.getDisplay().showMessage(sb.toString()); - ButtonUtil.enableOnlyCancel(); - } - - @Override - public void selectButtonCancel() { - cancel(); - } - - @Override - public void selectCard(Card card, PlayerZone zone) { - if (zone.is(Constant.Zone.Hand) && handList.contains(card)) { - // send in CardList for Typing - card.getController().discard(card, sp); - handList.remove(card); - nDiscard++; - - //in case no more cards in hand - if (nDiscard == nCards) - done(); - else if (AllZone.getHumanHand().size() == 0) // this really shouldn't happen - cancel(); - else - showMessage(); - } - } - - public void cancel() { - payment.setCancel(true); - stop(); - payment.payCost(); - } - - public void done() { - payment.setPayDiscard(true); - stop(); - payment.payCost(); - } - }; - - return target; - }//input_discard() - - /** - *

sacrificeThis.

- * - * @param sa a {@link forge.card.spellability.SpellAbility} object. - * @param payment a {@link forge.card.spellability.Cost_Payment} object. - * @return a {@link forge.gui.input.Input} object. - */ - public static Input sacrificeThis(final SpellAbility sa, final Cost_Payment payment) { - Input target = new Input() { - private static final long serialVersionUID = 2685832214519141903L; - - @Override - public void showMessage() { - Card card = sa.getSourceCard(); - if (card.getController().isHuman() && AllZoneUtil.isCardInPlay(card)) { - StringBuilder sb = new StringBuilder(); - sb.append(card.getName()); - sb.append(" - Sacrifice?"); - Object[] possibleValues = {"Yes", "No"}; - Object choice = JOptionPane.showOptionDialog(null, sb.toString(), card.getName() + " - Cost", - JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, - null, possibleValues, possibleValues[0]); - if (choice.equals(0)) { - payment.setPaySac(true); - payment.getAbility().addCostToHashList(card, "Sacrificed"); - AllZone.getGameAction().sacrifice(card); - stop(); - payment.payCost(); - } else { - payment.setCancel(true); - stop(); - payment.payCost(); - } - } - } - }; - - return target; - }//input_sacrifice() - - /** - *

sacrificeType.

- * - * @param sa a {@link forge.card.spellability.SpellAbility} object. - * @param type a {@link java.lang.String} object. - * @param payment a {@link forge.card.spellability.Cost_Payment} object. - * @return a {@link forge.gui.input.Input} object. - */ - public static Input sacrificeType(final SpellAbility sa, final String type, final Cost_Payment payment) { - Input target = new Input() { - private static final long serialVersionUID = 2685832214519141903L; - private CardList typeList; - private int nSacrifices = 0; - private int nNeeded = payment.getCost().getSacAmount(); - - @Override - public void showMessage() { - StringBuilder msg = new StringBuilder("Sacrifice "); - int nLeft = nNeeded - nSacrifices; - msg.append(nLeft).append(" "); - msg.append(type); - if (nLeft > 1) { - msg.append("s"); - } - - typeList = AllZoneUtil.getPlayerCardsInPlay(sa.getSourceCard().getController()); - typeList = typeList.getValidCards(type.split(";"), sa.getActivatingPlayer(), sa.getSourceCard()); - AllZone.getDisplay().showMessage(msg.toString()); - ButtonUtil.enableOnlyCancel(); - } - - @Override - public void selectButtonCancel() { - cancel(); - } - - @Override - public void selectCard(Card card, PlayerZone zone) { - if (typeList.contains(card)) { - nSacrifices++; - payment.getAbility().addCostToHashList(card, "Sacrificed"); - AllZone.getGameAction().sacrifice(card); - typeList.remove(card); - //in case nothing else to sacrifice - if (nSacrifices == nNeeded) - done(); - else if (typeList.size() == 0) // this really shouldn't happen - cancel(); - else - showMessage(); - } - } - - public void done() { - payment.setPaySac(true); - stop(); - payment.payCost(); - } - - public void cancel() { - payment.setCancel(true); - stop(); - payment.payCost(); - } - }; - - return target; - }//sacrificeType() - - /** - *

sacrificeAllType.

- * - * @param sa a {@link forge.card.spellability.SpellAbility} object. - * @param type a {@link java.lang.String} object. - * @param payment a {@link forge.card.spellability.Cost_Payment} object. - */ - public static void sacrificeAllType(final SpellAbility sa, final String type, final Cost_Payment payment) { - // TODO Ask First - - CardList typeList; - typeList = AllZoneUtil.getPlayerCardsInPlay(sa.getActivatingPlayer()); - typeList = typeList.getValidCards(type.split(";"), sa.getActivatingPlayer(), sa.getSourceCard()); - - for (Card card : typeList) { - payment.getAbility().addCostToHashList(card, "Sacrificed"); - AllZone.getGameAction().sacrifice(card); - } - - payment.setPaySac(true); - payment.payCost(); - } - - /** - *

sacrificeXType.

- * - * @param sa a {@link forge.card.spellability.SpellAbility} object. - * @param type a {@link java.lang.String} object. - * @param payment a {@link forge.card.spellability.Cost_Payment} object. - * @return a {@link forge.gui.input.Input} object. - */ - public static Input sacrificeXType(final SpellAbility sa, final String type, final Cost_Payment payment) { - Input target = new Input() { - private static final long serialVersionUID = -4496270321029213839L; - private CardList typeList; - private int nSacrifices = 0; - - @Override - public void showMessage() { - StringBuilder msg = new StringBuilder("Sacrifice X "); - msg.append(type).append("s. "); - msg.append("(").append(nSacrifices).append(" sacrificed so far.)"); - - typeList = AllZoneUtil.getPlayerCardsInPlay(sa.getSourceCard().getController()); - typeList = typeList.getValidCards(type.split(";"), sa.getActivatingPlayer(), sa.getSourceCard()); - AllZone.getDisplay().showMessage(msg.toString()); - ButtonUtil.enableAll(); - } - - @Override - public void selectButtonCancel() { - cancel(); - } - - @Override - public void selectButtonOK() { - done(); - } - - @Override - public void selectCard(Card card, PlayerZone zone) { - if (typeList.contains(card)) { - nSacrifices++; - payment.getAbility().addCostToHashList(card, "Sacrificed"); - AllZone.getGameAction().sacrifice(card); - typeList.remove(card); - if (typeList.size() == 0) // this really shouldn't happen - done(); - else - showMessage(); - } - } - - public void done() { - payment.setPaySac(true); - stop(); - payment.payCost(); - } - - public void cancel() { - payment.setCancel(true); - stop(); - payment.payCost(); - } - }; - - return target; - }//sacrificeXType() - - /** - *

exileThis.

- * - * @param sa a {@link forge.card.spellability.SpellAbility} object. - * @param payment a {@link forge.card.spellability.Cost_Payment} object. - * @return a {@link forge.gui.input.Input} object. - */ - public static Input exileThis(final SpellAbility sa, final Cost_Payment payment) { - Input target = new Input() { - private static final long serialVersionUID = 678668673002725001L; - - @Override - public void showMessage() { - Card card = sa.getSourceCard(); - if (card.getController().isHuman() && AllZoneUtil.isCardInPlay(card)) { - StringBuilder sb = new StringBuilder(); - sb.append(card.getName()); - sb.append(" - Exile?"); - Object[] possibleValues = {"Yes", "No"}; - Object choice = JOptionPane.showOptionDialog(null, sb.toString(), card.getName() + " - Cost", - JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, - null, possibleValues, possibleValues[0]); - if (choice.equals(0)) { - payment.setPayExile(true); - payment.getAbility().addCostToHashList(card, "Exiled"); - AllZone.getGameAction().exile(card); - stop(); - payment.payCost(); - } else { - payment.setCancel(true); - stop(); - payment.payCost(); - } - } - } - }; - - return target; - }//input_exile() - - /** - *

exileFromHandThis.

- * - * @param spell a {@link forge.card.spellability.SpellAbility} object. - * @param payment a {@link forge.card.spellability.Cost_Payment} object. - * @return a {@link forge.gui.input.Input} object. - */ - public static Input exileFromHandThis(final SpellAbility spell, final Cost_Payment payment) { - Input target = new Input() { - private static final long serialVersionUID = 2651542083913697972L; - - @Override - public void showMessage() { - Card card = spell.getSourceCard(); - if (card.getController().isHuman() && AllZoneUtil.isCardInPlayerHand(card.getController(), card)) { - StringBuilder sb = new StringBuilder(); - sb.append(card.getName()); - sb.append(" - Exile?"); - Object[] possibleValues = {"Yes", "No"}; - Object choice = JOptionPane.showOptionDialog(null, sb.toString(), card.getName() + " - Cost", - JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, - null, possibleValues, possibleValues[0]); - if (choice.equals(0)) { - payment.setPayExileFromHand(true); - payment.getAbility().addCostToHashList(card, "Exiled"); - AllZone.getGameAction().exile(card); - stop(); - payment.payCost(); - } else { - payment.setCancel(true); - stop(); - payment.payCost(); - } - } - } - }; - return target; - }//input_exile() - - /** - *

exileFromTopThis.

- * - * @param spell a {@link forge.card.spellability.SpellAbility} object. - * @param payment a {@link forge.card.spellability.Cost_Payment} object. - * @return a {@link forge.gui.input.Input} object. - */ - public static Input exileFromTopThis(final SpellAbility spell, final Cost_Payment payment) { - Input target = new Input() { - private static final long serialVersionUID = 3416809678763443014L; - - @Override - public void showMessage() { - Card card = spell.getSourceCard(); - if (card.getController().isHuman() && AllZoneUtil.isCardInPlayerHand(card.getController(), card)) { - //This can't really happen, but if for some reason it could.... - if (AllZoneUtil.getPlayerCardsInLibrary(card.getController()).size() > 0) { - payment.setPayExileFromTop(true); - payment.getAbility().addCostToHashList(card, "Exiled"); - AllZone.getGameAction().exile(card); - stop(); - payment.payCost(); - } else { - payment.setCancel(true); - stop(); - payment.payCost(); - } - } - } - }; - return target; - }//input_exile() - - /** - *

exileFromGraveThis.

- * - * @param spell a {@link forge.card.spellability.SpellAbility} object. - * @param payment a {@link forge.card.spellability.Cost_Payment} object. - * @return a {@link forge.gui.input.Input} object. - */ - public static Input exileFromGraveThis(final SpellAbility spell, final Cost_Payment payment) { - Input target = new Input() { - private static final long serialVersionUID = 6237561876518762902L; - - @Override - public void showMessage() { - Card card = spell.getSourceCard(); - if (card.getController().isHuman() && AllZoneUtil.isCardInPlayerGraveyard(card.getController(), card)) { - StringBuilder sb = new StringBuilder(); - sb.append(card.getName()); - sb.append(" - Exile?"); - Object[] possibleValues = {"Yes", "No"}; - Object choice = JOptionPane.showOptionDialog(null, sb.toString(), card.getName() + " - Cost", - JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, - null, possibleValues, possibleValues[0]); - if (choice.equals(0)) { - payment.setPayExileFromGrave(true); - payment.getAbility().addCostToHashList(card, "Exiled"); - AllZone.getGameAction().exile(card); - stop(); - payment.payCost(); - } else { - payment.setCancel(true); - stop(); - payment.payCost(); - } - } - } - }; - return target; - }//input_exile() - - /** - *

exileType.

- * - * @param sa a {@link forge.card.spellability.SpellAbility} object. - * @param type a {@link java.lang.String} object. - * @param payment a {@link forge.card.spellability.Cost_Payment} object. - * @return a {@link forge.gui.input.Input} object. - */ - public static Input exileType(final SpellAbility sa, final String type, final Cost_Payment payment) { - Input target = new Input() { - private static final long serialVersionUID = 1403915758082824694L; - - private CardList typeList; - private int nExiles = 0; - private int nNeeded = payment.getCost().getExileAmount(); - - @Override - public void showMessage() { - StringBuilder msg = new StringBuilder("Exile "); - int nLeft = nNeeded - nExiles; - msg.append(nLeft).append(" "); - msg.append(type); - if (nLeft > 1) { - msg.append("s"); - } - - typeList = AllZoneUtil.getPlayerCardsInPlay(sa.getSourceCard().getController()); - typeList = typeList.getValidCards(type.split(";"), sa.getActivatingPlayer(), sa.getSourceCard()); - AllZone.getDisplay().showMessage(msg.toString()); - ButtonUtil.enableOnlyCancel(); - } - - @Override - public void selectButtonCancel() { - cancel(); - } - - @Override - public void selectCard(Card card, PlayerZone zone) { - if (typeList.contains(card)) { - nExiles++; - payment.getAbility().addCostToHashList(card, "Exiled"); - AllZone.getGameAction().exile(card); - typeList.remove(card); - //in case nothing else to exile - if (nExiles == nNeeded) - done(); - else if (typeList.size() == 0) // this really shouldn't happen - cancel(); - else - showMessage(); - } - } - - public void done() { - payment.setPayExile(true); - stop(); - payment.payCost(); - } - - public void cancel() { - payment.setCancel(true); - stop(); - payment.payCost(); - } - }; - - return target; - }//exileType() - - /** - *

exileFromHandType.

- * - * @param spell a {@link forge.card.spellability.SpellAbility} object. - * @param type a {@link java.lang.String} object. - * @param payment a {@link forge.card.spellability.Cost_Payment} object. - * @return a {@link forge.gui.input.Input} object. - */ - public static Input exileFromHandType(final SpellAbility spell, final String type, final Cost_Payment payment) { - Input target = new Input() { - private static final long serialVersionUID = 759041801001973859L; - private CardList typeList; - private int nExiles = 0; - private int nNeeded = payment.getCost().getExileFromHandAmount(); - - @Override - public void showMessage() { - StringBuilder msg = new StringBuilder("Exile "); - int nLeft = nNeeded - nExiles; - msg.append(nLeft).append(" "); - msg.append(type); - if (nLeft > 1) { - msg.append("s"); - } - msg.append(" from your hand"); - - typeList = AllZoneUtil.getPlayerHand(spell.getSourceCard().getController()); - typeList = typeList.getValidCards(type.split(";"), spell.getActivatingPlayer(), spell.getSourceCard()); - AllZone.getDisplay().showMessage(msg.toString()); - ButtonUtil.enableOnlyCancel(); - } - - @Override - public void selectButtonCancel() { - cancel(); - } - - @Override - public void selectCard(Card card, PlayerZone zone) { - if (typeList.contains(card)) { - nExiles++; - payment.getAbility().addCostToHashList(card, "Exiled"); - AllZone.getGameAction().exile(card); - typeList.remove(card); - //in case nothing else to exile - if (nExiles == nNeeded) - done(); - else if (typeList.size() == 0) // this really shouldn't happen - cancel(); - else - showMessage(); - } - } - - public void done() { - payment.setPayExileFromHand(true); - stop(); - payment.payCost(); - } - - public void cancel() { - payment.setCancel(true); - stop(); - payment.payCost(); - } - }; - return target; - }//exileFromHandType() - - /** - *

exileFromGraveType.

- * - * @param spell a {@link forge.card.spellability.SpellAbility} object. - * @param type a {@link java.lang.String} object. - * @param payment a {@link forge.card.spellability.Cost_Payment} object. - * @return a {@link forge.gui.input.Input} object. - */ - public static Input exileFromGraveType(final SpellAbility spell, final String type, final Cost_Payment payment) { - Input target = new Input() { - private static final long serialVersionUID = 734256837615635021L; - - @Override - public void showMessage() { - CardList typeList; - int nNeeded = payment.getCost().getExileFromGraveAmount(); - typeList = AllZoneUtil.getPlayerGraveyard(spell.getSourceCard().getController()); - typeList = typeList.getValidCards(type.split(";"), spell.getActivatingPlayer(), spell.getSourceCard()); - - for (int i = 0; i < nNeeded; i++) { - if (typeList.size() == 0) - cancel(); - - Object o = GuiUtils.getChoiceOptional("Exile from grave", typeList.toArray()); - - if (o != null) { - Card c = (Card) o; - typeList.remove(c); - payment.getAbility().addCostToHashList(c, "Exiled"); - AllZone.getGameAction().exile(c); - if (i == nNeeded - 1) done(); - } - } - } - - @Override - public void selectButtonCancel() { - cancel(); - } - - public void done() { - payment.setPayExileFromGrave(true); - stop(); - payment.payCost(); - } - - public void cancel() { - payment.setCancel(true); - stop(); - payment.payCost(); - } - }; - return target; - }//exileFromGraveType() - - /** - *

exileFromTopType.

- * - * @param spell a {@link forge.card.spellability.SpellAbility} object. - * @param type a {@link java.lang.String} object. - * @param payment a {@link forge.card.spellability.Cost_Payment} object. - * @return a {@link forge.gui.input.Input} object. - */ - public static Input exileFromTopType(final SpellAbility spell, final String type, final Cost_Payment payment) { - Input target = new Input() { - private static final long serialVersionUID = -4764871768555887091L; - - @Override - public void showMessage() { - CardList typeList; - int nNeeded = payment.getCost().getExileFromTopAmount(); - PlayerZone lib = AllZone.getZone(Constant.Zone.Library, spell.getSourceCard().getController()); - typeList = AllZoneUtil.getPlayerCardsInLibrary(spell.getSourceCard().getController()); - typeList = typeList.getValidCards(type.split(";"), spell.getActivatingPlayer(), spell.getSourceCard()); - - for (int i = 0; i < nNeeded; i++) { - if (typeList.size() == 0) - cancel(); - - if (lib.size() > 0) { - Card c = typeList.get(0); - typeList.remove(c); - payment.getAbility().addCostToHashList(c, "Exiled"); - AllZone.getGameAction().exile(c); - if (i == nNeeded - 1) done(); - } - } - } - - @Override - public void selectButtonCancel() { - cancel(); - } - - public void done() { - payment.setPayExileFromTop(true); - stop(); - payment.payCost(); - } - - public void cancel() { - payment.setCancel(true); - stop(); - payment.payCost(); - } - }; - return target; - }//exileFromTopType() - - /** - *

input_tapXCost.

- * - * @param nCards a int. - * @param cardType a {@link java.lang.String} object. - * @param cardList a {@link forge.CardList} object. - * @param sa a {@link forge.card.spellability.SpellAbility} object. - * @param payment a {@link forge.card.spellability.Cost_Payment} object. - * @return a {@link forge.gui.input.Input} object. - */ - public static Input input_tapXCost(final int nCards, final String cardType, final CardList cardList, SpellAbility sa, final Cost_Payment payment) { - //final SpellAbility sp = sa; - Input target = new Input() { - - private static final long serialVersionUID = 6438988130447851042L; - int nTapped = 0; - - @Override - public void showMessage() { - if (cardList.size() == 0) stop(); - - int left = nCards - nTapped; - AllZone.getDisplay().showMessage("Select a " + cardType + " to tap (" + left + " left)"); - ButtonUtil.enableOnlyCancel(); - } - - @Override - public void selectButtonCancel() { - cancel(); - } - - @Override - public void selectCard(Card card, PlayerZone zone) { - if (zone.is(Constant.Zone.Battlefield) && cardList.contains(card) && card.isUntapped()) { - // send in CardList for Typing - card.tap(); - payment.addPayTapXTypeTappedList(card); - cardList.remove(card); - payment.getAbility().addCostToHashList(card, "Tapped"); - nTapped++; - - if (nTapped == nCards) - done(); - else if (cardList.size() == 0) // this really shouldn't happen - cancel(); - else - showMessage(); - } - } - - public void cancel() { - payment.setCancel(true); - stop(); - payment.payCost(); - } - - public void done() { - payment.setPayTapXType(true); - stop(); - payment.payCost(); - } - }; - - return target; - }//input_tapXCost() - - /** - *

returnThis.

- * - * @param sa a {@link forge.card.spellability.SpellAbility} object. - * @param payment a {@link forge.card.spellability.Cost_Payment} object. - * @return a {@link forge.gui.input.Input} object. - */ - public static Input returnThis(final SpellAbility sa, final Cost_Payment payment) { - Input target = new Input() { - private static final long serialVersionUID = 2685832214519141903L; - - @Override - public void showMessage() { - Card card = sa.getSourceCard(); - if (card.getController().isHuman() && AllZoneUtil.isCardInPlay(card)) { - StringBuilder sb = new StringBuilder(); - sb.append(card.getName()); - sb.append(" - Return to Hand?"); - Object[] possibleValues = {"Yes", "No"}; - Object choice = JOptionPane.showOptionDialog(null, sb.toString(), card.getName() + " - Cost", - JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, - null, possibleValues, possibleValues[0]); - if (choice.equals(0)) { - payment.setPayReturn(true); - AllZone.getGameAction().moveToHand(card); - stop(); - payment.payCost(); - } else { - payment.setCancel(true); - stop(); - payment.payCost(); - } - } - } - }; - - return target; - }//input_sacrifice() - - /** - *

returnType.

- * - * @param sa a {@link forge.card.spellability.SpellAbility} object. - * @param type a {@link java.lang.String} object. - * @param payment a {@link forge.card.spellability.Cost_Payment} object. - * @return a {@link forge.gui.input.Input} object. - */ - public static Input returnType(final SpellAbility sa, final String type, final Cost_Payment payment) { - Input target = new Input() { - private static final long serialVersionUID = 2685832214519141903L; - private CardList typeList; - private int nReturns = 0; - private int nNeeded = payment.getCost().getReturnAmount(); - - @Override - public void showMessage() { - StringBuilder msg = new StringBuilder("Return "); - int nLeft = nNeeded - nReturns; - msg.append(nLeft).append(" "); - msg.append(type); - if (nLeft > 1) { - msg.append("s"); - } - - typeList = AllZoneUtil.getPlayerCardsInPlay(sa.getSourceCard().getController()); - typeList = typeList.getValidCards(type.split(";"), sa.getActivatingPlayer(), sa.getSourceCard()); - AllZone.getDisplay().showMessage(msg.toString()); - ButtonUtil.enableOnlyCancel(); - } - - @Override - public void selectButtonCancel() { - cancel(); - } - - @Override - public void selectCard(Card card, PlayerZone zone) { - if (typeList.contains(card)) { - nReturns++; - AllZone.getGameAction().moveToHand(card); - typeList.remove(card); - //in case nothing else to return - if (nReturns == nNeeded) - done(); - else if (typeList.size() == 0) // this really shouldn't happen - cancel(); - else - showMessage(); - } - } - - public void done() { - payment.setPayReturn(true); - stop(); - payment.payCost(); - } - - public void cancel() { - payment.setCancel(true); - stop(); - payment.payCost(); - } - }; - - return target; - }//returnType() -} diff --git a/src/main/java/forge/card/spellability/Spell.java b/src/main/java/forge/card/spellability/Spell.java index 0f5d235051d..54ad4d95ab5 100644 --- a/src/main/java/forge/card/spellability/Spell.java +++ b/src/main/java/forge/card/spellability/Spell.java @@ -1,6 +1,8 @@ package forge.card.spellability; import forge.*; +import forge.card.cost.Cost; +import forge.card.cost.Cost_Payment; import forge.error.ErrorViewer; @@ -32,7 +34,7 @@ abstract public class Spell extends SpellAbility implements java.io.Serializable *

Constructor for Spell.

* * @param sourceCard a {@link forge.Card} object. - * @param abCost a {@link forge.card.spellability.Cost} object. + * @param abCost a {@link forge.card.cost.Cost} object. * @param abTgt a {@link forge.card.spellability.Target} object. */ public Spell(Card sourceCard, Cost abCost, Target abTgt) { diff --git a/src/main/java/forge/card/spellability/SpellAbility.java b/src/main/java/forge/card/spellability/SpellAbility.java index 9188b9f6b6e..61aca0ea4b1 100644 --- a/src/main/java/forge/card/spellability/SpellAbility.java +++ b/src/main/java/forge/card/spellability/SpellAbility.java @@ -2,6 +2,7 @@ package forge.card.spellability; import forge.*; import forge.card.abilityFactory.AbilityFactory; +import forge.card.cost.Cost; import forge.card.mana.Mana; import forge.gui.input.Input; @@ -505,7 +506,7 @@ public abstract class SpellAbility { /** *

Getter for the field payCosts.

* - * @return a {@link forge.card.spellability.Cost} object. + * @return a {@link forge.card.cost.Cost} object. */ public Cost getPayCosts() { return payCosts; @@ -514,7 +515,7 @@ public abstract class SpellAbility { /** *

Setter for the field payCosts.

* - * @param abCost a {@link forge.card.spellability.Cost} object. + * @param abCost a {@link forge.card.cost.Cost} object. */ public void setPayCosts(Cost abCost) { payCosts = abCost; @@ -752,6 +753,14 @@ public abstract class SpellAbility { chosenTarget.resetTargets(); resetTriggeringObjects(); + + //Clear SVars + for(String store : Card.getStorableSVars()){ + String value = sourceCard.getSVar(store); + if (value.length() > 0){ + sourceCard.setSVar(store, ""); + } + } } /** diff --git a/src/main/java/forge/card/spellability/SpellAbility_Requirements.java b/src/main/java/forge/card/spellability/SpellAbility_Requirements.java index e3760c18b24..faef3d7f6e1 100644 --- a/src/main/java/forge/card/spellability/SpellAbility_Requirements.java +++ b/src/main/java/forge/card/spellability/SpellAbility_Requirements.java @@ -4,6 +4,7 @@ import forge.AllZone; import forge.Card; import forge.PlayerZone; import forge.card.abilityFactory.AbilityFactory; +import forge.card.cost.Cost_Payment; import java.util.ArrayList; @@ -46,7 +47,7 @@ public class SpellAbility_Requirements { * * @param sa a {@link forge.card.spellability.SpellAbility} object. * @param ts a {@link forge.card.spellability.Target_Selection} object. - * @param cp a {@link forge.card.spellability.Cost_Payment} object. + * @param cp a {@link forge.card.cost.Cost_Payment} object. */ public SpellAbility_Requirements(SpellAbility sa, Target_Selection ts, Cost_Payment cp) { ability = sa; diff --git a/src/main/java/forge/card/spellability/SpellAbility_StackInstance.java b/src/main/java/forge/card/spellability/SpellAbility_StackInstance.java index b1f89392b49..28851b1310d 100644 --- a/src/main/java/forge/card/spellability/SpellAbility_StackInstance.java +++ b/src/main/java/forge/card/spellability/SpellAbility_StackInstance.java @@ -44,7 +44,9 @@ public class SpellAbility_StackInstance { // Triggers private HashMap triggeringObjects = new HashMap(); - + + private HashMap storedSVars = new HashMap(); + /** *

Constructor for SpellAbility_StackInstance.

* @@ -76,6 +78,17 @@ public class SpellAbility_StackInstance { tc = target.getTargetChoices(); ability.getTarget().resetTargets(); } + + Card source = ability.getSourceCard(); + + //Store SVars and Clear + for(String store : Card.getStorableSVars()){ + String value = source.getSVar(store); + if (value.length() > 0){ + storedSVars.put(store, value); + source.setSVar(store, ""); + } + } } /** @@ -101,6 +114,15 @@ public class SpellAbility_StackInstance { // Triggered ability.setAllTriggeringObjects(triggeringObjects); + // Add SVars back in + Card source = ability.getSourceCard(); + for(String store : storedSVars.keySet()){ + String value = storedSVars.get(store); + if (value.length() > 0){ + source.setSVar(store, value); + } + } + return ability; } diff --git a/src/main/java/forge/card/spellability/Spell_Permanent.java b/src/main/java/forge/card/spellability/Spell_Permanent.java index a345de24226..ef357d7ab7d 100644 --- a/src/main/java/forge/card/spellability/Spell_Permanent.java +++ b/src/main/java/forge/card/spellability/Spell_Permanent.java @@ -3,6 +3,7 @@ package forge.card.spellability; import forge.*; import forge.card.abilityFactory.AbilityFactory; import forge.card.cardFactory.CardFactoryUtil; +import forge.card.cost.Cost; import forge.card.trigger.Trigger; import forge.gui.input.Input; @@ -135,7 +136,7 @@ public class Spell_Permanent extends Spell { *

Constructor for Spell_Permanent.

* * @param sourceCard a {@link forge.Card} object. - * @param cost a {@link forge.card.spellability.Cost} object. + * @param cost a {@link forge.card.cost.Cost} object. * @param tgt a {@link forge.card.spellability.Target} object. */ public Spell_Permanent(Card sourceCard, Cost cost, Target tgt) { diff --git a/src/main/java/forge/card/trigger/TriggerHandler.java b/src/main/java/forge/card/trigger/TriggerHandler.java index 1c169ae2752..ff728bebfd5 100644 --- a/src/main/java/forge/card/trigger/TriggerHandler.java +++ b/src/main/java/forge/card/trigger/TriggerHandler.java @@ -2,6 +2,7 @@ package forge.card.trigger; import forge.*; import forge.card.abilityFactory.AbilityFactory; +import forge.card.cost.Cost; import forge.card.spellability.*; import forge.gui.input.Input; diff --git a/src/main/java/forge/card/trigger/Trigger_SpellAbilityCast.java b/src/main/java/forge/card/trigger/Trigger_SpellAbilityCast.java index b5f3ea95967..c6461f7f23b 100644 --- a/src/main/java/forge/card/trigger/Trigger_SpellAbilityCast.java +++ b/src/main/java/forge/card/trigger/Trigger_SpellAbilityCast.java @@ -3,7 +3,7 @@ package forge.card.trigger; import forge.AllZone; import forge.Card; import forge.Player; -import forge.card.spellability.Cost; +import forge.card.cost.Cost; import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility_StackInstance; diff --git a/src/main/java/forge/quest/data/pet/QuestPetPlant.java b/src/main/java/forge/quest/data/pet/QuestPetPlant.java index e5a7819ec54..fa330aaff0b 100644 --- a/src/main/java/forge/quest/data/pet/QuestPetPlant.java +++ b/src/main/java/forge/quest/data/pet/QuestPetPlant.java @@ -3,8 +3,8 @@ package forge.quest.data.pet; import forge.AllZone; import forge.Card; import forge.Constant; +import forge.card.cost.Cost; import forge.card.spellability.Ability_Activated; -import forge.card.spellability.Cost; import forge.card.spellability.SpellAbility; import forge.quest.data.bazaar.QuestStallManager;