From 3a82840e668488047667aedb2efb29c777c42916 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 16 Jul 2020 22:37:51 +0200 Subject: [PATCH] * Fixed some more available mana calculation problems. Mana of the pool is now taken into account (e.g. Coal Golem problem). Crucible of the Spirit Dragon - Conditional mana curretnly not handled correctly in available mana calculation. Crystalline Crawler works now.Related to #6698. --- .../java/mage/player/ai/ComputerPlayer.java | 18 ++---- .../cards/c/CrucibleOfTheSpiritDragon.java | 11 ++-- .../src/mage/cards/c/CrystallineCrawler.java | 7 ++- .../src/mage/cards/d/DeathriteShaman.java | 15 +++-- .../src/mage/cards/s/SarkhanFireblood.java | 5 +- .../keywords/SpliceOnArcaneTest.java | 2 +- .../cards/flip/SasayaOrochiAscendantTest.java | 43 +++++++------- .../cards/mana/TappedForManaRelatedTest.java | 41 ++++++++++++++ .../dynamicvalue/LimitedDynamicValue.java | 56 +++++++++++++++++++ .../AddConditionalManaOfAnyColorEffect.java | 29 ++++++---- .../effects/mana/AddManaOfAnyColorEffect.java | 28 +++++++++- .../mage/abilities/keyword/AssistAbility.java | 21 ++++--- .../abilities/mana/AnyColorManaAbility.java | 35 ++++++++++++ .../mana/ConditionalAnyColorManaAbility.java | 15 ++--- .../java/mage/abilities/mana/ManaOptions.java | 3 + .../main/java/mage/players/PlayerImpl.java | 13 +++-- 16 files changed, 253 insertions(+), 89 deletions(-) create mode 100644 Mage/src/main/java/mage/abilities/dynamicvalue/LimitedDynamicValue.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 d4b4b646287..59ae2a469b8 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 @@ -1,5 +1,9 @@ package mage.player.ai; +import java.io.IOException; +import java.io.Serializable; +import java.util.*; +import java.util.Map.Entry; import mage.ConditionalMana; import mage.MageObject; import mage.MageObjectReference; @@ -57,11 +61,6 @@ import mage.util.TournamentUtil; import mage.util.TreeNode; import org.apache.log4j.Logger; -import java.io.IOException; -import java.io.Serializable; -import java.util.*; -import java.util.Map.Entry; - /** * suitable for two player games and some multiplayer games * @@ -137,8 +136,6 @@ public class ComputerPlayer extends PlayerImpl implements Player { // - target.getTargetController(), this.getId() -- player that must makes choices (must be same with this.getId) // - target.getAbilityController(), abilityControllerId -- affected player/controller for all actions/filters // - affected controler can be different from target controller (another player makes choices for controller) - - // sometimes a target selection can be made from a player that does not control the ability UUID abilityControllerId = playerId; if (target.getTargetController() != null @@ -478,7 +475,6 @@ public class ComputerPlayer extends PlayerImpl implements Player { // target - real target, make all changes and add targets to it // target.getOriginalTarget() - copy spell effect replaces original target with TargetWithAdditionalFilter // use originalTarget to get filters and target class info - // source can be null (as example: legendary rule permanent selection) UUID sourceId = source != null ? source.getSourceId() : null; @@ -731,9 +727,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { // TODO: in multiplayer game there many opponents - if random opponents don't have targets then AI must use next opponent, but it skips // (e.g. you randomOpponentId must be replaced by List randomOpponents) - // normal cycle (good for you, bad for opponents) - // possible good/bad permanents if (outcome.isGood()) { targets = threats(abilityControllerId, source.getSourceId(), ((FilterPermanentOrPlayer) target.getFilter()).getPermanentFilter(), game, target.getTargets()); @@ -1312,7 +1306,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { playableAbilities.clear(); Set nonLands = hand.getCards(new FilterNonlandCard(), game); ManaOptions available = getManaAvailable(game); - available.addMana(manaPool.getMana()); +// available.addMana(manaPool.getMana()); for (Card card : nonLands) { ManaOptions options = card.getManaCost().getOptions(); @@ -2562,7 +2556,6 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } - protected List threats(UUID playerId, UUID sourceId, FilterPermanent filter, Game game, List targets) { return threats(playerId, sourceId, filter, game, targets, true); } @@ -2688,7 +2681,6 @@ public class ComputerPlayer extends PlayerImpl implements Player { return before != after; } - /** * Sets a possible target player */ diff --git a/Mage.Sets/src/mage/cards/c/CrucibleOfTheSpiritDragon.java b/Mage.Sets/src/mage/cards/c/CrucibleOfTheSpiritDragon.java index 0b8fe29b228..6d8c17dbbbe 100644 --- a/Mage.Sets/src/mage/cards/c/CrucibleOfTheSpiritDragon.java +++ b/Mage.Sets/src/mage/cards/c/CrucibleOfTheSpiritDragon.java @@ -1,4 +1,3 @@ - package mage.cards.c; import java.util.UUID; @@ -11,6 +10,7 @@ import mage.abilities.condition.Condition; import mage.abilities.costs.common.RemoveVariableCountersSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.dynamicvalue.common.RemovedCountersForCostValue; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.mana.ColorlessManaAbility; @@ -31,13 +31,13 @@ import mage.game.Game; public final class CrucibleOfTheSpiritDragon extends CardImpl { public CrucibleOfTheSpiritDragon(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); // {T}: Add {C}. this.addAbility(new ColorlessManaAbility()); // {1}, {T}: Put a storage counter on Crucible of the Spirit Dragon. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.STORAGE.createInstance()),new GenericManaCost(1)); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.STORAGE.createInstance()), new GenericManaCost(1)); ability.addCost(new TapSourceCost()); this.addAbility(ability); @@ -45,9 +45,10 @@ public final class CrucibleOfTheSpiritDragon extends CardImpl { ability = new ConditionalAnyColorManaAbility( new TapSourceCost(), RemovedCountersForCostValue.instance, + new CountersSourceCount(CounterType.STORAGE), new CrucibleOfTheSpiritDragonManaBuilder(), false - ); + ); ability.addCost(new RemoveVariableCountersSourceCost(CounterType.STORAGE.createInstance())); this.addAbility(ability); } @@ -94,4 +95,4 @@ class CrucibleOfTheSpiritDragonManaCondition implements Condition { } return false; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/c/CrystallineCrawler.java b/Mage.Sets/src/mage/cards/c/CrystallineCrawler.java index fcf5ff90bca..3ea9e497e2e 100644 --- a/Mage.Sets/src/mage/cards/c/CrystallineCrawler.java +++ b/Mage.Sets/src/mage/cards/c/CrystallineCrawler.java @@ -1,4 +1,3 @@ - package mage.cards.c; import java.util.UUID; @@ -8,6 +7,7 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.RemoveCountersSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.dynamicvalue.common.ColorsOfManaSpentToCastCount; +import mage.abilities.dynamicvalue.common.CountersSourceCount; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.mana.AnyColorManaAbility; import mage.cards.CardImpl; @@ -30,13 +30,14 @@ public final class CrystallineCrawler extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - // Converge — Crystalline Crawler enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it. + // Converge - Crystalline Crawler enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it. this.addAbility(new EntersBattlefieldAbility( new AddCountersSourceEffect(CounterType.P1P1.createInstance(), ColorsOfManaSpentToCastCount.getInstance(), true), null, "Converge — {this} enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it.", null)); // Remove a +1/+1 counter from Crystalline Crawler: Add one mana of any color. - this.addAbility(new AnyColorManaAbility(new RemoveCountersSourceCost(CounterType.P1P1.createInstance(1)))); + this.addAbility(new AnyColorManaAbility(new RemoveCountersSourceCost(CounterType.P1P1.createInstance(1)), + new CountersSourceCount(CounterType.P1P1), false)); // {T}: Put a +1/+1 counter on Crystalline Crawler. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance(1)), new TapSourceCost())); diff --git a/Mage.Sets/src/mage/cards/d/DeathriteShaman.java b/Mage.Sets/src/mage/cards/d/DeathriteShaman.java index 154c9a6c788..d2899fc2726 100644 --- a/Mage.Sets/src/mage/cards/d/DeathriteShaman.java +++ b/Mage.Sets/src/mage/cards/d/DeathriteShaman.java @@ -1,4 +1,3 @@ - package mage.cards.d; import java.util.UUID; @@ -7,16 +6,19 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.mana.AddManaOfAnyColorEffect; +import mage.abilities.dynamicvalue.LimitedDynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.effects.common.GainLifeEffect; import mage.abilities.effects.common.LoseLifeOpponentsEffect; +import mage.abilities.effects.mana.AddManaOfAnyColorEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.Zone; import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.filter.common.FilterCreatureCard; import mage.filter.common.FilterLandCard; import mage.filter.predicate.Predicates; @@ -37,17 +39,18 @@ public final class DeathriteShaman extends CardImpl { } public DeathriteShaman(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{B/G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B/G}"); this.subtype.add(SubType.ELF); this.subtype.add(SubType.SHAMAN); - this.power = new MageInt(1); this.toughness = new MageInt(2); // {T}: Exile target land card from a graveyard. Add one mana of any color. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(), new TapSourceCost()); - ability.addEffect(new AddManaOfAnyColorEffect()); + // Because this is no mana ability, this mana will not be calculated during available mana calculation + ability.addEffect(new AddManaOfAnyColorEffect(1, new LimitedDynamicValue(1, + new CardsInControllerGraveyardCount(StaticFilters.FILTER_CARD_LAND)), false)); ability.addTarget(new TargetCardInGraveyard(new FilterLandCard("land card from a graveyard"))); this.addAbility(ability); @@ -74,4 +77,4 @@ public final class DeathriteShaman extends CardImpl { public DeathriteShaman copy() { return new DeathriteShaman(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/s/SarkhanFireblood.java b/Mage.Sets/src/mage/cards/s/SarkhanFireblood.java index db16f3ff524..d79fd6fea92 100644 --- a/Mage.Sets/src/mage/cards/s/SarkhanFireblood.java +++ b/Mage.Sets/src/mage/cards/s/SarkhanFireblood.java @@ -10,11 +10,11 @@ import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.mana.AddConditionalManaOfAnyColorEffect; import mage.abilities.mana.conditional.ConditionalSpellManaBuilder; -import mage.constants.SubType; -import mage.constants.SuperType; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.filter.FilterSpell; import mage.game.permanent.token.DragonToken2; @@ -46,6 +46,7 @@ public final class SarkhanFireblood extends CardImpl { // +1: Add two mana of any combination of colors. Spend this mana only to cast Dragon spells. this.addAbility(new LoyaltyAbility( new AddConditionalManaOfAnyColorEffect( + StaticValue.get(2), StaticValue.get(2), new ConditionalSpellManaBuilder(filter), false diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SpliceOnArcaneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SpliceOnArcaneTest.java index b0bc8a24a22..2e76856b864 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SpliceOnArcaneTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/SpliceOnArcaneTest.java @@ -45,7 +45,7 @@ public class SpliceOnArcaneTest extends CardTestPlayerBase { assertHandCount(playerA, "Through the Breach", 1); assertPermanentCount(playerA, "Silvercoat Lion", 1); assertAbility(playerA, "Silvercoat Lion", HasteAbility.getInstance(), true); - Assert.assertEquals("All available mana has to be used", 0, playerA.getManaAvailable(currentGame).size()); + Assert.assertEquals("All available mana has to be used", "[]", playerA.getManaAvailable(currentGame).toString()); } @Test diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/flip/SasayaOrochiAscendantTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/flip/SasayaOrochiAscendantTest.java index 755a5b8bf58..d5f327daf97 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/flip/SasayaOrochiAscendantTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/flip/SasayaOrochiAscendantTest.java @@ -29,7 +29,7 @@ public class SasayaOrochiAscendantTest extends CardTestPlayerBase { addCard(Zone.HAND, playerA, "Upwelling", 1); // Enchantment {3}{G} activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reveal your hand: If you have seven or more land cards in your hand, flip"); - + activateManaAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: Add {G}"); activateManaAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: Add {G}"); castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Upwelling"); @@ -38,7 +38,7 @@ public class SasayaOrochiAscendantTest extends CardTestPlayerBase { setStopAt(1, PhaseStep.END_TURN); execute(); assertAllCommandsUsed(); - + assertPermanentCount(playerA, "Sasaya's Essence", 1); assertPermanentCount(playerA, "Upwelling", 1); @@ -48,11 +48,11 @@ public class SasayaOrochiAscendantTest extends CardTestPlayerBase { assertDuplicatedManaOptions(manaOptions); Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); - assertManaOptions("{G}{G}{G}", manaOptions); + assertManaOptions("{G}{G}{G}{G}{G}", manaOptions); } - - @Test + + @Test public void testSasayasEssence2() { addCard(Zone.HAND, playerA, "Plains", 7); addCard(Zone.BATTLEFIELD, playerA, "Brushland", 3); @@ -73,26 +73,25 @@ public class SasayaOrochiAscendantTest extends CardTestPlayerBase { setStopAt(1, PhaseStep.END_TURN); execute(); assertAllCommandsUsed(); - + assertPermanentCount(playerA, "Sasaya's Essence", 1); assertPermanentCount(playerA, "Upwelling", 1); assertManaPool(playerA, ManaType.GREEN, 2); assertLife(playerA, 18); - + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); assertDuplicatedManaOptions(manaOptions); Assert.assertEquals("mana variations don't fit", 3, manaOptions.size()); - assertManaOptions("{C}{C}{C}", manaOptions); - assertManaOptions("{G}{G}{G}", manaOptions); - assertManaOptions("{W}{W}{W}", manaOptions); + assertManaOptions("{C}{C}{C}{G}{G}", manaOptions); + assertManaOptions("{G}{G}{G}{G}{G}", manaOptions); + assertManaOptions("{W}{W}{W}{G}{G}", manaOptions); - } - - - @Test + } + + @Test public void testSasayasEssence3() { addCard(Zone.HAND, playerA, "Plains", 7); addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); @@ -115,22 +114,22 @@ public class SasayaOrochiAscendantTest extends CardTestPlayerBase { setStopAt(1, PhaseStep.END_TURN); execute(); assertAllCommandsUsed(); - + assertPermanentCount(playerA, "Sasaya's Essence", 1); assertPermanentCount(playerA, "Upwelling", 1); assertManaPool(playerA, ManaType.GREEN, 2); assertLife(playerA, 20); - + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); assertDuplicatedManaOptions(manaOptions); Assert.assertEquals("mana variations don't fit", 4, manaOptions.size()); - assertManaOptions("{R}{R}{R}{R}{G}{G}{G}", manaOptions); - assertManaOptions("{R}{R}{R}{G}{G}{G}{G}", manaOptions); - assertManaOptions("{R}{R}{G}{G}{G}{G}{G}", manaOptions); - assertManaOptions("{R}{G}{G}{G}{G}{G}{G}", manaOptions); - } - + assertManaOptions("{R}{R}{R}{R}{G}{G}{G}{G}{G}", manaOptions); + assertManaOptions("{R}{R}{R}{G}{G}{G}{G}{G}{G}", manaOptions); + assertManaOptions("{R}{R}{G}{G}{G}{G}{G}{G}{G}", manaOptions); + assertManaOptions("{R}{G}{G}{G}{G}{G}{G}{G}{G}", manaOptions); + } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/TappedForManaRelatedTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/TappedForManaRelatedTest.java index b8684b32a37..9767172efc8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/mana/TappedForManaRelatedTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/TappedForManaRelatedTest.java @@ -5,6 +5,7 @@ import mage.constants.PhaseStep; import mage.constants.Zone; import mage.counters.CounterType; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; import static org.mage.test.utils.ManaOptionsTestUtils.assertManaOptions; @@ -199,4 +200,44 @@ public class TappedForManaRelatedTest extends CardTestPlayerBase { assertManaOptions("{U}", manaOptions); assertManaOptions("{R}", manaOptions); } + + @Test + public void TestCrystallineCrawler() { + setStrictChooseMode(true); + // Converge - Crystalline Crawler enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it. + // Remove a +1/+1 counter from Crystalline Crawler: Add one mana of any color. + // {T}: Put a +1/+1 counter on Crystalline Crawler. + addCard(Zone.BATTLEFIELD, playerA, "Crystalline Crawler", 1); + addCounters(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Crystalline Crawler", CounterType.P1P1, 2); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + assertManaOptions("{Any}{Any}", manaOptions); + } + + @Test + @Ignore // Because this is no mana ability, this mana will not be calculated during available mana calculation + public void TestDeathriteShaman() { + setStrictChooseMode(true); + // {T}: Exile target land card from a graveyard. Add one mana of any color. + // {B}, {T}: Exile target instant or sorcery card from a graveyard. Each opponent loses 2 life. + // {G}, {T}: Exile target creature card from a graveyard. You gain 2 life. + addCard(Zone.BATTLEFIELD, playerA, "Deathrite Shaman", 1); + + addCard(Zone.GRAVEYARD, playerA, "Mountain", 3); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); + assertManaOptions("{Any}", manaOptions); + } } diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/LimitedDynamicValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/LimitedDynamicValue.java new file mode 100644 index 00000000000..d5a4ad4095f --- /dev/null +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/LimitedDynamicValue.java @@ -0,0 +1,56 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.abilities.dynamicvalue; + +import mage.abilities.Ability; +import mage.abilities.effects.Effect; +import mage.game.Game; + +/** + * + * @author LevelX2 + */ +public class LimitedDynamicValue implements DynamicValue { + + private final DynamicValue value; + private final int limit; + + /** + * Returns a dynamic but with an upper limit. + * + * @param limit the max value the dynamic value will return + * @param value the dynamic value to calculate the row dynamic value + */ + public LimitedDynamicValue(int limit, DynamicValue value) { + this.value = value; + this.limit = limit; + } + + LimitedDynamicValue(final LimitedDynamicValue dynamicValue) { + this.value = dynamicValue.value; + this.limit = dynamicValue.limit; + } + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return Math.min(limit, value.calculate(game, sourceAbility, effect)); + } + + @Override + public LimitedDynamicValue copy() { + return new LimitedDynamicValue(this); + } + + @Override + public String toString() { + return value.toString(); + } + + @Override + public String getMessage() { + return value.getMessage(); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaOfAnyColorEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaOfAnyColorEffect.java index 6b152acc443..4c4a23dafd3 100644 --- a/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaOfAnyColorEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/mana/AddConditionalManaOfAnyColorEffect.java @@ -1,5 +1,7 @@ package mage.abilities.effects.mana; +import java.util.ArrayList; +import java.util.List; import mage.ConditionalMana; import mage.Mana; import mage.abilities.Ability; @@ -13,9 +15,6 @@ import mage.players.Player; import mage.util.CardUtil; import org.apache.log4j.Logger; -import java.util.ArrayList; -import java.util.List; - /** * @author noxx */ @@ -24,20 +23,22 @@ public class AddConditionalManaOfAnyColorEffect extends ManaEffect { private static final Logger logger = Logger.getLogger(AddConditionalManaOfAnyColorEffect.class); private final DynamicValue amount; + private final DynamicValue netAmount; private final ConditionalManaBuilder manaBuilder; private final boolean oneChoice; public AddConditionalManaOfAnyColorEffect(int amount, ConditionalManaBuilder manaBuilder) { - this(StaticValue.get(amount), manaBuilder); + this(StaticValue.get(amount), StaticValue.get(amount), manaBuilder); } - public AddConditionalManaOfAnyColorEffect(DynamicValue amount, ConditionalManaBuilder manaBuilder) { - this(amount, manaBuilder, true); + public AddConditionalManaOfAnyColorEffect(DynamicValue amount, DynamicValue netAmount, ConditionalManaBuilder manaBuilder) { + this(amount, netAmount, manaBuilder, true); } - public AddConditionalManaOfAnyColorEffect(DynamicValue amount, ConditionalManaBuilder manaBuilder, boolean oneChoice) { + public AddConditionalManaOfAnyColorEffect(DynamicValue amount, DynamicValue netAmount, ConditionalManaBuilder manaBuilder, boolean oneChoice) { super(); this.amount = amount; + this.netAmount = netAmount; this.manaBuilder = manaBuilder; this.oneChoice = oneChoice; // @@ -53,6 +54,7 @@ public class AddConditionalManaOfAnyColorEffect extends ManaEffect { public AddConditionalManaOfAnyColorEffect(final AddConditionalManaOfAnyColorEffect effect) { super(effect); this.amount = effect.amount; + this.netAmount = effect.netAmount; this.manaBuilder = effect.manaBuilder; this.oneChoice = effect.oneChoice; } @@ -66,9 +68,16 @@ public class AddConditionalManaOfAnyColorEffect extends ManaEffect { public List getNetMana(Game game, Ability source) { List netMana = new ArrayList<>(); if (game != null) { - int value = amount.calculate(game, source, this); - if (value > 0) { - netMana.add(Mana.AnyMana(value)); + if (game.inCheckPlayableState()) { + int amountAvailableMana = netAmount.calculate(game, source, this); + if (amountAvailableMana > 0) { + netMana.add(manaBuilder.setMana(Mana.AnyMana(amountAvailableMana), source, game).build()); + } + } else { + int amountOfManaLeft = amount.calculate(game, source, this); + if (amountOfManaLeft > 0) { + netMana.add(Mana.AnyMana(amountOfManaLeft)); + } } } return netMana; diff --git a/Mage/src/main/java/mage/abilities/effects/mana/AddManaOfAnyColorEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddManaOfAnyColorEffect.java index 7fc082c33c2..987490072ed 100644 --- a/Mage/src/main/java/mage/abilities/effects/mana/AddManaOfAnyColorEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/mana/AddManaOfAnyColorEffect.java @@ -1,21 +1,22 @@ package mage.abilities.effects.mana; +import java.util.ArrayList; +import java.util.List; import mage.Mana; import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; import mage.choices.ChoiceColor; import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; -import java.util.ArrayList; -import java.util.List; - /** * @author BetaSteward_at_googlemail.com */ public class AddManaOfAnyColorEffect extends BasicManaEffect { protected final int amount; + protected final DynamicValue netAmount; protected final ArrayList netMana = new ArrayList<>(); protected final boolean setFlag; @@ -28,8 +29,13 @@ public class AddManaOfAnyColorEffect extends BasicManaEffect { } public AddManaOfAnyColorEffect(int amount, boolean setFlag) { + this(amount, null, setFlag); + } + + public AddManaOfAnyColorEffect(int amount, DynamicValue netAmount, boolean setFlag) { super(new Mana(0, 0, 0, 0, 0, 0, amount, 0)); this.amount = amount; + this.netAmount = netAmount; netMana.add(Mana.AnyMana(amount)); this.staticText = "add " + CardUtil.numberToText(amount) + " mana of any " + (amount > 1 ? "one " : "") + "color"; this.setFlag = setFlag; @@ -40,6 +46,12 @@ public class AddManaOfAnyColorEffect extends BasicManaEffect { this.amount = effect.amount; this.netMana.addAll(effect.netMana); this.setFlag = effect.setFlag; + if (effect.netAmount == null) { + this.netAmount = null; + } else { + this.netAmount = effect.netAmount.copy(); + } + } @Override @@ -49,6 +61,16 @@ public class AddManaOfAnyColorEffect extends BasicManaEffect { @Override public List getNetMana(Game game, Ability source) { + if (game != null && game.inCheckPlayableState()) { + if (netAmount != null) { + int count = netAmount.calculate(game, source, this); + Mana mana = new Mana(); + mana.setAny(count * amount); + ArrayList possibleNetMana = new ArrayList<>(); + possibleNetMana.add(mana); + return possibleNetMana; + } + } return new ArrayList<>(this.netMana); } diff --git a/Mage/src/main/java/mage/abilities/keyword/AssistAbility.java b/Mage/src/main/java/mage/abilities/keyword/AssistAbility.java index ae4e8371948..aec3f99d8b0 100644 --- a/Mage/src/main/java/mage/abilities/keyword/AssistAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/AssistAbility.java @@ -1,5 +1,6 @@ package mage.abilities.keyword; +import java.util.UUID; import mage.Mana; import mage.abilities.Ability; import mage.abilities.SpecialAction; @@ -20,22 +21,20 @@ import mage.target.Target; import mage.target.TargetPlayer; import mage.util.ManaUtil; -import java.util.UUID; - /** * 702.131. Assist *

