diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DelveTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DelveTest.java new file mode 100644 index 00000000000..fe9a4533824 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/DelveTest.java @@ -0,0 +1,84 @@ +package org.mage.test.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps; + +/** + * @author JayDi85 + */ + +public class DelveTest extends CardTestPlayerBaseWithAIHelps { + + // no simple playable tests for delve, it's same as ConvokeTest + + @Test + public void test_PlayDelve_Manual() { + // {4}{U}{U} creature + // Delve (Each card you exile from your graveyard while casting this spell pays for {1}.) + addCard(Zone.HAND, playerA, "Ethereal Forager", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.GRAVEYARD, playerA, "Balduvian Bears", 4); // delve pay + + // use special action to pay (need disabled auto-payment and prepared mana pool) + disableManaAutoPayment(playerA); + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 2); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ethereal Forager"); + setChoice(playerA, "Blue"); // pay 1 + setChoice(playerA, "Blue"); // pay 2 + // delve can be payed in test only by one card + setChoice(playerA, "Exile cards"); + setChoice(playerA, "Balduvian Bears"); // pay 3 as delve + setChoice(playerA, "Exile cards"); + setChoice(playerA, "Balduvian Bears"); // pay 4 as delve + setChoice(playerA, "Exile cards"); + setChoice(playerA, "Balduvian Bears"); // pay 5 as delve + setChoice(playerA, "Exile cards"); + setChoice(playerA, "Balduvian Bears"); // pay 6 as delve + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Ethereal Forager", 1); + } + + @Test + public void test_PlayDelve_AI_AutoPay() { + // {4}{U}{U} creature + // Delve (Each card you exile from your graveyard while casting this spell pays for {1}.) + addCard(Zone.HAND, playerA, "Ethereal Forager", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.GRAVEYARD, playerA, "Balduvian Bears", 4); // delve pay + + // AI must use special actions to pay as delve + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ethereal Forager"); + + //setStrictChooseMode(true); AI must choose targets + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Ethereal Forager", 1); + } + + @Test + public void test_PlayDelve_AI_FullPlay() { + // {4}{U}{U} creature + // Delve (Each card you exile from your graveyard while casting this spell pays for {1}.) + addCard(Zone.HAND, playerA, "Ethereal Forager", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.GRAVEYARD, playerA, "Balduvian Bears", 4); // delve pay + + aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Ethereal Forager", 1); + } +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/keyword/DelveAbility.java b/Mage/src/main/java/mage/abilities/keyword/DelveAbility.java index 2855262cdb0..6f8002ed78e 100644 --- a/Mage/src/main/java/mage/abilities/keyword/DelveAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/DelveAbility.java @@ -5,9 +5,14 @@ import mage.abilities.Ability; import mage.abilities.SpecialAction; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.ExileFromGraveCost; +import mage.abilities.costs.mana.ActivationManaAbilityStep; import mage.abilities.costs.mana.AlternateManaPaymentAbility; import mage.abilities.costs.mana.ManaCost; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; import mage.abilities.effects.OneShotEffect; +import mage.abilities.hint.ValueHint; +import mage.abilities.mana.ManaOptions; import mage.cards.Card; import mage.cards.Cards; import mage.cards.CardsImpl; @@ -17,6 +22,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterCard; import mage.game.Game; +import mage.game.stack.Spell; import mage.players.ManaPool; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; @@ -25,35 +31,42 @@ import mage.util.CardUtil; import java.util.List; /** - * 702.65. Delve 702.65a Delve is a static ability that functions while the - * spell with delve is on the stack. “Delve” means “For each generic mana in - * this spell's total cost, you may exile a card from your graveyard rather than - * pay that mana.” The delve ability isn't an additional or alternative cost and - * applies only after the total cost of the spell with delve is determined. - * 702.65b Multiple instances of delve on the same spell are redundant. + * 702.65. Delve *

- * The rules for delve have changed slightly since it was last in an expansion. - * Previously, delve reduced the cost to cast a spell. Under the current rules, - * you exile cards from your graveyard at the same time you pay the spell's - * cost. Exiling a card this way is simply another way to pay that cost. * Delve - * doesn't change a spell's mana cost or converted mana cost. For example, Dead - * Drop's converted mana cost is 10 even if you exiled three cards to cast it. * - * You can't exile cards to pay for the colored mana requirements of a spell - * with delve. * You can't exile more cards than the generic mana requirement of - * a spell with delve. For example, you can't exile more than nine cards from - * your graveyard to cast Dead Drop. * Because delve isn't an alternative cost, - * it can be used in conjunction with alternative costs. + * 702.65a Delve is a static ability that functions while the spell with delve is on the stack. “Delve” means “For + * each generic mana in this spell’s total cost, you may exile a card from your graveyard rather than pay that mana.” + *

+ * 702.65b The delve ability isn’t an additional or alternative cost and applies only after the total cost of the spell + * with delve is determined. + *

+ * 702.65c Multiple instances of delve on the same spell are redundant. + *

+ * The rules for delve have changed slightly since it was last in an expansion. Previously, delve reduced the cost + * to cast a spell. Under the current rules, you exile cards from your graveyard at the same time you pay the spell’s + * cost. Exiling a card this way is simply another way to pay that cost. (This is similar to the change made to + * convoke for the Magic 2015 Core Set.) + *

+ * You can’t exile cards to pay for the colored mana requirements of a spell with delve. + *

+ * You can’t exile more cards than the generic mana requirement of a spell with delve. For example, you can’t exile more + * than nine cards from your graveyard to cast Dead Drop. + *

+ * Because delve isn’t an alternative cost, it can be used in conjunction with alternative costs. * - * @author LevelX2 + * @author LevelX2, JayDi85 *

* TODO: Change card exiling to a way to pay mana costs, now it's maybe not * possible to pay costs from effects that increase the mana costs. + * If it real bug then possible fix: choose cards on apply like convoke and improvise, not as cost */ public class DelveAbility extends SimpleStaticAbility implements AlternateManaPaymentAbility { + private static final DynamicValue cardsInGraveyard = new CardsInControllerGraveyardCount(); + public DelveAbility() { - super(Zone.STACK, null); + super(Zone.ALL, null); this.setRuleAtTheTop(true); + this.addHint(new ValueHint("Cards in your graveyard", cardsInGraveyard)); } public DelveAbility(final DelveAbility ability) { @@ -70,12 +83,17 @@ public class DelveAbility extends SimpleStaticAbility implements AlternateManaPa return "Delve (Each card you exile from your graveyard while casting this spell pays for {1}.)"; } + @Override + public ActivationManaAbilityStep useOnActivationManaAbilityStep() { + return ActivationManaAbilityStep.AFTER; + } + @Override public void addSpecialAction(Ability source, Game game, ManaCost unpaid) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null && !controller.getGraveyard().isEmpty()) { - if (unpaid.getMana().getGeneric() > 0 && source.getAbilityType() == AbilityType.SPELL) { - SpecialAction specialAction = new DelveSpecialAction(); + if (source.getAbilityType() == AbilityType.SPELL && unpaid.getMana().getGeneric() > 0) { + SpecialAction specialAction = new DelveSpecialAction(this); specialAction.setControllerId(source.getControllerId()); specialAction.setSourceId(source.getSourceId()); int unpaidAmount = unpaid.getMana().getGeneric(); @@ -84,19 +102,30 @@ public class DelveAbility extends SimpleStaticAbility implements AlternateManaPa } specialAction.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard( 0, Math.min(controller.getGraveyard().size(), unpaidAmount), - new FilterCard("cards to exile for delve's pay from your graveyard"), true))); + new FilterCard("cards from your graveyard"), true))); if (specialAction.canActivate(source.getControllerId(), game).canActivate()) { game.getState().getSpecialActions().add(specialAction); } } } } + + @Override + public ManaOptions getManaOptions(Ability source, Game game, ManaCost unpaid) { + ManaOptions options = new ManaOptions(); + Player controller = game.getPlayer(source.getControllerId()); + int graveCount = cardsInGraveyard.calculate(game, source, null); + if (controller != null && graveCount > 0) { + options.addMana(Mana.GenericMana(Math.min(unpaid.getMana().getGeneric(), graveCount))); + } + return options; + } } class DelveSpecialAction extends SpecialAction { - public DelveSpecialAction() { - super(Zone.ALL, true); + public DelveSpecialAction(AlternateManaPaymentAbility manaAbility) { + super(Zone.ALL, manaAbility); this.addEffect(new DelveEffect()); } @@ -129,9 +158,9 @@ class DelveEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { + Spell spell = game.getStack().getSpell(source.getSourceId()); + if (controller != null && spell != null) { ExileFromGraveCost exileFromGraveCost = (ExileFromGraveCost) source.getCosts().get(0); - List exiledCards = exileFromGraveCost.getExiledCards(); if (!exiledCards.isEmpty()) { Cards toDelve = new CardsImpl(); @@ -139,7 +168,7 @@ class DelveEffect extends OneShotEffect { toDelve.add(card); } ManaPool manaPool = controller.getManaPool(); - manaPool.addMana(new Mana(0, 0, 0, 0, 0, 0, 0, toDelve.size()), game, source); + manaPool.addMana(Mana.ColorlessMana(toDelve.size()), game, source); manaPool.unlockManaType(ManaType.COLORLESS); String keyString = CardUtil.getCardZoneString("delvedCards", source.getSourceId(), game); @SuppressWarnings("unchecked") @@ -149,6 +178,9 @@ class DelveEffect extends OneShotEffect { } else { delvedCards.addAll(toDelve); } + + // can't use mana abilities after that (delve cost must be payed after mana abilities only) + spell.setCurrentActivatingManaAbilitiesStep(ActivationManaAbilityStep.AFTER); } return true; }