From 22b14d0b70ce97797fe0ca35ffcc6262b0330c3d Mon Sep 17 00:00:00 2001 From: ArsenalNut Date: Fri, 30 Dec 2011 20:36:21 +0000 Subject: [PATCH 01/11] Creating branch for changes for supporting "Produced$ Any" in mana abilities --- res/cardsfolder/c/city_of_brass.txt | 13 +- res/cardsfolder/g/glimmervoid.txt | 1 + src/main/java/forge/Card.java | 5 +- src/main/java/forge/ComputerUtil.java | 255 +++++++++++++++++- .../abilityfactory/AbilityFactoryMana.java | 53 +++- src/main/java/forge/card/mana/Mana.java | 11 + src/main/java/forge/card/mana/ManaPool.java | 165 +++++++----- .../forge/card/spellability/AbilityMana.java | 78 ++++++ .../forge/gui/input/InputPayManaCostUtil.java | 9 +- 9 files changed, 505 insertions(+), 85 deletions(-) diff --git a/res/cardsfolder/c/city_of_brass.txt b/res/cardsfolder/c/city_of_brass.txt index d20a3e2dd35..4dbfe52a056 100644 --- a/res/cardsfolder/c/city_of_brass.txt +++ b/res/cardsfolder/c/city_of_brass.txt @@ -2,11 +2,12 @@ Name:City of Brass ManaCost:no cost Types:Land Text:no text -A:AB$ Mana | Cost$ T | Produced$ W | SpellDescription$ Add W to your mana pool. -A:AB$ Mana | Cost$ T | Produced$ B | SpellDescription$ Add B to your mana pool. -A:AB$ Mana | Cost$ T | Produced$ U | SpellDescription$ Add U to your mana pool. -A:AB$ Mana | Cost$ T | Produced$ R | SpellDescription$ Add R to your mana pool. -A:AB$ Mana | Cost$ T | Produced$ G | SpellDescription$ Add G to your mana pool. +A:AB$ Mana | Cost$ T | Produced$ Any | Amount$ 1 | SpellDescription$ Add one mana of any color to your mana pool. +#A:AB$ Mana | Cost$ T | Produced$ W | SpellDescription$ Add W to your mana pool. +#A:AB$ Mana | Cost$ T | Produced$ B | SpellDescription$ Add B to your mana pool. +#A:AB$ Mana | Cost$ T | Produced$ U | SpellDescription$ Add U to your mana pool. +#A:AB$ Mana | Cost$ T | Produced$ R | SpellDescription$ Add R to your mana pool. +#A:AB$ Mana | Cost$ T | Produced$ G | SpellDescription$ Add G to your mana pool. T:Mode$ Taps | ValidCard$ Card.Self | Execute$ TrigDamage | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME becomes tapped, it deals 1 damage to you. SVar:TrigDamage:AB$DealDamage | Cost$ 0 | Defined$ You | NumDmg$ 1 SVar:Rarity:Rare @@ -18,4 +19,4 @@ SetInfo:5ED|Rare|http://magiccards.info/scans/en/5e/413.jpg SetInfo:6ED|Rare|http://magiccards.info/scans/en/6e/321.jpg SetInfo:CHR|Rare|http://magiccards.info/scans/en/ch/92.jpg SetInfo:ARN|Uncommon|http://magiccards.info/scans/en/an/85.jpg -End \ No newline at end of file +End diff --git a/res/cardsfolder/g/glimmervoid.txt b/res/cardsfolder/g/glimmervoid.txt index 32cbdaae78e..bbd5a00569c 100644 --- a/res/cardsfolder/g/glimmervoid.txt +++ b/res/cardsfolder/g/glimmervoid.txt @@ -2,6 +2,7 @@ Name:Glimmervoid ManaCost:no cost Types:Land Text:no text +#A:AB$ Mana | Cost$ T | Produced$ Any | Amount$ 1 | SpellDescription$ Add one mana of any color to your mana pool. A:AB$ Mana | Cost$ T | Produced$ W | SpellDescription$ Add W to your mana pool. A:AB$ Mana | Cost$ T | Produced$ B | SpellDescription$ Add B to your mana pool. A:AB$ Mana | Cost$ T | Produced$ U | SpellDescription$ Add U to your mana pool. diff --git a/src/main/java/forge/Card.java b/src/main/java/forge/Card.java index 870b0f55d6a..165d66a60eb 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() || am.isAnyMana()) && !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..34e49a491de 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) } } } @@ -615,6 +618,168 @@ public class ComputerUtil { cost.setManaNeededToAvoidNegativeEffect(card.getSVar("ManaNeededToAvoidNegativeEffect").split(",")); } + + // 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().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++) { + ManaCost costPart = new ManaCost(costParts[nPart]); + ArrayList srcFound = new ArrayList(); + // get colored mana required + ManaCost onlyColored = costPart; + onlyColored.removeColorlessMana(); + for (String color : shortColors) { + if (onlyColored.isColor(color)) { + // Is source available? + if (manaAbilityMap.containsKey(color)) { + srcFound.addAll(manaAbilityMap.get(color)); + } + } + } + // Test for Colorless + if (costPart.isColor("1")) { + srcFound.addAll(manaAbilityMap.get("1")); + } + // Test for Snow + if (costPart.isColor("S")) { + if (manaAbilityMap.containsKey("S")) { + srcFound.addAll(manaAbilityMap.get("S")); + } + } + + // 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; + } + */ + + ManaCost testCost = new ManaCost(cost.toString()); + // 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 (m.getPayCosts() != null) { + if (!canPayAdditionalCosts(m, player)) { + continue; + } + } else if (sourceCard.isTapped()) { + continue; + } + + // add source card to used list + usedSources.add(sourceCard); + +// // add source card to used list +// if (!test) { +// //Pay additional costs +// if (m.getPayCosts() != null) { +// Cost_Payment pay = new Cost_Payment(m.getPayCosts(), m); +// if (!pay.payComputerCosts()) { +// continue; +// } +// } +// else { +// sourceCard.tap(); +// } +// // resolve mana ability +// m.resolve(); +// // subtract mana from mana pool +// cost = manapool.subtractMana(sa, cost, m); +// } +// else { + String manaProduced; + // Check if paying snow mana + if ("S".equals(costParts[nPart])) { + manaProduced = "S"; + } + else { + // check if ability produces any color + //TODO: This won't work with Hybrid mana or colorless costs (111230 - ArsenalNut) + if (m.isAnyMana()) { + m.setAnyChoice(costParts[nPart]); + } + // get produced mana + manaProduced = m.getManaProduced(); + } + // pay cost + String color = InputPayManaCostUtil.getLongColorString(manaProduced); + testCost.payMana(color); + costPart.payMana(color); +// } + // check if color is still needed + if (costPart.isPaid()) { + break; + } + } // end of mana ability loop + if (!costPart.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; +// } + final CardList manaSources = ComputerUtil.getAvailableMana(); // this is to prevent errors for mana sources that have abilities that @@ -861,7 +1026,7 @@ public class ComputerUtil { sortedManaSources.add(card); } } - + //TODO: Check for cards that produce "Any" as base mana (ArsenalNut) // 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++) { @@ -983,7 +1148,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..13e803f6877 100644 --- a/src/main/java/forge/card/abilityfactory/AbilityFactoryMana.java +++ b/src/main/java/forge/card/abilityfactory/AbilityFactoryMana.java @@ -18,6 +18,7 @@ package forge.card.abilityfactory; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.Random; @@ -85,6 +86,11 @@ public class AbilityFactoryMana { return false; } + @Override + public String getManaProduced() { + // TODO Auto-generated method stub + return manaGenerated(this, this.af, this); + } }; return abMana; } @@ -117,6 +123,10 @@ public class AbilityFactoryMana { return false; } + @Override + public String getManaProduced() { + return manaGenerated(this.tmpMana, this.af, this); + } }; @Override @@ -168,6 +178,10 @@ public class AbilityFactoryMana { return false; } + @Override + public String getManaProduced() { + return manaGenerated(this.tmpMana, this.af, this); + } }; @Override @@ -241,6 +255,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); + } /** *

