From 1ae9fc883eeef2910df4716db64de677af8ea434 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov <jaydi85@gmail.com> Date: Sat, 4 Jan 2020 20:22:49 +0400 Subject: [PATCH] * Replicate abilities - fixed that AI can freeze the game after play card with replicate (AI don't use it now); --- .../abilities/keywords/ReplicateTest.java | 77 +++++++++++++++---- .../main/java/mage/abilities/costs/Cost.java | 10 ++- .../costs/OptionalAdditionalSourceCosts.java | 4 +- .../abilities/keyword/ReplicateAbility.java | 6 +- 4 files changed, 74 insertions(+), 23 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ReplicateTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ReplicateTest.java index a347456ec9..335d4d850f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ReplicateTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ReplicateTest.java @@ -1,14 +1,12 @@ - - package org.mage.test.cards.abilities.keywords; import mage.constants.PhaseStep; import mage.constants.Zone; +import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * * @author BetaSteward */ public class ReplicateTest extends CardTestPlayerBase { @@ -24,17 +22,16 @@ public class ReplicateTest extends CardTestPlayerBase { * replicate cost follows the rules for paying additional costs in rules 601.2b and 601.2e–g. * 702.55b If a spell has multiple instances of replicate, each is paid separately and triggers based on * the payments made for it, not any other instance of replicate. - * - */ - - /** - * Train of Thought - * Sorcery, 1U (2) - * Replicate {1}{U} (When you cast this spell, copy it for each time you paid its replicate cost.) - * Draw a card. * */ - + + /** + * Train of Thought + * Sorcery, 1U (2) + * Replicate {1}{U} (When you cast this spell, copy it for each time you paid its replicate cost.) + * Draw a card. + */ + @Test public void testReplicate1Time() { addCard(Zone.BATTLEFIELD, playerA, "Island", 6); @@ -49,9 +46,8 @@ public class ReplicateTest extends CardTestPlayerBase { assertGraveyardCount(playerA, "Train of Thought", 1); assertHandCount(playerA, 2); - } - + @Test public void testReplicate2Times() { addCard(Zone.BATTLEFIELD, playerA, "Island", 6); @@ -67,7 +63,6 @@ public class ReplicateTest extends CardTestPlayerBase { assertGraveyardCount(playerA, "Train of Thought", 1); assertHandCount(playerA, 3); - } @Test @@ -83,7 +78,55 @@ public class ReplicateTest extends CardTestPlayerBase { assertGraveyardCount(playerA, "Train of Thought", 1); assertHandCount(playerA, 1); - } - + + @Test + public void testReplicate_User() { + // Replicate {1}{R} (When you cast this spell, copy it for each time you paid its replicate cost. You may choose new targets for the copies.) + // Pyromatics deals 1 damage to any target. + addCard(Zone.HAND, playerA, "Pyromatics", 1); // {1}{R} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pyromatics"); + addTarget(playerA, playerB); + setChoice(playerA, "Yes"); // replicate 1 + setChoice(playerA, "Yes"); // replicate 2 + setChoice(playerA, "No"); // stop + // + setChoice(playerA, "No"); // don't change target 1 + setChoice(playerA, "No"); // don't change target 2 + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertLife(playerB, 20 - 3); // 1 + 2 replicates + } + + @Test + @Ignore // TODO: enable test after replicate ability will be supported by AI + public void testReplicate_AI() { + // Replicate {1}{R} (When you cast this spell, copy it for each time you paid its replicate cost. You may choose new targets for the copies.) + // Pyromatics deals 1 damage to any target. + addCard(Zone.HAND, playerA, "Pyromatics", 1); // {1}{R} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pyromatics"); + addTarget(playerA, playerB); + //setChoice(playerA, "Yes"); // replicate 1 - AI must choice max possible + //setChoice(playerA, "Yes"); // replicate 2 - AI must choice max possible + //setChoice(playerA, "No"); // stop - AI must choice max possible + // + //setChoice(playerA, "No"); // don't change target 1 + //setChoice(playerA, "No"); // don't change target 2 + + //setStrictChooseMode(true); - AI must choice + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + assertLife(playerB, 20 - 3); // 1 + 2 replicates + } + } diff --git a/Mage/src/main/java/mage/abilities/costs/Cost.java b/Mage/src/main/java/mage/abilities/costs/Cost.java index a95e92bdc3..af3ffa182a 100644 --- a/Mage/src/main/java/mage/abilities/costs/Cost.java +++ b/Mage/src/main/java/mage/abilities/costs/Cost.java @@ -1,12 +1,12 @@ - package mage.abilities.costs; -import java.io.Serializable; -import java.util.UUID; import mage.abilities.Ability; import mage.game.Game; import mage.target.Targets; +import java.io.Serializable; +import java.util.UUID; + public interface Cost extends Serializable { UUID getId(); @@ -15,6 +15,10 @@ public interface Cost extends Serializable { void setText(String text); + /** + * Check is it possible to pay + * For mana it checks only single color and amount available, not total mana cost + */ boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game); boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana); diff --git a/Mage/src/main/java/mage/abilities/costs/OptionalAdditionalSourceCosts.java b/Mage/src/main/java/mage/abilities/costs/OptionalAdditionalSourceCosts.java index 80d5088455..7893c3505e 100644 --- a/Mage/src/main/java/mage/abilities/costs/OptionalAdditionalSourceCosts.java +++ b/Mage/src/main/java/mage/abilities/costs/OptionalAdditionalSourceCosts.java @@ -1,4 +1,3 @@ - package mage.abilities.costs; import mage.abilities.Ability; @@ -6,7 +5,7 @@ import mage.game.Game; /** * Interface for abilities that add additional costs to the source. - * + * <p> * Example of such additional source costs: * {@link mage.abilities.keyword.KickerAbility} * @@ -14,6 +13,7 @@ import mage.game.Game; */ public interface OptionalAdditionalSourceCosts { + // TODO: add AI support to use buyback, replicate and other additional costs (current version can't calc available mana before buyback use) void addOptionalAdditionalCosts(Ability ability, Game game); String getCastMessageSuffix(); diff --git a/Mage/src/main/java/mage/abilities/keyword/ReplicateAbility.java b/Mage/src/main/java/mage/abilities/keyword/ReplicateAbility.java index 48de601e55..f870269abd 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ReplicateAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ReplicateAbility.java @@ -88,8 +88,12 @@ public class ReplicateAbility extends StaticAbility implements OptionalAdditiona int numActivations = additionalCost.getActivateCount(); times = (numActivations + 1) + (numActivations == 0 ? " time " : " times "); } + + // test costs + // TODO: add AI support to find max number of possible activations (from available mana) + // canPay checks only single mana available, not total mana usage if (additionalCost.canPay(ability, sourceId, controllerId, game) - && player.chooseUse(Outcome.Benefit, new StringBuilder("Pay ").append(times).append(additionalCost.getText(false)).append(" ?").toString(), ability, game)) { + && player.chooseUse(/*Outcome.Benefit*/Outcome.AIDontUseIt, new StringBuilder("Pay ").append(times).append(additionalCost.getText(false)).append(" ?").toString(), ability, game)) { additionalCost.activate(); for (Iterator it = ((Costs) additionalCost).iterator(); it.hasNext(); ) { Cost cost = (Cost) it.next();