diff --git a/Mage.Sets/src/mage/cards/d/DreamLeash.java b/Mage.Sets/src/mage/cards/d/DreamLeash.java
index 8fd1f3da98..a68a361904 100644
--- a/Mage.Sets/src/mage/cards/d/DreamLeash.java
+++ b/Mage.Sets/src/mage/cards/d/DreamLeash.java
@@ -1,3 +1,4 @@
+
package mage.cards.d;
import mage.abilities.Ability;
@@ -11,6 +12,7 @@ import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone;
+import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.TargetPermanent;
@@ -18,12 +20,13 @@ import mage.target.TargetPermanent;
import java.util.UUID;
/**
+ *
* @author maxlebedev
*/
public final class DreamLeash extends CardImpl {
public DreamLeash(UUID ownerId, CardSetInfo setInfo) {
- super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}{U}");
+ super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{U}{U}");
this.subtype.add(SubType.AURA);
// Enchant permanent
@@ -51,9 +54,19 @@ public final class DreamLeash extends CardImpl {
class DreamLeashTarget extends TargetPermanent {
+ DreamLeashTarget() {}
+
+ private DreamLeashTarget(DreamLeashTarget target) {
+ super(target);
+ }
+
+ @Override
+ public DreamLeashTarget copy() {
+ return new DreamLeashTarget(this);
+ }
+
@Override
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
-
if (super.canTarget(controllerId, id, source, game)) {
Permanent permanent = game.getPermanent(id);
return permanent.isTapped();
@@ -61,4 +74,10 @@ class DreamLeashTarget extends TargetPermanent {
return false;
}
+ // See ruling: https://www.mtgsalvation.com/forums/magic-fundamentals/magic-rulings/magic-rulings-archives/253345-dream-leash
+ @Override
+ public boolean stillLegalTarget(UUID id, Ability source, Game game) {
+ Permanent permanent = game.getPermanent(id);
+ return permanent != null && StaticFilters.FILTER_PERMANENT.match(permanent, game);
+ }
}
diff --git a/Mage.Sets/src/mage/cards/e/EnthrallingHold.java b/Mage.Sets/src/mage/cards/e/EnthrallingHold.java
index 9060709592..f1a53c0a3f 100644
--- a/Mage.Sets/src/mage/cards/e/EnthrallingHold.java
+++ b/Mage.Sets/src/mage/cards/e/EnthrallingHold.java
@@ -12,9 +12,9 @@ import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone;
-import mage.filter.FilterPermanent;
-import mage.filter.common.FilterCreaturePermanent;
-import mage.filter.predicate.permanent.TappedPredicate;
+import mage.filter.StaticFilters;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
import mage.target.TargetPermanent;
import mage.target.common.TargetCreaturePermanent;
@@ -26,26 +26,19 @@ import java.util.UUID;
*/
public final class EnthrallingHold extends CardImpl {
- private static final FilterPermanent filter = new FilterCreaturePermanent();
-
- static {
- filter.add(TappedPredicate.instance);
- }
-
public EnthrallingHold(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}{U}");
-
+
this.subtype.add(SubType.AURA);
// Enchant creature
- TargetPermanent auraTarget = new TargetCreaturePermanent();
+ TargetPermanent auraTarget = new EnthrallingHoldTarget();
this.getSpellAbility().addTarget(auraTarget);
this.getSpellAbility().addEffect(new AttachEffect(Outcome.GainControl));
Ability ability = new EnchantAbility(auraTarget.getTargetName());
this.addAbility(ability);
// You can't choose an untapped creature as this spell's target as you cast it.
- auraTarget.replaceFilter(filter);
Effect controlEnchantedEffect = new ControlEnchantedEffect();
controlEnchantedEffect.setText("You can't choose an untapped creature as this spell's target as you cast it.
" + controlEnchantedEffect.getText(null));
@@ -62,3 +55,33 @@ public final class EnthrallingHold extends CardImpl {
return new EnthrallingHold(this);
}
}
+
+class EnthrallingHoldTarget extends TargetCreaturePermanent {
+
+ EnthrallingHoldTarget() {}
+
+ private EnthrallingHoldTarget(EnthrallingHoldTarget target) {
+ super(target);
+ }
+
+ @Override
+ public EnthrallingHoldTarget copy() {
+ return new EnthrallingHoldTarget(this);
+ }
+
+ @Override
+ public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
+ if (super.canTarget(controllerId, id, source, game)) {
+ Permanent permanent = game.getPermanent(id);
+ return permanent.isTapped();
+ }
+ return false;
+ }
+
+ // See ruling: https://www.mtgsalvation.com/forums/magic-fundamentals/magic-rulings/magic-rulings-archives/253345-dream-leash
+ @Override
+ public boolean stillLegalTarget(UUID id, Ability source, Game game) {
+ Permanent permanent = game.getPermanent(id);
+ return permanent != null && StaticFilters.FILTER_PERMANENT_CREATURE.match(permanent, game);
+ }
+}
\ No newline at end of file
diff --git a/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java b/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java
index 614653fe34..5d2aaf48a8 100644
--- a/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java
+++ b/Mage.Sets/src/mage/cards/g/GarrukSavageHerald.java
@@ -5,6 +5,7 @@ import mage.abilities.LoyaltyAbility;
import mage.abilities.common.DamageAsThoughNotBlockedAbility;
import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
import mage.abilities.effects.ContinuousEffect;
+import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DamageWithPowerFromOneToAnotherTargetEffect;
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
@@ -12,7 +13,12 @@ import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.CardsImpl;
-import mage.constants.*;
+import mage.constants.CardType;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import mage.constants.SubType;
+import mage.constants.SuperType;
+import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.mageobject.AnotherTargetPredicate;
import mage.game.Game;
@@ -39,7 +45,7 @@ public final class GarrukSavageHerald extends CardImpl {
this.addAbility(new LoyaltyAbility(new GarrukSavageHeraldEffect(), 1));
// −2: Target creature you control deals damage equal to its power to another target creature.
- DamageWithPowerFromOneToAnotherTargetEffect effect = new DamageWithPowerFromOneToAnotherTargetEffect();
+ Effect effect = new DamageWithPowerFromOneToAnotherTargetEffect();
effect.setText("Target creature you control deals damage equal to its power to another target creature");
Ability minusAbility = new LoyaltyAbility(effect, -2);
@@ -47,10 +53,10 @@ public final class GarrukSavageHerald extends CardImpl {
controlledCreature.setTargetTag(1);
minusAbility.addTarget(controlledCreature);
- FilterCreaturePermanent filter = new FilterCreaturePermanent("Another creature: damage dealt to");
+ FilterCreaturePermanent filter = new FilterCreaturePermanent();
filter.add(new AnotherTargetPredicate(2));
TargetCreaturePermanent anotherTargetCreature = new TargetCreaturePermanent(filter);
- minusAbility.addTarget(anotherTargetCreature);
+ minusAbility.addTarget(anotherTargetCreature.withChooseHint("another creature to deal damage to"));
this.addAbility(minusAbility);
diff --git a/Mage.Sets/src/mage/cards/l/LilianaDeathMage.java b/Mage.Sets/src/mage/cards/l/LilianaDeathMage.java
index 45c189ded9..0e1d8341ce 100644
--- a/Mage.Sets/src/mage/cards/l/LilianaDeathMage.java
+++ b/Mage.Sets/src/mage/cards/l/LilianaDeathMage.java
@@ -9,7 +9,11 @@ import mage.abilities.effects.common.LoseLifeTargetControllerEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
-import mage.constants.*;
+import mage.constants.CardType;
+import mage.constants.Outcome;
+import mage.constants.SubType;
+import mage.constants.SuperType;
+import mage.constants.Zone;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.players.Player;
@@ -78,7 +82,7 @@ class LilianaDeathMagePlusEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
- if (player == null || !player.chooseUse(Outcome.Benefit, "Return a creature card from your graveyard to your hand?", source, game)) {
+ if (player == null) {
return false;
}
Card card = game.getCard(source.getTargets().get(0).getFirstTarget());
diff --git a/Mage.Sets/src/mage/cards/n/Necromentia.java b/Mage.Sets/src/mage/cards/n/Necromentia.java
index 985e0fc22e..888d90311d 100644
--- a/Mage.Sets/src/mage/cards/n/Necromentia.java
+++ b/Mage.Sets/src/mage/cards/n/Necromentia.java
@@ -20,6 +20,7 @@ import mage.target.TargetCard;
import mage.target.common.TargetCardInLibrary;
import mage.target.common.TargetOpponent;
+import java.util.HashSet;
import java.util.UUID;
/**
@@ -94,6 +95,8 @@ class NecromentiaEffect extends OneShotEffect {
numberOfCardsExiledFromHand = target.getTargets().size();
controller.moveCards(new CardsImpl(target.getTargets()), Zone.EXILED, source, game);
}
+ } else {
+ targetPlayer.revealCards(targetPlayer.getName() + "'s Hand", targetPlayer.getHand(), game);
}
// cards in Library
@@ -106,6 +109,8 @@ class NecromentiaEffect extends OneShotEffect {
if (controller.choose(Outcome.Exile, cardsInLibrary, targetLib, game)) {
controller.moveCards(new CardsImpl(targetLib.getTargets()), Zone.EXILED, source, game);
}
+ } else {
+ targetPlayer.revealCards(targetPlayer.getName() + "'s Library", new CardsImpl(new HashSet<>(targetPlayer.getLibrary().getCards(game))), game);
}
targetPlayer.shuffleLibrary(source, game);
diff --git a/Mage.Sets/src/mage/cards/s/SanctumOfAll.java b/Mage.Sets/src/mage/cards/s/SanctumOfAll.java
index ccb8c70cf7..97386229b2 100644
--- a/Mage.Sets/src/mage/cards/s/SanctumOfAll.java
+++ b/Mage.Sets/src/mage/cards/s/SanctumOfAll.java
@@ -97,9 +97,11 @@ class SanctumOfAllTriggerEffect extends ReplacementEffectImpl {
// Only trigger while you control six or more Shrines
int numShrines = SanctumOfAll.count.calculate(game, source, this);
if (numShrines >= 6) {
- // Only for triggers of Shrines
+ // Only for triggers of other Shrines
Permanent permanent = game.getPermanent(event.getSourceId());
- return permanent != null && permanent.hasSubtype(SubType.SHRINE, game);
+ return permanent != null
+ && !permanent.getId().equals(source.getSourceId())
+ && permanent.hasSubtype(SubType.SHRINE, game);
}
}
return false;
diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/EnthrallingHoldTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/EnthrallingHoldTest.java
new file mode 100644
index 0000000000..a322395590
--- /dev/null
+++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/m21/EnthrallingHoldTest.java
@@ -0,0 +1,79 @@
+package org.mage.test.cards.single.m21;
+
+import mage.constants.PhaseStep;
+import mage.constants.Zone;
+import org.junit.Test;
+import org.mage.test.serverside.base.CardTestPlayerBase;
+
+public class EnthrallingHoldTest extends CardTestPlayerBase {
+
+ @Test
+ public void testTappedTarget_untapped_doesNotFizzle() {
+ // Traxos, Scourge of Kroog enters the battlefield tapped and doesn't untap during your untap step.
+ addCard(Zone.BATTLEFIELD, playerB, "Traxos, Scourge of Kroog");
+
+ addCard(Zone.BATTLEFIELD, playerA, "Island", 6);
+ /*
+ * {3}{U}{U}
+ * Enchant creature
+ * You can't choose an untapped creature as this spell's target as you cast it.
+ * You control enchanted creature.
+ */
+ addCard(Zone.HAND, playerA, "Enthralling Hold");
+ /*
+ * {U}
+ * You may tap or untap target artifact, creature, or land.
+ */
+ addCard(Zone.HAND, playerA, "Twiddle");
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Enthralling Hold", "Traxos, Scourge of Kroog");
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Twiddle", "Traxos, Scourge of Kroog");
+
+ setChoice(playerA, "Yes");
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_COMBAT);
+ execute();
+ assertAllCommandsUsed();
+
+ assertPermanentCount(playerB, "Traxos, Scourge of Kroog", 0);
+ assertPermanentCount(playerA, "Traxos, Scourge of Kroog", 1);
+ assertPermanentCount(playerA, "Enthralling Hold", 1);
+ }
+
+ @Test
+ public void testTappedTarget_becomesIllegal_fizzles() {
+ // Traxos, Scourge of Kroog enters the battlefield tapped and doesn't untap during your untap step.
+ addCard(Zone.BATTLEFIELD, playerB, "Traxos, Scourge of Kroog");
+
+ addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5);
+ addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
+ /*
+ * {3}{U}{U}
+ * Enchant creature
+ * You can't choose an untapped creature as this spell's target as you cast it.
+ * You control enchanted creature.
+ */
+ addCard(Zone.HAND, playerA, "Enthralling Hold");
+ /*
+ * {1}{B}
+ * Destroy target nonblack creature
+ */
+ addCard(Zone.HAND, playerA, "Doom Blade");
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Enthralling Hold", "Traxos, Scourge of Kroog");
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Doom Blade", "Traxos, Scourge of Kroog");
+
+ setStrictChooseMode(true);
+ setStopAt(1, PhaseStep.END_COMBAT);
+ execute();
+ assertAllCommandsUsed();
+
+ assertPermanentCount(playerB, "Traxos, Scourge of Kroog", 0);
+ assertPermanentCount(playerA, "Traxos, Scourge of Kroog", 0);
+
+ assertGraveyardCount(playerB, "Traxos, Scourge of Kroog", 1);
+ assertGraveyardCount(playerA, "Enthralling Hold", 1);
+ assertGraveyardCount(playerA, "Doom Blade", 1);
+ }
+}
diff --git a/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerLeftGameRange1Test.java b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerLeftGameRange1Test.java
index c37ce869a2..8699e82cd9 100644
--- a/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerLeftGameRange1Test.java
+++ b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerLeftGameRange1Test.java
@@ -1,5 +1,6 @@
package org.mage.test.multiplayer;
+import java.io.FileNotFoundException;
import mage.constants.MultiplayerAttackOption;
import mage.constants.PhaseStep;
import mage.constants.RangeOfInfluence;
@@ -14,8 +15,6 @@ import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
-import java.io.FileNotFoundException;
-
/**
* @author LevelX2
*/
@@ -344,4 +343,54 @@ public class PlayerLeftGameRange1Test extends CardTestMultiPlayerBase {
Assert.assertTrue("Staff of player B could be used", staffPlayerB.isTapped());
}
+
+ /**
+ * Captive Audience doesn't work correctly in multiplayer #5593
+ *
+ * Currently, if the controller of Captive Audience leaves the game, Captive
+ * Audience returns to its owner instead of being exiled.
+ */
+ @Test
+ public void TestCaptiveAudienceGoesToExile() {
+ addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4);
+ addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
+ // Captive Audience enters the battlefield under the control of an opponent of your choice.
+ // At the beginning of your upkeep, choose one that hasn't been chosen —
+ // • Your life total becomes 4.
+ // • Discard your hand.
+ // • Each opponent creates five 2/2 black Zombie creature tokens.
+ addCard(Zone.HAND, playerA, "Captive Audience"); // Enchantment {5}{B}{R}
+
+ addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
+ addCard(Zone.BATTLEFIELD, playerA, "Pillarfield Ox", 1);
+
+ setChoice(playerA, "PlayerA"); // Starting Player
+
+ castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Captive Audience");
+ setChoice(playerA, "PlayerD");
+
+ setModeChoice(playerD, "1");
+
+ attack(5, playerA, "Silvercoat Lion", playerD);
+ attack(5, playerA, "Pillarfield Ox", playerD);
+
+ setStopAt(5, PhaseStep.POSTCOMBAT_MAIN);
+
+ setStrictChooseMode(true);
+ execute();
+
+ assertAllCommandsUsed();
+
+ assertLife(playerA, 2);
+
+ Assert.assertFalse("Player D is no longer in the game", playerD.isInGame());
+
+ assertPermanentCount(playerD, 0);
+
+ assertPermanentCount(playerA, "Captive Audience", 0);
+ assertGraveyardCount(playerA, "Captive Audience", 0);
+ assertExileCount(playerA, "Captive Audience", 1);
+
+ }
+
}
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 c3ae20419e..7b39681940 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
@@ -1,5 +1,13 @@
package org.mage.test.serverside.base.impl;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
import mage.MageObject;
import mage.Mana;
import mage.ObjectColor;
@@ -35,15 +43,6 @@ import org.mage.test.player.TestPlayer;
import org.mage.test.serverside.base.CardTestAPI;
import org.mage.test.serverside.base.MageTestPlayerBase;
-import java.io.FileNotFoundException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.UUID;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-
/**
* API for test initialization and asserting the test results.
*
@@ -275,7 +274,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
}
}
Assert.assertFalse("Wrong stop command on " + this.stopOnTurn + " / " + this.stopAtStep + " (" + this.stopAtStep.getIndex() + ")"
- + " (found actions after stop on " + maxTurn + " / " + maxPhase + ")",
+ + " (found actions after stop on " + maxTurn + " / " + maxPhase + ")",
(maxTurn > this.stopOnTurn) || (maxTurn == this.stopOnTurn && maxPhase > this.stopAtStep.getIndex()));
for (Player player : currentGame.getPlayers().values()) {
@@ -508,8 +507,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* Add a card to specified zone of specified player.
*
* @param gameZone {@link mage.constants.Zone} to add cards to.
- * @param player {@link Player} to add cards for. Use either playerA or
- * playerB.
+ * @param player {@link Player} to add cards for. Use either playerA or
+ * playerB.
* @param cardName Card name in string format.
*/
@Override
@@ -521,10 +520,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* Add any amount of cards to specified zone of specified player.
*
* @param gameZone {@link mage.constants.Zone} to add cards to.
- * @param player {@link Player} to add cards for. Use either playerA or
- * playerB.
+ * @param player {@link Player} to add cards for. Use either playerA or
+ * playerB.
* @param cardName Card name in string format.
- * @param count Amount of cards to be added.
+ * @param count Amount of cards to be added.
*/
@Override
public void addCard(Zone gameZone, TestPlayer player, String cardName, int count) {
@@ -535,13 +534,13 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* Add any amount of cards to specified zone of specified player.
*
* @param gameZone {@link mage.constants.Zone} to add cards to.
- * @param player {@link Player} to add cards for. Use either playerA or
- * playerB.
+ * @param player {@link Player} to add cards for. Use either playerA or
+ * playerB.
* @param cardName Card name in string format.
- * @param count Amount of cards to be added.
- * @param tapped In case gameZone is Battlefield, determines whether
- * permanent should be tapped. In case gameZone is other than Battlefield,
- * {@link IllegalArgumentException} is thrown
+ * @param count Amount of cards to be added.
+ * @param tapped In case gameZone is Battlefield, determines whether
+ * permanent should be tapped. In case gameZone is other than Battlefield,
+ * {@link IllegalArgumentException} is thrown
*/
@Override
public void addCard(Zone gameZone, TestPlayer player, String cardName, int count, boolean tapped) {
@@ -622,7 +621,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* Set player's initial life count.
*
* @param player {@link Player} to set life count for.
- * @param life Life count to set.
+ * @param life Life count to set.
*/
@Override
public void setLife(TestPlayer player, int life) {
@@ -699,7 +698,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* Assert player's life count after test execution.
*
* @param player {@link Player} to get life for comparison.
- * @param life Expected player's life to compare with.
+ * @param life Expected player's life to compare with.
*/
@Override
public void assertLife(Player player, int life) throws AssertionError {
@@ -716,14 +715,14 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* params 3b. all: there is at least one creature with the cardName with the
* different p\t params
*
- * @param player {@link Player} to get creatures for comparison.
- * @param cardName Card name to compare with.
- * @param power Expected power to compare with.
+ * @param player {@link Player} to get creatures for comparison.
+ * @param cardName Card name to compare with.
+ * @param power Expected power to compare with.
* @param toughness Expected toughness to compare with.
- * @param scope {@link mage.filter.Filter.ComparisonScope} Use ANY, if you
- * want "at least one creature with given name should have specified p\t"
- * Use ALL, if you want "all creature with gived name should have specified
- * p\t"
+ * @param scope {@link mage.filter.Filter.ComparisonScope} Use ANY, if you
+ * want "at least one creature with given name should have specified p\t"
+ * Use ALL, if you want "all creature with gived name should have specified
+ * p\t"
*/
@Override
public void assertPowerToughness(Player player, String cardName, int power, int toughness, Filter.ComparisonScope scope)
@@ -813,8 +812,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* @param cardName
* @param ability
* @param mustHave true if creature should contain ability, false if it
- * should NOT contain it instead
- * @param count number of permanents with that ability
+ * should NOT contain it instead
+ * @param count number of permanents with that ability
* @throws AssertionError
*/
public void assertAbility(Player player, String cardName, Ability ability, boolean mustHave, int count) throws AssertionError {
@@ -847,7 +846,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* Assert permanent count under player's control.
*
* @param player {@link Player} which permanents should be counted.
- * @param count Expected count.
+ * @param count Expected count.
*/
@Override
public void assertPermanentCount(Player player, int count) throws AssertionError {
@@ -863,9 +862,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
/**
* Assert permanent count under player's control.
*
- * @param player {@link Player} which permanents should be counted.
+ * @param player {@link Player} which permanents should be counted.
* @param cardName Name of the cards that should be counted.
- * @param count Expected count.
+ * @param count Expected count.
*/
@Override
public void assertPermanentCount(Player player, String cardName, int count) throws AssertionError {
@@ -915,8 +914,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* Assert counter count on a permanent
*
* @param cardName Name of the cards that should be counted.
- * @param type Type of the counter that should be counted.
- * @param count Expected count.
+ * @param type Type of the counter that should be counted.
+ * @param count Expected count.
*/
public void assertCounterCount(String cardName, CounterType type, int count) throws AssertionError {
this.assertCounterCount(null, cardName, type, count);
@@ -939,8 +938,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* Assert counter count on a card in exile
*
* @param cardName Name of the cards that should be counted.
- * @param type Type of the counter that should be counted.
- * @param count Expected count.
+ * @param type Type of the counter that should be counted.
+ * @param count Expected count.
*/
public void assertCounterOnExiledCardCount(String cardName, CounterType type, int count) throws AssertionError {
//Assert.assertNotEquals("", cardName);
@@ -963,8 +962,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* Assert counter count on a player
*
* @param player The player whos counters should be counted.
- * @param type Type of the counter that should be counted.
- * @param count Expected count.
+ * @param type Type of the counter that should be counted.
+ * @param count Expected count.
*/
public void assertCounterCount(Player player, CounterType type, int count) throws AssertionError {
Assert.assertEquals("(Battlefield) Counter counts are not equal (" + player.getName() + ':' + type + ')', count, player.getCounters().getCount(type));
@@ -974,7 +973,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* Assert whether a permanent is a specified type or not
*
* @param cardName Name of the permanent that should be checked.
- * @param type A type to test for
+ * @param type A type to test for
* @param mustHave true if creature should have type, false if it should not
*/
public void assertType(String cardName, CardType type, boolean mustHave) throws AssertionError {
@@ -999,8 +998,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* Assert whether a permanent is a specified type
*
* @param cardName Name of the permanent that should be checked.
- * @param type A type to test for
- * @param subType a subtype to test for
+ * @param type A type to test for
+ * @param subType a subtype to test for
*/
public void assertType(String cardName, CardType type, SubType subType) throws AssertionError {
//Assert.assertNotEquals("", cardName);
@@ -1015,7 +1014,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* Assert whether a permanent is not a specified type
*
* @param cardName Name of the permanent that should be checked.
- * @param type A type to test for
+ * @param type A type to test for
*/
public void assertNotType(String cardName, CardType type) throws AssertionError {
//Assert.assertNotEquals("", cardName);
@@ -1027,7 +1026,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* Assert whether a permanent is not a specified subtype
*
* @param cardName Name of the permanent that should be checked.
- * @param subType a subtype to test for
+ * @param subType a subtype to test for
*/
public void assertNotSubtype(String cardName, SubType subType) throws AssertionError {
//Assert.assertNotEquals("", cardName);
@@ -1040,10 +1039,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
/**
* Assert permanent color
*
- * @param player player to check
- * @param cardName card name on battlefield from player
+ * @param player player to check
+ * @param cardName card name on battlefield from player
* @param searchColors colors list with searchable values
- * @param mustHave must or not must have that colors
+ * @param mustHave must or not must have that colors
*/
public void assertColor(Player player, String cardName, ObjectColor searchColors, boolean mustHave) {
//Assert.assertNotEquals("", cardName);
@@ -1078,7 +1077,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* Assert whether a permanent is tapped or not
*
* @param cardName Name of the permanent that should be checked.
- * @param tapped Whether the permanent is tapped or not
+ * @param tapped Whether the permanent is tapped or not
*/
public void assertTapped(String cardName, boolean tapped) throws AssertionError {
//Assert.assertNotEquals("", cardName);
@@ -1105,8 +1104,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* Assert whether X permanents of the same name are tapped or not.
*
* @param cardName Name of the permanent that should be checked.
- * @param tapped Whether the permanent is tapped or not
- * @param count The amount of this permanents that should be tapped
+ * @param tapped Whether the permanent is tapped or not
+ * @param count The amount of this permanents that should be tapped
*/
public void assertTappedCount(String cardName, boolean tapped, int count) throws AssertionError {
//Assert.assertNotEquals("", cardName);
@@ -1128,7 +1127,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
/**
* Assert whether a permanent is attacking or not
*
- * @param cardName Name of the permanent that should be checked.
+ * @param cardName Name of the permanent that should be checked.
* @param attacking Whether the permanent is attacking or not
*/
public void assertAttacking(String cardName, boolean attacking) throws AssertionError {
@@ -1150,7 +1149,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* Assert card count in player's hand.
*
* @param player {@link Player} who's hand should be counted.
- * @param count Expected count.
+ * @param count Expected count.
*/
public void assertHandCount(Player player, int count) throws AssertionError {
int actual = currentGame.getPlayer(player.getId()).getHand().size();
@@ -1160,9 +1159,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
/**
* Assert card count in player's hand.
*
- * @param player {@link Player} who's hand should be counted.
+ * @param player {@link Player} who's hand should be counted.
* @param cardName Name of the cards that should be counted.
- * @param count Expected count.
+ * @param count Expected count.
*/
public void assertHandCount(Player player, String cardName, int count) throws AssertionError {
//Assert.assertNotEquals("", cardName);
@@ -1210,7 +1209,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* Assert card count in player's graveyard.
*
* @param player {@link Player} who's graveyard should be counted.
- * @param count Expected count.
+ * @param count Expected count.
*/
public void assertGraveyardCount(Player player, int count) throws AssertionError {
int actual = currentGame.getPlayer(player.getId()).getGraveyard().size();
@@ -1221,7 +1220,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* Assert card count in exile.
*
* @param cardName Name of the cards that should be counted.
- * @param count Expected count.
+ * @param count Expected count.
*/
public void assertExileCount(String cardName, int count) throws AssertionError {
//Assert.assertNotEquals("", cardName);
@@ -1259,9 +1258,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
/**
* Assert card count in player's exile.
*
- * @param owner {@link Player} who's exile should be counted.
+ * @param owner {@link Player} who's exile should be counted.
* @param cardName Name of the cards that should be counted.
- * @param count Expected count.
+ * @param count Expected count.
*/
public void assertExileCount(Player owner, String cardName, int count) throws AssertionError {
//Assert.assertNotEquals("", cardName);
@@ -1280,9 +1279,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
/**
* Assert card count in player's graveyard.
*
- * @param player {@link Player} who's graveyard should be counted.
+ * @param player {@link Player} who's graveyard should be counted.
* @param cardName Name of the cards that should be counted.
- * @param count Expected count.
+ * @param count Expected count.
*/
public void assertGraveyardCount(Player player, String cardName, int count) throws AssertionError {
assertAliaseSupportInActivateCommand(cardName, true);
@@ -1301,7 +1300,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* Assert library card count.
*
* @param player {@link Player} who's library should be counted.
- * @param count Expected count.
+ * @param count Expected count.
*/
public void assertLibraryCount(Player player, int count) throws AssertionError {
List libraryList = player.getLibrary().getCards(currentGame);
@@ -1312,9 +1311,9 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
/**
* Assert specific card count in player's library.
*
- * @param player {@link Player} who's library should be counted.
+ * @param player {@link Player} who's library should be counted.
* @param cardName Name of the cards that should be counted.
- * @param count Expected count.
+ * @param count Expected count.
*/
public void assertLibraryCount(Player player, String cardName, int count) throws AssertionError {
//Assert.assertNotEquals("", cardName);
@@ -1353,8 +1352,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
}
/**
- * Raise error on any unused commands, choices or targets
- * If you want to test that ability can't be activated then use call checkPlayableAbility()
+ * Raise error on any unused commands, choices or targets If you want to
+ * test that ability can't be activated then use call checkPlayableAbility()
*
* @throws AssertionError
*/
@@ -1501,8 +1500,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* @param player
* @param cardName
* @param targetName for modes you can add "mode=3" before target name,
- * multiple targets can be seperated by ^, not target marks as
- * TestPlayer.NO_TARGET
+ * multiple targets can be seperated by ^, not target marks as
+ * TestPlayer.NO_TARGET
*/
public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, String targetName) {
//Assert.assertNotEquals("", cardName);
@@ -1525,8 +1524,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* @param step
* @param player
* @param cardName
- * @param targetName for modal spells add the mode to the name e.g.
- * "mode=2SilvercoatLion^mode3=PillarfieldOx"
+ * @param targetName for modal spells add the mode to the name e.g.
+ * "mode=2SilvercoatLion^mode3=PillarfieldOx"
* @param spellOnStack
*/
public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, String targetName, String spellOnStack) {
@@ -1613,7 +1612,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* @param step
* @param player
* @param ability
- * @param targetName use NO_TARGET if there is no target to set
+ * @param targetName use NO_TARGET if there is no target to set
* @param spellOnStack
*/
public void activateAbility(int turnNum, PhaseStep step, TestPlayer player, String ability, String targetName, String spellOnStack) {
@@ -1626,8 +1625,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* @param step
* @param player
* @param ability
- * @param targetName if not target has to be defined use the constant
- * NO_TARGET
+ * @param targetName if not target has to be defined use the constant
+ * NO_TARGET
* @param spellOnStack
* @param clause
*/
@@ -1697,9 +1696,12 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
}
/**
- * For use choices set "Yes" or "No" the the choice string. For X values set
- * "X=[xValue]" example: for X=3 set choice string to "X=3".
- *
For ColorChoice use "Red", "Green", "Blue", "Black" or "White"
+ * For use choices set "Yes" or "No" the the choice string.
+ * For X values set "X=[xValue]" example: for X=3 set choice string to
+ * "X=3".
+ * For ColorChoice use "Red", "Green", "Blue", "Black" or "White"
+ * use command setModeChoice if you have to set a mode from modal
+ * ability
*
* @param player
* @param choice
@@ -1719,10 +1721,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
*
* @param player
* @param choice starting with "1" for mode 1, "2" for mode 2 and so on (to
- * set multiple modes call the command multiple times). If a spell mode can
- * be used only once like Demonic Pact, the value has to be set to the
- * number of the remaining modes (e.g. if only 2 are left the number need to
- * be 1 or 2).
+ * set multiple modes call the command multiple times). If a spell mode can
+ * be used only once like Demonic Pact, the value has to be set to the
+ * number of the remaining modes (e.g. if only 2 are left the number need to
+ * be 1 or 2).
*/
public void setModeChoice(TestPlayer player, String choice) {
player.addModeChoice(choice);
@@ -1733,12 +1735,12 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
*
* @param player
* @param target you can add multiple targets by separating them by the "^"
- * character e.g. "creatureName1^creatureName2" you can qualify the target
- * additional by setcode e.g. "creatureName-M15" you can add [no copy] to
- * the end of the target name to prohibit targets that are copied you can
- * add [only copy] to the end of the target name to allow only targets that
- * are copies. For modal spells use a prefix with the mode number:
- * mode=1Lightning Bolt^mode=2Silvercoat Lion
+ * character e.g. "creatureName1^creatureName2" you can qualify the target
+ * additional by setcode e.g. "creatureName-M15" you can add [no copy] to
+ * the end of the target name to prohibit targets that are copied you can
+ * add [only copy] to the end of the target name to allow only targets that
+ * are copies. For modal spells use a prefix with the mode number:
+ * mode=1Lightning Bolt^mode=2Silvercoat Lion
*/
// TODO: mode options doesn't work here (see BrutalExpulsionTest)
public void addTarget(TestPlayer player, String target) {
@@ -1770,7 +1772,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
/**
* @param player
* @param target use TestPlayer.TARGET_SKIP to 0 targets selects or to stop
- * "up two xxx" selection
+ * "up two xxx" selection
* @param amount
*/
public void addTargetAmount(TestPlayer player, String target, int amount) {
diff --git a/Mage/src/main/java/mage/abilities/effects/common/EntersBattlefieldUnderControlOfOpponentOfChoiceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/EntersBattlefieldUnderControlOfOpponentOfChoiceEffect.java
index 2d3a8c5639..5b1b4f4e94 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/EntersBattlefieldUnderControlOfOpponentOfChoiceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/EntersBattlefieldUnderControlOfOpponentOfChoiceEffect.java
@@ -1,72 +1,73 @@
-/*
- * 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.effects.common;
-
-import mage.abilities.Ability;
-import mage.abilities.effects.ContinuousEffect;
-import mage.abilities.effects.OneShotEffect;
-import mage.abilities.effects.common.continuous.GainControlTargetEffect;
-import mage.constants.Duration;
-import mage.constants.Outcome;
-import static mage.constants.Outcome.Benefit;
-import mage.game.Game;
-import mage.game.permanent.Permanent;
-import mage.players.Player;
-import mage.target.Target;
-import mage.target.common.TargetOpponent;
-import mage.target.targetpointer.FixedTarget;
-
-/**
- * Use this effect only with EntersBattlefieldAbility like abilities
- *
- * @author LevelX2
- */
-
-public class EntersBattlefieldUnderControlOfOpponentOfChoiceEffect extends OneShotEffect {
-
- public EntersBattlefieldUnderControlOfOpponentOfChoiceEffect() {
- super(Benefit);
- staticText = "under the control of an opponent of your choice";
- }
-
- private EntersBattlefieldUnderControlOfOpponentOfChoiceEffect(final EntersBattlefieldUnderControlOfOpponentOfChoiceEffect effect) {
- super(effect);
- }
-
- @Override
- public EntersBattlefieldUnderControlOfOpponentOfChoiceEffect copy() {
- return new EntersBattlefieldUnderControlOfOpponentOfChoiceEffect(this);
- }
-
- @Override
- public boolean apply(Game game, Ability source) {
- Player controller = game.getPlayer(source.getControllerId());
- if (controller == null) {
- return false;
- }
- Target target = new TargetOpponent();
- target.setNotTarget(true);
- if (!controller.choose(Outcome.Benefit, target, source.getSourceId(), game)) {
- return false;
- }
- Player opponent = game.getPlayer(target.getFirstTarget());
- if (opponent == null) {
- return false;
- }
- Permanent permanent = game.getPermanentEntering(source.getSourceId());
- if (permanent != null) {
- game.informPlayers(permanent.getLogName() + " enters the battlefield under the control of " + opponent.getLogName());
- }
- ContinuousEffect continuousEffect = new GainControlTargetEffect(
- Duration.Custom, true, opponent.getId()
- );
- continuousEffect.setTargetPointer(new FixedTarget(
- source.getSourceId(), source.getSourceObjectZoneChangeCounter()
- ));
- game.addEffect(continuousEffect, source);
- return true;
- }
-}
\ No newline at end of file
+/*
+ * 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.effects.common;
+
+import mage.abilities.Ability;
+import mage.abilities.effects.ContinuousEffect;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.continuous.GainControlTargetEffect;
+import mage.constants.Duration;
+import mage.constants.Outcome;
+import static mage.constants.Outcome.Benefit;
+import mage.game.Game;
+import mage.game.permanent.Permanent;
+import mage.players.Player;
+import mage.target.Target;
+import mage.target.common.TargetOpponent;
+import mage.target.targetpointer.FixedTarget;
+
+/**
+ * Use this effect only with EntersBattlefieldAbility like abilities
+ *
+ * @author LevelX2
+ */
+public class EntersBattlefieldUnderControlOfOpponentOfChoiceEffect extends OneShotEffect {
+
+ public EntersBattlefieldUnderControlOfOpponentOfChoiceEffect() {
+ super(Benefit);
+ staticText = "under the control of an opponent of your choice";
+ }
+
+ private EntersBattlefieldUnderControlOfOpponentOfChoiceEffect(final EntersBattlefieldUnderControlOfOpponentOfChoiceEffect effect) {
+ super(effect);
+ }
+
+ @Override
+ public EntersBattlefieldUnderControlOfOpponentOfChoiceEffect copy() {
+ return new EntersBattlefieldUnderControlOfOpponentOfChoiceEffect(this);
+ }
+
+ @Override
+ public boolean apply(Game game, Ability source) {
+ Player controller = game.getPlayer(source.getControllerId());
+ if (controller == null) {
+ return false;
+ }
+ Target target = new TargetOpponent();
+ target.setNotTarget(true);
+ if (!controller.choose(Outcome.Benefit, target, source.getSourceId(), game)) {
+ return false;
+ }
+ Player opponent = game.getPlayer(target.getFirstTarget());
+ if (opponent == null) {
+ return false;
+ }
+ Permanent permanent = game.getPermanentEntering(source.getSourceId());
+ if (permanent != null) {
+ permanent.setOriginalControllerId(opponent.getId()); // permanent was controlled by this player since the existance of this object so original controller has to be set to the first controller
+ permanent.setControllerId(opponent.getId()); // neccessary to set already here because spell caster never controlled the permanent (important for rule 800.4a)
+ game.informPlayers(permanent.getLogName() + " enters the battlefield under the control of " + opponent.getLogName());
+ }
+ ContinuousEffect continuousEffect = new GainControlTargetEffect(
+ Duration.Custom, true, opponent.getId()
+ );
+ continuousEffect.setTargetPointer(new FixedTarget(
+ source.getSourceId(), source.getSourceObjectZoneChangeCounter()
+ ));
+ game.addEffect(continuousEffect, source);
+ return true;
+ }
+}
diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java
index 673c2f056f..90a360c070 100644
--- a/Mage/src/main/java/mage/game/GameImpl.java
+++ b/Mage/src/main/java/mage/game/GameImpl.java
@@ -1,5 +1,9 @@
package mage.game;
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.*;
+import java.util.Map.Entry;
import mage.MageException;
import mage.MageObject;
import mage.abilities.*;
@@ -67,11 +71,6 @@ import mage.util.functions.ApplyToPermanent;
import mage.watchers.common.*;
import org.apache.log4j.Logger;
-import java.io.IOException;
-import java.io.Serializable;
-import java.util.*;
-import java.util.Map.Entry;
-
public abstract class GameImpl implements Game, Serializable {
private static final int ROLLBACK_TURNS_MAX = 4;
@@ -562,7 +561,7 @@ public abstract class GameImpl implements Game, Serializable {
@Override
public void saveState(boolean bookmark) {
- if (!simulation && gameStates != null) {
+ if (!simulation && gameStates != null) {
if (bookmark || saveGame) {
gameStates.save(state);
}
@@ -1549,7 +1548,7 @@ public abstract class GameImpl implements Game, Serializable {
/**
* @param emblem
* @param sourceObject
- * @param toPlayerId controller and owner of the emblem
+ * @param toPlayerId controller and owner of the emblem
*/
@Override
public void addEmblem(Emblem emblem, MageObject sourceObject, UUID toPlayerId) {
@@ -1567,8 +1566,8 @@ public abstract class GameImpl implements Game, Serializable {
/**
* @param plane
* @param sourceObject
- * @param toPlayerId controller and owner of the plane (may only be one per
- * game..)
+ * @param toPlayerId controller and owner of the plane (may only be one per
+ * game..)
* @return boolean - whether the plane was added successfully or not
*/
@Override
@@ -1642,7 +1641,7 @@ public abstract class GameImpl implements Game, Serializable {
newBluePrint.reset(this);
//getState().addCard(permanent);
- if (copyFromPermanent.isMorphed() || copyFromPermanent.isManifested()
+ if (copyFromPermanent.isMorphed() || copyFromPermanent.isManifested()
|| copyFromPermanent.isFaceDown(this)) {
MorphAbility.setPermanentToFaceDownCreature(newBluePrint);
}
@@ -1805,7 +1804,7 @@ public abstract class GameImpl implements Game, Serializable {
break;
}
// triggered abilities that don't use the stack have to be executed first (e.g. Banisher Priest Return exiled creature
- for (Iterator it = abilities.iterator(); it.hasNext(); ) {
+ for (Iterator it = abilities.iterator(); it.hasNext();) {
TriggeredAbility triggeredAbility = it.next();
if (!triggeredAbility.isUsesStack()) {
state.removeTriggeredAbility(triggeredAbility);
@@ -1880,7 +1879,7 @@ public abstract class GameImpl implements Game, Serializable {
Zone currentZone = this.getState().getZone(card.getId());
String currentZoneInfo = (currentZone == null ? "(error)" : "(" + currentZone.name() + ")");
if (player.chooseUse(Outcome.Benefit, "Move " + card.getIdName()
- + " to the command zone or leave it in current zone " + currentZoneInfo + "?", "You can only make this choice once per object",
+ + " to the command zone or leave it in current zone " + currentZoneInfo + "?", "You can only make this choice once per object",
"Move to command", "Leave in current zone " + currentZoneInfo, null, this)) {
toMove.add(card);
} else {
@@ -2596,7 +2595,7 @@ public abstract class GameImpl implements Game, Serializable {
}
//20100423 - 800.4a
Set toOutside = new HashSet<>();
- for (Iterator it = getBattlefield().getAllPermanents().iterator(); it.hasNext(); ) {
+ for (Iterator it = getBattlefield().getAllPermanents().iterator(); it.hasNext();) {
Permanent perm = it.next();
if (perm.isOwnedBy(playerId)) {
if (perm.getAttachedTo() != null) {
@@ -2621,6 +2620,10 @@ public abstract class GameImpl implements Game, Serializable {
for (ContinuousEffect effect : getContinuousEffects().getLayeredEffects(this)) {
if (effect.hasLayer(Layer.ControlChangingEffects_2)) {
for (Ability ability : getContinuousEffects().getLayeredEffectAbilities(effect)) {
+ if (effect.getTargetPointer().getTargets(this, ability).contains(perm.getId())) {
+ effect.discard();
+ continue Effects;
+ }
for (Target target : ability.getTargets()) {
for (UUID targetId : target.getTargets()) {
if (targetId.equals(perm.getId())) {
@@ -2630,6 +2633,7 @@ public abstract class GameImpl implements Game, Serializable {
}
}
}
+
}
}
}
@@ -2641,7 +2645,7 @@ public abstract class GameImpl implements Game, Serializable {
player.moveCards(toOutside, Zone.OUTSIDE, null, this);
// triggered abilities that don't use the stack have to be executed
List abilities = state.getTriggered(player.getId());
- for (Iterator it = abilities.iterator(); it.hasNext(); ) {
+ for (Iterator it = abilities.iterator(); it.hasNext();) {
TriggeredAbility triggeredAbility = it.next();
if (!triggeredAbility.isUsesStack()) {
state.removeTriggeredAbility(triggeredAbility);
@@ -2661,7 +2665,7 @@ public abstract class GameImpl implements Game, Serializable {
// Remove cards from the player in all exile zones
for (ExileZone exile : this.getExile().getExileZones()) {
- for (Iterator it = exile.iterator(); it.hasNext(); ) {
+ for (Iterator it = exile.iterator(); it.hasNext();) {
Card card = this.getCard(it.next());
if (card != null && card.isOwnedBy(playerId)) {
it.remove();
@@ -2671,7 +2675,7 @@ public abstract class GameImpl implements Game, Serializable {
//Remove all commander/emblems/plane the player controls
boolean addPlaneAgain = false;
- for (Iterator it = this.getState().getCommand().iterator(); it.hasNext(); ) {
+ for (Iterator it = this.getState().getCommand().iterator(); it.hasNext();) {
CommandObject obj = it.next();
if (obj.isControlledBy(playerId)) {
if (obj instanceof Emblem) {
diff --git a/Mage/src/main/java/mage/game/permanent/Permanent.java b/Mage/src/main/java/mage/game/permanent/Permanent.java
index 7999673d27..f9612d1925 100644
--- a/Mage/src/main/java/mage/game/permanent/Permanent.java
+++ b/Mage/src/main/java/mage/game/permanent/Permanent.java
@@ -1,5 +1,8 @@
package mage.game.permanent;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
import mage.MageObject;
import mage.MageObjectReference;
import mage.abilities.Ability;
@@ -10,12 +13,10 @@ import mage.game.Controllable;
import mage.game.Game;
import mage.game.GameState;
-import java.util.List;
-import java.util.Set;
-import java.util.UUID;
-
public interface Permanent extends Card, Controllable {
+ void setOriginalControllerId(UUID controllerId);
+
void setControllerId(UUID controllerId);
boolean isTapped();
@@ -103,7 +104,8 @@ public interface Permanent extends Card, Controllable {
/**
* @param source
* @param game
- * @param silentMode - use it to ignore warning message for users (e.g. for checking only)
+ * @param silentMode - use it to ignore warning message for users (e.g. for
+ * checking only)
* @return
*/
boolean cantBeAttachedBy(MageObject source, Game game, boolean silentMode);
@@ -217,7 +219,7 @@ public interface Permanent extends Card, Controllable {
/**
* @param defenderId id of planeswalker or player to attack - can be empty
- * to check generally
+ * to check generally
* @param game
* @return
*/
diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
index b2e8fa813d..44b0261490 100644
--- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
+++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java
@@ -1,5 +1,7 @@
package mage.game.permanent;
+import java.io.Serializable;
+import java.util.*;
import mage.MageObject;
import mage.MageObjectReference;
import mage.ObjectColor;
@@ -38,9 +40,6 @@ import mage.util.GameLog;
import mage.util.ThreadLocalStringBuilder;
import org.apache.log4j.Logger;
-import java.io.Serializable;
-import java.util.*;
-
/**
* @author BetaSteward_at_googlemail.com
*/
@@ -184,6 +183,11 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
abilities.setControllerId(controllerId);
}
+ @Override
+ public void setOriginalControllerId(UUID originalControllerId) {
+ this.originalControllerId = originalControllerId;
+ }
+
/**
* Called before each applyEffects or if after a permanent was copied for
* the copied object
@@ -793,7 +797,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
this.attachedTo = attachToObjectId;
this.attachedToZoneChangeCounter = game.getState().getZoneChangeCounter(attachToObjectId);
for (Ability ability : this.getAbilities()) {
- for (Iterator ite = ability.getEffects(game, EffectType.CONTINUOUS).iterator(); ite.hasNext(); ) {
+ for (Iterator ite = ability.getEffects(game, EffectType.CONTINUOUS).iterator(); ite.hasNext();) {
ContinuousEffect effect = (ContinuousEffect) ite.next();
game.getContinuousEffects().setOrder(effect);
// It's important to update the timestamp of the copied effect in ContinuousEffects because it does the action
@@ -848,8 +852,8 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
* @param game
* @param preventable
* @param combat
- * @param markDamage If true, damage will be dealt later in applyDamage
- * method
+ * @param markDamage If true, damage will be dealt later in applyDamage
+ * method
* @return
*/
private int damage(int damageAmount, UUID sourceId, Game game, boolean preventable, boolean combat, boolean markDamage, List appliedEffects) {
@@ -1066,9 +1070,9 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
if (game.getPlayer(this.getControllerId()).hasOpponent(sourceControllerId, game)
&& game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) == null
&& abilities.stream()
- .filter(HexproofBaseAbility.class::isInstance)
- .map(HexproofBaseAbility.class::cast)
- .anyMatch(ability -> ability.checkObject(source, game))) {
+ .filter(HexproofBaseAbility.class::isInstance)
+ .map(HexproofBaseAbility.class::cast)
+ .anyMatch(ability -> ability.checkObject(source, game))) {
return false;
}
@@ -1619,9 +1623,9 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, List appliedEffects) {
Zone fromZone = game.getState().getZone(objectId);
ZoneChangeEvent event = new ZoneChangeEvent(this, sourceId, ownerId, fromZone, Zone.EXILED, appliedEffects);
- ZoneChangeInfo.Exile info = new ZoneChangeInfo.Exile(event, exileId, name);
+ ZoneChangeInfo.Exile zcInfo = new ZoneChangeInfo.Exile(event, exileId, name);
- boolean successfullyMoved = ZonesHandler.moveCard(info, game);
+ boolean successfullyMoved = ZonesHandler.moveCard(zcInfo, game);
//20180810 - 701.3d
detachAllAttachments(game);
return successfullyMoved;
diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java
index 696e98884e..0cba9775d2 100644
--- a/Mage/src/main/java/mage/players/PlayerImpl.java
+++ b/Mage/src/main/java/mage/players/PlayerImpl.java
@@ -1,6 +1,10 @@
package mage.players;
import com.google.common.collect.ImmutableMap;
+import java.io.Serializable;
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
import mage.ConditionalMana;
import mage.MageObject;
import mage.MageObjectReference;
@@ -66,11 +70,6 @@ import mage.util.GameLog;
import mage.util.RandomUtil;
import org.apache.log4j.Logger;
-import java.io.Serializable;
-import java.util.*;
-import java.util.Map.Entry;
-import java.util.stream.Collectors;
-
public abstract class PlayerImpl implements Player, Serializable {
private static final Logger logger = Logger.getLogger(PlayerImpl.class);
@@ -614,9 +613,9 @@ public abstract class PlayerImpl implements Player, Serializable {
&& this.hasOpponent(sourceControllerId, game)
&& game.getContinuousEffects().asThough(this.getId(), AsThoughEffectType.HEXPROOF, null, sourceControllerId, game) == null
&& abilities.stream()
- .filter(HexproofBaseAbility.class::isInstance)
- .map(HexproofBaseAbility.class::cast)
- .anyMatch(ability -> ability.checkObject(source, game))) {
+ .filter(HexproofBaseAbility.class::isInstance)
+ .map(HexproofBaseAbility.class::cast)
+ .anyMatch(ability -> ability.checkObject(source, game))) {
return false;
}
@@ -656,7 +655,7 @@ public abstract class PlayerImpl implements Player, Serializable {
game.informPlayers(getLogName() + " discards down to "
+ this.maxHandSize
+ (this.maxHandSize == 1
- ? " hand card" : " hand cards"));
+ ? " hand card" : " hand cards"));
}
discard(hand.size() - this.maxHandSize, false, null, game);
}
@@ -805,7 +804,7 @@ public abstract class PlayerImpl implements Player, Serializable {
}
GameEvent gameEvent = GameEvent.getEvent(GameEvent.EventType.DISCARD_CARD,
card.getId(), source == null
- ? null : source.getSourceId(), playerId);
+ ? null : source.getSourceId(), playerId);
gameEvent.setFlag(source != null); // event from effect or from cost (source == null)
if (game.replaceEvent(gameEvent, source)) {
return false;
@@ -1842,9 +1841,9 @@ public abstract class PlayerImpl implements Player, Serializable {
}
private List getPermanentsThatCanBeUntapped(Game game,
- List canBeUntapped,
- RestrictionUntapNotMoreThanEffect handledEffect,
- Map>, Integer> notMoreThanEffectsUsage) {
+ List canBeUntapped,
+ RestrictionUntapNotMoreThanEffect handledEffect,
+ Map>, Integer> notMoreThanEffectsUsage) {
List leftForUntap = new ArrayList<>();
// select permanents that can still be untapped
for (Permanent permanent : canBeUntapped) {
@@ -2553,7 +2552,7 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean searchLibrary(TargetCardInLibrary target, Ability source, Game game, UUID targetPlayerId,
- boolean triggerEvents) {
+ boolean triggerEvents) {
//20091005 - 701.14c
Library searchedLibrary = null;
String searchInfo = null;
@@ -2755,7 +2754,7 @@ public abstract class PlayerImpl implements Player, Serializable {
/**
* @param game
* @param appliedEffects
- * @param numSides Number of sides the dice has
+ * @param numSides Number of sides the dice has
* @return the number that the player rolled
*/
@Override
@@ -2792,16 +2791,16 @@ public abstract class PlayerImpl implements Player, Serializable {
/**
* @param game
* @param appliedEffects
- * @param numberChaosSides The number of chaos sides the planar die
- * currently has (normally 1 but can be 5)
+ * @param numberChaosSides The number of chaos sides the planar die
+ * currently has (normally 1 but can be 5)
* @param numberPlanarSides The number of chaos sides the planar die
- * currently has (normally 1)
+ * currently has (normally 1)
* @return the outcome that the player rolled. Either ChaosRoll, PlanarRoll
* or NilRoll
*/
@Override
public PlanarDieRoll rollPlanarDie(Game game, List appliedEffects, int numberChaosSides,
- int numberPlanarSides) {
+ int numberPlanarSides) {
int result = RandomUtil.nextInt(9) + 1;
PlanarDieRoll roll = PlanarDieRoll.NIL_ROLL;
if (numberChaosSides + numberPlanarSides > 9) {
@@ -2958,7 +2957,8 @@ public abstract class PlayerImpl implements Player, Serializable {
/**
* @param ability
- * @param availableMana if null, it won't be checked if enough mana is available
+ * @param availableMana if null, it won't be checked if enough mana is
+ * available
* @param sourceObject
* @param game
* @return
@@ -3290,6 +3290,17 @@ public abstract class PlayerImpl implements Player, Serializable {
return getPlayable(game, hidden, Zone.ALL, true);
}
+ /**
+ * Returns a list of all available spells and abilities the player can
+ * currently cast/activate with his available ressources
+ *
+ * @param game
+ * @param hidden also from hidden objects (e.g. turned face down cards ?)
+ * @param fromZone of objects from which zone (ALL = from all zones)
+ * @param hideDuplicatedAbilities if equal abilities exist return only the
+ * first instance
+ * @return
+ */
public List getPlayable(Game game, boolean hidden, Zone fromZone, boolean hideDuplicatedAbilities) {
List playable = new ArrayList<>();
if (shouldSkipGettingPlayable(game)) {
@@ -3655,7 +3666,7 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean canPaySacrificeCost(Permanent permanent, UUID sourceId,
- UUID controllerId, Game game
+ UUID controllerId, Game game
) {
return sacrificeCostFilter == null || !sacrificeCostFilter.match(permanent, sourceId, controllerId, game);
}
@@ -3808,8 +3819,8 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean moveCards(Card card, Zone toZone,
- Ability source, Game game,
- boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects
+ Ability source, Game game,
+ boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects
) {
Set cardList = new HashSet<>();
if (card != null) {
@@ -3820,22 +3831,22 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean moveCards(Cards cards, Zone toZone,
- Ability source, Game game
+ Ability source, Game game
) {
return moveCards(cards.getCards(game), toZone, source, game);
}
@Override
public boolean moveCards(Set cards, Zone toZone,
- Ability source, Game game
+ Ability source, Game game
) {
return moveCards(cards, toZone, source, game, false, false, false, null);
}
@Override
public boolean moveCards(Set cards, Zone toZone,
- Ability source, Game game,
- boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects
+ Ability source, Game game,
+ boolean tapped, boolean faceDown, boolean byOwner, List appliedEffects
) {
if (cards.isEmpty()) {
return true;
@@ -3937,8 +3948,8 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean moveCardsToExile(Card card, Ability source,
- Game game, boolean withName, UUID exileId,
- String exileZoneName
+ Game game, boolean withName, UUID exileId,
+ String exileZoneName
) {
Set cards = new HashSet<>();
cards.add(card);
@@ -3947,8 +3958,8 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean moveCardsToExile(Set cards, Ability source,
- Game game, boolean withName, UUID exileId,
- String exileZoneName
+ Game game, boolean withName, UUID exileId,
+ String exileZoneName
) {
if (cards.isEmpty()) {
return true;
@@ -3964,14 +3975,14 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean moveCardToHandWithInfo(Card card, UUID sourceId,
- Game game
+ Game game
) {
return this.moveCardToHandWithInfo(card, sourceId, game, true);
}
@Override
public boolean moveCardToHandWithInfo(Card card, UUID sourceId,
- Game game, boolean withName
+ Game game, boolean withName
) {
boolean result = false;
Zone fromZone = game.getState().getZone(card.getId());
@@ -3996,7 +4007,7 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public Set moveCardsToGraveyardWithInfo(Set allCards, Ability source,
- Game game, Zone fromZone
+ Game game, Zone fromZone
) {
UUID sourceId = source == null ? null : source.getSourceId();
Set movedCards = new LinkedHashSet<>();
@@ -4004,7 +4015,7 @@ public abstract class PlayerImpl implements Player, Serializable {
// identify cards from one owner
Cards cards = new CardsImpl();
UUID ownerId = null;
- for (Iterator it = allCards.iterator(); it.hasNext(); ) {
+ for (Iterator it = allCards.iterator(); it.hasNext();) {
Card card = it.next();
if (cards.isEmpty()) {
ownerId = card.getOwnerId();
@@ -4067,7 +4078,7 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean moveCardToGraveyardWithInfo(Card card, UUID sourceId,
- Game game, Zone fromZone
+ Game game, Zone fromZone
) {
if (card == null) {
return false;
@@ -4096,8 +4107,8 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean moveCardToLibraryWithInfo(Card card, UUID sourceId,
- Game game, Zone fromZone,
- boolean toTop, boolean withName
+ Game game, Zone fromZone,
+ boolean toTop, boolean withName
) {
if (card == null) {
return false;
@@ -4162,7 +4173,7 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override
public boolean moveCardToExileWithInfo(Card card, UUID exileId, String exileName, UUID sourceId,
- Game game, Zone fromZone, boolean withName) {
+ Game game, Zone fromZone, boolean withName) {
if (card == null) {
return false;
}
@@ -4185,7 +4196,7 @@ public abstract class PlayerImpl implements Player, Serializable {
game.informPlayers(this.getLogName() + " moves " + (withName ? card.getLogName()
+ (card.isCopy() ? " (Copy)" : "") : "a card face down") + ' '
+ (fromZone != null ? "from " + fromZone.toString().toLowerCase(Locale.ENGLISH)
- + ' ' : "") + "to the exile zone");
+ + ' ' : "") + "to the exile zone");
}
result = true;
diff --git a/Mage/src/main/java/mage/target/Target.java b/Mage/src/main/java/mage/target/Target.java
index 267c97558b..87c6e89c07 100644
--- a/Mage/src/main/java/mage/target/Target.java
+++ b/Mage/src/main/java/mage/target/Target.java
@@ -52,6 +52,8 @@ public interface Target extends Serializable {
boolean canTarget(UUID id, Ability source, Game game);
+ boolean stillLegalTarget(UUID id, Ability source, Game game);
+
boolean canTarget(UUID playerId, UUID id, Ability source, Game game);
boolean isLegal(Ability source, Game game);
diff --git a/Mage/src/main/java/mage/target/TargetImpl.java b/Mage/src/main/java/mage/target/TargetImpl.java
index 9d0eaebd0d..c7e656c70e 100644
--- a/Mage/src/main/java/mage/target/TargetImpl.java
+++ b/Mage/src/main/java/mage/target/TargetImpl.java
@@ -12,7 +12,14 @@ import mage.game.events.GameEvent.EventType;
import mage.players.Player;
import mage.util.RandomUtil;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
/**
* @author BetaSteward_at_googlemail.com
@@ -337,7 +344,7 @@ public abstract class TargetImpl implements Target {
illegalTargets.add(targetId);
continue;
}
- if (!canTarget(targetId, source, game)) {
+ if (!stillLegalTarget(targetId, source, game)) {
illegalTargets.add(targetId);
}
}
@@ -473,6 +480,11 @@ public abstract class TargetImpl implements Target {
return null;
}
+ @Override
+ public boolean stillLegalTarget(UUID id, Ability source, Game game) {
+ return canTarget(id, source, game);
+ }
+
@Override
public void setNotTarget(boolean notTarget) {
this.notTarget = notTarget;