@@ -277,6 +303,22 @@ 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(choice.substring(0, 1).toUpperCase()); + } + } + } + } + for (final Player player : tgtPlayers) { abMana.produceMana(AbilityFactoryMana.generatedMana(abMana, af, sa), player); } @@ -319,7 +361,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)); 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/ManaPool.java b/src/main/java/forge/card/mana/ManaPool.java index ffdb93bb732..9b57120118e 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,78 +817,81 @@ 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)) { + 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; + } + } + // 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; + } + } + } + if (manaAccounted == mana.length) { + break; + } + } + } + // 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; } - 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]; - } - } - } 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) { - 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; - } - } - 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; } - + /** *

* unpaid. @@ -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..4174c9d4d5e 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); } 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()); From 5a9e8d4c5e04f3374a313e637b681a2b2c86e23b Mon Sep 17 00:00:00 2001 From: ArsenalNut Date: Fri, 30 Dec 2011 21:11:29 +0000 Subject: [PATCH 02/11] added to manamorphose to produce any mana branch --- .gitattributes | 1 + res/cardsfolder/m/manamorphose.txt | 13 +++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 res/cardsfolder/m/manamorphose.txt diff --git a/.gitattributes b/.gitattributes index f2ac347c791..f542247e14c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5043,6 +5043,7 @@ res/cardsfolder/m/manacles_of_decay.txt svneol=native#text/plain res/cardsfolder/m/manaforce_mace.txt svneol=native#text/plain res/cardsfolder/m/manakin.txt svneol=native#text/plain res/cardsfolder/m/manalith.txt svneol=native#text/plain +res/cardsfolder/m/manamorphose.txt -text res/cardsfolder/m/manaplasm.txt svneol=native#text/plain res/cardsfolder/m/mangara_of_corondor.txt svneol=native#text/plain res/cardsfolder/m/maniacal_rage.txt svneol=native#text/plain diff --git a/res/cardsfolder/m/manamorphose.txt b/res/cardsfolder/m/manamorphose.txt new file mode 100644 index 00000000000..5cd59b127db --- /dev/null +++ b/res/cardsfolder/m/manamorphose.txt @@ -0,0 +1,13 @@ +Name:Manamorphose +ManaCost:1 RG +Types:Instant +Text:no text +A:SP$ Mana | Cost$ 1 RG | Produced$ Any | Amount$ 1 | SubAbility$ DBMana | SpellDescription$ Add two mana in any combination of colors to your mana pool. Draw a card. +SVar:DBMana:DB$ Mana | Cost$ 0 | Produced$ Any | Amount$ 1 | SubAbility$ DBDraw +SVar:DBDraw:DB$ Draw | Defined$ You | NumCards$ 1 +SVar:RemAIDeck:True +SVar:Rarity:Common +SVar:Picture:http://www.wizards.com/global/images/magic/general/manamorphose.jpg +SetInfo:SHM|Common|http://magiccards.info/scans/en/shm/211.jpg +Oracle:Add two mana in any combination of colors to your mana pool.\nDraw a card. +End \ No newline at end of file From ecad2f5005abbd7eecffd6e64fe4d20336b23be8 Mon Sep 17 00:00:00 2001 From: ArsenalNut Date: Sat, 31 Dec 2011 18:37:52 +0000 Subject: [PATCH 03/11] update isBasic method of AbilityMana to account for any mana --- src/main/java/forge/Card.java | 2 +- src/main/java/forge/card/spellability/AbilityMana.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/forge/Card.java b/src/main/java/forge/Card.java index 165d66a60eb..a84e363f277 100644 --- a/src/main/java/forge/Card.java +++ b/src/main/java/forge/Card.java @@ -2790,7 +2790,7 @@ public class Card extends GameEntity implements Comparable { continue; } - if ((am.isBasic() || am.isReflectedMana() || am.isAnyMana()) && !res.contains(am)) { + if ((am.isBasic() || am.isReflectedMana()) && !res.contains(am)) { res.add(am); } diff --git a/src/main/java/forge/card/spellability/AbilityMana.java b/src/main/java/forge/card/spellability/AbilityMana.java index 4174c9d4d5e..f2cc460be43 100644 --- a/src/main/java/forge/card/spellability/AbilityMana.java +++ b/src/main/java/forge/card/spellability/AbilityMana.java @@ -369,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; } From 405cdd6ee56d5868b016c7deeeb26b4e6cf21658 Mon Sep 17 00:00:00 2001 From: ArsenalNut Date: Sat, 31 Dec 2011 19:07:35 +0000 Subject: [PATCH 04/11] significantly reworked logic in payManaCost to account for mana sources that produce any color and several other bugs --- src/main/java/forge/ComputerUtil.java | 188 ++++++++++++++------ src/main/java/forge/card/mana/ManaCost.java | 9 + src/main/java/forge/card/mana/ManaPool.java | 10 +- 3 files changed, 147 insertions(+), 60 deletions(-) diff --git a/src/main/java/forge/ComputerUtil.java b/src/main/java/forge/ComputerUtil.java index 34e49a491de..6eb37d5a573 100644 --- a/src/main/java/forge/ComputerUtil.java +++ b/src/main/java/forge/ComputerUtil.java @@ -614,47 +614,86 @@ public class ComputerUtil { ArrayList colors; cost = manapool.subtractMana(sa, cost); + //ManaCost testCost = new ManaCost(cost.toString()); // temp variable for testing only if (card.getSVar("ManaNeededToAvoidNegativeEffect") != "") { - cost.setManaNeededToAvoidNegativeEffect(card.getSVar("ManaNeededToAvoidNegativeEffect").split(",")); + String[] negEffects = card.getSVar("ManaNeededToAvoidNegativeEffect").split(","); + 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]); + } + } + cost.setManaNeededToAvoidNegativeEffect(negEffects); + //testCost.setManaNeededToAvoidNegativeEffect(negEffects); } - // 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().split(" "); + String[] costParts = cost.toString().replace("X ", "").replace("P", "").split(" "); Boolean foundAllSources = true; if (manaAbilityMap.isEmpty()) { foundAllSources = false; } else { + //TODO: Sort colorless sources based on ManaNeededToAvoidNegativeEffect + // (ArsenalNut - 111230) String[] shortColors = {"W", "U", "B" , "R", "G"}; // loop over cost parts for (int nPart = 0; nPart < costParts.length; nPart++) { - ManaCost costPart = new ManaCost(costParts[nPart]); ArrayList srcFound = new ArrayList(); - // get colored mana required - ManaCost onlyColored = costPart; - onlyColored.removeColorlessMana(); - for (String color : shortColors) { - if (onlyColored.isColor(color)) { - // Is source available? - if (manaAbilityMap.containsKey(color)) { - srcFound.addAll(manaAbilityMap.get(color)); + // 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)); + } } } } - // Test for Colorless - if (costPart.isColor("1")) { - srcFound.addAll(manaAbilityMap.get("1")); + 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)); + } + } } - // Test for Snow - if (costPart.isColor("S")) { - if (manaAbilityMap.containsKey("S")) { - srcFound.addAll(manaAbilityMap.get("S")); + else { // single color + if (manaAbilityMap.containsKey(costParts[nPart])) { + srcFound.addAll(manaAbilityMap.get(costParts[nPart])); } } @@ -676,7 +715,6 @@ public class ComputerUtil { } } } - /* if (!foundAllSources) { if (!test) { // real payment should not arrive here @@ -684,9 +722,7 @@ public class ComputerUtil { } return false; } - */ - ManaCost testCost = new ManaCost(cost.toString()); // 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. @@ -720,24 +756,34 @@ public class ComputerUtil { // add source card to used list usedSources.add(sourceCard); -// // add source card to used list -// if (!test) { -// //Pay additional costs -// if (m.getPayCosts() != null) { -// Cost_Payment pay = new Cost_Payment(m.getPayCosts(), m); -// if (!pay.payComputerCosts()) { -// continue; -// } -// } -// else { -// sourceCard.tap(); -// } -// // resolve mana ability -// m.resolve(); -// // subtract mana from mana pool -// cost = manapool.subtractMana(sa, cost, m); -// } -// else { + // 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; + } + } + 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])) { @@ -745,24 +791,54 @@ public class ComputerUtil { } else { // check if ability produces any color - //TODO: This won't work with Hybrid mana or colorless costs (111230 - ArsenalNut) if (m.isAnyMana()) { - m.setAnyChoice(costParts[nPart]); + 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); - testCost.payMana(color); + cost.payMana(color); costPart.payMana(color); -// } - // check if color is still needed + } + // check if cost part is paid if (costPart.isPaid()) { break; } } // end of mana ability loop - if (!costPart.isPaid()) { + if (!costPart.isPaid() || cost.isPaid()) { break; } else { @@ -771,15 +847,16 @@ public class ComputerUtil { } // 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; -// } + // 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; + } + /* final CardList manaSources = ComputerUtil.getAvailableMana(); // this is to prevent errors for mana sources that have abilities that @@ -888,7 +965,8 @@ public class ComputerUtil { } } - } + } + */ if (!test) { final StringBuilder sb = new StringBuilder(); 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 9b57120118e..88f0982b00b 100644 --- a/src/main/java/forge/card/mana/ManaPool.java +++ b/src/main/java/forge/card/mana/ManaPool.java @@ -820,7 +820,7 @@ public class ManaPool { if ((payMana.size() == 0) && (this.floatingMana.size() == 0)) { return false; } - + final ArrayList removePaying = new ArrayList(); final ArrayList removeFloating = new ArrayList(); @@ -828,7 +828,7 @@ public class ManaPool { // loop over mana paid for (Mana manaPaid : payMana) { if (manaPaid.fromSourceCard(c)) { - for (int i = 0;i < mana.length;i++ ) { + for (int i = 0; i < mana.length; i++) { if (manaPaid.getColor().equals(InputPayManaCostUtil.getLongColorString(mana[i]))) { final int amt = manaPaid.getColorlessAmount(); if (amt > 0) { @@ -854,7 +854,7 @@ public class ManaPool { if (manaAccounted < mana.length) { for (Mana manaFloat : this.floatingMana) { if (manaFloat.fromSourceCard(c)) { - for (int i = 0;i < mana.length;i++ ) { + for (int i = 0; i < mana.length; i++) { if (manaFloat.getColor().equals(InputPayManaCostUtil.getLongColorString(mana[i]))) { final int amt = manaFloat.getColorlessAmount(); if (amt > 0) { @@ -875,7 +875,7 @@ public class ManaPool { if (manaAccounted == mana.length) { break; } - } + } } // When is it legitimate for all the mana not to be accountable? // Does this condition really indicate an bug in Forge? @@ -891,7 +891,7 @@ public class ManaPool { } return true; } - + /** *

