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 2ce534fb718..e762d4df863 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 @@ -801,7 +801,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { } - @Override + @Override public boolean activateAbility(ActivatedAbility ability, Game game) { for (Target target: ability.getModes().getMode().getTargets()) { for (UUID targetId: target.getTargets()) { @@ -883,8 +883,8 @@ public class ComputerPlayer extends PlayerImpl implements Player { for (Mana avail: available) { if (mana.enough(avail)) { SpellAbility ability = card.getSpellAbility(); - if (ability != null && ability.canActivate(playerId, game) && game.getContinuousEffects(). - preventedByRuleModification(GameEvent.getEvent(GameEvent.EventType.CAST_SPELL, ability.getSourceId(), ability.getSourceId(), playerId), game, true)) { + if (ability != null && ability.canActivate(playerId, game) && + game.getContinuousEffects().preventedByRuleModification(GameEvent.getEvent(GameEvent.EventType.CAST_SPELL, ability.getSourceId(), ability.getSourceId(), playerId), game, true)) { if (card.getCardType().contains(CardType.INSTANT) || card.hasAbility(FlashAbility.getInstance().getId(), game)) { playableInstant.add(card); diff --git a/Mage.Sets/src/mage/sets/timeshifted/WallOfRoots.java b/Mage.Sets/src/mage/sets/timeshifted/WallOfRoots.java index a0fe83710f4..27c4d289546 100644 --- a/Mage.Sets/src/mage/sets/timeshifted/WallOfRoots.java +++ b/Mage.Sets/src/mage/sets/timeshifted/WallOfRoots.java @@ -62,8 +62,7 @@ public class WallOfRoots extends CardImpl { // Defender this.addAbility(DefenderAbility.getInstance()); // Put a -0/-1 counter on Wall of Roots: Add {G} to your mana pool. Activate this ability only once each turn. - Ability ability = new ActivateOncePerTurnManaAbility(Zone.BATTLEFIELD, new BasicManaEffect(Mana.GreenMana), new WallOfRootsCost()); - this.addAbility(ability); + this.addAbility(new ActivateOncePerTurnManaAbility(Zone.BATTLEFIELD, new BasicManaEffect(Mana.GreenMana), new WallOfRootsCost())); } public WallOfRoots(final WallOfRoots card) { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/rules/ActivateAbilityOnlyLimitedTimesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/rules/ActivateAbilityOnlyLimitedTimesTest.java new file mode 100644 index 00000000000..f9efa5a92ea --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/rules/ActivateAbilityOnlyLimitedTimesTest.java @@ -0,0 +1,119 @@ +/* + * 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.rules; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class ActivateAbilityOnlyLimitedTimesTest extends CardTestPlayerBase { + + /** + * Wall of Roots {1}{G} + * Creature - Plant Wall + * 0/5 + * Defender + * Put a -0/-1 counter on Wall of Roots: Add {G} to your mana pool. Activate this ability only once each turn. + * + */ + @Test + public void testAbilityCanBeActivatedTwice() { + + addCard(Zone.HAND, playerA, "Runeclaw Bear"); + addCard(Zone.BATTLEFIELD, playerA, "Wall of Roots", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Runeclaw Bear"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPowerToughness(playerA, "Wall of Roots", 0, 4); + assertPermanentCount(playerA, "Runeclaw Bear", 1); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + } + + @Test + public void testAbilityCantBeActivatedTwice() { + + addCard(Zone.HAND, playerA, "Garruk's Companion"); + addCard(Zone.BATTLEFIELD, playerA, "Wall of Roots",2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Garruk's Companion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPowerToughness(playerA, "Wall of Roots", 0, 4); + assertHandCount(playerA, "Garruk's Companion", 0); + assertPermanentCount(playerA, "Garruk's Companion", 1); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + } + + /** + * Momentary Blink {1}{W} + * Instant + * Exile target creature you control, then return it to the battlefield under its owner's control. + * Flashback (You may cast this card from your graveyard for its flashback cost. Then exile it.) + */ + @Test + public void testAbilityCanBeActivatedTwiceIfBlinked() { + + addCard(Zone.HAND, playerA, "Wall of Wood",2); + addCard(Zone.HAND, playerA, "Momentary Blink"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerA, "Wall of Roots"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wall of Wood"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Momentary Blink", "Wall of Roots", "Cast Wall of Wood"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wall of Wood"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPowerToughness(playerA, "Wall of Roots", 0, 4); + assertHandCount(playerA, "Momentary Blink", 0); + assertPermanentCount(playerA, "Wall of Wood", 2); + + + assertLife(playerA, 20); + assertLife(playerB, 20); + + } +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index 44626f06274..059c2d1880d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -727,6 +727,17 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement player.addAction(turnNum, step, "activate:" + ability + ";target=" + targetName); } + public void activateAbility(int turnNum, PhaseStep step, TestPlayer player, String ability, String targetName, String spellOnStack) { + StringBuilder sb = new StringBuilder("activate:").append(ability); + if (targetName != null && !targetName.isEmpty()) { + sb.append(";target=" ).append(targetName); + } + if (spellOnStack != null && !spellOnStack.isEmpty()) { + sb.append(";spellOnStack=").append(spellOnStack); + } + player.addAction(turnNum, step, sb.toString()); + } + public void addCounters(int turnNum, PhaseStep step, TestPlayer player, String cardName, CounterType type, int count) { player.addAction(turnNum, step, "addCounters:" + cardName + ";" + type.getName() + ";" + count); } diff --git a/Mage/src/mage/abilities/effects/common/AddManaOfAnyColorEffect.java b/Mage/src/mage/abilities/effects/common/AddManaOfAnyColorEffect.java index 1d179e91575..3e78bcac4f6 100644 --- a/Mage/src/mage/abilities/effects/common/AddManaOfAnyColorEffect.java +++ b/Mage/src/mage/abilities/effects/common/AddManaOfAnyColorEffect.java @@ -92,4 +92,9 @@ public class AddManaOfAnyColorEffect extends ManaEffect { return false; } + + public int getAmount() { + return amount; + } + } diff --git a/Mage/src/mage/abilities/effects/common/ManaEffect.java b/Mage/src/mage/abilities/effects/common/ManaEffect.java index 7b6217164cb..74bf3cf5872 100644 --- a/Mage/src/mage/abilities/effects/common/ManaEffect.java +++ b/Mage/src/mage/abilities/effects/common/ManaEffect.java @@ -28,8 +28,8 @@ package mage.abilities.effects.common; -import mage.constants.Outcome; import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; /** * diff --git a/Mage/src/mage/abilities/mana/ActivateIfConditionManaAbility.java b/Mage/src/mage/abilities/mana/ActivateIfConditionManaAbility.java index 08d311a00e3..e7946940209 100644 --- a/Mage/src/mage/abilities/mana/ActivateIfConditionManaAbility.java +++ b/Mage/src/mage/abilities/mana/ActivateIfConditionManaAbility.java @@ -43,7 +43,7 @@ import mage.game.Game; public class ActivateIfConditionManaAbility extends ManaAbility { - private Condition condition; + private final Condition condition; public ActivateIfConditionManaAbility(Zone zone, BasicManaEffect effect, Cost cost, Condition condition) { super(zone, effect, cost); diff --git a/Mage/src/mage/abilities/mana/ActivateOncePerTurnManaAbility.java b/Mage/src/mage/abilities/mana/ActivateOncePerTurnManaAbility.java index 7a7824a5448..fc90fa52749 100644 --- a/Mage/src/mage/abilities/mana/ActivateOncePerTurnManaAbility.java +++ b/Mage/src/mage/abilities/mana/ActivateOncePerTurnManaAbility.java @@ -29,10 +29,12 @@ package mage.abilities.mana; import java.util.UUID; -import mage.constants.Zone; import mage.abilities.costs.Cost; -import mage.abilities.effects.common.ManaEffect; +import mage.abilities.effects.common.AddManaOfAnyColorEffect; +import mage.abilities.effects.common.BasicManaEffect; +import mage.constants.Zone; import mage.game.Game; +import mage.util.CardUtil; /** * @@ -40,8 +42,25 @@ import mage.game.Game; */ public class ActivateOncePerTurnManaAbility extends ManaAbility { - public ActivateOncePerTurnManaAbility(Zone zone, ManaEffect effect, Cost cost) { + class ActivationInfo { + + public int turnNum; + public int activationCounter; + + public ActivationInfo(int turnNum, int activationCounter) { + this.turnNum = turnNum; + this.activationCounter = activationCounter; + } + } + + public ActivateOncePerTurnManaAbility(Zone zone, BasicManaEffect effect, Cost cost) { super(zone, effect, cost); + this.netMana = effect.getMana(); + } + + public ActivateOncePerTurnManaAbility(Zone zone, AddManaOfAnyColorEffect effect, Cost cost) { + super(zone, effect, cost); + this.netMana.setAny(effect.getAmount()); } public ActivateOncePerTurnManaAbility(ActivateOncePerTurnManaAbility ability) { @@ -51,13 +70,10 @@ public class ActivateOncePerTurnManaAbility extends ManaAbility { @Override public boolean canActivate(UUID playerId, Game game) { if (super.canActivate(playerId, game)) { - Boolean activated = (Boolean)game.getState().getValue(this.sourceId.toString() + "activated"); - if (activated == null) { + ActivationInfo activationInfo = (ActivationInfo) game.getState().getValue(CardUtil.getCardZoneString("activations", sourceId, game)); + if (activationInfo == null || activationInfo.turnNum != game.getTurnNum() || activationInfo.activationCounter < 1) { return true; } - else { - return !activated; - } } return false; } @@ -66,18 +82,24 @@ public class ActivateOncePerTurnManaAbility extends ManaAbility { public boolean activate(Game game, boolean noMana) { if (canActivate(this.controllerId, game)) { if (super.activate(game, noMana)) { - game.getState().setValue(this.sourceId.toString() + "activated", Boolean.TRUE); + ActivationInfo activationInfo = (ActivationInfo) game.getState().getValue(CardUtil.getCardZoneString("activations", sourceId, game)); + if (activationInfo == null) { + activationInfo = new ActivationInfo(game.getTurnNum(), 1); + } else { + if (activationInfo.turnNum != game.getTurnNum()) { + activationInfo.turnNum = game.getTurnNum(); + activationInfo.activationCounter = 1; + } else { + activationInfo.activationCounter++; + } + } + game.getState().setValue(CardUtil.getCardZoneString("activations", sourceId, game), activationInfo); return true; } } return false; } - @Override - public void reset(Game game) { - game.getState().setValue(this.sourceId.toString() + "activated", Boolean.FALSE); - } - @Override public boolean resolve(Game game) { if (super.resolve(game)) { diff --git a/Mage/src/mage/abilities/mana/ManaOptions.java b/Mage/src/mage/abilities/mana/ManaOptions.java index b9ed490ea26..5e76d770565 100644 --- a/Mage/src/mage/abilities/mana/ManaOptions.java +++ b/Mage/src/mage/abilities/mana/ManaOptions.java @@ -53,8 +53,9 @@ public class ManaOptions extends ArrayList { } public void addMana(List abilities, Game game) { - if (isEmpty()) + if (isEmpty()) { this.add(new Mana()); + } if (!abilities.isEmpty()) { if (abilities.size() == 1) { //if there is only one mana option available add it to all the existing options diff --git a/Mage/src/mage/util/CardUtil.java b/Mage/src/mage/util/CardUtil.java index 332f2ee8f55..55c17cbada0 100644 --- a/Mage/src/mage/util/CardUtil.java +++ b/Mage/src/mage/util/CardUtil.java @@ -30,6 +30,7 @@ package mage.util; import java.util.Iterator; import java.util.UUID; +import mage.MageObject; import mage.Mana; import mage.abilities.Ability;