From 3972695428a2baad71adfc48b013bbfe2e91fcda Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 21 May 2018 11:50:49 +0200 Subject: [PATCH] * Some changes to improve mana use of AI. --- .../java/mage/player/ai/ComputerPlayer.java | 65 +++++++-- .../test/cards/mana/EldraziTempleTest.java | 111 +++++++++++++++ .../org/mage/test/utils/ManaOptionsTest.java | 44 +++++- .../mana/ActivateIfConditionManaAbility.java | 2 +- .../mana/ActivatedManaAbilityImpl.java | 6 +- .../mana/ConditionalColorlessManaAbility.java | 3 +- .../java/mage/abilities/mana/ManaOptions.java | 128 +++++++++++------- 7 files changed, 297 insertions(+), 62 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/mana/EldraziTempleTest.java diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 7059fe715e3..e7f90f20c4b 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -31,6 +31,7 @@ import java.io.IOException; import java.io.Serializable; import java.util.*; import java.util.Map.Entry; +import mage.ConditionalMana; import mage.MageObject; import mage.Mana; import mage.abilities.*; @@ -1261,7 +1262,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { return result; } - protected boolean playManaHandling(Ability ability, ManaCost unpaid, Game game) { + protected boolean playManaHandling(Ability ability, ManaCost unpaid, final Game game) { // log.info("paying for " + unpaid.getText()); UUID spendAnyManaId = game.getContinuousEffects().asThough(ability.getSourceId(), AsThoughEffectType.SPEND_OTHER_MANA, ability, ability.getControllerId(), game); ManaCost cost; @@ -1279,7 +1280,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { // use color producing mana abilities with costs first that produce all color manas that are needed to pay // otherwise the computer may not be able to pay the cost for that source ManaAbility: - for (ActivatedManaAbilityImpl manaAbility : mageObject.getAbilities().getAvailableActivatedManaAbilities(Zone.BATTLEFIELD, game)) { + for (ActivatedManaAbilityImpl manaAbility : getManaAbilitiesSortedByManaCount(mageObject, game)) { int colored = 0; for (Mana mana : manaAbility.getNetMana(game)) { if (!unpaid.getMana().includesMana(mana)) { @@ -1288,9 +1289,11 @@ public class ComputerPlayer extends PlayerImpl implements Player { colored += mana.countColored(); } if (colored > 1 && (cost instanceof ColoredManaCost)) { - for (Mana netMana : manaAbility.getNetMana(game)) { if (cost.testPay(netMana)) { + if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) { + continue; + } if (activateAbility(manaAbility, game)) { return true; } @@ -1302,10 +1305,13 @@ public class ComputerPlayer extends PlayerImpl implements Player { for (MageObject mageObject : producers) { // pay all colored costs first - for (ActivatedManaAbilityImpl manaAbility : mageObject.getAbilities().getAvailableActivatedManaAbilities(Zone.BATTLEFIELD, game)) { + for (ActivatedManaAbilityImpl manaAbility : getManaAbilitiesSortedByManaCount(mageObject, game)) { if (cost instanceof ColoredManaCost) { for (Mana netMana : manaAbility.getNetMana(game)) { if (cost.testPay(netMana) || spendAnyManaId != null) { + if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) { + continue; + } if (activateAbility(manaAbility, game)) { return true; } @@ -1314,10 +1320,13 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } // pay snow covered mana - for (ActivatedManaAbilityImpl manaAbility : mageObject.getAbilities().getAvailableActivatedManaAbilities(Zone.BATTLEFIELD, game)) { + for (ActivatedManaAbilityImpl manaAbility : getManaAbilitiesSortedByManaCount(mageObject, game)) { if (cost instanceof SnowManaCost) { for (Mana netMana : manaAbility.getNetMana(game)) { if (cost.testPay(netMana) || spendAnyManaId != null) { + if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) { + continue; + } if (activateAbility(manaAbility, game)) { return true; } @@ -1326,10 +1335,13 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } // then pay hybrid - for (ActivatedManaAbilityImpl manaAbility : mageObject.getAbilities().getAvailableActivatedManaAbilities(Zone.BATTLEFIELD, game)) { + for (ActivatedManaAbilityImpl manaAbility : getManaAbilitiesSortedByManaCount(mageObject, game)) { if (cost instanceof HybridManaCost) { for (Mana netMana : manaAbility.getNetMana(game)) { if (cost.testPay(netMana) || spendAnyManaId != null) { + if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) { + continue; + } if (activateAbility(manaAbility, game)) { return true; } @@ -1338,10 +1350,13 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } // then pay mono hybrid - for (ActivatedManaAbilityImpl manaAbility : mageObject.getAbilities().getAvailableActivatedManaAbilities(Zone.BATTLEFIELD, game)) { + for (ActivatedManaAbilityImpl manaAbility : getManaAbilitiesSortedByManaCount(mageObject, game)) { if (cost instanceof MonoHybridManaCost) { for (Mana netMana : manaAbility.getNetMana(game)) { if (cost.testPay(netMana) || spendAnyManaId != null) { + if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) { + continue; + } if (activateAbility(manaAbility, game)) { return true; } @@ -1350,10 +1365,13 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } // pay colorless - for (ActivatedManaAbilityImpl manaAbility : mageObject.getAbilities().getAvailableActivatedManaAbilities(Zone.BATTLEFIELD, game)) { + for (ActivatedManaAbilityImpl manaAbility : getManaAbilitiesSortedByManaCount(mageObject, game)) { if (cost instanceof ColorlessManaCost) { for (Mana netMana : manaAbility.getNetMana(game)) { if (cost.testPay(netMana) || spendAnyManaId != null) { + if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) { + continue; + } if (activateAbility(manaAbility, game)) { return true; } @@ -1362,10 +1380,13 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } // finally pay generic - for (ActivatedManaAbilityImpl manaAbility : mageObject.getAbilities().getAvailableActivatedManaAbilities(Zone.BATTLEFIELD, game)) { + for (ActivatedManaAbilityImpl manaAbility : getManaAbilitiesSortedByManaCount(mageObject, game)) { if (cost instanceof GenericManaCost) { for (Mana netMana : manaAbility.getNetMana(game)) { if (cost.testPay(netMana) || spendAnyManaId != null) { + if (netMana instanceof ConditionalMana && !((ConditionalMana) netMana).apply(ability, game, getId(), cost)) { + continue; + } if (activateAbility(manaAbility, game)) { return true; } @@ -1383,6 +1404,32 @@ public class ComputerPlayer extends PlayerImpl implements Player { return false; } + private Abilities getManaAbilitiesSortedByManaCount(MageObject mageObject, final Game game) { + Abilities manaAbilities = mageObject.getAbilities().getAvailableActivatedManaAbilities(Zone.BATTLEFIELD, game); + if (manaAbilities.size() > 1) { + // Sort mana abilities by numbver of produced manas, to use ability first that produces most mana (maybe also conditional if possible) + Collections.sort(manaAbilities, new Comparator() { + @Override + public int compare(ActivatedManaAbilityImpl a1, ActivatedManaAbilityImpl a2) { + int a1Max = 0; + for (Mana netMana : a1.getNetMana(game)) { + if (netMana.count() > a1Max) { + a1Max = netMana.count(); + } + } + int a2Max = 0; + for (Mana netMana : a2.getNetMana(game)) { + if (netMana.count() > a2Max) { + a2Max = netMana.count(); + } + } + return a2Max - a1Max; + } + }); + } + return manaAbilities; + } + /** * * returns a list of Permanents that produce mana sorted by the number of diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/EldraziTempleTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/EldraziTempleTest.java new file mode 100644 index 00000000000..20f7586743f --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/EldraziTempleTest.java @@ -0,0 +1,111 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package org.mage.test.cards.mana; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class EldraziTempleTest extends CardTestPlayerBase { + + @Test + public void TestWithoutDampingSphere() { + // {T}: Add {C}. + // {T}: Add {C}{C}. Spend this mana only to cast colorless Eldrazi spells or activate abilities of colorless Eldrazi. + addCard(Zone.BATTLEFIELD, playerA, "Eldrazi Temple", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // Devoid (This card has no color.) + // {2}{R}, Sacrifice another colorless creature: Barrage Tyrant deals damage equal to the sacrificed creature's power to target creature or player. + addCard(Zone.HAND, playerA, "Barrage Tyrant", 1); // Creature {4}{R} + + // If a land is tapped for two or more mana, it produces {C} instead of any other type and amount. + // Each spell a player casts costs {1} more to cast for each other spell that player has cast this turn. + // addCard(Zone.BATTLEFIELD, playerB, "Damping Sphere", 1); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Barrage Tyrant"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Barrage Tyrant", 1); + + } + + /** + * Damping Sphere vs Eldrazi + * + * My opponent managed to produce 7 mana to cast a All Is Dust with 6 lands + * where one was an Eldrazi Temple. This should not be possible if my + * reading of Damping Sphere (If a land is tapped for two or more mana, it + * produces {C} instead of any other type and amount.) is correct. It should + * produce a single colorless mana rather than 2 colorless for the eldrazi + * tribal spell + */ + @Test + public void TestWithDampingSphere() { + // {T}: Add {C}. + // {T}: Add {C}{C}. Spend this mana only to cast colorless Eldrazi spells or activate abilities of colorless Eldrazi. + addCard(Zone.BATTLEFIELD, playerA, "Eldrazi Temple", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // Devoid (This card has no color.) + // {2}{R}, Sacrifice another colorless creature: Barrage Tyrant deals damage equal to the sacrificed creature's power to target creature or player. + addCard(Zone.HAND, playerA, "Barrage Tyrant", 1); // Creature {4}{R} + + // If a land is tapped for two or more mana, it produces {C} instead of any other type and amount. + // Each spell a player casts costs {1} more to cast for each other spell that player has cast this turn. + addCard(Zone.BATTLEFIELD, playerB, "Damping Sphere", 1); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Barrage Tyrant"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Barrage Tyrant", 0); + } + + @Test + public void TestUseOfUnconditionalMana() { + // {T}: Add {C}. + // {T}: Add {C}{C}. Spend this mana only to cast colorless Eldrazi spells or activate abilities of colorless Eldrazi. + addCard(Zone.BATTLEFIELD, playerA, "Eldrazi Temple", 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + // {T}: Add {W}, {B}, or {G} to your mana pool. + // {W}{B}{G}, {T}, Sacrifice Abzan Banner: Draw a card. + addCard(Zone.HAND, playerA, "Abzan Banner", 1); // Artifact {3} + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Abzan Banner"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Abzan Banner", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/utils/ManaOptionsTest.java b/Mage.Tests/src/test/java/org/mage/test/utils/ManaOptionsTest.java index 2cfb533c4ca..f96b0d1d92a 100644 --- a/Mage.Tests/src/test/java/org/mage/test/utils/ManaOptionsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/utils/ManaOptionsTest.java @@ -158,7 +158,7 @@ public class ManaOptionsTest extends CardTestPlayerBase { assertDuplicatedManaOptions(manaOptions); Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); - assertManaOptions("{C}{W}{W}{G}{G}", manaOptions); + assertManaOptions("{C}{W}{W}{G}{G}", manaOptions); } // Crystal Quarry @@ -236,6 +236,29 @@ public class ManaOptionsTest extends CardTestPlayerBase { assertManaOptions("{C}{G}{Any}", manaOptions); } + // Nykthos, Shrine to Nyx + // {T}: Add {C}. + // {2}, {T}: Choose a color. Add an amount of mana of that color equal to your devotion to that color. (Your devotion to a color is the number of mana symbols of that color in the mana costs of permanents you control.) + @Test + public void testNykthos4() { + // If a land is tapped for two or more mana, it produces {C} instead of any other type and amount. + // Each spell a player casts costs {1} more to cast for each other spell that player has cast this turn. + addCard(Zone.BATTLEFIELD, playerA, "Damping Sphere", 1); + addCard(Zone.BATTLEFIELD, playerA, "Sedge Scorpion", 4); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + addCard(Zone.BATTLEFIELD, playerA, "Nykthos, Shrine to Nyx", 1); + + setStopAt(1, PhaseStep.UPKEEP); + execute(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + assertDuplicatedManaOptions(manaOptions); + + Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + assertManaOptions("{C}{G}{G}{G}", manaOptions); + + } + @Test public void testDuplicatedDontHave1() { addCard(Zone.BATTLEFIELD, playerA, "City of Brass", 2); // Any @@ -401,4 +424,23 @@ public class ManaOptionsTest extends CardTestPlayerBase { Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); assertManaOptions("{B}{B}", manaOptions); } + + @Test + public void testDampingSphere() { + // If a land is tapped for two or more mana, it produces {C} instead of any other type and amount. + // Each spell a player casts costs {1} more to cast for each other spell that player has cast this turn. + addCard(Zone.BATTLEFIELD, playerA, "Damping Sphere", 1); + // {T}: Add {C}. + // {T}: Add {C}{C}. Spend this mana only to cast colorless Eldrazi spells or activate abilities of colorless Eldrazi. + addCard(Zone.BATTLEFIELD, playerA, "Eldrazi Temple", 1); + + setStopAt(1, PhaseStep.UPKEEP); + execute(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + assertDuplicatedManaOptions(manaOptions); + + Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + assertManaOptions("{C}", manaOptions); + } } diff --git a/Mage/src/main/java/mage/abilities/mana/ActivateIfConditionManaAbility.java b/Mage/src/main/java/mage/abilities/mana/ActivateIfConditionManaAbility.java index dbbf9bb9103..953f3542b3d 100644 --- a/Mage/src/main/java/mage/abilities/mana/ActivateIfConditionManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/ActivateIfConditionManaAbility.java @@ -59,7 +59,7 @@ public class ActivateIfConditionManaAbility extends ActivatedManaAbilityImpl { @Override public String getRule() { - return new StringBuilder(super.getRule()).append(" Activate this ability only if ").append(condition.toString()).append('.').toString(); + return super.getRule() + " Activate this ability only if " + condition.toString() + '.'; } @Override diff --git a/Mage/src/main/java/mage/abilities/mana/ActivatedManaAbilityImpl.java b/Mage/src/main/java/mage/abilities/mana/ActivatedManaAbilityImpl.java index 9f7c294c6c3..eb065d69270 100644 --- a/Mage/src/main/java/mage/abilities/mana/ActivatedManaAbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/mana/ActivatedManaAbilityImpl.java @@ -103,7 +103,11 @@ public abstract class ActivatedManaAbilityImpl extends ActivatedAbilityImpl impl } } } - return netMana; + ArrayList netManaCopy = new ArrayList<>(); + for (Mana mana : netMana) { + netManaCopy.add(mana.copy()); + } + return netManaCopy; } /** diff --git a/Mage/src/main/java/mage/abilities/mana/ConditionalColorlessManaAbility.java b/Mage/src/main/java/mage/abilities/mana/ConditionalColorlessManaAbility.java index 6c67a7f6579..09f903b0756 100644 --- a/Mage/src/main/java/mage/abilities/mana/ConditionalColorlessManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/ConditionalColorlessManaAbility.java @@ -5,7 +5,6 @@ */ package mage.abilities.mana; -import mage.Mana; import mage.abilities.costs.Cost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.mana.AddConditionalColorlessManaEffect; @@ -24,7 +23,7 @@ public class ConditionalColorlessManaAbility extends ActivatedManaAbilityImpl { public ConditionalColorlessManaAbility(Cost cost, int amount, ConditionalManaBuilder manaBuilder) { super(Zone.BATTLEFIELD, new AddConditionalColorlessManaEffect(amount, manaBuilder), cost); - this.netMana.add(Mana.ColorlessMana(amount)); + // this.netMana.add(Mana.ColorlessMana(amount)); // When produced during runtime, condition is added (how to add condition here?) } public ConditionalColorlessManaAbility(final ConditionalColorlessManaAbility ability) { diff --git a/Mage/src/main/java/mage/abilities/mana/ManaOptions.java b/Mage/src/main/java/mage/abilities/mana/ManaOptions.java index dd90f3a57d0..151535799a2 100644 --- a/Mage/src/main/java/mage/abilities/mana/ManaOptions.java +++ b/Mage/src/main/java/mage/abilities/mana/ManaOptions.java @@ -31,9 +31,13 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; - import mage.Mana; +import mage.abilities.Ability; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.TapSourceCost; import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ManaEvent; /** * @@ -64,43 +68,51 @@ public class ManaOptions extends ArrayList { //if there is only one mana option available add it to all the existing options List netManas = abilities.get(0).getNetMana(game); if (netManas.size() == 1) { - addMana(netManas.get(0)); + if (!hasTapCost(abilities.get(0)) || checkTappedForManaReplacement(abilities.get(0), game, netManas.get(0))) { + addMana(netManas.get(0)); + } } else { List copy = copy(); this.clear(); + boolean hasTapCost = hasTapCost(abilities.get(0)); for (Mana netMana : netManas) { for (Mana mana : copy) { - Mana newMana = new Mana(); - newMana.add(mana); - newMana.add(netMana); - this.add(newMana); + if (!hasTapCost || checkTappedForManaReplacement(abilities.get(0), game, netMana)) { + Mana newMana = new Mana(); + newMana.add(mana); + newMana.add(netMana); + this.add(newMana); + } } } } - } else if (abilities.size() > 1) { + } else { // mana source has more than 1 ability //perform a union of all existing options and the new options List copy = copy(); this.clear(); for (ActivatedManaAbilityImpl ability : abilities) { + boolean hasTapCost = hasTapCost(ability); for (Mana netMana : ability.getNetMana(game)) { - SkipAddMana: - for (Mana mana : copy) { - Mana newMana = new Mana(); - newMana.add(mana); - newMana.add(netMana); - for (Mana existingMana : this) { - if (existingMana.equalManaValue(newMana)) { - continue SkipAddMana; - } - Mana moreValuable = Mana.getMoreValuableMana(newMana, existingMana); - if (moreValuable != null) { - // only keep the more valuable mana - existingMana.setToMana(moreValuable); - continue SkipAddMana; + if (!hasTapCost || checkTappedForManaReplacement(ability, game, netMana)) { + SkipAddMana: + for (Mana mana : copy) { + Mana newMana = new Mana(); + newMana.add(mana); + newMana.add(netMana); + for (Mana existingMana : this) { + if (existingMana.equalManaValue(newMana)) { + continue SkipAddMana; + } + Mana moreValuable = Mana.getMoreValuableMana(newMana, existingMana); + if (moreValuable != null) { + // only keep the more valuable mana + existingMana.setToMana(moreValuable); + continue SkipAddMana; + } } + this.add(newMana); } - this.add(newMana); } } } @@ -108,6 +120,23 @@ public class ManaOptions extends ArrayList { } } + private boolean checkTappedForManaReplacement(Ability ability, Game game, Mana mana) { + ManaEvent event = new ManaEvent(GameEvent.EventType.TAPPED_FOR_MANA, ability.getSourceId(), ability.getSourceId(), ability.getControllerId(), mana); + if (!game.replaceEvent(event)) { + return true; + } + return false; + } + + private boolean hasTapCost(Ability ability) { + for (Cost cost : ability.getCosts()) { + if (cost instanceof TapSourceCost) { + return true; + } + } + return false; + } + public void addManaWithCost(List abilities, Game game) { if (isEmpty()) { this.add(new Mana()); @@ -153,37 +182,41 @@ public class ManaOptions extends ArrayList { List copy = copy(); this.clear(); for (ActivatedManaAbilityImpl ability : abilities) { - + boolean hasTapCost = hasTapCost(ability); List netManas = ability.getNetMana(game); if (ability.getManaCosts().isEmpty()) { for (Mana netMana : netManas) { - for (Mana mana : copy) { - Mana newMana = new Mana(); - newMana.add(mana); - newMana.add(netMana); - this.add(newMana); + if (!hasTapCost || checkTappedForManaReplacement(ability, game, netMana)) { + for (Mana mana : copy) { + Mana newMana = new Mana(); + newMana.add(mana); + newMana.add(netMana); + this.add(newMana); + } } } } else { for (Mana netMana : netManas) { - for (Mana previousMana : copy) { - CombineWithExisting: - for (Mana manaOption : ability.getManaCosts().getManaOptions()) { - Mana newMana = new Mana(previousMana); - if (previousMana.includesMana(manaOption)) { // costs can be paid - newMana.subtractCost(manaOption); - newMana.add(netMana); - // if the new mana is in all colors more than another already existing than replace - for (Mana existingMana : this) { - Mana moreValuable = Mana.getMoreValuableMana(newMana, existingMana); - if (moreValuable != null) { - existingMana.setToMana(moreValuable); - continue CombineWithExisting; + if (!hasTapCost || checkTappedForManaReplacement(ability, game, netMana)) { + for (Mana previousMana : copy) { + CombineWithExisting: + for (Mana manaOption : ability.getManaCosts().getManaOptions()) { + Mana newMana = new Mana(previousMana); + if (previousMana.includesMana(manaOption)) { // costs can be paid + newMana.subtractCost(manaOption); + newMana.add(netMana); + // if the new mana is in all colors more than another already existing than replace + for (Mana existingMana : this) { + Mana moreValuable = Mana.getMoreValuableMana(newMana, existingMana); + if (moreValuable != null) { + existingMana.setToMana(moreValuable); + continue CombineWithExisting; + } } + // no existing Mana includes this new mana so add + this.add(newMana); } - // no existing Mana includes this new mana so add - this.add(newMana); } } } @@ -330,16 +363,15 @@ public class ManaOptions extends ArrayList { payCombinationsStrings.add(newMana.toString()); } - - public void removeDuplicated(){ + public void removeDuplicated() { Set list = new HashSet<>(); - for(int i = this.size() - 1; i >= 0; i--){ + for (int i = this.size() - 1; i >= 0; i--) { String s = this.get(i).toString(); - if (list.contains(s)){ + if (list.contains(s)) { // remove duplicated this.remove(i); - }else{ + } else { list.add(s); } }