* unpaid. From d4362e1c8b3c67af2d45a70b63a85d628ce71fb9 Mon Sep 17 00:00:00 2001 From: ArsenalNut Date: Sat, 31 Dec 2011 19:10:39 +0000 Subject: [PATCH 05/11] modified City of Brass, Glimmervoid, and Mox Opal to use "Produced$ Any" --- res/cardsfolder/c/city_of_brass.txt | 5 ----- res/cardsfolder/g/glimmervoid.txt | 7 +------ res/cardsfolder/m/mox_opal.txt | 6 +----- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/res/cardsfolder/c/city_of_brass.txt b/res/cardsfolder/c/city_of_brass.txt index 4dbfe52a056..f8fe36d1b3c 100644 --- a/res/cardsfolder/c/city_of_brass.txt +++ b/res/cardsfolder/c/city_of_brass.txt @@ -3,11 +3,6 @@ ManaCost:no cost Types:Land Text:no text A:AB$ Mana | Cost$ T | Produced$ Any | Amount$ 1 | SpellDescription$ Add one mana of any color to your mana pool. -#A:AB$ Mana | Cost$ T | Produced$ W | SpellDescription$ Add W to your mana pool. -#A:AB$ Mana | Cost$ T | Produced$ B | SpellDescription$ Add B to your mana pool. -#A:AB$ Mana | Cost$ T | Produced$ U | SpellDescription$ Add U to your mana pool. -#A:AB$ Mana | Cost$ T | Produced$ R | SpellDescription$ Add R to your mana pool. -#A:AB$ Mana | Cost$ T | Produced$ G | SpellDescription$ Add G to your mana pool. T:Mode$ Taps | ValidCard$ Card.Self | Execute$ TrigDamage | TriggerZones$ Battlefield | TriggerDescription$ Whenever CARDNAME becomes tapped, it deals 1 damage to you. SVar:TrigDamage:AB$DealDamage | Cost$ 0 | Defined$ You | NumDmg$ 1 SVar:Rarity:Rare diff --git a/res/cardsfolder/g/glimmervoid.txt b/res/cardsfolder/g/glimmervoid.txt index bbd5a00569c..84f998a5e4c 100644 --- a/res/cardsfolder/g/glimmervoid.txt +++ b/res/cardsfolder/g/glimmervoid.txt @@ -2,12 +2,7 @@ Name:Glimmervoid ManaCost:no cost Types:Land Text:no text -#A:AB$ Mana | Cost$ T | Produced$ Any | Amount$ 1 | SpellDescription$ Add one mana of any color to your mana pool. -A:AB$ Mana | Cost$ T | Produced$ W | SpellDescription$ Add W to your mana pool. -A:AB$ Mana | Cost$ T | Produced$ B | SpellDescription$ Add B to your mana pool. -A:AB$ Mana | Cost$ T | Produced$ U | SpellDescription$ Add U to your mana pool. -A:AB$ Mana | Cost$ T | Produced$ R | SpellDescription$ Add R to your mana pool. -A:AB$ Mana | Cost$ T | Produced$ G | SpellDescription$ Add G to your mana pool. +A:AB$ Mana | Cost$ T | Produced$ Any | Amount$ 1 | SpellDescription$ Add one mana of any color to your mana pool. T:Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield | IsPresent$ Artifact.YouCtrl | PresentCompare$ EQ0 | Execute$ TrigSac | TriggerDescription$ At the beginning of the end step, if you control no artifacts, sacrifice CARDNAME. SVar:TrigSac:AB$Sacrifice | Cost$ 0 | Defined$ Self SVar:RemRandomDeck:True diff --git a/res/cardsfolder/m/mox_opal.txt b/res/cardsfolder/m/mox_opal.txt index bb897a99aa6..b8084951af4 100644 --- a/res/cardsfolder/m/mox_opal.txt +++ b/res/cardsfolder/m/mox_opal.txt @@ -2,11 +2,7 @@ Name:Mox Opal ManaCost:0 Types:Legendary Artifact Text:no text -A:AB$ Mana | Cost$ T | Produced$ W | Activation$ Metalcraft | PrecostDesc$ Metalcraft - | SpellDescription$ Add W to your mana pool. Activate this ability only if you control three or more artifacts. -A:AB$ Mana | Cost$ T | Produced$ U | Activation$ Metalcraft | PrecostDesc$ Metalcraft - | SpellDescription$ Add U to your mana pool. Activate this ability only if you control three or more artifacts. -A:AB$ Mana | Cost$ T | Produced$ B | Activation$ Metalcraft | PrecostDesc$ Metalcraft - | SpellDescription$ Add B to your mana pool. Activate this ability only if you control three or more artifacts. -A:AB$ Mana | Cost$ T | Produced$ R | Activation$ Metalcraft | PrecostDesc$ Metalcraft - | SpellDescription$ Add R to your mana pool. Activate this ability only if you control three or more artifacts. -A:AB$ Mana | Cost$ T | Produced$ G | Activation$ Metalcraft | PrecostDesc$ Metalcraft - | SpellDescription$ Add G to your mana pool. Activate this ability only if you control three or more artifacts. +A:AB$ Mana | Cost$ T | Produced$ Any | Activation$ Metalcraft | PrecostDesc$ Metalcraft - | SpellDescription$ Add one mana of any color to your mana pool. Activate this ability only if you control three or more artifacts. SVar:RemRandomDeck:True SVar:Rarity:Mythic SVar:Picture:http://www.wizards.com/global/images/magic/general/mox_opal.jpg From 0690743192cfce73c3912ee1e04305348853fadc Mon Sep 17 00:00:00 2001 From: ArsenalNut Date: Sat, 31 Dec 2011 21:16:16 +0000 Subject: [PATCH 06/11] modified to account for lands that use "Produced$ Any" --- src/main/java/forge/game/limited/BoosterDraftAI.java | 3 ++- src/main/java/forge/game/limited/SealedDeck.java | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) 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; } } From 3050338cd2e51d6214574e15473aa69279925e96 Mon Sep 17 00:00:00 2001 From: ArsenalNut Date: Sat, 31 Dec 2011 21:17:39 +0000 Subject: [PATCH 07/11] modified to allow reflected mana to interact with any color sources --- .../java/forge/card/abilityfactory/AbilityFactoryMana.java | 4 ++-- src/main/java/forge/card/spellability/AbilityMana.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/forge/card/abilityfactory/AbilityFactoryMana.java b/src/main/java/forge/card/abilityfactory/AbilityFactoryMana.java index 13e803f6877..6a773c15e35 100644 --- a/src/main/java/forge/card/abilityfactory/AbilityFactoryMana.java +++ b/src/main/java/forge/card/abilityfactory/AbilityFactoryMana.java @@ -313,7 +313,7 @@ public class AbilityFactoryMana { return; } String choice = (String) o; - abMana.setAnyChoice(choice.substring(0, 1).toUpperCase()); + abMana.setAnyChoice(InputPayManaCostUtil.getShortColorString(choice)); } } } @@ -735,7 +735,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/spellability/AbilityMana.java b/src/main/java/forge/card/spellability/AbilityMana.java index f2cc460be43..b114cde04ae 100644 --- a/src/main/java/forge/card/spellability/AbilityMana.java +++ b/src/main/java/forge/card/spellability/AbilityMana.java @@ -302,7 +302,7 @@ public abstract class AbilityMana extends AbilityActivated implements java.io.Se public String getLastProduced() { return this.lastProduced; } - + /** *

