diff --git a/src/main/java/forge/Card.java b/src/main/java/forge/Card.java index cf4b2921f1e..8adbaa0cb09 100644 --- a/src/main/java/forge/Card.java +++ b/src/main/java/forge/Card.java @@ -2790,11 +2790,10 @@ public class Card extends GameEntity implements Comparable { continue; } - if (am.isBasic() && !res.contains(am)) { - res.add(am); - } else if (am.isReflectedMana() && !res.contains(am)) { + if ((am.isBasic() || am.isReflectedMana()) && !res.contains(am)) { res.add(am); } + } return res; diff --git a/src/main/java/forge/ComputerUtil.java b/src/main/java/forge/ComputerUtil.java index aa3db3879ad..4128ca9ef36 100644 --- a/src/main/java/forge/ComputerUtil.java +++ b/src/main/java/forge/ComputerUtil.java @@ -36,6 +36,7 @@ import forge.card.spellability.AbilityMana; import forge.card.spellability.SpellAbility; import forge.card.spellability.Target; import forge.error.ErrorViewer; +import forge.gui.input.InputPayManaCostUtil; /** *

@@ -177,6 +178,8 @@ public class ComputerUtil { final CostPayment pay = new CostPayment(cost, sa); if (pay.payComputerCosts()) { AllZone.getStack().addAndUnfreeze(sa); + //TODO: solve problems with TapsForMana triggers by adding + // sources tapped here if possible (ArsenalNut) } } } @@ -608,121 +611,253 @@ public class ComputerUtil { return true; } - ArrayList colors; - cost = manapool.subtractMana(sa, cost); if (card.getSVar("ManaNeededToAvoidNegativeEffect") != "") { - cost.setManaNeededToAvoidNegativeEffect(card.getSVar("ManaNeededToAvoidNegativeEffect").split(",")); + String[] negEffects = card.getSVar("ManaNeededToAvoidNegativeEffect").split(","); + int amountAdded = 0; + for (int nStr = 0; nStr < negEffects.length; nStr++) { + // convert long color strings to short color strings + if (negEffects[nStr].length() > 1) { + negEffects[nStr] = InputPayManaCostUtil.getShortColorString(negEffects[nStr]); + } + // make mana mandatory for AI + if (!cost.isColor(negEffects[nStr])) { + cost.combineManaCost(negEffects[nStr]); + amountAdded++; + } + } + cost.setManaNeededToAvoidNegativeEffect(negEffects); + //TODO: should it be an error condition if amountAdded is greater than the colorless + // in the original cost? (ArsenalNut - 120102) + // adjust colorless amount to account for added mana + cost.decreaseColorlessMana(amountAdded); } - final CardList manaSources = ComputerUtil.getAvailableMana(); - - // this is to prevent errors for mana sources that have abilities that - // cost mana. - manaSources.remove(sa.getSourceCard()); - - for (int i = 0; i < manaSources.size(); i++) { - final Card sourceCard = manaSources.get(i); - ArrayList manaAbilities = sourceCard.getAIPlayableMana(); - - boolean used = false; // this is for testing paying mana only - - manaAbilities = ComputerUtil.sortForNeeded(cost, manaAbilities, player); - - for (final AbilityMana m : manaAbilities) { - - if (used) { - break; // mana source already used in the test + // get map of mana abilities + HashMap> manaAbilityMap = mapManaSources(player); + // initialize ArrayList list for mana needed + ArrayList> partSources = + new ArrayList>(); + ArrayList partPriority = new ArrayList(); + String[] costParts = cost.toString().replace("X ", "").replace("P", "").split(" "); + Boolean foundAllSources = true; + if (manaAbilityMap.isEmpty()) { + foundAllSources = false; + } + else { + String[] shortColors = {"W", "U", "B" , "R", "G"}; + // loop over cost parts + for (int nPart = 0; nPart < costParts.length; nPart++) { + ArrayList srcFound = new ArrayList(); + // Test for: + // 1) Colorless + // 2) Split e.g. 2/G + // 3) Hybrid e.g. UG + // defaults to single short color + if (costParts[nPart].matches("[0-9]+")) { // Colorless + srcFound.addAll(manaAbilityMap.get("1")); } + else if (costParts[nPart].contains("/")) { // Split + String colorKey = costParts[nPart].replace("2/", ""); + // add specified color sources first + if (manaAbilityMap.containsKey(colorKey)) { + srcFound.addAll(manaAbilityMap.get(colorKey)); + } + // add other available colors + for (String color : shortColors) { + if (!colorKey.contains(color)) { + // Is source available? + if (manaAbilityMap.containsKey(color)) { + srcFound.addAll(manaAbilityMap.get(color)); + } + } + } + } + else if (costParts[nPart].length() > 1) { // Hybrid + String firstColor = costParts[nPart].substring(0, 1); + String secondColor = costParts[nPart].substring(1); + Boolean foundFirst = manaAbilityMap.containsKey(firstColor); + Boolean foundSecond = manaAbilityMap.containsKey(secondColor); + if (foundFirst || foundSecond) { + if (!foundFirst) { + srcFound.addAll(manaAbilityMap.get(secondColor)); + } + else if (!foundSecond) { + srcFound.addAll(manaAbilityMap.get(firstColor)); + } + else if (manaAbilityMap.get(firstColor).size() > manaAbilityMap.get(secondColor).size()) { + srcFound.addAll(manaAbilityMap.get(firstColor)); + srcFound.addAll(manaAbilityMap.get(secondColor)); + } + else { + srcFound.addAll(manaAbilityMap.get(secondColor)); + srcFound.addAll(manaAbilityMap.get(firstColor)); + } + } + } + else { // single color + if (manaAbilityMap.containsKey(costParts[nPart])) { + srcFound.addAll(manaAbilityMap.get(costParts[nPart])); + } + } + + // add sources to array lists + partSources.add(nPart, srcFound); + // add to sorted priority list + if (srcFound.size() > 0) { + int i; + for (i = 0; i < partPriority.size(); i++) { + if (srcFound.size() < partSources.get(i).size()) { + break; + } + } + partPriority.add(i, nPart); + } + else { + foundAllSources = false; + break; + } + } + } + if (!foundAllSources) { + if (!test) { + // real payment should not arrive here + throw new RuntimeException("ComputerUtil : payManaCost() cost was not paid for " + sa.getSourceCard().getName()); + } + return false; + } + + // Create array to keep track of sources used + ArrayList usedSources = new ArrayList(); + //this is to prevent errors for mana sources that have abilities that cost mana. + usedSources.add(sa.getSourceCard()); + // Loop over mana needed + int nPriority = 0; + while (nPriority < partPriority.size()) { + int nPart = partPriority.get(nPriority); + ArrayList manaAbilities = partSources.get(nPart); + ManaCost costPart = new ManaCost(costParts[nPart]); + // Loop over mana abilities that can be used to current mana cost part + for (AbilityMana m : manaAbilities) { + Card sourceCard = m.getSourceCard(); + + // Check if source has already been used + if (usedSources.contains(sourceCard)) { + continue; + } + + // Check if AI can still play this mana ability m.setActivatingPlayer(player); - // if the AI can't pay the additional costs skip the mana - // ability + //if the AI can't pay the additional costs skip the mana ability if (m.getPayCosts() != null) { - if (!ComputerUtil.canPayAdditionalCosts(m, player)) { + if (!canPayAdditionalCosts(m, player)) { continue; } } else if (sourceCard.isTapped()) { continue; } - // don't use abilities with dangerous drawbacks - if (m.getSubAbility() != null) { - if (!m.getSubAbility().chkAIDrawback()) { - continue; - } - } + // add source card to used list + usedSources.add(sourceCard); - colors = ComputerUtil.getProduceableColors(m, player); - for (int j = 0; j < colors.size(); j++) { - if (used) { - break; // mana source already used in the test - } - - if (cost.isNeeded(colors.get(j))) { - if (!test) { - // Pay additional costs - if (m.getPayCosts() != null) { - final CostPayment pay = new CostPayment(m.getPayCosts(), m); - if (!pay.payComputerCosts()) { - continue; - } - } else { - sourceCard.tap(); - } - } else { - used = true; // mana source is now used in the test + // add source card to used list + if (!test) { + //Pay additional costs + if (m.getPayCosts() != null) { + CostPayment pay = new CostPayment(m.getPayCosts(), m); + if (!pay.payComputerCosts()) { + continue; } - - cost.payMana(colors.get(j)); - - if (!test) { - // resolve subabilities - final AbilityFactory af = m.getAbilityFactory(); - if (af != null) { - AbilityFactory.resolveSubAbilities(m); - } - - if (sourceCard.getName().equals("Undiscovered Paradise")) { - sourceCard.setBounceAtUntap(true); - } - - if (sourceCard.getName().equals("Rainbow Vale")) { - final StringBuilder sb = new StringBuilder(); - sb.append("An opponent gains control of CARDNAME "); - sb.append("at the beginning of the next end step."); - sourceCard.addExtrinsicKeyword(sb.toString()); - } - - // System.out.println("just subtracted " + - // colors.get(j) + ", cost is now: " + - // cost.toString()); - // Run triggers - final HashMap runParams = new HashMap(); - - runParams.put("Card", sourceCard); - runParams.put("Player", player); - runParams.put("Produced", colors.get(j)); // can't - // tell - // what - // mana - // the - // computer - // just - // paid? - AllZone.getTriggerHandler().runTrigger("TapsForMana", runParams); - } // not a test } - if (cost.isPaid()) { - // if (sa instanceof Spell_Permanent) // should probably - // add this - sa.getSourceCard().setColorsPaid(cost.getColorsPaid()); - sa.getSourceCard().setSunburstValue(cost.getSunburst()); - manapool.clearPay(sa, test); - return true; + else { + sourceCard.tap(); } + // resolve mana ability + m.resolve(); + // subtract mana from mana pool + cost = manapool.subtractMana(sa, cost, m); + String manaProduced; + // Check if paying snow mana + if ("S".equals(costParts[nPart])) { + manaProduced = "S"; + } + else { + manaProduced = m.getLastProduced(); + } + String color = InputPayManaCostUtil.getLongColorString(manaProduced); + costPart.payMana(color); } + else { + String manaProduced; + // Check if paying snow mana + if ("S".equals(costParts[nPart])) { + manaProduced = "S"; + } + else { + // check if ability produces any color + if (m.isAnyMana()) { + String colorChoice = costParts[nPart]; + ArrayList negEffect = cost.getManaNeededToAvoidNegativeEffect(); + ArrayList negEffectPaid = cost.getManaPaidToAvoidNegativeEffect(); + // Check for + // 1) Colorless + // 2) Split e.g. 2/G + // 3) Hybrid e.g. UG + if (costParts[nPart].matches("[0-9]+")) { + colorChoice = "W"; + for (int n = 0; n < negEffect.size(); n++) { + if (!negEffectPaid.contains(negEffect.get(n))) { + colorChoice = negEffect.get(n); + break; + } + } + } + else if (costParts[nPart].contains("/")) { + colorChoice = costParts[nPart].replace("2/", ""); + } + else if (costParts[nPart].length() > 1) { + colorChoice = costParts[nPart].substring(0, 1); + for (int n = 0; n < negEffect.size(); n++) { + if (costParts[nPart].contains(negEffect.get(n)) + && !negEffectPaid.contains(negEffect.get(n))) { + colorChoice = negEffect.get(n); + break; + } + } + } + m.setAnyChoice(colorChoice); + } + // get produced mana + //TODO: Change this if AI is able use to mana abilities that + // produce more than one mana (111230 - ArsenalNut) + manaProduced = m.getManaProduced(); + } + // pay cost + String color = InputPayManaCostUtil.getLongColorString(manaProduced); + cost.payMana(color); + costPart.payMana(color); + } + // check if cost part is paid + if (costPart.isPaid()) { + break; + } + } // end of mana ability loop + if (!costPart.isPaid() || cost.isPaid()) { + break; + } + else { + nPriority++; } + } // end of cost parts loop + + // check if paid + if (cost.isPaid()) { + //if (sa instanceof Spell_Permanent) // should probably add this + sa.getSourceCard().setColorsPaid(cost.getColorsPaid()); + sa.getSourceCard().setSunburstValue(cost.getSunburst()); + manapool.clearPay(sa, test); + return true; } if (!test) { @@ -790,6 +925,7 @@ public class ComputerUtil { return ComputerUtil.getAvailableMana(AllZone.getComputerPlayer()); } // getAvailableMana() + // gets available mana sources and sorts them /** *

@@ -817,22 +953,40 @@ public class ComputerUtil { }); // CardListFilter final CardList sortedManaSources = new CardList(); + final CardList otherManaSources = new CardList(); + final CardList colorlessManaSources = new CardList(); + final CardList oneManaSources = new CardList(); + final CardList twoManaSources = new CardList(); + final CardList threeManaSources = new CardList(); + final CardList fourManaSources = new CardList(); + final CardList fiveManaSources = new CardList(); + final CardList anyColorManaSources = new CardList(); + // Sort mana sources // 1. Use lands that can only produce colorless mana without // drawback/cost first + // 2. Search for mana sources that have a certain number of abilities + // 3. Use lands that produce any color many + // 4. all other sources (creature, costs, drawback, etc.) for (int i = 0; i < manaSources.size(); i++) { final Card card = manaSources.get(i); if (card.isCreature() || card.isEnchanted()) { + otherManaSources.add(card); continue; // don't use creatures before other permanents } int usableManaAbilities = 0; boolean needsLimitedResources = false; + boolean producesAnyColor = false; final ArrayList manaAbilities = card.getAIPlayableMana(); for (final AbilityMana m : manaAbilities) { + if (m.isAnyMana()) { + producesAnyColor = true; + } + final Cost cost = m.getPayCosts(); needsLimitedResources |= !cost.isReusuableResource(); @@ -856,61 +1010,42 @@ public class ComputerUtil { usableManaAbilities++; } - // use lands that can only produce colorless mana first - if ((usableManaAbilities == 1) && !needsLimitedResources && manaAbilities.get(0).mana().equals("1")) { - sortedManaSources.add(card); + if (needsLimitedResources) { + otherManaSources.add(card); } - } - - // 2. Search for mana sources that have a certain number of mana - // abilities (start with 1 and go up to 5) and no drawback/costs - for (int number = 1; number < 6; number++) { - for (int i = 0; i < manaSources.size(); i++) { - final Card card = manaSources.get(i); - - if (card.isCreature() || card.isEnchanted()) { - continue; // don't use creatures before other permanents + else if (producesAnyColor) { + anyColorManaSources.add(card); + } + else if (usableManaAbilities == 1) { + if (manaAbilities.get(0).mana().equals("1")) { + colorlessManaSources.add(card); } - - int usableManaAbilities = 0; - boolean needsLimitedResources = false; - final ArrayList manaAbilities = card.getAIPlayableMana(); - - for (final AbilityMana m : manaAbilities) { - - final Cost cost = m.getPayCosts(); - needsLimitedResources |= !cost.isReusuableResource(); - // if the AI can't pay the additional costs skip the mana - // ability - if (cost != null) { - if (!ComputerUtil.canPayAdditionalCosts(m, player)) { - continue; - } - } - - // don't use abilities with dangerous drawbacks - if (m.getSubAbility() != null) { - if (!m.getSubAbility().chkAIDrawback()) { - continue; - } - needsLimitedResources = true; // TODO: check for good - // drawbacks (gainLife) - } - usableManaAbilities++; - } - - if ((usableManaAbilities == number) && !needsLimitedResources && !sortedManaSources.contains(card)) { - sortedManaSources.add(card); + else { + oneManaSources.add(card); } } - } - - // Add the rest - for (int j = 0; j < manaSources.size(); j++) { - if (!sortedManaSources.contains(manaSources.get(j))) { - sortedManaSources.add(manaSources.get(j)); + else if (usableManaAbilities == 2) { + twoManaSources.add(card); } + else if (usableManaAbilities == 3) { + threeManaSources.add(card); + } + else if (usableManaAbilities == 4) { + fourManaSources.add(card); + } + else { + fiveManaSources.add(card); + } + } + sortedManaSources.addAll(colorlessManaSources); + sortedManaSources.addAll(oneManaSources); + sortedManaSources.addAll(twoManaSources); + sortedManaSources.addAll(threeManaSources); + sortedManaSources.addAll(fourManaSources); + sortedManaSources.addAll(fiveManaSources); + sortedManaSources.addAll(anyColorManaSources); + sortedManaSources.addAll(otherManaSources); return sortedManaSources; } // getAvailableMana() @@ -983,7 +1118,93 @@ public class ComputerUtil { return res; } - // plays a land if one is available + /** + *

mapManaSources.

+ * + * @param player a {@link forge.Player} object. + * @return HashMap + */ + public static HashMap> mapManaSources(final Player player) { + HashMap> manaMap = new HashMap>(); + + ArrayList whiteSources = new ArrayList(); + ArrayList blueSources = new ArrayList(); + ArrayList blackSources = new ArrayList(); + ArrayList redSources = new ArrayList(); + ArrayList greenSources = new ArrayList(); + ArrayList colorlessSources = new ArrayList(); + ArrayList snowSources = new ArrayList(); + + // Get list of current available mana sources + final CardList manaSources = ComputerUtil.getAvailableMana(); + + // Loop over all mana sources + for (int i = 0; i < manaSources.size(); i++) { + Card sourceCard = manaSources.get(i); + ArrayList manaAbilities = sourceCard.getAIPlayableMana(); + + // Loop over all mana abilities for a source + for (AbilityMana m : manaAbilities) { + + //don't use abilities with dangerous drawbacks + if (m.getSubAbility() != null) { + if (!m.getSubAbility().chkAIDrawback()) { + continue; + } + } + + // add to colorless source list + colorlessSources.add(m); + + // find possible colors + if (m.canProduce("W") || m.isAnyMana()) { + whiteSources.add(m); + } + if (m.canProduce("U") || m.isAnyMana()) { + blueSources.add(m); + } + if (m.canProduce("B") || m.isAnyMana()) { + blackSources.add(m); + } + if (m.canProduce("R") || m.isAnyMana()) { + redSources.add(m); + } + if (m.canProduce("G") || m.isAnyMana()) { + greenSources.add(m); + } + if (m.isSnow()) { + snowSources.add(m); + } + } // end of mana abilities loop + } // end of mana sources loop + + // Add sources + if (!whiteSources.isEmpty()) { + manaMap.put("W", whiteSources); + } + if (!blueSources.isEmpty()) { + manaMap.put("U", blueSources); + } + if (!blackSources.isEmpty()) { + manaMap.put("B", blackSources); + } + if (!redSources.isEmpty()) { + manaMap.put("R", redSources); + } + if (!greenSources.isEmpty()) { + manaMap.put("G", greenSources); + } + if (!colorlessSources.isEmpty()) { + manaMap.put("1", colorlessSources); + } + if (!snowSources.isEmpty()) { + manaMap.put("S", snowSources); + } + + return manaMap; + } + + //plays a land if one is available /** *

* chooseLandsToPlay. diff --git a/src/main/java/forge/card/abilityfactory/AbilityFactoryMana.java b/src/main/java/forge/card/abilityfactory/AbilityFactoryMana.java index 3cb0cacc17e..6582f97fb61 100644 --- a/src/main/java/forge/card/abilityfactory/AbilityFactoryMana.java +++ b/src/main/java/forge/card/abilityfactory/AbilityFactoryMana.java @@ -32,6 +32,7 @@ import forge.Constant.Zone; import forge.Counters; import forge.MyRandom; import forge.Player; +import forge.card.cardfactory.CardFactoryUtil; import forge.card.cost.Cost; import forge.card.spellability.AbilityActivated; import forge.card.spellability.AbilityMana; @@ -85,6 +86,10 @@ public class AbilityFactoryMana { return false; } + @Override + public String getManaProduced() { + return manaGenerated(this, this.af, this); + } }; return abMana; } @@ -242,6 +247,18 @@ public class AbilityFactoryMana { return sb.toString(); } + /** + *

manaGenerated.

+ * + * @param abMana a {@link forge.card.spellability.Ability_Mana} object. + * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. + * @param sa a {@link forge.card.spellability.SpellAbility} object. + * @return a {@link java.lang.String} object. + */ + public static String manaGenerated(final AbilityMana abMana, final AbilityFactory af, final SpellAbility sa) { + return generatedMana(abMana, af, sa); + } + /** *

* manaResolve. @@ -277,6 +294,40 @@ public class AbilityFactoryMana { tgtPlayers = AbilityFactory.getDefinedPlayers(sa.getSourceCard(), params.get("Defined"), sa); } + if (abMana.isAnyMana()) { + for (Player p : tgtPlayers) { + if (tgt == null || p.canBeTargetedBy(sa)) { + // AI color choice is set in ComputerUtils so only human players need to make a choice + if (sa.getActivatingPlayer().isHuman()) { + Object o = GuiUtils.getChoice("Choose a color", Constant.Color.ONLY_COLORS); + if (null == o) { + return; + } + String choice = (String) o; + abMana.setAnyChoice(InputPayManaCostUtil.getShortColorString(choice)); + } + else { + if (params.containsKey("AILogic")) { + final String logic = params.get("AILogic"); + String chosen = Constant.Color.BLACK; + if (logic.equals("MostProminentInComputerHand")) { + chosen = CardFactoryUtil.getMostProminentColor(AllZone.getComputerPlayer().getCardsIn( + Zone.Hand)); + } + GuiUtils.getChoice("Computer picked: ", chosen); + abMana.setAnyChoice(InputPayManaCostUtil.getShortColorString(chosen)); + } + if (abMana.getAnyChoice().isEmpty()) { + final StringBuilder sb = new StringBuilder(); + sb.append("AbilityFactoryMana::manaResolve() - any color mana choice is empty for "); + sb.append(sa.getSourceCard().getName()); + throw new RuntimeException(sb.toString()); + } + } + } + } + } + for (final Player player : tgtPlayers) { abMana.produceMana(AbilityFactoryMana.generatedMana(abMana, af, sa), player); } @@ -319,7 +370,16 @@ public class AbilityFactoryMana { int amount = params.containsKey("Amount") ? AbilityFactory.calculateAmount(af.getHostCard(), params.get("Amount"), sa) : 1; - String baseMana = abMana.mana(); + String baseMana; + if (abMana.isAnyMana()) { + baseMana = abMana.getAnyChoice(); + if (baseMana.isEmpty()) { + baseMana = "Any"; + } + } else { + baseMana = abMana.mana(); + } + if (baseMana.equals("Chosen")) { // this will only support 1 chosen color for now. baseMana = InputPayManaCostUtil.getShortColorString(card.getChosenColor().get(0)); @@ -684,7 +744,7 @@ public class AbilityFactoryMana { final ArrayList colors) { for (final String col : Constant.Color.ONLY_COLORS) { final String s = InputPayManaCostUtil.getShortColorString(col); - if (ab.canProduce(s) && !colors.contains(col)) { + if ((ab.canProduce(s) || ab.isAnyMana()) && !colors.contains(col)) { colors.add(col); } } diff --git a/src/main/java/forge/card/mana/Mana.java b/src/main/java/forge/card/mana/Mana.java index 90fd88aec55..ba028aa8480 100644 --- a/src/main/java/forge/card/mana/Mana.java +++ b/src/main/java/forge/card/mana/Mana.java @@ -246,4 +246,15 @@ public class Mana { public final void decrementAmount() { this.amount--; } + + /** + *

+ * decrementAmount. + *

+ * + * @param amountDecrement a int. + */ + public void decrementAmount(final int amountDecrement) { + this.amount -= amountDecrement; + } } diff --git a/src/main/java/forge/card/mana/ManaCost.java b/src/main/java/forge/card/mana/ManaCost.java index d9f0f9bdc6f..923a9dfa01c 100644 --- a/src/main/java/forge/card/mana/ManaCost.java +++ b/src/main/java/forge/card/mana/ManaCost.java @@ -704,4 +704,13 @@ public class ManaCost { public final ArrayList getManaNeededToAvoidNegativeEffect() { return this.manaNeededToAvoidNegativeEffect; } + + /** + * Gets the mana paid so far to avoid negative effect. + * + * @return the mana paid to avoid negative effect + */ + public final ArrayList getManaPaidToAvoidNegativeEffect() { + return this.manaPaidToAvoidNegativeEffect; + } } diff --git a/src/main/java/forge/card/mana/ManaPool.java b/src/main/java/forge/card/mana/ManaPool.java index ffdb93bb732..88f0982b00b 100644 --- a/src/main/java/forge/card/mana/ManaPool.java +++ b/src/main/java/forge/card/mana/ManaPool.java @@ -498,7 +498,7 @@ public class ManaPool { break; } } - this.removeManaFrom(pool, set); + this.removeManaFrom(pool, set, mana.getAmount()); } /** @@ -510,21 +510,23 @@ public class ManaPool { * a {@link java.util.ArrayList} object. * @param choice * a {@link forge.card.mana.Mana} object. + * @param amount + * an int . */ - public final void removeManaFrom(final ArrayList pool, final Mana choice) { + public final void removeManaFrom(final ArrayList pool, final Mana choice, final int amount) { if (choice != null) { - if (choice.getAmount() == 1) { + if (choice.getAmount() == amount) { pool.remove(choice); } else { - choice.decrementAmount(); + choice.decrementAmount(amount); } if (pool.equals(this.floatingMana)) { int i = ManaPool.MAP.get(choice.getColor()); if (choice.isSnow()) { - this.floatingSnowTotals[i] -= choice.getAmount(); + this.floatingSnowTotals[i] -= amount; } else { - this.floatingTotals[i] -= choice.getAmount(); + this.floatingTotals[i] -= amount; } } owner.updateObservers(); @@ -685,7 +687,7 @@ public class ManaPool { * @return a {@link forge.card.mana.ManaCost} object. */ public final ManaCost subtractMana(final SpellAbility sa, ManaCost m, final AbilityMana... mAbilities) { - final ArrayList paidAbs = sa.getPayingManaAbilities(); + final ArrayList paidAbs = sa.getPayingManaAbilities(); // why??? if (mAbilities.length == 0) { // paying from Mana Pool @@ -699,8 +701,17 @@ public class ManaPool { // paying via Mana Abilities for (final AbilityMana mability : mAbilities) { - paidAbs.add(mability); - final String[] cost = ManaPool.formatMana(mability); + paidAbs.add(mability); // why??? + //TODO: Look at using new getManaProduced() method of Ability_Mana (ArsenalNut) + //String[] cost = formatMana(mability); + String[] cost = null; + if (mability.isAnyMana()) { + cost = new String[1]; + cost[0] = mability.getAnyChoice(); + } + else { + cost = formatMana(mability); + } m = this.subtractMultiple(sa, cost, m); } @@ -738,7 +749,7 @@ public class ManaPool { for (final Mana m : manaArray) { if (manaCost.isNeeded(m)) { manaCost.payMana(m); - payMana.add(m); + payMana.add(m); // what is this used for? anything this.findAndRemoveFrom(this.floatingMana, m); } else { break; @@ -806,74 +817,77 @@ public class ManaPool { // TODO account for unpaying mana in payMana and floatingPool final ArrayList payMana = sa.getPayingMana(); + if ((payMana.size() == 0) && (this.floatingMana.size() == 0)) { + return false; + } + final ArrayList removePaying = new ArrayList(); final ArrayList removeFloating = new ArrayList(); - int i = 0, j = 0; - boolean usePay = payMana.size() > 0; - final boolean flag = false; - - String manaStr = mana[i]; - String color = InputPayManaCostUtil.getLongColorString(manaStr); - - if (!usePay && (this.floatingMana.size() == 0)) { - return false; + int manaAccounted = 0; + // loop over mana paid + for (Mana manaPaid : payMana) { + if (manaPaid.fromSourceCard(c)) { + for (int i = 0; i < mana.length; i++) { + if (manaPaid.getColor().equals(InputPayManaCostUtil.getLongColorString(mana[i]))) { + final int amt = manaPaid.getColorlessAmount(); + if (amt > 0) { + final int difference = Integer.parseInt(mana[i]) - amt; + if (difference > 0) { + mana[i] = Integer.toString(difference); + } else { + manaAccounted += amt; + } + } else { + manaAccounted += manaPaid.getAmount(); + } + removePaying.add(manaPaid); + break; + } + } + } + if (manaAccounted == mana.length) { + break; + } } - - while (i < mana.length) { - - final Mana m = usePay ? payMana.get(j) : this.floatingMana.get(j); - - if (m.fromSourceCard(c) && m.getColor().equals(color)) { - final int amt = m.getColorlessAmount(); - if (amt > 0) { - final int difference = Integer.parseInt(manaStr) - amt; - if (difference > 0) { - manaStr = Integer.toString(difference); - } else { - i += amt; - if (i < mana.length) { - manaStr = mana[i]; + // loop over mana pool if not all of the generated mana is accounted for + if (manaAccounted < mana.length) { + for (Mana manaFloat : this.floatingMana) { + if (manaFloat.fromSourceCard(c)) { + for (int i = 0; i < mana.length; i++) { + if (manaFloat.getColor().equals(InputPayManaCostUtil.getLongColorString(mana[i]))) { + final int amt = manaFloat.getColorlessAmount(); + if (amt > 0) { + final int difference = Integer.parseInt(mana[i]) - amt; + if (difference > 0) { + mana[i] = Integer.toString(difference); + } else { + manaAccounted += amt; + } + } else { + manaAccounted += manaFloat.getAmount(); + } + removeFloating.add(manaFloat); + break; } } - } else { - i += m.getAmount(); - if (i < mana.length) { - manaStr = mana[i]; - } } - color = InputPayManaCostUtil.getLongColorString(manaStr); - if (usePay) { - removePaying.add(m); - } else { - removeFloating.add(m); - } - - // If mana has been depleted, break from loop. All Accounted - // for! - if (i == mana.length) { + if (manaAccounted == mana.length) { break; } } - - j++; // increase j until we reach the end of paying, then reset and - // use floating. - if (usePay) { - if (payMana.size() == j) { - j = 0; - usePay = false; - } - } - if (!usePay && (this.floatingMana.size() == j) && !flag) { - return false; - } + } + // When is it legitimate for all the mana not to be accountable? + // Does this condition really indicate an bug in Forge? + if (manaAccounted < mana.length) { + return false; } for (int k = 0; k < removePaying.size(); k++) { - this.removeManaFrom(payMana, removePaying.get(k)); + this.removeManaFrom(payMana, removePaying.get(k), removePaying.get(k).getAmount()); } for (int k = 0; k < removeFloating.size(); k++) { - this.removeManaFrom(this.floatingMana, removeFloating.get(k)); + this.removeManaFrom(this.floatingMana, removeFloating.get(k), removeFloating.get(k).getAmount()); } return true; } @@ -896,7 +910,16 @@ public class ManaPool { // go through paidAbilities if they are undoable for (final AbilityMana am : payAbs) { if (am.isUndoable()) { - final String[] formattedMana = ManaPool.formatMana(am); + //final String[] formattedMana = ManaPool.formatMana(am); + /*String[] formattedMana = null; + if (am.isAnyMana()) { + formattedMana = new String[1]; + formattedMana[0] = am.getAnyChoice(); + } + else { + formattedMana = formatMana(am); + }*/ + final String[] formattedMana = am.getLastProduced().split(" "); if (this.accountFor(sa, formattedMana, am.getSourceCard())) { am.undo(); } diff --git a/src/main/java/forge/card/spellability/AbilityMana.java b/src/main/java/forge/card/spellability/AbilityMana.java index 8bf108b0e3c..b114cde04ae 100644 --- a/src/main/java/forge/card/spellability/AbilityMana.java +++ b/src/main/java/forge/card/spellability/AbilityMana.java @@ -39,6 +39,8 @@ public abstract class AbilityMana extends AbilityActivated implements java.io.Se private static final long serialVersionUID = -6816356991224950520L; private String origProduced; + private String lastAnyChoice = ""; + private String lastProduced = ""; private int amount = 1; /** The reflected. */ @@ -177,6 +179,8 @@ public abstract class AbilityMana extends AbilityActivated implements java.io.Se // this.getActivatingPlayer().ManaPool.addManaToFloating(origProduced, // getSourceCard()); manaPool.addManaToFloating(produced, source); + //store produced to last produced + this.lastProduced = produced; // TODO all of the following would be better as trigger events // "tapped for mana" @@ -202,6 +206,35 @@ public abstract class AbilityMana extends AbilityActivated implements java.io.Se } // end produceMana(String) + /** + *

+ * getProducedMana. + *

+ * + * @return a {@link java.lang.String} object. + */ + public String getManaProduced() { + StringBuilder sb = new StringBuilder(); + if (this.amount == 0) { + sb.append("0"); + } + else { + try { + // if baseMana is an integer(colorless), just multiply amount and baseMana + int base = Integer.parseInt(this.origProduced); + sb.append(base * this.amount); + } catch (NumberFormatException e) { + for (int i = 0; i < this.amount; i++) { + if (i != 0) { + sb.append(" "); + } + sb.append(this.origProduced); + } + } + } + return sb.toString(); + } + /** *

* mana. @@ -237,6 +270,39 @@ public abstract class AbilityMana extends AbilityActivated implements java.io.Se this.reflected = bReflect; } + /** + *

+ * setAnyChoice. + *

+ * + * @param s a {@link java.lang.String} object. + */ + public void setAnyChoice(String s) { + this.lastAnyChoice = s; + } + + /** + *

+ * Getter for the field lastAnyChoice. + *

+ * + * @return a {@link java.lang.String} object. + */ + public String getAnyChoice() { + return this.lastAnyChoice; + } + + /** + *

+ * Getter for the field lastProduced. + *

+ * + * @return a {@link java.lang.String} object. + */ + public String getLastProduced() { + return this.lastProduced; + } + /** *

* isSnow. @@ -270,6 +336,17 @@ public abstract class AbilityMana extends AbilityActivated implements java.io.Se return this.reflected; } + /** + *

+ * isAnyMana. + *

+ * + * @return a boolean. + */ + public boolean isAnyMana() { + return this.origProduced.contains("Any"); + } + /** *

* canProduce. @@ -280,6 +357,7 @@ public abstract class AbilityMana extends AbilityActivated implements java.io.Se * @return a boolean. */ public final boolean canProduce(final String s) { + //TODO: look at where this called from and take "Any" into account return this.origProduced.contains(s); } @@ -291,7 +369,7 @@ public abstract class AbilityMana extends AbilityActivated implements java.io.Se * @return a boolean. */ public final boolean isBasic() { - if (this.origProduced.length() != 1) { + if (this.origProduced.length() != 1 && !this.origProduced.contains("Any")) { return false; } diff --git a/src/main/java/forge/game/limited/BoosterDraftAI.java b/src/main/java/forge/game/limited/BoosterDraftAI.java index d1f482df654..3e5ab7603a7 100644 --- a/src/main/java/forge/game/limited/BoosterDraftAI.java +++ b/src/main/java/forge/game/limited/BoosterDraftAI.java @@ -231,7 +231,8 @@ public class BoosterDraftAI { final ArrayList maList = typeList.get(i).getManaAbility(); for (int j = 0; j < maList.size(); j++) { if (maList.get(j).canProduce(this.playerColors.get(player).getMana1()) - || maList.get(j).canProduce(this.playerColors.get(player).getMana2())) { + || maList.get(j).canProduce(this.playerColors.get(player).getMana2()) + || maList.get(j).isAnyMana()) { wouldPick.add(typeList.get(i)); } } diff --git a/src/main/java/forge/game/limited/SealedDeck.java b/src/main/java/forge/game/limited/SealedDeck.java index 5c722dcdd4a..4c540dec763 100644 --- a/src/main/java/forge/game/limited/SealedDeck.java +++ b/src/main/java/forge/game/limited/SealedDeck.java @@ -331,7 +331,9 @@ public class SealedDeck { public boolean addCard(final Card c) { final ArrayList maList = c.getManaAbility(); for (int j = 0; j < maList.size(); j++) { - if (maList.get(j).canProduce(aiDC.mana1) || maList.get(j).canProduce(aiDC.mana2)) { + if (maList.get(j).canProduce(aiDC.mana1) + || maList.get(j).canProduce(aiDC.mana2) + || maList.get(j).isAnyMana()) { return true; } } diff --git a/src/main/java/forge/gui/input/InputPayManaCostUtil.java b/src/main/java/forge/gui/input/InputPayManaCostUtil.java index 17abae25f8f..3417e63e5df 100644 --- a/src/main/java/forge/gui/input/InputPayManaCostUtil.java +++ b/src/main/java/forge/gui/input/InputPayManaCostUtil.java @@ -119,6 +119,8 @@ public class InputPayManaCostUtil { colorMatches.add(am); } } + } else if (am.isAnyMana()) { + colorMatches.add(am); } else { final String[] m = ManaPool.formatMana(am); for (final String color : m) { @@ -130,7 +132,8 @@ public class InputPayManaCostUtil { } } - if ((colorMatches.size() == 0) || (colorMatches.size() == abilities.size())) { + // Why is choice made "false" if colorMatches and abilities have same size + if ((colorMatches.size() == 0)) { // can only match colorless just grab the first and move on. choice = false; } else if (colorMatches.size() < abilities.size()) { @@ -211,7 +214,9 @@ public class InputPayManaCostUtil { if (mana.contains("S") && am.isSnow()) { return true; } - + if (am.isAnyMana()) { + return true; + } if (am.isReflectedMana()) { final ArrayList reflectableColors = AbilityFactoryMana.reflectableMana(am, am.getAbilityFactory(), new ArrayList(), new ArrayList());