- * 702.131a Assist is a static ability that modifies the rules of paying for the spell with assist (see rules 601.2g-h). - * If the total cost to cast a spell with assist includes a generic mana component, before you activate mana - * abilities while casting it, you may choose another player. That player has a chance to activate mana abilities. - * Once that player chooses not to activate any more mana abilities, you have a chance to activate mana abilities. - * Before you begin to pay the total cost of the spell, the player you chose may pay for any amount of the generic - * mana in the spell’s total cost. + * 702.131a Assist is a static ability that modifies the rules of paying for the + * spell with assist (see rules 601.2g-h). If the total cost to cast a spell + * with assist includes a generic mana component, before you activate mana + * abilities while casting it, you may choose another player. That player has a + * chance to activate mana abilities. Once that player chooses not to activate + * any more mana abilities, you have a chance to activate mana abilities. Before + * you begin to pay the total cost of the spell, the player you chose may pay + * for any amount of the generic mana in the spell’s total cost. * * @author emerald000, JayDi85 */ - - public class AssistAbility extends SimpleStaticAbility implements AlternateManaPaymentAbility { private static final FilterPlayer filter = new FilterPlayer("another player"); @@ -107,7 +106,7 @@ public class AssistAbility extends SimpleStaticAbility implements AlternateManaP if (opponent != null) { // basic and pool, but no coditional mana ManaOptions availableMana = opponent.getManaAvailable(game); - availableMana.addMana(opponent.getManaPool().getMana()); +// availableMana.addMana(opponent.getManaPool().getMana()); for (Mana mana : availableMana) { if (mana.count() > 0) { opponentCanPayMax = Math.max(opponentCanPayMax, mana.count()); diff --git a/Mage/src/main/java/mage/abilities/mana/AnyColorManaAbility.java b/Mage/src/main/java/mage/abilities/mana/AnyColorManaAbility.java index e22d6b79a4f..c5921d5372b 100644 --- a/Mage/src/main/java/mage/abilities/mana/AnyColorManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/AnyColorManaAbility.java @@ -1,10 +1,16 @@ package mage.abilities.mana; +import java.util.ArrayList; +import java.util.List; import mage.Mana; import mage.abilities.costs.Cost; import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.ManaEffect; import mage.abilities.effects.mana.AddManaOfAnyColorEffect; import mage.constants.Zone; +import mage.game.Game; public class AnyColorManaAbility extends ActivatedManaAbilityImpl { @@ -21,10 +27,39 @@ public class AnyColorManaAbility extends ActivatedManaAbilityImpl { this.netMana.add(new Mana(0, 0, 0, 0, 0, 0, 1, 0)); } + /** + * + * @param cost + * @param netAmount dynamic value used during available mana calculation to + * set the max possible amount the source can produce + * @param setFlag + */ + public AnyColorManaAbility(Cost cost, DynamicValue netAmount, boolean setFlag) { + super(Zone.BATTLEFIELD, new AddManaOfAnyColorEffect(1, netAmount, setFlag), cost); + this.netMana.add(new Mana(0, 0, 0, 0, 0, 0, 1, 0)); + } + public AnyColorManaAbility(final AnyColorManaAbility ability) { super(ability); } + @Override + public List getNetMana(Game game) { + if (game != null && game.inCheckPlayableState()) { + List dynamicNetMana = new ArrayList<>(); + for (Effect effect : getEffects()) { + if (effect instanceof ManaEffect) { + List effectNetMana = ((ManaEffect) effect).getNetMana(game, this); + if (effectNetMana != null) { + dynamicNetMana.addAll(effectNetMana); + } + } + } + return dynamicNetMana; + } + return super.getNetMana(game); + } + @Override public AnyColorManaAbility copy() { return new AnyColorManaAbility(this); diff --git a/Mage/src/main/java/mage/abilities/mana/ConditionalAnyColorManaAbility.java b/Mage/src/main/java/mage/abilities/mana/ConditionalAnyColorManaAbility.java index fd4920d5834..e9065044d9e 100644 --- a/Mage/src/main/java/mage/abilities/mana/ConditionalAnyColorManaAbility.java +++ b/Mage/src/main/java/mage/abilities/mana/ConditionalAnyColorManaAbility.java @@ -1,6 +1,6 @@ - package mage.abilities.mana; +import java.util.List; import mage.Mana; import mage.abilities.costs.Cost; import mage.abilities.costs.common.TapSourceCost; @@ -11,11 +11,9 @@ import mage.abilities.mana.builder.ConditionalManaBuilder; import mage.constants.Zone; import mage.game.Game; -import java.util.List; - /** - * For cards like: - * {tap}: Add three mana of any one color. Spend this mana only to cast creature spells. + * For cards like: {tap}: Add three mana of any one color. Spend this mana only + * to cast creature spells. * * @author noxx */ @@ -32,11 +30,11 @@ public class ConditionalAnyColorManaAbility extends ActivatedManaAbilityImpl { } public ConditionalAnyColorManaAbility(Cost cost, int amount, ConditionalManaBuilder manaBuilder, boolean oneChoice) { - this(cost, StaticValue.get(amount), manaBuilder, oneChoice); + this(cost, StaticValue.get(amount), StaticValue.get(amount), manaBuilder, oneChoice); } - public ConditionalAnyColorManaAbility(Cost cost, DynamicValue amount, ConditionalManaBuilder manaBuilder, boolean oneChoice) { - super(Zone.BATTLEFIELD, new AddConditionalManaOfAnyColorEffect(amount, manaBuilder, oneChoice), cost); + public ConditionalAnyColorManaAbility(Cost cost, DynamicValue amount, DynamicValue netAmount, ConditionalManaBuilder manaBuilder, boolean oneChoice) { + super(Zone.BATTLEFIELD, new AddConditionalManaOfAnyColorEffect(amount, netAmount, manaBuilder, oneChoice), cost); this.amount = amount; } @@ -60,7 +58,6 @@ public class ConditionalAnyColorManaAbility extends ActivatedManaAbilityImpl { return true; } - @Override public ConditionalAnyColorManaAbility copy() { return new ConditionalAnyColorManaAbility(this); diff --git a/Mage/src/main/java/mage/abilities/mana/ManaOptions.java b/Mage/src/main/java/mage/abilities/mana/ManaOptions.java index 106f7edf2e8..faa011b4a4a 100644 --- a/Mage/src/main/java/mage/abilities/mana/ManaOptions.java +++ b/Mage/src/main/java/mage/abilities/mana/ManaOptions.java @@ -22,6 +22,9 @@ import org.apache.log4j.Logger; * be used to find all the ways to pay a mana cost or all the different mana * combinations available to a player * + * TODO: Conditional Mana is not supported yet. The mana adding removes the + * condition of conditional mana + * */ public class ManaOptions extends ArrayList { diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 80893282ffd..77cbcf6ce44 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -2881,6 +2881,11 @@ public abstract class PlayerImpl implements Player, Serializable { game.setCheckPlayableState(true); ManaOptions availableMana = new ManaOptions(); + availableMana.addMana(manaPool.getMana()); + // conditional mana + for (ConditionalMana conditionalMana : manaPool.getConditionalMana()) { + availableMana.addMana(conditionalMana); + } List> sourceWithoutManaCosts = new ArrayList<>(); List> sourceWithCosts = new ArrayList<>(); @@ -3407,11 +3412,11 @@ public abstract class PlayerImpl implements Player, Serializable { try { // basic mana ManaOptions availableMana = getManaAvailable(game); - availableMana.addMana(manaPool.getMana()); + // availableMana.addMana(manaPool.getMana()); // conditional mana - for (ConditionalMana conditionalMana : manaPool.getConditionalMana()) { - availableMana.addMana(conditionalMana); - } +// for (ConditionalMana conditionalMana : manaPool.getConditionalMana()) { +// availableMana.addMana(conditionalMana); +// } boolean fromAll = fromZone.equals(Zone.ALL); if (hidden && (fromAll || fromZone == Zone.HAND)) {