* isSnow. From 8881b74fc7917ef2af1cb063f7dcac21a5bad6b5 Mon Sep 17 00:00:00 2001 From: ArsenalNut Date: Sun, 1 Jan 2012 01:31:31 +0000 Subject: [PATCH 08/11] removed getManaProduced overrides from spells and drawbacks --- .../forge/card/abilityfactory/AbilityFactoryMana.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/main/java/forge/card/abilityfactory/AbilityFactoryMana.java b/src/main/java/forge/card/abilityfactory/AbilityFactoryMana.java index 6a773c15e35..7e713a4b823 100644 --- a/src/main/java/forge/card/abilityfactory/AbilityFactoryMana.java +++ b/src/main/java/forge/card/abilityfactory/AbilityFactoryMana.java @@ -88,7 +88,6 @@ public class AbilityFactoryMana { @Override public String getManaProduced() { - // TODO Auto-generated method stub return manaGenerated(this, this.af, this); } }; @@ -123,10 +122,6 @@ public class AbilityFactoryMana { return false; } - @Override - public String getManaProduced() { - return manaGenerated(this.tmpMana, this.af, this); - } }; @Override @@ -178,10 +173,6 @@ public class AbilityFactoryMana { return false; } - @Override - public String getManaProduced() { - return manaGenerated(this.tmpMana, this.af, this); - } }; @Override From 65238c63c619e580d12f42d9947d0648f34333df Mon Sep 17 00:00:00 2001 From: ArsenalNut Date: Sun, 1 Jan 2012 21:53:59 +0000 Subject: [PATCH 09/11] add color choice logic for future use by AI --- res/cardsfolder/m/manamorphose.txt | 4 +- src/main/java/forge/ComputerUtil.java | 114 ------------------ .../abilityfactory/AbilityFactoryMana.java | 22 +++- 3 files changed, 22 insertions(+), 118 deletions(-) diff --git a/res/cardsfolder/m/manamorphose.txt b/res/cardsfolder/m/manamorphose.txt index 5cd59b127db..f6281f9b68c 100644 --- a/res/cardsfolder/m/manamorphose.txt +++ b/res/cardsfolder/m/manamorphose.txt @@ -2,8 +2,8 @@ Name:Manamorphose ManaCost:1 RG Types:Instant Text:no text -A:SP$ Mana | Cost$ 1 RG | Produced$ Any | Amount$ 1 | SubAbility$ DBMana | SpellDescription$ Add two mana in any combination of colors to your mana pool. Draw a card. -SVar:DBMana:DB$ Mana | Cost$ 0 | Produced$ Any | Amount$ 1 | SubAbility$ DBDraw +A:SP$ Mana | Cost$ 1 RG | Produced$ Any | Amount$ 1 | SubAbility$ DBMana | AILogic$ MostProminentInComputerHand | SpellDescription$ Add two mana in any combination of colors to your mana pool. Draw a card. +SVar:DBMana:DB$ Mana | Cost$ 0 | Produced$ Any | Amount$ 1 | AILogic$ MostProminentInComputerHand | SubAbility$ DBDraw SVar:DBDraw:DB$ Draw | Defined$ You | NumCards$ 1 SVar:RemAIDeck:True SVar:Rarity:Common diff --git a/src/main/java/forge/ComputerUtil.java b/src/main/java/forge/ComputerUtil.java index 6eb37d5a573..c5c12040138 100644 --- a/src/main/java/forge/ComputerUtil.java +++ b/src/main/java/forge/ComputerUtil.java @@ -611,8 +611,6 @@ public class ComputerUtil { return true; } - ArrayList colors; - cost = manapool.subtractMana(sa, cost); //ManaCost testCost = new ManaCost(cost.toString()); // temp variable for testing only if (card.getSVar("ManaNeededToAvoidNegativeEffect") != "") { @@ -856,118 +854,6 @@ public class ComputerUtil { return true; } - /* - 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 - } - m.setActivatingPlayer(player); - // if the AI can't pay the additional costs skip the mana - // ability - if (m.getPayCosts() != null) { - if (!ComputerUtil.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; - } - } - - 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 - } - - 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; - } - } - } - - } - */ - if (!test) { final StringBuilder sb = new StringBuilder(); sb.append("ComputerUtil : payManaCost() cost was not paid for "); diff --git a/src/main/java/forge/card/abilityfactory/AbilityFactoryMana.java b/src/main/java/forge/card/abilityfactory/AbilityFactoryMana.java index 7e713a4b823..6582f97fb61 100644 --- a/src/main/java/forge/card/abilityfactory/AbilityFactoryMana.java +++ b/src/main/java/forge/card/abilityfactory/AbilityFactoryMana.java @@ -18,7 +18,6 @@ package forge.card.abilityfactory; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.Random; @@ -33,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; @@ -246,7 +246,7 @@ public class AbilityFactoryMana { return sb.toString(); } - + /** *

manaGenerated.

* @@ -306,6 +306,24 @@ public class AbilityFactoryMana { 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()); + } + } } } } From 5cbbea36198ef4c4b3eb0cfb19af08568712eae0 Mon Sep 17 00:00:00 2001 From: ArsenalNut Date: Mon, 2 Jan 2012 02:03:12 +0000 Subject: [PATCH 10/11] changed sorting of mana sources to account of any color mana --- src/main/java/forge/ComputerUtil.java | 98 +++++++++++++-------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/src/main/java/forge/ComputerUtil.java b/src/main/java/forge/ComputerUtil.java index c5c12040138..4d3ce659bae 100644 --- a/src/main/java/forge/ComputerUtil.java +++ b/src/main/java/forge/ComputerUtil.java @@ -919,6 +919,7 @@ public class ComputerUtil { return ComputerUtil.getAvailableMana(AllZone.getComputerPlayer()); } // getAvailableMana() + // gets available mana sources and sorts them /** *

