diff --git a/Mage.Sets/src/mage/cards/o/Omniscience.java b/Mage.Sets/src/mage/cards/o/Omniscience.java index 8ebf2337f6..9ee549903e 100644 --- a/Mage.Sets/src/mage/cards/o/Omniscience.java +++ b/Mage.Sets/src/mage/cards/o/Omniscience.java @@ -36,6 +36,7 @@ import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceIsSpellCondition; import mage.abilities.costs.AlternativeCostSourceAbility; import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.continuous.CastFromHandWithoutPayingManaCostEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -61,7 +62,7 @@ public class Omniscience extends CardImpl { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{7}{U}{U}{U}"); // You may cast nonland cards from your hand without paying their mana costs. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new OmniscienceCastingEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CastFromHandWithoutPayingManaCostEffect())); } public Omniscience(final Omniscience card) { @@ -74,62 +75,3 @@ public class Omniscience extends CardImpl { } } -class OmniscienceCastingEffect extends ContinuousEffectImpl { - - public OmniscienceCastingEffect() { - super(Duration.WhileOnBattlefield, Outcome.Detriment); - staticText = "You may cast nonland cards from your hand without paying their mana costs"; - } - - public OmniscienceCastingEffect(final OmniscienceCastingEffect effect) { - super(effect); - } - - @Override - public OmniscienceCastingEffect copy() { - return new OmniscienceCastingEffect(this); - } - - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - controller.getAlternativeSourceCosts().add(new AlternativeCostSourceAbility( - null, new CompoundCondition(SourceIsSpellCondition.getInstance(), new IsBeingCastFromHandCondition()), null, new FilterNonlandCard(), true)); - return true; - } - return false; - } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.RulesEffects; - } -} - -class IsBeingCastFromHandCondition implements Condition { - - @Override - public boolean apply(Game game, Ability source) { - MageObject object = game.getObject(source.getSourceId()); - if (object instanceof SplitCardHalf) { - UUID splitCardId = ((Card) object).getMainCard().getId(); - object = game.getObject(splitCardId); - } - if (object instanceof Spell) { // needed to check if it can be cast by alternate cost - Spell spell = (Spell) object; - return spell.getFromZone() == Zone.HAND; - } - if (object instanceof Card) { // needed for the check what's playable - Card card = (Card) object; - return game.getPlayer(card.getOwnerId()).getHand().get(card.getId(), game) != null; - } - return false; - } - -} diff --git a/Mage.Sets/src/mage/cards/t/TamiyoFieldResearcher.java b/Mage.Sets/src/mage/cards/t/TamiyoFieldResearcher.java index 9c5dbd9ec9..99026dce25 100644 --- a/Mage.Sets/src/mage/cards/t/TamiyoFieldResearcher.java +++ b/Mage.Sets/src/mage/cards/t/TamiyoFieldResearcher.java @@ -47,6 +47,7 @@ import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEf import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.GetEmblemEffect; import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.continuous.CastFromHandWithoutPayingManaCostEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -207,66 +208,7 @@ class TamiyoFieldResearcherEmblem extends Emblem { this.setName("Emblem Tamiyo"); - this.getAbilities().add(new SimpleStaticAbility(Zone.COMMAND, new TamiyoFieldResearcherCastingEffect())); + this.getAbilities().add(new SimpleStaticAbility(Zone.COMMAND, new CastFromHandWithoutPayingManaCostEffect())); } } -class TamiyoFieldResearcherCastingEffect extends ContinuousEffectImpl { - - public TamiyoFieldResearcherCastingEffect() { - super(Duration.WhileOnBattlefield, Outcome.Detriment); - staticText = "You may cast nonland cards from your hand without paying their mana costs"; - } - - public TamiyoFieldResearcherCastingEffect(final TamiyoFieldResearcherCastingEffect effect) { - super(effect); - } - - @Override - public TamiyoFieldResearcherCastingEffect copy() { - return new TamiyoFieldResearcherCastingEffect(this); - } - - @Override - public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - controller.getAlternativeSourceCosts().add(new AlternativeCostSourceAbility( - null, new CompoundCondition(SourceIsSpellCondition.getInstance(), new IsBeingCastFromHandCondition()), null, new FilterNonlandCard(), true)); - return true; - } - return false; - } - - @Override - public boolean apply(Game game, Ability source) { - return false; - } - - @Override - public boolean hasLayer(Layer layer) { - return layer == Layer.RulesEffects; - } -} - -class IsBeingCastFromHandCondition implements Condition { - - @Override - public boolean apply(Game game, Ability source) { - MageObject object = game.getObject(source.getSourceId()); - if (object instanceof SplitCardHalf) { - UUID splitCardId = ((Card) object).getMainCard().getId(); - object = game.getObject(splitCardId); - } - if (object instanceof Spell) { // needed to check if it can be cast by alternate cost - Spell spell = (Spell) object; - return spell.getFromZone() == Zone.HAND; - } - if (object instanceof Card) { // needed for the check what's playable - Card card = (Card) object; - return game.getPlayer(card.getOwnerId()).getHand().get(card.getId(), game) != null; - } - return false; - } - -} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/OmniscienceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/CastFromHandWithoutPayingManaCostTest.java similarity index 76% rename from Mage.Tests/src/test/java/org/mage/test/cards/single/OmniscienceTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/CastFromHandWithoutPayingManaCostTest.java index 088b01789c..674e733ad5 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/OmniscienceTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/CastFromHandWithoutPayingManaCostTest.java @@ -1,31 +1,4 @@ -/* - * 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.single; +package org.mage.test.cards.cost.alternate; import mage.constants.PhaseStep; import mage.constants.Zone; @@ -33,19 +6,82 @@ import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; -/** - * - * @author LevelX2 - */ -public class OmniscienceTest extends CardTestPlayerBase { +public class CastFromHandWithoutPayingManaCostTest extends CardTestPlayerBase { + + @Test + public void testSpellNoCost() { + // You may cast nonland cards from your hand without paying their mana costs. + addCard(Zone.BATTLEFIELD, playerA, "Omniscience", 1); + + addCard(Zone.HAND, playerA, "Gray Ogre", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gray Ogre"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + //Gray Ogre is cast because it is free + assertPermanentCount(playerA, "Gray Ogre", 1); + } + + @Test + public void testSpellHasCostIfCastFromGraveyard() { + // You may cast nonland cards from your hand without paying their mana costs. + addCard(Zone.BATTLEFIELD, playerA, "Omniscience", 1); + + addCard(Zone.BATTLEFIELD, playerA, "Haakon, Stromgald Scourge", 1); + + addCard(Zone.GRAVEYARD, playerA, "Knight of the White Orchid", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Knight of the White Orchid"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + //Knight of the White Orchid was not cast due to lack of mana + assertPermanentCount(playerA, "Knight of the White Orchid", 0); + } /** - * Omniscience {7}{U}{U}{U} - * - * Enchantment You may cast nonland cards from your hand without paying - * their mana costs. - * + * If you cast a card with monocolored hybrid mana with Omniscience's + * alternate casting cost, you will be asked to pay 1 colorless mana per + * monocolored hybrid mana in its cost. For example, while casting Beseech + * the Queen, you are asked to pay {1}{1}{1}. */ + @Test + public void testMonocoloredHybridMana() { + // You may cast nonland cards from your hand without paying their mana costs. + addCard(Zone.BATTLEFIELD, playerA, "Omniscience", 1); + + // ({2B} can be paid with any two mana or with {B}. This card's converted mana cost is 6.) + // Search your library for a card with converted mana cost less than or equal to the number of lands you control, reveal it, and put it into your hand. Then shuffle your library. + addCard(Zone.HAND, playerA, "Beseech the Queen", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Beseech the Queen"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + // Beseech the Queen is cast because it is free + assertGraveyardCount(playerA, "Beseech the Queen", 1); + } + + @Test + public void testColorlessMana() { + // You may cast nonland cards from your hand without paying their mana costs. + addCard(Zone.BATTLEFIELD, playerA, "Omniscience", 1); + + addCard(Zone.HAND, playerA, "Reality Smasher", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reality Smasher"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + // Relaity Smasher is cast because it is free + assertPermanentCount(playerA, "Reality Smasher", 1); + } + @Test public void testCastingCreature() { addCard(Zone.BATTLEFIELD, playerA, "Omniscience"); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/OmniscienceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/OmniscienceTest.java deleted file mode 100644 index c0c755ef3a..0000000000 --- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/alternate/OmniscienceTest.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.mage.test.cards.cost.alternate; - -import mage.constants.PhaseStep; -import mage.constants.Zone; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -public class OmniscienceTest extends CardTestPlayerBase { - - @Test - public void testSpellNoCost() { - // You may cast nonland cards from your hand without paying their mana costs. - addCard(Zone.BATTLEFIELD, playerA, "Omniscience", 1); - - addCard(Zone.HAND, playerA, "Gray Ogre", 1); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gray Ogre"); - - setStopAt(1, PhaseStep.END_TURN); - execute(); - - //Gray Ogre is cast because it is free - assertPermanentCount(playerA, "Gray Ogre", 1); - } - - @Test - public void testSpellHasCostIfCastFromGraveyard() { - // You may cast nonland cards from your hand without paying their mana costs. - addCard(Zone.BATTLEFIELD, playerA, "Omniscience", 1); - - addCard(Zone.BATTLEFIELD, playerA, "Haakon, Stromgald Scourge", 1); - - addCard(Zone.GRAVEYARD, playerA, "Knight of the White Orchid", 1); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Knight of the White Orchid"); - - setStopAt(1, PhaseStep.END_TURN); - execute(); - - //Knight of the White Orchid was not cast due to lack of mana - assertPermanentCount(playerA, "Knight of the White Orchid", 0); - } - - /** - * If you cast a card with monocolored hybrid mana with Omniscience's - * alternate casting cost, you will be asked to pay 1 colorless mana per - * monocolored hybrid mana in its cost. For example, while casting Beseech - * the Queen, you are asked to pay {1}{1}{1}. - */ - @Test - public void testMonocoloredHybridMana() { - // You may cast nonland cards from your hand without paying their mana costs. - addCard(Zone.BATTLEFIELD, playerA, "Omniscience", 1); - - // ({2B} can be paid with any two mana or with {B}. This card's converted mana cost is 6.) - // Search your library for a card with converted mana cost less than or equal to the number of lands you control, reveal it, and put it into your hand. Then shuffle your library. - addCard(Zone.HAND, playerA, "Beseech the Queen", 1); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Beseech the Queen"); - - setStopAt(1, PhaseStep.END_TURN); - execute(); - - // Beseech the Queen is cast because it is free - assertGraveyardCount(playerA, "Beseech the Queen", 1); - } -} diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/CastFromHandWithoutPayingManaCostEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/CastFromHandWithoutPayingManaCostEffect.java new file mode 100644 index 0000000000..e71c15fd29 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/CastFromHandWithoutPayingManaCostEffect.java @@ -0,0 +1,77 @@ +package mage.abilities.effects.common.continuous; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.condition.CompoundCondition; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.SourceIsSpellCondition; +import mage.abilities.costs.AlternativeCostSourceAbility; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.cards.Card; +import mage.cards.SplitCardHalf; +import mage.constants.*; +import mage.filter.common.FilterNonlandCard; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.players.Player; + +import java.util.UUID; + +public class CastFromHandWithoutPayingManaCostEffect extends ContinuousEffectImpl { + + public CastFromHandWithoutPayingManaCostEffect() { + super(Duration.WhileOnBattlefield, Outcome.Detriment); + staticText = "You may cast nonland cards from your hand without paying their mana costs"; + } + + public CastFromHandWithoutPayingManaCostEffect(final CastFromHandWithoutPayingManaCostEffect effect) { + super(effect); + } + + @Override + public CastFromHandWithoutPayingManaCostEffect copy() { + return new CastFromHandWithoutPayingManaCostEffect(this); + } + + @Override + public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + controller.getAlternativeSourceCosts().add(new AlternativeCostSourceAbility( + null, new CompoundCondition(SourceIsSpellCondition.getInstance(), new IsBeingCastFromHandCondition()), null, new FilterNonlandCard(), true)); + return true; + } + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public boolean hasLayer(Layer layer) { + return layer == Layer.RulesEffects; + } +} + +class IsBeingCastFromHandCondition implements Condition { + + @Override + public boolean apply(Game game, Ability source) { + MageObject object = game.getObject(source.getSourceId()); + if (object instanceof SplitCardHalf) { + UUID splitCardId = ((Card) object).getMainCard().getId(); + object = game.getObject(splitCardId); + } + if (object instanceof Spell) { // needed to check if it can be cast by alternate cost + Spell spell = (Spell) object; + return spell.getFromZone() == Zone.HAND; + } + if (object instanceof Card) { // needed for the check what's playable + Card card = (Card) object; + return game.getPlayer(card.getOwnerId()).getHand().get(card.getId(), game) != null; + } + return false; + } +} diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index 85911fb421..422bc41ba3 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -31,6 +31,9 @@ import java.util.Arrays; import java.util.HashSet; import java.util.Set; import java.util.UUID; +import java.util.function.Consumer; +import java.util.function.Supplier; + import mage.MageObject; import mage.Mana; import mage.ObjectColor; @@ -271,12 +274,12 @@ public final class CardUtil { reduceMana.add(manaCost.getMana()); } } - ManaCosts manaCostToCheckForColorless = new ManaCostsImpl<>(); - // subtract colored mana + ManaCosts manaCostToCheckForGeneric = new ManaCostsImpl<>(); + // subtract non-generic mana for (ManaCost newManaCost : previousCost) { Mana mana = newManaCost.getMana(); if (!(newManaCost instanceof MonoHybridManaCost) && mana.getGeneric() > 0) { - manaCostToCheckForColorless.add(newManaCost); + manaCostToCheckForGeneric.add(newManaCost); continue; } boolean hybridMana = newManaCost instanceof HybridManaCost; @@ -340,6 +343,17 @@ public final class CardUtil { continue; } } + + if(mana.getColorless() > 0 && reduceMana.getColorless() > 0) { + if(reduceMana.getColorless() > mana.getColorless()) { + reduceMana.setColorless(reduceMana.getColorless() - mana.getColorless()); + mana.setColorless(0); + } else { + mana.setColorless(mana.getColorless() - reduceMana.getColorless()); + reduceMana.setColorless(0); + } + } + if (mana.count() > 0) { if (newManaCost instanceof MonoHybridManaCost) { if (mana.count() == 2) { @@ -347,7 +361,7 @@ public final class CardUtil { continue; } } - manaCostToCheckForColorless.add(newManaCost); + manaCostToCheckForGeneric.add(newManaCost); } } @@ -360,7 +374,7 @@ public final class CardUtil { reduceAmount = reduceMana.getGeneric(); } if (reduceAmount > 0) { - for (ManaCost newManaCost : manaCostToCheckForColorless) { + for (ManaCost newManaCost : manaCostToCheckForGeneric) { Mana mana = newManaCost.getMana(); if (mana.getGeneric() == 0 || reduceAmount == 0) { adjustedCost.add(newManaCost); @@ -387,7 +401,7 @@ public final class CardUtil { } } } else { - adjustedCost.addAll(manaCostToCheckForColorless); + adjustedCost.addAll(manaCostToCheckForGeneric); } if (adjustedCost.isEmpty()) { adjustedCost.add(new GenericManaCost(0)); // neede to check if cost was reduced to 0 @@ -397,6 +411,10 @@ public final class CardUtil { spellAbility.getManaCostsToPay().addAll(adjustedCost); } + private void reduceMana(Mana mana, Mana reduceMana, Supplier manaAmountSupplier, Consumer manaAmountConsumer) { + + } + /** * Returns function that copies params\abilities from one card to * {@link Token}.