@@ -946,22 +947,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(); @@ -985,61 +1004,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); } - } - //TODO: Check for cards that produce "Any" as base mana (ArsenalNut) - // 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() From fd78663fca1b6fd6f905081d9e8d65d643918e7d Mon Sep 17 00:00:00 2001 From: ArsenalNut Date: Mon, 2 Jan 2012 18:23:20 +0000 Subject: [PATCH 11/11] add logic to make ManaNeededToAvoidNegativeEffect mana a mandatory part of the cost for the AI --- src/main/java/forge/ComputerUtil.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/java/forge/ComputerUtil.java b/src/main/java/forge/ComputerUtil.java index 4d3ce659bae..4128ca9ef36 100644 --- a/src/main/java/forge/ComputerUtil.java +++ b/src/main/java/forge/ComputerUtil.java @@ -612,17 +612,25 @@ public class ComputerUtil { } cost = manapool.subtractMana(sa, cost); - //ManaCost testCost = new ManaCost(cost.toString()); // temp variable for testing only if (card.getSVar("ManaNeededToAvoidNegativeEffect") != "") { 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); - //testCost.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); } // get map of mana abilities @@ -637,8 +645,6 @@ public class ComputerUtil { foundAllSources = false; } else { - //TODO: Sort colorless sources based on ManaNeededToAvoidNegativeEffect - // (ArsenalNut - 111230) String[] shortColors = {"W", "U", "B" , "R", "G"}; // loop over cost parts for (int nPart = 0; nPart < costParts.length; nPart++) {